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)
161 memset(sb->head, 'F', sb->truelen);
168 /* Sets the `data' and `data_len' to the buffer pointer sent as argument.
169 The data area is automatically set to the `data_len'. This function
170 can be used to set the data to static buffer without needing any
171 memory allocations. The `data' will not be copied to the buffer. */
174 void silc_buffer_set(SilcBuffer sb, unsigned char *data, SilcUInt32 data_len)
176 sb->data = sb->head = data;
177 sb->tail = sb->end = data + data_len;
178 sb->len = sb->truelen = data_len;
181 /* Pulls current data area towards end. The length of the currently
182 valid data area is also decremented. Returns pointer to the data
186 ---------------------------------
187 | head | data | tail |
188 ---------------------------------
190 Pulls the start of the data area.
192 ---------------------------------
193 | head | data | tail |
194 ---------------------------------
199 unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
201 unsigned char *old_data = sb->data;
204 assert(len <= (SilcUInt32)(sb->tail - sb->data));
213 /* Pushes current data area towards beginning. Length of the currently
214 valid data area is also incremented. Returns a pointer to the
215 data area before pushing.
218 ---------------------------------
219 | head | data | tail |
220 ---------------------------------
222 Pushes the start of the data area.
224 ---------------------------------
225 | head | data | tail |
226 ---------------------------------
231 unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len)
233 unsigned char *old_data = sb->data;
236 assert((sb->data - len) >= sb->head);
245 /* Pulls current tail section towards end. Length of the current valid
246 data area is also incremented. Returns a pointer to the data area
250 ---------------------------------
251 | head | data | tail |
252 ---------------------------------
254 Pulls the start of the tail section.
256 ---------------------------------
257 | head | data | tail |
258 ---------------------------------
263 unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
265 unsigned char *old_tail = sb->tail;
268 assert((SilcUInt32)(sb->end - sb->tail) >= len);
277 /* Pushes current tail section towards beginning. Length of the current
278 valid data area is also decremented. Returns a pointer to the
279 tail section before pushing.
282 ---------------------------------
283 | head | data | tail |
284 ---------------------------------
286 Pushes the start of the tail section.
288 ---------------------------------
289 | head | data | tail |
290 ---------------------------------
295 unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len)
297 unsigned char *old_tail = sb->tail;
300 assert((sb->tail - len) >= sb->data);
309 /* Puts data at the head of the buffer. Returns pointer to the copied
313 ---------------------------------
314 | head | data | tail |
315 ---------------------------------
317 Puts data to the head section.
321 unsigned char *silc_buffer_put_head(SilcBuffer sb,
322 const unsigned char *data,
326 assert((SilcUInt32)(sb->data - sb->head) >= len);
328 return (unsigned char *)memcpy(sb->head, data, len);
331 /* Puts data at the start of the valid data area. Returns a pointer
332 to the copied data area.
335 ---------------------------------
336 | head | data | tail |
337 ---------------------------------
339 Puts data to the data section.
343 unsigned char *silc_buffer_put(SilcBuffer sb,
344 const unsigned char *data,
348 assert((SilcUInt32)(sb->tail - sb->data) >= len);
350 return (unsigned char *)memcpy(sb->data, data, len);
353 /* Puts data at the tail of the buffer. Returns pointer to the copied
357 ---------------------------------
358 | head | data | tail |
359 ---------------------------------
361 Puts data to the tail section.
365 unsigned char *silc_buffer_put_tail(SilcBuffer sb,
366 const unsigned char *data,
370 assert((SilcUInt32)(sb->end - sb->tail) >= len);
372 return (unsigned char *)memcpy(sb->tail, data, len);
375 /* Allocates `len' bytes size buffer and moves the tail area automaticlly
376 `len' bytes so that the buffer is ready to use without calling the
377 silc_buffer_pull_tail. */
380 SilcBuffer silc_buffer_alloc_size(SilcUInt32 len)
382 SilcBuffer sb = silc_buffer_alloc(len);
385 silc_buffer_pull_tail(sb, len);
389 /* Clears and initialiazes the buffer to the state as if it was just
390 allocated by silc_buffer_alloc. */
393 void silc_buffer_clear(SilcBuffer sb)
395 memset(sb->head, 0, sb->truelen);
401 /* Generates copy of a SilcBuffer. This copies everything inside the
402 currently valid data area, nothing more. Use silc_buffer_clone to
403 copy entire buffer. */
406 SilcBuffer silc_buffer_copy(SilcBuffer sb)
410 sb_new = silc_buffer_alloc_size(sb->len);
413 silc_buffer_put(sb_new, sb->data, sb->len);
418 /* Clones SilcBuffer. This generates new SilcBuffer and copies
419 everything from the source buffer. The result is exact clone of
420 the original buffer. */
423 SilcBuffer silc_buffer_clone(SilcBuffer sb)
427 sb_new = silc_buffer_alloc_size(sb->truelen);
430 silc_buffer_put(sb_new, sb->head, sb->truelen);
431 sb_new->data = sb_new->head + (sb->data - sb->head);
432 sb_new->tail = sb_new->data + sb->len;
433 sb_new->len = sb->len;
438 /* Reallocates buffer. Old data is saved into the new buffer. Returns
439 new SilcBuffer pointer. The buffer is exact clone of the old one
440 except that there is now more space at the end of buffer. */
443 SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize)
448 return silc_buffer_alloc(newsize);
450 if (newsize <= sb->truelen)
453 sb_new = silc_buffer_alloc_size(newsize);
456 silc_buffer_put(sb_new, sb->head, sb->truelen);
457 sb_new->data = sb_new->head + (sb->data - sb->head);
458 sb_new->tail = sb_new->data + sb->len;
459 sb_new->len = sb->len;
461 silc_buffer_free(sb);