updates.
[crypto.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    SilcUInt32 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    SilcUInt32 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   SilcUInt32 truelen;
115   SilcUInt32 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(SilcUInt32 len)
132 {
133   SilcBuffer sb;
134
135   /* Allocate new SilcBuffer */
136   sb = (SilcBuffer)silc_calloc(1, sizeof(*sb));
137   if (!sb)
138     return NULL;
139
140   /* Allocate the actual data area */
141   sb->head = (unsigned char *)silc_calloc(len, sizeof(*sb->head));
142   if (!sb->head)
143     return NULL;
144
145   /* Set pointers to the new buffer */
146   sb->truelen = len;
147   sb->data = sb->head;
148   sb->tail = sb->head;
149   sb->end = sb->head + sb->truelen;
150
151   return sb;
152 }
153
154 /* Free's a SilcBuffer */
155
156 static inline
157 void silc_buffer_free(SilcBuffer sb)
158 {
159   if (sb) {
160     memset(sb->head, 'F', sb->truelen);
161     silc_free(sb->head);
162     silc_free(sb);
163   }
164 }
165
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. */
170
171 static inline
172 void silc_buffer_set(SilcBuffer sb, unsigned char *data, SilcUInt32 data_len)
173 {
174   sb->data = sb->head = data;
175   sb->tail = sb->end = data + data_len;
176   sb->len = sb->truelen = data_len;
177 }
178
179 /* Pulls current data area towards end. The length of the currently
180    valid data area is also decremented. Returns pointer to the data
181    area before pulling. 
182
183    Example:
184    ---------------------------------
185    | head  | data       | tail     |
186    ---------------------------------
187            ^
188            Pulls the start of the data area.
189
190    ---------------------------------
191    | head     | data    | tail     |
192    ---------------------------------
193            ^
194 */
195
196 static inline 
197 unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
198 {
199   unsigned char *old_data = sb->data;
200
201 #ifdef SILC_DEBUG
202   assert(len <= (SilcUInt32)(sb->tail - sb->data));
203 #endif
204
205   sb->data += len;
206   sb->len -= len;
207
208   return old_data;
209 }
210
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. 
214
215    Example:
216    ---------------------------------
217    | head     | data    | tail     |
218    ---------------------------------
219               ^
220               Pushes the start of the data area.
221
222    ---------------------------------
223    | head  | data       | tail     |
224    ---------------------------------
225               ^
226 */
227
228 static inline 
229 unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len)
230 {
231   unsigned char *old_data = sb->data;
232
233 #ifdef SILC_DEBUG
234   assert((sb->data - len) >= sb->head);
235 #endif
236
237   sb->data -= len;
238   sb->len += len;
239
240   return old_data;
241 }
242
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 
245    before pulling.
246
247    Example:
248    ---------------------------------
249    | head  | data       | tail     |
250    ---------------------------------
251                         ^
252                         Pulls the start of the tail section.
253
254    ---------------------------------
255    | head  | data           | tail |
256    ---------------------------------
257                         ^
258 */
259
260 static inline 
261 unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
262 {
263   unsigned char *old_tail = sb->tail;
264
265 #ifdef SILC_DEBUG
266   assert((SilcUInt32)(sb->end - sb->tail) >= len);
267 #endif
268
269   sb->tail += len;
270   sb->len += len;
271
272   return old_tail;
273 }
274
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. 
278
279    Example:
280    ---------------------------------
281    | head  | data           | tail |
282    ---------------------------------
283                             ^
284                             Pushes the start of the tail section.
285
286    ---------------------------------
287    | head  | data       | tail     |
288    ---------------------------------
289                             ^
290 */
291
292 static inline
293 unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len)
294 {
295   unsigned char *old_tail = sb->tail;
296
297 #ifdef SILC_DEBUG
298   assert((sb->tail - len) >= sb->data);
299 #endif
300
301   sb->tail -= len;
302   sb->len -= len;
303
304   return old_tail;
305 }
306
307 /* Puts data at the head of the buffer. Returns pointer to the copied
308    data area. 
309    
310    Example:
311    ---------------------------------
312    | head  | data       | tail     |
313    ---------------------------------
314    ^
315    Puts data to the head section. 
316 */
317
318 static inline
319 unsigned char *silc_buffer_put_head(SilcBuffer sb, 
320                                     const unsigned char *data,
321                                     SilcUInt32 len)
322 {
323 #ifdef SILC_DEBUG
324   assert((SilcUInt32)(sb->data - sb->head) >= len);
325 #endif
326   return (unsigned char *)memcpy(sb->head, data, len);
327 }
328
329 /* Puts data at the start of the valid data area. Returns a pointer 
330    to the copied data area. 
331
332    Example:
333    ---------------------------------
334    | head  | data       | tail     |
335    ---------------------------------
336            ^
337            Puts data to the data section.
338 */
339
340 static inline
341 unsigned char *silc_buffer_put(SilcBuffer sb, 
342                                const unsigned char *data,
343                                SilcUInt32 len)
344 {
345 #ifdef SILC_DEBUG
346   assert((SilcUInt32)(sb->tail - sb->data) >= len);
347 #endif
348   return (unsigned char *)memcpy(sb->data, data, len);
349 }
350
351 /* Puts data at the tail of the buffer. Returns pointer to the copied
352    data area. 
353
354    Example:
355    ---------------------------------
356    | head  | data           | tail |
357    ---------------------------------
358                             ^
359                             Puts data to the tail section.
360 */
361
362 static inline
363 unsigned char *silc_buffer_put_tail(SilcBuffer sb, 
364                                     const unsigned char *data,
365                                     SilcUInt32 len)
366 {
367 #ifdef SILC_DEBUG
368   assert((SilcUInt32)(sb->end - sb->tail) >= len);
369 #endif
370   return (unsigned char *)memcpy(sb->tail, data, len);
371 }
372
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. */
376
377 static inline
378 SilcBuffer silc_buffer_alloc_size(SilcUInt32 len)
379 {
380   SilcBuffer sb = silc_buffer_alloc(len);
381   if (!sb)
382     return NULL;
383   silc_buffer_pull_tail(sb, len);
384   return sb;
385 }
386
387 /* Clears and initialiazes the buffer to the state as if it was just
388    allocated by silc_buffer_alloc. */
389
390 static inline
391 void silc_buffer_clear(SilcBuffer sb)
392 {
393   memset(sb->head, 0, sb->truelen);
394   sb->data = sb->head;
395   sb->tail = sb->head;
396   sb->len = 0;
397 }
398
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. */
402
403 static inline
404 SilcBuffer silc_buffer_copy(SilcBuffer sb)
405 {
406   SilcBuffer sb_new;
407
408   sb_new = silc_buffer_alloc_size(sb->len);
409   if (!sb_new)
410     return NULL;
411   silc_buffer_put(sb_new, sb->data, sb->len);
412
413   return sb_new;
414 }
415
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. */
419
420 static inline
421 SilcBuffer silc_buffer_clone(SilcBuffer sb)
422 {
423   SilcBuffer sb_new;
424
425   sb_new = silc_buffer_alloc_size(sb->truelen);
426   if (!sb_new)
427     return NULL;
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;
432
433   return sb_new;
434 }
435
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. */
439
440 static inline
441 SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize)
442 {
443   SilcBuffer sb_new;
444
445   if (!sb)
446     return silc_buffer_alloc(newsize);
447
448   if (newsize <= sb->truelen)
449     return sb;
450
451   sb_new = silc_buffer_alloc_size(newsize);
452   if (!sb_new)
453     return NULL;
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;
458
459   silc_buffer_free(sb);
460
461   return sb_new;
462 }
463
464 #endif