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. */
28 SilcBuffer is very simple and easy to use, yet you can do to the
29 buffer almost anything you want with its method functions. The buffer
30 is constructed of four different data sections that in whole creates
31 the allocated data area. Following short description of the fields
36 True length of the buffer. This is set at the allocation of the
37 buffer and it should not be touched after that. This field should
38 be considered read-only.
42 Length of the currently valid data area. Tells the length of the
43 data at the buffer. This is set to zero at the allocation of the
44 buffer and should not be updated by hand. Method functions of the
45 buffer automatically updates this field. However, it is not
46 read-only field and can be updated manually if necessary.
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.
55 --------------------------------
56 | head | data | tail |
57 --------------------------------
60 Current head section in the buffer is sb->data - sb->head.
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.
67 --------------------------------
68 | head | data | tail |
69 --------------------------------
72 Current valid data area in the buffer is sb->tail - sb->data.
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.
79 --------------------------------
80 | head | data | tail |
81 --------------------------------
84 Current tail section in the buffer is sb->end - sb->tail.
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
93 --------------------------------
94 | head | data | tail |
95 --------------------------------
98 Length of the entire buffer is (ie. truelen) sb->end - sb->head.
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 thus the data section
108 This buffer scheme is based on Linux kernel's Socket Buffer, the
109 idea were taken directly from there and credits should go there.
120 } *SilcBuffer, SilcBufferStruct;
124 /* Returns the true length of the buffer. This is used to pull
125 the buffer area to the end of the buffer. */
126 #define SILC_BUFFER_END(x) ((x)->end - (x)->head)
128 /* Inline functions */
131 SilcBuffer silc_buffer_alloc(SilcUInt32 len)
135 /* Allocate new SilcBuffer */
136 sb = (SilcBuffer)silc_calloc(1, sizeof(*sb));
140 /* Allocate the actual data area */
141 sb->head = (unsigned char *)silc_calloc(len, sizeof(*sb->head));
145 /* Set pointers to the new buffer */
149 sb->end = sb->head + sb->truelen;
154 /* Free's a SilcBuffer */
157 void silc_buffer_free(SilcBuffer sb)
160 memset(sb->head, 'F', sb->truelen);
166 /* Sets the `data' and `data_len' to the buffer pointer sent as argument.
167 The data area is automatically set to the `data_len'. This function
168 can be used to set the data to static buffer without needing any
169 memory allocations. The `data' will not be copied to the buffer. */
172 void silc_buffer_set(SilcBuffer sb, unsigned char *data, SilcUInt32 data_len)
174 sb->data = sb->head = data;
175 sb->tail = sb->end = data + data_len;
176 sb->len = sb->truelen = data_len;
179 /* Pulls current data area towards end. The length of the currently
180 valid data area is also decremented. Returns pointer to the data
184 ---------------------------------
185 | head | data | tail |
186 ---------------------------------
188 Pulls the start of the data area.
190 ---------------------------------
191 | head | data | tail |
192 ---------------------------------
197 unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
199 unsigned char *old_data = sb->data;
202 assert(len <= (SilcUInt32)(sb->tail - sb->data));
211 /* Pushes current data area towards beginning. Length of the currently
212 valid data area is also incremented. Returns a pointer to the
213 data area before pushing.
216 ---------------------------------
217 | head | data | tail |
218 ---------------------------------
220 Pushes the start of the data area.
222 ---------------------------------
223 | head | data | tail |
224 ---------------------------------
229 unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len)
231 unsigned char *old_data = sb->data;
234 assert((sb->data - len) >= sb->head);
243 /* Pulls current tail section towards end. Length of the current valid
244 data area is also incremented. Returns a pointer to the data area
248 ---------------------------------
249 | head | data | tail |
250 ---------------------------------
252 Pulls the start of the tail section.
254 ---------------------------------
255 | head | data | tail |
256 ---------------------------------
261 unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
263 unsigned char *old_tail = sb->tail;
266 assert((SilcUInt32)(sb->end - sb->tail) >= len);
275 /* Pushes current tail section towards beginning. Length of the current
276 valid data area is also decremented. Returns a pointer to the
277 tail section before pushing.
280 ---------------------------------
281 | head | data | tail |
282 ---------------------------------
284 Pushes the start of the tail section.
286 ---------------------------------
287 | head | data | tail |
288 ---------------------------------
293 unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len)
295 unsigned char *old_tail = sb->tail;
298 assert((sb->tail - len) >= sb->data);
307 /* Puts data at the head of the buffer. Returns pointer to the copied
311 ---------------------------------
312 | head | data | tail |
313 ---------------------------------
315 Puts data to the head section.
319 unsigned char *silc_buffer_put_head(SilcBuffer sb,
320 const unsigned char *data,
324 assert((SilcUInt32)(sb->data - sb->head) >= len);
326 return (unsigned char *)memcpy(sb->head, data, len);
329 /* Puts data at the start of the valid data area. Returns a pointer
330 to the copied data area.
333 ---------------------------------
334 | head | data | tail |
335 ---------------------------------
337 Puts data to the data section.
341 unsigned char *silc_buffer_put(SilcBuffer sb,
342 const unsigned char *data,
346 assert((SilcUInt32)(sb->tail - sb->data) >= len);
348 return (unsigned char *)memcpy(sb->data, data, len);
351 /* Puts data at the tail of the buffer. Returns pointer to the copied
355 ---------------------------------
356 | head | data | tail |
357 ---------------------------------
359 Puts data to the tail section.
363 unsigned char *silc_buffer_put_tail(SilcBuffer sb,
364 const unsigned char *data,
368 assert((SilcUInt32)(sb->end - sb->tail) >= len);
370 return (unsigned char *)memcpy(sb->tail, data, len);
373 /* Allocates `len' bytes size buffer and moves the tail area automaticlly
374 `len' bytes so that the buffer is ready to use without calling the
375 silc_buffer_pull_tail. */
378 SilcBuffer silc_buffer_alloc_size(SilcUInt32 len)
380 SilcBuffer sb = silc_buffer_alloc(len);
383 silc_buffer_pull_tail(sb, len);
387 /* Clears and initialiazes the buffer to the state as if it was just
388 allocated by silc_buffer_alloc. */
391 void silc_buffer_clear(SilcBuffer sb)
393 memset(sb->head, 0, sb->truelen);
399 /* Generates copy of a SilcBuffer. This copies everything inside the
400 currently valid data area, nothing more. Use silc_buffer_clone to
401 copy entire buffer. */
404 SilcBuffer silc_buffer_copy(SilcBuffer sb)
408 sb_new = silc_buffer_alloc_size(sb->len);
411 silc_buffer_put(sb_new, sb->data, sb->len);
416 /* Clones SilcBuffer. This generates new SilcBuffer and copies
417 everything from the source buffer. The result is exact clone of
418 the original buffer. */
421 SilcBuffer silc_buffer_clone(SilcBuffer sb)
425 sb_new = silc_buffer_alloc_size(sb->truelen);
428 silc_buffer_put(sb_new, sb->head, sb->truelen);
429 sb_new->data = sb_new->head + (sb->data - sb->head);
430 sb_new->tail = sb_new->data + sb->len;
431 sb_new->len = sb->len;
436 /* Reallocates buffer. Old data is saved into the new buffer. Returns
437 new SilcBuffer pointer. The buffer is exact clone of the old one
438 except that there is now more space at the end of buffer. */
441 SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize)
446 return silc_buffer_alloc(newsize);
448 if (newsize <= sb->truelen)
451 sb_new = silc_buffer_alloc_size(newsize);
454 silc_buffer_put(sb_new, sb->head, sb->truelen);
455 sb_new->data = sb_new->head + (sb->data - sb->head);
456 sb_new->tail = sb_new->data + sb->len;
457 sb_new->len = sb->len;
459 silc_buffer_free(sb);