updates.
[silc.git] / lib / silcutil / silcbuffer.h
1 /*
2
3   silcbuffer.h 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1998 - 2002 Pekka Riikonen
8
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.
12
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.
17
18 */
19 /* $Id$ */
20 /* Optimized buffer managing routines.  These are short inline functions. */
21
22 #ifndef SILCBUFFER_H
23 #define SILCBUFFER_H
24
25 /* 
26    SILC Buffer object.
27
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
32    of the buffer.
33
34    uint32 truelen;
35
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.
39
40    uint32 len;
41
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.
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 thus the data section
106     becomes larger.
107
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.
110
111 */
112
113 typedef struct {
114   uint32 truelen;
115   uint32 len;
116   unsigned char *head;
117   unsigned char *data;
118   unsigned char *tail;
119   unsigned char *end;
120 } *SilcBuffer, SilcBufferStruct;
121
122 /* Macros */
123
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)
127
128 /* Inline functions */
129
130 static inline
131 SilcBuffer silc_buffer_alloc(uint32 len)
132 {
133   SilcBuffer sb;
134
135   /* Allocate new SilcBuffer */
136   sb = (SilcBuffer)silc_calloc(1, sizeof(*sb));
137
138   /* Allocate the actual data area */
139   sb->head = (unsigned char *)silc_calloc(len, sizeof(*sb->head));
140
141   /* Set pointers to the new buffer */
142   sb->truelen = len;
143   sb->data = sb->head;
144   sb->tail = sb->head;
145   sb->end = sb->head + sb->truelen;
146
147   return sb;
148 }
149
150 /* Free's a SilcBuffer */
151
152 static inline
153 void silc_buffer_free(SilcBuffer sb)
154 {
155   if (sb) {
156     memset(sb->head, 'F', sb->truelen);
157     silc_free(sb->head);
158     silc_free(sb);
159   }
160 }
161
162 /* Sets the `data' and `data_len' to the buffer pointer sent as argument.
163    The data area is automatically set to the `data_len'. This function
164    can be used to set the data to static buffer without needing any
165    memory allocations. The `data' will not be copied to the buffer. */
166
167 static inline
168 void silc_buffer_set(SilcBuffer sb, unsigned char *data, uint32 data_len)
169 {
170   sb->data = sb->head = data;
171   sb->tail = sb->end = data + data_len;
172   sb->len = sb->truelen = data_len;
173 }
174
175 /* Pulls current data area towards end. The length of the currently
176    valid data area is also decremented. Returns pointer to the data
177    area before pulling. 
178
179    Example:
180    ---------------------------------
181    | head  | data       | tail     |
182    ---------------------------------
183            ^
184            Pulls the start of the data area.
185
186    ---------------------------------
187    | head     | data    | tail     |
188    ---------------------------------
189            ^
190 */
191
192 static inline 
193 unsigned char *silc_buffer_pull(SilcBuffer sb, uint32 len)
194 {
195   unsigned char *old_data = sb->data;
196
197 #ifdef SILC_DEBUG
198   assert(len <= (uint32)(sb->tail - sb->data));
199 #endif
200
201   sb->data += len;
202   sb->len -= len;
203
204   return old_data;
205 }
206
207 /* Pushes current data area towards beginning. Length of the currently
208    valid data area is also incremented. Returns a pointer to the 
209    data area before pushing. 
210
211    Example:
212    ---------------------------------
213    | head     | data    | tail     |
214    ---------------------------------
215               ^
216               Pushes the start of the data area.
217
218    ---------------------------------
219    | head  | data       | tail     |
220    ---------------------------------
221               ^
222 */
223
224 static inline 
225 unsigned char *silc_buffer_push(SilcBuffer sb, uint32 len)
226 {
227   unsigned char *old_data = sb->data;
228
229 #ifdef SILC_DEBUG
230   assert((sb->data - len) >= sb->head);
231 #endif
232
233   sb->data -= len;
234   sb->len += len;
235
236   return old_data;
237 }
238
239 /* Pulls current tail section towards end. Length of the current valid
240    data area is also incremented. Returns a pointer to the data area 
241    before pulling.
242
243    Example:
244    ---------------------------------
245    | head  | data       | tail     |
246    ---------------------------------
247                         ^
248                         Pulls the start of the tail section.
249
250    ---------------------------------
251    | head  | data           | tail |
252    ---------------------------------
253                         ^
254 */
255
256 static inline 
257 unsigned char *silc_buffer_pull_tail(SilcBuffer sb, uint32 len)
258 {
259   unsigned char *old_tail = sb->tail;
260
261 #ifdef SILC_DEBUG
262   assert((uint32)(sb->end - sb->tail) >= len);
263 #endif
264
265   sb->tail += len;
266   sb->len += len;
267
268   return old_tail;
269 }
270
271 /* Pushes current tail section towards beginning. Length of the current
272    valid data area is also decremented. Returns a pointer to the 
273    tail section before pushing. 
274
275    Example:
276    ---------------------------------
277    | head  | data           | tail |
278    ---------------------------------
279                             ^
280                             Pushes the start of the tail section.
281
282    ---------------------------------
283    | head  | data       | tail     |
284    ---------------------------------
285                             ^
286 */
287
288 static inline
289 unsigned char *silc_buffer_push_tail(SilcBuffer sb, uint32 len)
290 {
291   unsigned char *old_tail = sb->tail;
292
293 #ifdef SILC_DEBUG
294   assert((sb->tail - len) >= sb->data);
295 #endif
296
297   sb->tail -= len;
298   sb->len -= len;
299
300   return old_tail;
301 }
302
303 /* Puts data at the head of the buffer. Returns pointer to the copied
304    data area. 
305    
306    Example:
307    ---------------------------------
308    | head  | data       | tail     |
309    ---------------------------------
310    ^
311    Puts data to the head section. 
312 */
313
314 static inline
315 unsigned char *silc_buffer_put_head(SilcBuffer sb, 
316                                     const unsigned char *data,
317                                     uint32 len)
318 {
319 #ifdef SILC_DEBUG
320   assert((uint32)(sb->data - sb->head) >= len);
321 #endif
322   return (unsigned char *)memcpy(sb->head, data, len);
323 }
324
325 /* Puts data at the start of the valid data area. Returns a pointer 
326    to the copied data area. 
327
328    Example:
329    ---------------------------------
330    | head  | data       | tail     |
331    ---------------------------------
332            ^
333            Puts data to the data section.
334 */
335
336 static inline
337 unsigned char *silc_buffer_put(SilcBuffer sb, 
338                                const unsigned char *data,
339                                uint32 len)
340 {
341 #ifdef SILC_DEBUG
342   assert((uint32)(sb->tail - sb->data) >= len);
343 #endif
344   return (unsigned char *)memcpy(sb->data, data, len);
345 }
346
347 /* Puts data at the tail of the buffer. Returns pointer to the copied
348    data area. 
349
350    Example:
351    ---------------------------------
352    | head  | data           | tail |
353    ---------------------------------
354                             ^
355                             Puts data to the tail section.
356 */
357
358 static inline
359 unsigned char *silc_buffer_put_tail(SilcBuffer sb, 
360                                     const unsigned char *data,
361                                     uint32 len)
362 {
363 #ifdef SILC_DEBUG
364   assert((uint32)(sb->end - sb->tail) >= len);
365 #endif
366   return (unsigned char *)memcpy(sb->tail, data, len);
367 }
368
369 /* Clears and initialiazes the buffer to the state as if it was just
370    allocated by silc_buffer_alloc. */
371
372 static inline
373 void silc_buffer_clear(SilcBuffer sb)
374 {
375   memset(sb->head, 0, sb->truelen);
376   sb->data = sb->head;
377   sb->tail = sb->head;
378   sb->len = 0;
379 }
380
381 /* Generates copy of a SilcBuffer. This copies everything inside the
382    currently valid data area, nothing more. Use silc_buffer_clone to
383    copy entire buffer. */
384
385 static inline
386 SilcBuffer silc_buffer_copy(SilcBuffer sb)
387 {
388   SilcBuffer sb_new;
389
390   sb_new = silc_buffer_alloc(sb->len);
391   silc_buffer_pull_tail(sb_new, SILC_BUFFER_END(sb_new));
392   silc_buffer_put(sb_new, sb->data, sb->len);
393
394   return sb_new;
395 }
396
397 /* Clones SilcBuffer. This generates new SilcBuffer and copies
398    everything from the source buffer. The result is exact clone of
399    the original buffer. */
400
401 static inline
402 SilcBuffer silc_buffer_clone(SilcBuffer sb)
403 {
404   SilcBuffer sb_new;
405
406   sb_new = silc_buffer_alloc(sb->truelen);
407   silc_buffer_pull_tail(sb_new, SILC_BUFFER_END(sb_new));
408   silc_buffer_put(sb_new, sb->head, sb->truelen);
409   sb_new->data = sb_new->head + (sb->data - sb->head);
410   sb_new->tail = sb_new->data + sb->len;
411   sb_new->len = sb->len;
412
413   return sb_new;
414 }
415
416 /* Reallocates buffer. Old data is saved into the new buffer. Returns
417    new SilcBuffer pointer. The buffer is exact clone of the old one
418    except that there is now more space at the end of buffer. */
419
420 static inline
421 SilcBuffer silc_buffer_realloc(SilcBuffer sb, uint32 newsize)
422 {
423   SilcBuffer sb_new;
424
425   if (!sb)
426     return silc_buffer_alloc(newsize);
427
428   if (newsize <= sb->truelen)
429     return sb;
430
431   sb_new = silc_buffer_alloc(newsize);
432   silc_buffer_pull_tail(sb_new, SILC_BUFFER_END(sb_new));
433   silc_buffer_put(sb_new, sb->head, sb->truelen);
434   sb_new->data = sb_new->head + (sb->data - sb->head);
435   sb_new->tail = sb_new->data + sb->len;
436   sb_new->len = sb->len;
437
438   silc_buffer_free(sb);
439
440   return sb_new;
441 }
442
443 #endif