Applied fixed from cras to fix crashes in irssi.
[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 #ifdef SILC_DEBUG
161     memset(sb->head, 'F', sb->truelen);
162 #endif
163     silc_free(sb->head);
164     silc_free(sb);
165   }
166 }
167
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. */
172
173 static inline
174 void silc_buffer_set(SilcBuffer sb, unsigned char *data, SilcUInt32 data_len)
175 {
176   sb->data = sb->head = data;
177   sb->tail = sb->end = data + data_len;
178   sb->len = sb->truelen = data_len;
179 }
180
181 /* Pulls current data area towards end. The length of the currently
182    valid data area is also decremented. Returns pointer to the data
183    area before pulling. 
184
185    Example:
186    ---------------------------------
187    | head  | data       | tail     |
188    ---------------------------------
189            ^
190            Pulls the start of the data area.
191
192    ---------------------------------
193    | head     | data    | tail     |
194    ---------------------------------
195            ^
196 */
197
198 static inline 
199 unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
200 {
201   unsigned char *old_data = sb->data;
202
203 #ifdef SILC_DEBUG
204   assert(len <= (SilcUInt32)(sb->tail - sb->data));
205 #endif
206
207   sb->data += len;
208   sb->len -= len;
209
210   return old_data;
211 }
212
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. 
216
217    Example:
218    ---------------------------------
219    | head     | data    | tail     |
220    ---------------------------------
221               ^
222               Pushes the start of the data area.
223
224    ---------------------------------
225    | head  | data       | tail     |
226    ---------------------------------
227               ^
228 */
229
230 static inline 
231 unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len)
232 {
233   unsigned char *old_data = sb->data;
234
235 #ifdef SILC_DEBUG
236   assert((sb->data - len) >= sb->head);
237 #endif
238
239   sb->data -= len;
240   sb->len += len;
241
242   return old_data;
243 }
244
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 
247    before pulling.
248
249    Example:
250    ---------------------------------
251    | head  | data       | tail     |
252    ---------------------------------
253                         ^
254                         Pulls the start of the tail section.
255
256    ---------------------------------
257    | head  | data           | tail |
258    ---------------------------------
259                         ^
260 */
261
262 static inline 
263 unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
264 {
265   unsigned char *old_tail = sb->tail;
266
267 #ifdef SILC_DEBUG
268   assert((SilcUInt32)(sb->end - sb->tail) >= len);
269 #endif
270
271   sb->tail += len;
272   sb->len += len;
273
274   return old_tail;
275 }
276
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. 
280
281    Example:
282    ---------------------------------
283    | head  | data           | tail |
284    ---------------------------------
285                             ^
286                             Pushes the start of the tail section.
287
288    ---------------------------------
289    | head  | data       | tail     |
290    ---------------------------------
291                             ^
292 */
293
294 static inline
295 unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len)
296 {
297   unsigned char *old_tail = sb->tail;
298
299 #ifdef SILC_DEBUG
300   assert((sb->tail - len) >= sb->data);
301 #endif
302
303   sb->tail -= len;
304   sb->len -= len;
305
306   return old_tail;
307 }
308
309 /* Puts data at the head of the buffer. Returns pointer to the copied
310    data area. 
311    
312    Example:
313    ---------------------------------
314    | head  | data       | tail     |
315    ---------------------------------
316    ^
317    Puts data to the head section. 
318 */
319
320 static inline
321 unsigned char *silc_buffer_put_head(SilcBuffer sb, 
322                                     const unsigned char *data,
323                                     SilcUInt32 len)
324 {
325 #ifdef SILC_DEBUG
326   assert((SilcUInt32)(sb->data - sb->head) >= len);
327 #endif
328   return (unsigned char *)memcpy(sb->head, data, len);
329 }
330
331 /* Puts data at the start of the valid data area. Returns a pointer 
332    to the copied data area. 
333
334    Example:
335    ---------------------------------
336    | head  | data       | tail     |
337    ---------------------------------
338            ^
339            Puts data to the data section.
340 */
341
342 static inline
343 unsigned char *silc_buffer_put(SilcBuffer sb, 
344                                const unsigned char *data,
345                                SilcUInt32 len)
346 {
347 #ifdef SILC_DEBUG
348   assert((SilcUInt32)(sb->tail - sb->data) >= len);
349 #endif
350   return (unsigned char *)memcpy(sb->data, data, len);
351 }
352
353 /* Puts data at the tail of the buffer. Returns pointer to the copied
354    data area. 
355
356    Example:
357    ---------------------------------
358    | head  | data           | tail |
359    ---------------------------------
360                             ^
361                             Puts data to the tail section.
362 */
363
364 static inline
365 unsigned char *silc_buffer_put_tail(SilcBuffer sb, 
366                                     const unsigned char *data,
367                                     SilcUInt32 len)
368 {
369 #ifdef SILC_DEBUG
370   assert((SilcUInt32)(sb->end - sb->tail) >= len);
371 #endif
372   return (unsigned char *)memcpy(sb->tail, data, len);
373 }
374
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. */
378
379 static inline
380 SilcBuffer silc_buffer_alloc_size(SilcUInt32 len)
381 {
382   SilcBuffer sb = silc_buffer_alloc(len);
383   if (!sb)
384     return NULL;
385   silc_buffer_pull_tail(sb, len);
386   return sb;
387 }
388
389 /* Clears and initialiazes the buffer to the state as if it was just
390    allocated by silc_buffer_alloc. */
391
392 static inline
393 void silc_buffer_clear(SilcBuffer sb)
394 {
395   memset(sb->head, 0, sb->truelen);
396   sb->data = sb->head;
397   sb->tail = sb->head;
398   sb->len = 0;
399 }
400
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. */
404
405 static inline
406 SilcBuffer silc_buffer_copy(SilcBuffer sb)
407 {
408   SilcBuffer sb_new;
409
410   sb_new = silc_buffer_alloc_size(sb->len);
411   if (!sb_new)
412     return NULL;
413   silc_buffer_put(sb_new, sb->data, sb->len);
414
415   return sb_new;
416 }
417
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. */
421
422 static inline
423 SilcBuffer silc_buffer_clone(SilcBuffer sb)
424 {
425   SilcBuffer sb_new;
426
427   sb_new = silc_buffer_alloc_size(sb->truelen);
428   if (!sb_new)
429     return NULL;
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;
434
435   return sb_new;
436 }
437
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. */
441
442 static inline
443 SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize)
444 {
445   SilcBuffer sb_new;
446
447   if (!sb)
448     return silc_buffer_alloc(newsize);
449
450   if (newsize <= sb->truelen)
451     return sb;
452
453   sb_new = silc_buffer_alloc_size(newsize);
454   if (!sb_new)
455     return NULL;
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;
460
461   silc_buffer_free(sb);
462
463   return sb_new;
464 }
465
466 #endif