5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1998 - 2002 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
20 /* Optimized buffer managing routines. These are short inline functions. */
25 /****h* silcutil/SILC Buffer Interface
29 * SilcBuffer is very simple and easy to use, yet you can do to the
30 * buffer almost anything you want with its method functions. The buffer
31 * is constructed of four different data sections that in whole creates
32 * the allocated data area.
34 * This buffer scheme is based on Linux kernel's Socket Buffer, the
35 * idea were taken directly from there and credits should go there.
39 /****s* silcutil/SilcBufferAPI/SilcBuffer
43 * typedef struct { ... } *SilcBuffer, SilcBufferStruct;
47 * SILC Buffer object. Following short description of the fields
54 * True length of the buffer. This is set at the allocation of the
55 * buffer and it should not be touched after that. This field should
56 * be considered read-only.
60 * Length of the currently valid data area. Tells the length of the
61 * data at the buffer. This is set to zero at the allocation of the
62 * buffer and should not be updated by hand. Method functions of the
63 * buffer automatically updates this field. However, it is not
64 * read-only field and can be updated manually if necessary.
68 * Head of the allocated buffer. This is the start of the allocated
69 * data area and remains as same throughout the lifetime of the buffer.
70 * However, the end of the head area or the start of the currently valid
71 * data area is variable.
73 * --------------------------------
74 * | head | data | tail |
75 * --------------------------------
78 * Current head section in the buffer is sb->data - sb->head.
80 * unsigned char *data;
82 * Currently valid data area. This is the start of the currently valid
83 * main data area. The data area is variable in all directions.
85 * --------------------------------
86 * | head | data | tail |
87 * --------------------------------
90 * Current valid data area in the buffer is sb->tail - sb->data.
92 * unsigned char *tail;
94 * Tail of the buffer. This is the end of the currently valid data area
95 * or start of the tail area. The start of the tail area is variable.
97 * --------------------------------
98 * | head | data | tail |
99 * --------------------------------
102 * Current tail section in the buffer is sb->end - sb->tail.
104 * unsigned char *end;
106 * End of the allocated buffer. This is the end of the allocated data
107 * area and remains as same throughout the lifetime of the buffer.
108 * Usually this field is not needed except when checking the size
111 * --------------------------------
112 * | head | data | tail |
113 * --------------------------------
116 * Length of the entire buffer is (ie. truelen) sb->end - sb->head.
118 * Currently valid data area is considered to be the main data area in
119 * the buffer. However, the entire buffer is of course valid data and can
120 * be used as such. Usually head section of the buffer includes different
121 * kind of headers or similar. Data section includes the main data of
122 * the buffer. Tail section can be seen as a reserve space of the data
123 * section. Tail section can be pulled towards end, and thus the data
124 * section becomes larger.
135 } *SilcBuffer, SilcBufferStruct;
140 /****d* silcutil/SilcBufferAPI/SILC_BUFFER_END
144 * #define SILC_BUFFER_END(...)
148 * Returns the true length of the buffer. This is used to pull
149 * the buffer area to the end of the buffer.
153 #define SILC_BUFFER_END(x) ((x)->end - (x)->head)
156 /* Inline functions */
158 /****f* silcutil/SilcBufferAPI/silc_buffer_alloc
163 * SilcBuffer silc_buffer_alloc(SilcUInt32 len);
167 * Allocates new SilcBuffer and returns it.
172 SilcBuffer silc_buffer_alloc(SilcUInt32 len)
176 /* Allocate new SilcBuffer */
177 sb = (SilcBuffer)silc_calloc(1, sizeof(*sb));
181 /* Allocate the actual data area */
182 sb->head = (unsigned char *)silc_calloc(len, sizeof(*sb->head));
186 /* Set pointers to the new buffer */
190 sb->end = sb->head + sb->truelen;
195 /****f* silcutil/SilcBufferAPI/silc_buffer_free
200 * void silc_buffer_free(SilcBuffer sb);
209 void silc_buffer_free(SilcBuffer sb)
212 #if defined(SILC_DEBUG)
213 memset(sb->head, 'F', sb->truelen);
220 /****f* silcutil/SilcBufferAPI/silc_buffer_set
225 * void silc_buffer_set(SilcBuffer sb,
226 * unsigned char *data,
227 * SilcUInt32 data_len);
231 * Sets the `data' and `data_len' to the buffer pointer sent as argument.
232 * The data area is automatically set to the `data_len'. This function
233 * can be used to set the data to static buffer without needing any
234 * memory allocations. The `data' will not be copied to the buffer.
239 void silc_buffer_set(SilcBuffer sb, unsigned char *data, SilcUInt32 data_len)
241 sb->data = sb->head = data;
242 sb->tail = sb->end = data + data_len;
243 sb->len = sb->truelen = data_len;
246 /****f* silcutil/SilcBufferAPI/silc_buffer_pull
251 * unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len);
255 * Pulls current data area towards end. The length of the currently
256 * valid data area is also decremented. Returns pointer to the data
257 * area before pulling.
261 * ---------------------------------
262 * | head | data | tail |
263 * ---------------------------------
265 * Pulls the start of the data area.
267 * ---------------------------------
268 * | head | data | tail |
269 * ---------------------------------
274 unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
276 unsigned char *old_data = sb->data;
278 #if defined(SILC_DEBUG)
279 assert(len <= (SilcUInt32)(sb->tail - sb->data));
288 /****f* silcutil/SilcBufferAPI/silc_buffer_push
293 * unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len);
297 * Pushes current data area towards beginning. Length of the currently
298 * valid data area is also incremented. Returns a pointer to the
299 * data area before pushing.
303 * ---------------------------------
304 * | head | data | tail |
305 * ---------------------------------
307 * Pushes the start of the data area.
309 * ---------------------------------
310 * | head | data | tail |
311 * ---------------------------------
317 unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len)
319 unsigned char *old_data = sb->data;
321 #if defined(SILC_DEBUG)
322 assert((sb->data - len) >= sb->head);
331 /****f* silcutil/SilcBufferAPI/silc_buffer_pull_tail
336 * unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len);
340 * Pulls current tail section towards end. Length of the current valid
341 * data area is also incremented. Returns a pointer to the data area
346 * ---------------------------------
347 * | head | data | tail |
348 * ---------------------------------
350 * Pulls the start of the tail section.
352 * ---------------------------------
353 * | head | data | tail |
354 * ---------------------------------
360 unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
362 unsigned char *old_tail = sb->tail;
364 #if defined(SILC_DEBUG)
365 assert((SilcUInt32)(sb->end - sb->tail) >= len);
374 /****f* silcutil/SilcBufferAPI/silc_buffer_push_tail
379 * unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len);
383 * Pushes current tail section towards beginning. Length of the current
384 * valid data area is also decremented. Returns a pointer to the
385 * tail section before pushing.
389 * ---------------------------------
390 * | head | data | tail |
391 * ---------------------------------
393 * Pushes the start of the tail section.
395 * ---------------------------------
396 * | head | data | tail |
397 * ---------------------------------
403 unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len)
405 unsigned char *old_tail = sb->tail;
407 #if defined(SILC_DEBUG)
408 assert((sb->tail - len) >= sb->data);
417 /****f* silcutil/SilcBufferAPI/silc_buffer_put_head
422 * unsigned char *silc_buffer_put_head(SilcBuffer sb,
423 * const unsigned char *data,
428 * Puts data at the head of the buffer. Returns pointer to the copied
433 * ---------------------------------
434 * | head | data | tail |
435 * ---------------------------------
437 * Puts data to the head section.
442 unsigned char *silc_buffer_put_head(SilcBuffer sb,
443 const unsigned char *data,
446 #if defined(SILC_DEBUG)
447 assert((SilcUInt32)(sb->data - sb->head) >= len);
449 return (unsigned char *)memcpy(sb->head, data, len);
452 /****f* silcutil/SilcBufferAPI/silc_buffer_put
457 * unsigned char *silc_buffer_put(SilcBuffer sb,
458 * const unsigned char *data,
463 * Puts data at the start of the valid data area. Returns a pointer
464 * to the copied data area.
468 * ---------------------------------
469 * | head | data | tail |
470 * ---------------------------------
472 * Puts data to the data section.
477 unsigned char *silc_buffer_put(SilcBuffer sb,
478 const unsigned char *data,
481 #if defined(SILC_DEBUG)
482 assert((SilcUInt32)(sb->tail - sb->data) >= len);
484 return (unsigned char *)memcpy(sb->data, data, len);
487 /****f* silcutil/SilcBufferAPI/silc_buffer_put_tail
492 * unsigned char *silc_buffer_put_tail(SilcBuffer sb,
493 * const unsigned char *data,
498 * Puts data at the tail of the buffer. Returns pointer to the copied
503 * ---------------------------------
504 * | head | data | tail |
505 * ---------------------------------
507 * Puts data to the tail section.
512 unsigned char *silc_buffer_put_tail(SilcBuffer sb,
513 const unsigned char *data,
516 #if defined(SILC_DEBUG)
517 assert((SilcUInt32)(sb->end - sb->tail) >= len);
519 return (unsigned char *)memcpy(sb->tail, data, len);
522 /****f* silcutil/SilcBufferAPI/silc_buffer_alloc_size
527 * SilcBuffer silc_buffer_alloc_size(SilcUInt32 len);
531 * Allocates `len' bytes size buffer and moves the tail area automatically
532 * `len' bytes so that the buffer is ready to use without calling the
533 * silc_buffer_pull_tail.
538 SilcBuffer silc_buffer_alloc_size(SilcUInt32 len)
540 SilcBuffer sb = silc_buffer_alloc(len);
543 silc_buffer_pull_tail(sb, len);
547 /****f* silcutil/SilcBufferAPI/silc_buffer_clear
552 * void silc_buffer_clear(SilcBuffer sb);
556 * Clears and initialiazes the buffer to the state as if it was just
557 * allocated by silc_buffer_alloc.
562 void silc_buffer_clear(SilcBuffer sb)
564 memset(sb->head, 0, sb->truelen);
570 /****f* silcutil/SilcBufferAPI/silc_buffer_copy
575 * SilcBuffer silc_buffer_copy(SilcBuffer sb);
579 * Generates copy of a SilcBuffer. This copies everything inside the
580 * currently valid data area, nothing more. Use silc_buffer_clone to
581 * copy entire buffer.
586 SilcBuffer silc_buffer_copy(SilcBuffer sb)
590 sb_new = silc_buffer_alloc_size(sb->len);
593 silc_buffer_put(sb_new, sb->data, sb->len);
598 /****f* silcutil/SilcBufferAPI/silc_buffer_clone
603 * SilcBuffer silc_buffer_clone(SilcBuffer sb);
607 * Clones SilcBuffer. This generates new SilcBuffer and copies
608 * everything from the source buffer. The result is exact clone of
609 * the original buffer.
614 SilcBuffer silc_buffer_clone(SilcBuffer sb)
618 sb_new = silc_buffer_alloc_size(sb->truelen);
621 silc_buffer_put(sb_new, sb->head, sb->truelen);
622 sb_new->data = sb_new->head + (sb->data - sb->head);
623 sb_new->tail = sb_new->data + sb->len;
624 sb_new->len = sb->len;
629 /****f* silcutil/SilcBufferAPI/silc_buffer_realloc
634 * SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize);
638 * Reallocates buffer. Old data is saved into the new buffer. Returns
639 * new SilcBuffer pointer. The buffer is exact clone of the old one
640 * except that there is now more space at the end of buffer.
645 SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize)
650 return silc_buffer_alloc(newsize);
652 if (newsize <= sb->truelen)
655 sb_new = silc_buffer_alloc_size(newsize);
658 silc_buffer_put(sb_new, sb->head, sb->truelen);
659 sb_new->data = sb_new->head + (sb->data - sb->head);
660 sb_new->tail = sb_new->data + sb->len;
661 sb_new->len = sb->len;
663 silc_buffer_free(sb);