Merged silc_1_0_branch to trunk.
[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     if (sb->head)
214       memset(sb->head, 'F', sb->truelen);
215 #endif
216     silc_free(sb->head);
217     silc_free(sb);
218   }
219 }
220
221 /****f* silcutil/SilcBufferAPI/silc_buffer_steal
222  *
223  * SYNOPSIS
224  *
225  *    static inline
226  *    unsigned char *silc_buffer_steal(SilcBuffer sb, SilcUInt32 *data_len);
227  *
228  * DESCRIPTION
229  *
230  *    Steals the data from the buffer `sb'.  This returns pointer to the
231  *    start of the buffer and the true length of that buffer.  The `sb'
232  *    cannot be used anymore after calling this function because the
233  *    data buffer was stolen.  The `sb' must be freed with silc_buffer_free.
234  *    The caller is responsible of freeing the stolen data buffer with
235  *    silc_free.
236  *
237  ***/
238
239 static inline
240 unsigned char *silc_buffer_steal(SilcBuffer sb, SilcUInt32 *data_len)
241 {
242   unsigned char *buf = sb->head;
243   if (data_len)
244     *data_len = sb->truelen;
245   sb->head = sb->data = sb->tail = sb->end = NULL;
246   sb->len = sb->truelen = 0;
247   return buf;
248 }
249
250 /****f* silcutil/SilcBufferAPI/silc_buffer_set
251  *
252  * SYNOPSIS
253  *
254  *    static inline
255  *    void silc_buffer_set(SilcBuffer sb,
256  *                         unsigned char *data,
257  *                         SilcUInt32 data_len);
258  *
259  * DESCRIPTION
260  *
261  *    Sets the `data' and `data_len' to the buffer pointer sent as argument.
262  *    The data area is automatically set to the `data_len'. This function
263  *    can be used to set the data to static buffer without needing any
264  *    memory allocations. The `data' will not be copied to the buffer.
265  *
266  ***/
267
268 static inline
269 void silc_buffer_set(SilcBuffer sb, unsigned char *data, SilcUInt32 data_len)
270 {
271   sb->data = sb->head = data;
272   sb->tail = sb->end = data + data_len;
273   sb->len = sb->truelen = data_len;
274 }
275
276 /****f* silcutil/SilcBufferAPI/silc_buffer_pull
277  *
278  * SYNOPSIS
279  *
280  *    static inline
281  *    unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len);
282  *
283  * DESCRIPTION
284  *
285  *    Pulls current data area towards end. The length of the currently
286  *    valid data area is also decremented. Returns pointer to the data
287  *    area before pulling.
288  *
289  * EXAMPLE
290  *
291  *    ---------------------------------
292  *    | head  | data       | tail     |
293  *    ---------------------------------
294  *            ^
295  *            Pulls the start of the data area.
296  *
297  *    ---------------------------------
298  *    | head     | data    | tail     |
299  *    ---------------------------------
300  *            ^
301  ***/
302
303 static inline
304 unsigned char *silc_buffer_pull(SilcBuffer sb, SilcUInt32 len)
305 {
306   unsigned char *old_data = sb->data;
307
308 #if defined(SILC_DEBUG)
309   assert(len <= (SilcUInt32)(sb->tail - sb->data));
310 #endif
311
312   sb->data += len;
313   sb->len -= len;
314
315   return old_data;
316 }
317
318 /****f* silcutil/SilcBufferAPI/silc_buffer_push
319  *
320  * SYNOPSIS
321  *
322  *    static inline
323  *    unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len);
324  *
325  * DESCRIPTION
326  *
327  *    Pushes current data area towards beginning. Length of the currently
328  *    valid data area is also incremented. Returns a pointer to the 
329  *    data area before pushing. 
330  *
331  * EXAMPLE
332  *
333  *    ---------------------------------
334  *    | head     | data    | tail     |
335  *    ---------------------------------
336  *               ^
337  *               Pushes the start of the data area.
338  *
339  *    ---------------------------------
340  *    | head  | data       | tail     |
341  *    ---------------------------------
342  *               ^
343  *
344  ***/
345
346 static inline
347 unsigned char *silc_buffer_push(SilcBuffer sb, SilcUInt32 len)
348 {
349   unsigned char *old_data = sb->data;
350
351 #if defined(SILC_DEBUG)
352   assert((sb->data - len) >= sb->head);
353 #endif
354
355   sb->data -= len;
356   sb->len += len;
357
358   return old_data;
359 }
360
361 /****f* silcutil/SilcBufferAPI/silc_buffer_pull_tail
362  *
363  * SYNOPSIS
364  *
365  *    static inline 
366  *    unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len);
367  *
368  * DESCRIPTION
369  *
370  *    Pulls current tail section towards end. Length of the current valid
371  *    data area is also incremented. Returns a pointer to the data area 
372  *    before pulling.
373  *
374  * EXAMPLE
375  *
376  *    ---------------------------------
377  *    | head  | data       | tail     |
378  *    ---------------------------------
379  *                         ^
380  *                         Pulls the start of the tail section.
381  *
382  *    ---------------------------------
383  *    | head  | data           | tail |
384  *    ---------------------------------
385  *                         ^
386  *
387  ***/
388
389 static inline
390 unsigned char *silc_buffer_pull_tail(SilcBuffer sb, SilcUInt32 len)
391 {
392   unsigned char *old_tail = sb->tail;
393
394 #if defined(SILC_DEBUG)
395   assert((SilcUInt32)(sb->end - sb->tail) >= len);
396 #endif
397
398   sb->tail += len;
399   sb->len += len;
400
401   return old_tail;
402 }
403
404 /****f* silcutil/SilcBufferAPI/silc_buffer_push_tail
405  *
406  * SYNOPSIS
407  *
408  *    static inline
409  *    unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len);
410  *
411  * DESCRIPTION
412  *
413  *    Pushes current tail section towards beginning. Length of the current
414  *    valid data area is also decremented. Returns a pointer to the
415  *    tail section before pushing.
416  *
417  * EXAMPLE
418  *
419  *    ---------------------------------
420  *    | head  | data           | tail |
421  *    ---------------------------------
422  *                             ^
423  *                             Pushes the start of the tail section.
424  *
425  *    ---------------------------------
426  *    | head  | data       | tail     |
427  *    ---------------------------------
428  *                             ^
429  *
430  ***/
431
432 static inline
433 unsigned char *silc_buffer_push_tail(SilcBuffer sb, SilcUInt32 len)
434 {
435   unsigned char *old_tail = sb->tail;
436
437 #if defined(SILC_DEBUG)
438   assert((sb->tail - len) >= sb->data);
439 #endif
440
441   sb->tail -= len;
442   sb->len -= len;
443
444   return old_tail;
445 }
446
447 /****f* silcutil/SilcBufferAPI/silc_buffer_put_head
448  *
449  * SYNOPSIS
450  *
451  *    static inline
452  *    unsigned char *silc_buffer_put_head(SilcBuffer sb, 
453  *                                        const unsigned char *data,
454  *                                        SilcUInt32 len);
455  *
456  * DESCRIPTION
457  *
458  *    Puts data at the head of the buffer. Returns pointer to the copied
459  *    data area. 
460  *
461  * EXAMPLE
462  *
463  *    ---------------------------------
464  *    | head  | data       | tail     |
465  *    ---------------------------------
466  *    ^
467  *    Puts data to the head section. 
468  *
469  ***/
470
471 static inline
472 unsigned char *silc_buffer_put_head(SilcBuffer sb,
473                                     const unsigned char *data,
474                                     SilcUInt32 len)
475 {
476 #if defined(SILC_DEBUG)
477   assert((SilcUInt32)(sb->data - sb->head) >= len);
478 #endif
479   return (unsigned char *)memcpy(sb->head, data, len);
480 }
481
482 /****f* silcutil/SilcBufferAPI/silc_buffer_put
483  *
484  * SYNOPSIS
485  *
486  *    static inline
487  *    unsigned char *silc_buffer_put(SilcBuffer sb,
488  *                                   const unsigned char *data,
489  *                                   SilcUInt32 len);
490  *
491  * DESCRIPTION
492  *
493  *    Puts data at the start of the valid data area. Returns a pointer
494  *    to the copied data area.
495  *
496  * EXAMPLE
497  *
498  *    ---------------------------------
499  *    | head  | data       | tail     |
500  *    ---------------------------------
501  *            ^
502  *            Puts data to the data section.
503  *
504  ***/
505
506 static inline
507 unsigned char *silc_buffer_put(SilcBuffer sb,
508                                const unsigned char *data,
509                                SilcUInt32 len)
510 {
511 #if defined(SILC_DEBUG)
512   assert((SilcUInt32)(sb->tail - sb->data) >= len);
513 #endif
514   return (unsigned char *)memcpy(sb->data, data, len);
515 }
516
517 /****f* silcutil/SilcBufferAPI/silc_buffer_put_tail
518  *
519  * SYNOPSIS
520  *
521  *    static inline
522  *    unsigned char *silc_buffer_put_tail(SilcBuffer sb,
523  *                                        const unsigned char *data,
524  *                                        SilcUInt32 len);
525  *
526  * DESCRIPTION
527  *
528  *    Puts data at the tail of the buffer. Returns pointer to the copied
529  *    data area. 
530  *
531  * EXAMPLE
532  *
533  *    ---------------------------------
534  *    | head  | data           | tail |
535  *    ---------------------------------
536  *                             ^
537  *                             Puts data to the tail section.
538  *
539  ***/
540
541 static inline
542 unsigned char *silc_buffer_put_tail(SilcBuffer sb,
543                                     const unsigned char *data,
544                                     SilcUInt32 len)
545 {
546 #if defined(SILC_DEBUG)
547   assert((SilcUInt32)(sb->end - sb->tail) >= len);
548 #endif
549   return (unsigned char *)memcpy(sb->tail, data, len);
550 }
551
552 /****f* silcutil/SilcBufferAPI/silc_buffer_alloc_size
553  *
554  * SYNOPSIS
555  *
556  *    static inline
557  *    SilcBuffer silc_buffer_alloc_size(SilcUInt32 len);
558  *
559  * DESCRIPTION
560  *
561  *    Allocates `len' bytes size buffer and moves the tail area automatically
562  *    `len' bytes so that the buffer is ready to use without calling the
563  *    silc_buffer_pull_tail.
564  *
565  ***/
566
567 static inline
568 SilcBuffer silc_buffer_alloc_size(SilcUInt32 len)
569 {
570   SilcBuffer sb = silc_buffer_alloc(len);
571   if (!sb)
572     return NULL;
573   silc_buffer_pull_tail(sb, len);
574   return sb;
575 }
576
577 /****f* silcutil/SilcBufferAPI/silc_buffer_clear
578  *
579  * SYNOPSIS
580  *
581  *    static inline
582  *    void silc_buffer_clear(SilcBuffer sb);
583  *
584  * DESCRIPTION
585  *
586  *    Clears and initialiazes the buffer to the state as if it was just
587  *    allocated by silc_buffer_alloc.
588  *
589  ***/
590
591 static inline
592 void silc_buffer_clear(SilcBuffer sb)
593 {
594   if (!sb)
595     return;
596   memset(sb->head, 0, sb->truelen);
597   sb->data = sb->head;
598   sb->tail = sb->head;
599   sb->len = 0;
600 }
601
602 /****f* silcutil/SilcBufferAPI/silc_buffer_copy
603  *
604  * SYNOPSIS
605  *
606  *    static inline
607  *    SilcBuffer silc_buffer_copy(SilcBuffer sb);
608  *
609  * DESCRIPTION
610  *
611  *    Generates copy of a SilcBuffer. This copies everything inside the
612  *    currently valid data area, nothing more. Use silc_buffer_clone to
613  *    copy entire buffer.
614  *
615  ***/
616
617 static inline
618 SilcBuffer silc_buffer_copy(SilcBuffer sb)
619 {
620   SilcBuffer sb_new;
621
622   sb_new = silc_buffer_alloc_size(sb->len);
623   if (!sb_new)
624     return NULL;
625   silc_buffer_put(sb_new, sb->data, sb->len);
626
627   return sb_new;
628 }
629
630 /****f* silcutil/SilcBufferAPI/silc_buffer_clone
631  *
632  * SYNOPSIS
633  *
634  *    static inline
635  *    SilcBuffer silc_buffer_clone(SilcBuffer sb);
636  *
637  * DESCRIPTION
638  *
639  *    Clones SilcBuffer. This generates new SilcBuffer and copies
640  *    everything from the source buffer. The result is exact clone of
641  *    the original buffer.
642  *
643  ***/
644
645 static inline
646 SilcBuffer silc_buffer_clone(SilcBuffer sb)
647 {
648   SilcBuffer sb_new;
649
650   sb_new = silc_buffer_alloc_size(sb->truelen);
651   if (!sb_new)
652     return NULL;
653   silc_buffer_put(sb_new, sb->head, sb->truelen);
654   sb_new->data = sb_new->head + (sb->data - sb->head);
655   sb_new->tail = sb_new->data + sb->len;
656   sb_new->len = sb->len;
657
658   return sb_new;
659 }
660
661 /****f* silcutil/SilcBufferAPI/silc_buffer_realloc
662  *
663  * SYNOPSIS
664  *
665  *    static inline
666  *    SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize);
667  *
668  * DESCRIPTION
669  *
670  *    Reallocates buffer. Old data is saved into the new buffer. Returns
671  *    new SilcBuffer pointer. The buffer is exact clone of the old one
672  *    except that there is now more space at the end of buffer.
673  *
674  ***/
675
676 static inline
677 SilcBuffer silc_buffer_realloc(SilcBuffer sb, SilcUInt32 newsize)
678 {
679   SilcBuffer sb_new;
680
681   if (!sb)
682     return silc_buffer_alloc(newsize);
683
684   if (newsize <= sb->truelen)
685     return sb;
686
687   sb_new = silc_buffer_alloc_size(newsize);
688   if (!sb_new)
689     return NULL;
690   silc_buffer_put(sb_new, sb->head, sb->truelen);
691   sb_new->data = sb_new->head + (sb->data - sb->head);
692   sb_new->tail = sb_new->data + sb->len;
693   sb_new->len = sb->len;
694
695   silc_buffer_free(sb);
696
697   return sb_new;
698 }
699
700 #endif