b84630446ee9b9924d007b12c04bb2159c068dfc
[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 /****h* silcutil/SILC Buffer Interface
26  *
27  * DESCRIPTION
28  *
29  *    SilcBuffer is very simple and easy to use, yet you can do to the
30  *    buffer almost anything you want with its method functions. The buffer
31  *    is constructed of four different data sections that in whole creates
32  *    the allocated data area.
33  *
34  *    This buffer scheme is based on Linux kernel's Socket Buffer, the
35  *    idea were taken directly from there and credits should go there.
36  *
37  ***/
38
39 /****s* silcutil/SilcBufferAPI/SilcBuffer
40  *
41  * NAME
42  *
43  *    typedef struct { ... } *SilcBuffer, SilcBufferStruct;
44  *
45  * DESCRIPTION
46  *
47  *    SILC Buffer object. Following short description of the fields
48  *    of the buffer.
49  *
50  * EXAMPLE
51  *
52  *    SilcUInt32 truelen;
53  *
54  *        True length of the buffer. This is set at the allocation of the
55  *        buffer and it should not be touched after that. This field should
56  *        be considered read-only.
57  *
58  *    SilcUInt32 len;
59  *
60  *        Length of the currently valid data area. Tells the length of the
61  *        data at the buffer. This is set to zero at the allocation of the
62  *        buffer and should not be updated by hand. Method functions of the
63  *        buffer automatically updates this field. However, it is not
64  *        read-only field and can be updated manually if necessary.
65  *
66  *    unsiged char *head;
67  *
68  *        Head of the allocated buffer. This is the start of the allocated
69  *        data area and remains as same throughout the lifetime of the buffer.
70  *        However, the end of the head area or the start of the currently valid
71  *        data area is variable.
72  *
73  *        --------------------------------
74  *        | head  | data         | tail  |
75  *        --------------------------------
76  *        ^       ^
77  *
78  *        Current head section in the buffer is sb->data - sb->head.
79  *
80  *    unsigned char *data;
81  *
82  *        Currently valid data area. This is the start of the currently valid
83  *        main data area. The data area is variable in all directions.
84  *
85  *        --------------------------------
86  *        | head  | data         | tail  |
87  *        --------------------------------
88  *                ^              ^
89  *
90  *        Current valid data area in the buffer is sb->tail - sb->data.
91  *
92  *     unsigned char *tail;
93  *
94  *        Tail of the buffer. This is the end of the currently valid data area
95  *        or start of the tail area. The start of the tail area is variable.
96  *
97  *        --------------------------------
98  *        | head  | data         | tail  |
99  *        --------------------------------
100  *                               ^       ^
101  *
102  *        Current tail section in the buffer is sb->end - sb->tail.
103  *
104  *    unsigned char *end;
105  *
106  *        End of the allocated buffer. This is the end of the allocated data
107  *        area and remains as same throughout the lifetime of the buffer.
108  *        Usually this field is not needed except when checking the size
109  *        of the buffer.
110  *
111  *        --------------------------------
112  *        | head  | data         | tail  |
113  *        --------------------------------
114  *                                       ^
115  *
116  *        Length of the entire buffer is (ie. truelen) sb->end - sb->head.
117  *
118  *     Currently valid data area is considered to be the main data area in
119  *     the buffer. However, the entire buffer is of course valid data and can
120  *     be used as such. Usually head section of the buffer includes different
121  *     kind of headers or similar. Data section includes the main data of
122  *     the buffer. Tail section can be seen as a reserve space of the data
123  *     section. Tail section can be pulled towards end, and thus the data
124  *     section becomes larger.
125  *
126  * SOURCE
127  */
128 typedef struct {
129   SilcUInt32 truelen;
130   SilcUInt32 len;
131   unsigned char *head;
132   unsigned char *data;
133   unsigned char *tail;
134   unsigned char *end;
135 } *SilcBuffer, SilcBufferStruct;
136 /***/
137
138 /* Macros */
139
140 /****d* silcutil/SilcBufferAPI/SILC_BUFFER_END
141  * 
142  * NAME
143  *
144  *    #define SILC_BUFFER_END(...)
145  *
146  * DESCRIPTION
147  *
148  *    Returns the true length of the buffer. This is used to pull
149  *    the buffer area to the end of the buffer.
150  *
151  * SOURCE
152  */
153 #define SILC_BUFFER_END(x) ((x)->end - (x)->head)
154 /***/
155
156 /* Inline functions */
157
158 /****f* silcutil/SilcBufferAPI/silc_buffer_alloc
159  *
160  * SYNOPSIS
161  *
162  *    static inline
163  *    SilcBuffer silc_buffer_alloc(SilcUInt32 len);
164  *
165  * DESCRIPTION
166  *
167  *    Allocates new SilcBuffer and returns it.
168  *
169  ***/
170  
171 static inline
172 SilcBuffer silc_buffer_alloc(SilcUInt32 len)
173 {
174   SilcBuffer sb;
175
176   /* Allocate new SilcBuffer */
177   sb = (SilcBuffer)silc_calloc(1, sizeof(*sb));
178   if (!sb)
179     return NULL;
180
181   /* Allocate the actual data area */
182   sb->head = (unsigned char *)silc_calloc(len, sizeof(*sb->head));
183   if (!sb->head)
184     return NULL;
185
186   /* Set pointers to the new buffer */
187   sb->truelen = len;
188   sb->data = sb->head;
189   sb->tail = sb->head;
190   sb->end = sb->head + sb->truelen;
191
192   return sb;
193 }
194
195 /****f* silcutil/SilcBufferAPI/silc_buffer_free
196  *
197  * SYNOPSIS
198  *
199  *    static inline
200  *    void silc_buffer_free(SilcBuffer sb);
201  *
202  * DESCRIPTION
203  *
204  *    Frees SilcBuffer.
205  *
206  ***/
207
208 static inline
209 void silc_buffer_free(SilcBuffer sb)
210 {
211   if (sb) {
212 #if defined(SILC_DEBUG)
213     memset(sb->head, 'F', sb->truelen);
214 #endif
215     silc_free(sb->head);
216     silc_free(sb);
217   }
218 }
219
220 /****f* silcutil/SilcBufferAPI/silc_buffer_set
221  *
222  * SYNOPSIS
223  *
224  *    static inline
225  *    void silc_buffer_set(SilcBuffer sb,
226  *                         unsigned char *data,
227  *                         SilcUInt32 data_len);
228  *
229  * DESCRIPTION
230  *
231  *    Sets the `data' and `data_len' to the buffer pointer sent as argument.
232  *    The data area is automatically set to the `data_len'. This function
233  *    can be used to set the data to static buffer without needing any
234  *    memory allocations. The `data' will not be copied to the buffer.
235  *
236  ***/
237
238 static inline
239 void silc_buffer_set(SilcBuffer sb, unsigned char *data, SilcUInt32 data_len)
240 {
241   sb->data = sb->head = data;
242   sb->tail = sb->end = data + data_len;
243   sb->len = sb->truelen = data_len;
244 }
245
246 /****f* silcutil/SilcBufferAPI/silc_buffer_pull
247  *
248  * SYNOPSIS
249  *
250  *    static inline
251  *    unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len);
252  *
253  * DESCRIPTION
254  *
255  *    Pulls current data area towards end. The length of the currently
256  *    valid data area is also decremented. Returns pointer to the data
257  *    area before pulling.
258  *
259  * EXAMPLE
260  *
261  *    ---------------------------------
262  *    | head  | data       | tail     |
263  *    ---------------------------------
264  *            ^
265  *            Pulls the start of the data area.
266  *
267  *    ---------------------------------
268  *    | head     | data    | tail     |
269  *    ---------------------------------
270  *            ^
271  ***/
272
273 static inline
274 unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
275 {
276   unsigned char *old_data = sb->data;
277
278 #if defined(SILC_DEBUG)
279   assert(len <= (SilcUInt32)(sb->tail - sb->data));
280 #endif
281
282   sb->data += len;
283   sb->len -= len;
284
285   return old_data;
286 }
287
288 /****f* silcutil/SilcBufferAPI/silc_buffer_push
289  *
290  * SYNOPSIS
291  *
292  *    static inline
293  *    unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len);
294  *
295  * DESCRIPTION
296  *
297  *    Pushes current data area towards beginning. Length of the currently
298  *    valid data area is also incremented. Returns a pointer to the 
299  *    data area before pushing. 
300  *
301  * EXAMPLE
302  *
303  *    ---------------------------------
304  *    | head     | data    | tail     |
305  *    ---------------------------------
306  *               ^
307  *               Pushes the start of the data area.
308  *
309  *    ---------------------------------
310  *    | head  | data       | tail     |
311  *    ---------------------------------
312  *               ^
313  *
314  ***/
315
316 static inline
317 unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len)
318 {
319   unsigned char *old_data = sb->data;
320
321 #if defined(SILC_DEBUG)
322   assert((sb->data - len) >= sb->head);
323 #endif
324
325   sb->data -= len;
326   sb->len += len;
327
328   return old_data;
329 }
330
331 /****f* silcutil/SilcBufferAPI/silc_buffer_pull_tail
332  *
333  * SYNOPSIS
334  *
335  *    static inline 
336  *    unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len);
337  *
338  * DESCRIPTION
339  *
340  *    Pulls current tail section towards end. Length of the current valid
341  *    data area is also incremented. Returns a pointer to the data area 
342  *    before pulling.
343  *
344  * EXAMPLE
345  *
346  *    ---------------------------------
347  *    | head  | data       | tail     |
348  *    ---------------------------------
349  *                         ^
350  *                         Pulls the start of the tail section.
351  *
352  *    ---------------------------------
353  *    | head  | data           | tail |
354  *    ---------------------------------
355  *                         ^
356  *
357  ***/
358
359 static inline
360 unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
361 {
362   unsigned char *old_tail = sb->tail;
363
364 #if defined(SILC_DEBUG)
365   assert((SilcUInt32)(sb->end - sb->tail) >= len);
366 #endif
367
368   sb->tail += len;
369   sb->len += len;
370
371   return old_tail;
372 }
373
374 /****f* silcutil/SilcBufferAPI/silc_buffer_push_tail
375  *
376  * SYNOPSIS
377  *
378  *    static inline
379  *    unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len);
380  *
381  * DESCRIPTION
382  *
383  *    Pushes current tail section towards beginning. Length of the current
384  *    valid data area is also decremented. Returns a pointer to the
385  *    tail section before pushing.
386  *
387  * EXAMPLE
388  *
389  *    ---------------------------------
390  *    | head  | data           | tail |
391  *    ---------------------------------
392  *                             ^
393  *                             Pushes the start of the tail section.
394  *
395  *    ---------------------------------
396  *    | head  | data       | tail     |
397  *    ---------------------------------
398  *                             ^
399  *
400  ***/
401
402 static inline
403 unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len)
404 {
405   unsigned char *old_tail = sb->tail;
406
407 #if defined(SILC_DEBUG)
408   assert((sb->tail - len) >= sb->data);
409 #endif
410
411   sb->tail -= len;
412   sb->len -= len;
413
414   return old_tail;
415 }
416
417 /****f* silcutil/SilcBufferAPI/silc_buffer_put_head
418  *
419  * SYNOPSIS
420  *
421  *    static inline
422  *    unsigned char *silc_buffer_put_head(SilcBuffer sb, 
423  *                                        const unsigned char *data,
424  *                                        SilcUInt32 len);
425  *
426  * DESCRIPTION
427  *
428  *    Puts data at the head of the buffer. Returns pointer to the copied
429  *    data area. 
430  *
431  * EXAMPLE
432  *
433  *    ---------------------------------
434  *    | head  | data       | tail     |
435  *    ---------------------------------
436  *    ^
437  *    Puts data to the head section. 
438  *
439  ***/
440
441 static inline
442 unsigned char *silc_buffer_put_head(SilcBuffer sb,
443                                     const unsigned char *data,
444                                     SilcUInt32 len)
445 {
446 #if defined(SILC_DEBUG)
447   assert((SilcUInt32)(sb->data - sb->head) >= len);
448 #endif
449   return (unsigned char *)memcpy(sb->head, data, len);
450 }
451
452 /****f* silcutil/SilcBufferAPI/silc_buffer_put
453  *
454  * SYNOPSIS
455  *
456  *    static inline
457  *    unsigned char *silc_buffer_put(SilcBuffer sb,
458  *                                   const unsigned char *data,
459  *                                   SilcUInt32 len);
460  *
461  * DESCRIPTION
462  *
463  *    Puts data at the start of the valid data area. Returns a pointer
464  *    to the copied data area.
465  *
466  * EXAMPLE
467  *
468  *    ---------------------------------
469  *    | head  | data       | tail     |
470  *    ---------------------------------
471  *            ^
472  *            Puts data to the data section.
473  *
474  ***/
475
476 static inline
477 unsigned char *silc_buffer_put(SilcBuffer sb,
478                                const unsigned char *data,
479                                SilcUInt32 len)
480 {
481 #if defined(SILC_DEBUG)
482   assert((SilcUInt32)(sb->tail - sb->data) >= len);
483 #endif
484   return (unsigned char *)memcpy(sb->data, data, len);
485 }
486
487 /****f* silcutil/SilcBufferAPI/silc_buffer_put_tail
488  *
489  * SYNOPSIS
490  *
491  *    static inline
492  *    unsigned char *silc_buffer_put_tail(SilcBuffer sb,
493  *                                        const unsigned char *data,
494  *                                        SilcUInt32 len);
495  *
496  * DESCRIPTION
497  *
498  *    Puts data at the tail of the buffer. Returns pointer to the copied
499  *    data area. 
500  *
501  * EXAMPLE
502  *
503  *    ---------------------------------
504  *    | head  | data           | tail |
505  *    ---------------------------------
506  *                             ^
507  *                             Puts data to the tail section.
508  *
509  ***/
510
511 static inline
512 unsigned char *silc_buffer_put_tail(SilcBuffer sb,
513                                     const unsigned char *data,
514                                     SilcUInt32 len)
515 {
516 #if defined(SILC_DEBUG)
517   assert((SilcUInt32)(sb->end - sb->tail) >= len);
518 #endif
519   return (unsigned char *)memcpy(sb->tail, data, len);
520 }
521
522 /****f* silcutil/SilcBufferAPI/silc_buffer_alloc_size
523  *
524  * SYNOPSIS
525  *
526  *    static inline
527  *    SilcBuffer silc_buffer_alloc_size(SilcUInt32 len);
528  *
529  * DESCRIPTION
530  *
531  *    Allocates `len' bytes size buffer and moves the tail area automatically
532  *    `len' bytes so that the buffer is ready to use without calling the
533  *    silc_buffer_pull_tail.
534  *
535  ***/
536
537 static inline
538 SilcBuffer silc_buffer_alloc_size(SilcUInt32 len)
539 {
540   SilcBuffer sb = silc_buffer_alloc(len);
541   if (!sb)
542     return NULL;
543   silc_buffer_pull_tail(sb, len);
544   return sb;
545 }
546
547 /****f* silcutil/SilcBufferAPI/silc_buffer_clear
548  *
549  * SYNOPSIS
550  *
551  *    static inline
552  *    void silc_buffer_clear(SilcBuffer sb);
553  *
554  * DESCRIPTION
555  *
556  *    Clears and initialiazes the buffer to the state as if it was just
557  *    allocated by silc_buffer_alloc.
558  *
559  ***/
560
561 static inline
562 void silc_buffer_clear(SilcBuffer sb)
563 {
564   memset(sb->head, 0, sb->truelen);
565   sb->data = sb->head;
566   sb->tail = sb->head;
567   sb->len = 0;
568 }
569
570 /****f* silcutil/SilcBufferAPI/silc_buffer_copy
571  *
572  * SYNOPSIS
573  *
574  *    static inline
575  *    SilcBuffer silc_buffer_copy(SilcBuffer sb);
576  *
577  * DESCRIPTION
578  *
579  *    Generates copy of a SilcBuffer. This copies everything inside the
580  *    currently valid data area, nothing more. Use silc_buffer_clone to
581  *    copy entire buffer.
582  *
583  ***/
584
585 static inline
586 SilcBuffer silc_buffer_copy(SilcBuffer sb)
587 {
588   SilcBuffer sb_new;
589
590   sb_new = silc_buffer_alloc_size(sb->len);
591   if (!sb_new)
592     return NULL;
593   silc_buffer_put(sb_new, sb->data, sb->len);
594
595   return sb_new;
596 }
597
598 /****f* silcutil/SilcBufferAPI/silc_buffer_clone
599  *
600  * SYNOPSIS
601  *
602  *    static inline
603  *    SilcBuffer silc_buffer_clone(SilcBuffer sb);
604  *
605  * DESCRIPTION
606  *
607  *    Clones SilcBuffer. This generates new SilcBuffer and copies
608  *    everything from the source buffer. The result is exact clone of
609  *    the original buffer.
610  *
611  ***/
612
613 static inline
614 SilcBuffer silc_buffer_clone(SilcBuffer sb)
615 {
616   SilcBuffer sb_new;
617
618   sb_new = silc_buffer_alloc_size(sb->truelen);
619   if (!sb_new)
620     return NULL;
621   silc_buffer_put(sb_new, sb->head, sb->truelen);
622   sb_new->data = sb_new->head + (sb->data - sb->head);
623   sb_new->tail = sb_new->data + sb->len;
624   sb_new->len = sb->len;
625
626   return sb_new;
627 }
628
629 /****f* silcutil/SilcBufferAPI/silc_buffer_realloc
630  *
631  * SYNOPSIS
632  *
633  *    static inline
634  *    SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize);
635  *
636  * DESCRIPTION
637  *
638  *    Reallocates buffer. Old data is saved into the new buffer. Returns
639  *    new SilcBuffer pointer. The buffer is exact clone of the old one
640  *    except that there is now more space at the end of buffer.
641  *
642  ***/
643
644 static inline
645 SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize)
646 {
647   SilcBuffer sb_new;
648
649   if (!sb)
650     return silc_buffer_alloc(newsize);
651
652   if (newsize <= sb->truelen)
653     return sb;
654
655   sb_new = silc_buffer_alloc_size(newsize);
656   if (!sb_new)
657     return NULL;
658   silc_buffer_put(sb_new, sb->head, sb->truelen);
659   sb_new->data = sb_new->head + (sb->data - sb->head);
660   sb_new->tail = sb_new->data + sb->len;
661   sb_new->len = sb->len;
662
663   silc_buffer_free(sb);
664
665   return sb_new;
666 }
667
668 #endif