Added SILC_STR_BUFFER to format SilcBuffer.
[silc.git] / lib / silcutil / silcbuffmt.c
1 /*
2
3   silcbuffmt.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2006 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
21 #include "silc.h"
22
23 /************************** Types and definitions ***************************/
24
25 /* Check that buffer has enough room to format data in it, if not
26    allocate more. */
27 #define FORMAT_HAS_SPACE(__s__, __x__, __req__)                             \
28 do {                                                                        \
29   if (__req__ > silc_buffer_len((__x__)))                                   \
30     if (!silc_buffer_srealloc_size((__s__), (__x__),                        \
31                                    silc_buffer_truelen((__x__)) + __req__)) \
32       goto fail;                                                            \
33   flen += __req__;                                                          \
34 } while(0)
35
36 /* Check that there is data to be unformatted */
37 #define UNFORMAT_HAS_SPACE(__x__, __req__)      \
38 do {                                            \
39   if (__req__ > silc_buffer_len((__x__)))       \
40     goto fail;                                  \
41   if ((__req__ + 1) <= 0)                       \
42     goto fail;                                  \
43 } while(0)
44
45
46 /******************************* Formatting *********************************/
47
48 int silc_buffer_format(SilcBuffer dst, ...)
49 {
50   va_list ap;
51   int ret;
52
53   va_start(ap, dst);
54   ret = silc_buffer_sformat_vp(NULL, dst, ap);
55   va_end(ap);
56
57   return ret;
58 }
59
60 int silc_buffer_format_vp(SilcBuffer dst, va_list ap)
61 {
62   return silc_buffer_sformat_vp(NULL, dst, ap);
63 }
64
65 int silc_buffer_sformat(SilcStack stack, SilcBuffer dst, ...)
66 {
67   va_list ap;
68   int ret;
69
70   va_start(ap, dst);
71   ret = silc_buffer_sformat_vp(stack, dst, ap);
72   va_end(ap);
73
74   return ret;
75 }
76
77 int silc_buffer_sformat_vp(SilcStack stack, SilcBuffer dst, va_list ap)
78 {
79   SilcBufferParamType fmt;
80   int flen = 0;
81   SilcBool advance = FALSE;
82
83   /* Parse the arguments by formatting type. */
84   while (1) {
85     fmt = va_arg(ap, SilcBufferParamType);
86
87     switch(fmt) {
88     case SILC_PARAM_OFFSET:
89       {
90         int offst = va_arg(ap, int);
91         if (!offst)
92           break;
93         if (offst > 1) {
94           if (offst > silc_buffer_len(dst))
95             goto fail;
96           silc_buffer_pull(dst, offst);
97           flen += offst;
98         } else {
99           silc_buffer_push(dst, -(offst));
100           flen += -(offst);
101         }
102         break;
103       }
104     case SILC_PARAM_SI8_CHAR:
105       {
106         char x = (char)va_arg(ap, int);
107         FORMAT_HAS_SPACE(stack, dst, 1);
108         silc_buffer_put(dst, &x, 1);
109         silc_buffer_pull(dst, 1);
110         break;
111       }
112     case SILC_PARAM_UI8_CHAR:
113       {
114         unsigned char x = (unsigned char)va_arg(ap, int);
115         FORMAT_HAS_SPACE(stack, dst, 1);
116         silc_buffer_put(dst, &x, 1);
117         silc_buffer_pull(dst, 1);
118         break;
119       }
120     case SILC_PARAM_SI16_SHORT:
121       {
122         unsigned char xf[2];
123         SilcInt16 x = (SilcInt16)va_arg(ap, int);
124         FORMAT_HAS_SPACE(stack, dst, 2);
125         SILC_PUT16_MSB(x, xf);
126         silc_buffer_put(dst, xf, 2);
127         silc_buffer_pull(dst, 2);
128         break;
129       }
130     case SILC_PARAM_UI16_SHORT:
131       {
132         unsigned char xf[2];
133         SilcUInt16 x = (SilcUInt16)va_arg(ap, int);
134         FORMAT_HAS_SPACE(stack, dst, 2);
135         SILC_PUT16_MSB(x, xf);
136         silc_buffer_put(dst, xf, 2);
137         silc_buffer_pull(dst, 2);
138         break;
139       }
140     case SILC_PARAM_SI32_INT:
141       {
142         unsigned char xf[4];
143         SilcInt32 x = va_arg(ap, SilcInt32);
144         FORMAT_HAS_SPACE(stack, dst, 4);
145         SILC_PUT32_MSB(x, xf);
146         silc_buffer_put(dst, xf, 4);
147         silc_buffer_pull(dst, 4);
148         break;
149       }
150     case SILC_PARAM_UI32_INT:
151       {
152         unsigned char xf[4];
153         SilcUInt32 x = va_arg(ap, SilcUInt32);
154         FORMAT_HAS_SPACE(stack, dst, 4);
155         SILC_PUT32_MSB(x, xf);
156         silc_buffer_put(dst, xf, 4);
157         silc_buffer_pull(dst, 4);
158         break;
159       }
160     case SILC_PARAM_SI64_INT:
161       {
162         unsigned char xf[8];
163         SilcInt64 x = va_arg(ap, SilcInt64);
164         FORMAT_HAS_SPACE(stack, dst, sizeof(SilcInt64));
165         SILC_PUT64_MSB(x, xf);
166         silc_buffer_put(dst, xf, sizeof(SilcInt64));
167         silc_buffer_pull(dst, sizeof(SilcInt64));
168         break;
169       }
170     case SILC_PARAM_UI64_INT:
171       {
172         unsigned char xf[8];
173         SilcUInt64 x = va_arg(ap, SilcUInt64);
174         FORMAT_HAS_SPACE(stack, dst, sizeof(SilcUInt64));
175         SILC_PUT64_MSB(x, xf);
176         silc_buffer_put(dst, xf, sizeof(SilcUInt64));
177         silc_buffer_pull(dst, sizeof(SilcUInt64));
178         break;
179       }
180     case SILC_PARAM_UI8_STRING:
181     case SILC_PARAM_UI16_STRING:
182     case SILC_PARAM_UI32_STRING:
183     case SILC_PARAM_UI8_STRING_ALLOC:
184     case SILC_PARAM_UI16_STRING_ALLOC:
185     case SILC_PARAM_UI32_STRING_ALLOC:
186       {
187         unsigned char *x = va_arg(ap, unsigned char *);
188         SilcUInt32 tmp_len = strlen(x);
189         FORMAT_HAS_SPACE(stack, dst, tmp_len);
190         silc_buffer_put(dst, x, tmp_len);
191         silc_buffer_pull(dst, tmp_len);
192         break;
193       }
194     case SILC_PARAM_UI8_NSTRING:
195     case SILC_PARAM_UI16_NSTRING:
196     case SILC_PARAM_UI32_NSTRING:
197     case SILC_PARAM_UI_XNSTRING:
198     case SILC_PARAM_DATA:
199     case SILC_PARAM_UI8_NSTRING_ALLOC:
200     case SILC_PARAM_UI16_NSTRING_ALLOC:
201     case SILC_PARAM_UI32_NSTRING_ALLOC:
202     case SILC_PARAM_UI_XNSTRING_ALLOC:
203     case SILC_PARAM_DATA_ALLOC:
204       {
205         unsigned char *x = va_arg(ap, unsigned char *);
206         SilcUInt32 tmp_len = va_arg(ap, SilcUInt32);
207         if (x && tmp_len) {
208           FORMAT_HAS_SPACE(stack, dst, tmp_len);
209           silc_buffer_put(dst, x, tmp_len);
210           silc_buffer_pull(dst, tmp_len);
211         }
212         break;
213       }
214     case SILC_PARAM_BUFFER:
215     case SILC_PARAM_BUFFER_ALLOC:
216       {
217         SilcBuffer x = va_arg(ap, SilcBuffer);
218         unsigned char xf[4];
219         if (x && silc_buffer_len(x)) {
220           FORMAT_HAS_SPACE(stack, dst, silc_buffer_len(x) + 4);
221           SILC_PUT32_MSB(silc_buffer_len(x), xf);
222           silc_buffer_put(dst, xf, 4);
223           silc_buffer_pull(dst, 4);
224           silc_buffer_put(dst, silc_buffer_data(x), silc_buffer_len(x));
225           silc_buffer_pull(dst, silc_buffer_len(x));
226         }
227       }
228       break;
229     case SILC_PARAM_END:
230       goto ok;
231       break;
232     case SILC_PARAM_ADVANCE:
233       advance = TRUE;
234       break;
235     default:
236       SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not "
237                       "format the data.", fmt));
238       goto fail;
239       break;
240     }
241   }
242
243  fail:
244   SILC_LOG_DEBUG(("Error occured while formatting data"));
245   if (!advance)
246     silc_buffer_push(dst, flen);
247   return -1;
248
249  ok:
250   /* Push the buffer back to where it belongs. */
251   if (!advance)
252     silc_buffer_push(dst, flen);
253   return flen;
254 }
255
256
257 /****************************** Unformatting ********************************/
258
259 int silc_buffer_unformat(SilcBuffer src, ...)
260 {
261   va_list ap;
262   int ret;
263
264   va_start(ap, src);
265   ret = silc_buffer_unformat_vp(src, ap);
266   va_end(ap);
267
268   return ret;
269 }
270
271 int silc_buffer_unformat_vp(SilcBuffer src, va_list ap)
272 {
273   SilcBufferParamType fmt;
274   unsigned char *start_ptr = src->data;
275   int len = 0;
276
277   /* Parse the arguments by formatting type. */
278   while(1) {
279     fmt = va_arg(ap, SilcBufferParamType);
280
281     switch(fmt) {
282     case SILC_PARAM_OFFSET:
283       {
284         int offst = va_arg(ap, int);
285         if (!offst)
286           break;
287         if (offst > 1) {
288           UNFORMAT_HAS_SPACE(src, offst);
289           silc_buffer_pull(src, offst);
290         } else {
291           silc_buffer_push(src, -(offst));
292         }
293         break;
294       }
295     case SILC_PARAM_SI8_CHAR:
296       {
297         char *x = va_arg(ap, char *);
298         UNFORMAT_HAS_SPACE(src, 1);
299         if (x)
300           *x = src->data[0];
301         silc_buffer_pull(src, 1);
302         break;
303       }
304     case SILC_PARAM_UI8_CHAR:
305       {
306         unsigned char *x = va_arg(ap, unsigned char *);
307         UNFORMAT_HAS_SPACE(src, 1);
308         if (x)
309           *x = src->data[0];
310         silc_buffer_pull(src, 1);
311         break;
312       }
313     case SILC_PARAM_SI16_SHORT:
314       {
315         SilcInt16 *x = va_arg(ap, SilcInt16 *);
316         UNFORMAT_HAS_SPACE(src, 2);
317         if (x)
318           SILC_GET16_MSB(*x, src->data);
319         silc_buffer_pull(src, 2);
320         break;
321       }
322     case SILC_PARAM_UI16_SHORT:
323       {
324         SilcUInt16 *x = va_arg(ap, SilcUInt16 *);
325         UNFORMAT_HAS_SPACE(src, 2);
326         if (x)
327           SILC_GET16_MSB(*x, src->data);
328         silc_buffer_pull(src, 2);
329         break;
330       }
331     case SILC_PARAM_SI32_INT:
332       {
333         SilcInt32 *x = va_arg(ap, SilcInt32 *);
334         UNFORMAT_HAS_SPACE(src, 4);
335         if (x)
336           SILC_GET32_MSB(*x, src->data);
337         silc_buffer_pull(src, 4);
338         break;
339       }
340     case SILC_PARAM_UI32_INT:
341       {
342         SilcUInt32 *x = va_arg(ap, SilcUInt32 *);
343         UNFORMAT_HAS_SPACE(src, 4);
344         if (x)
345           SILC_GET32_MSB(*x, src->data);
346         silc_buffer_pull(src, 4);
347         break;
348       }
349     case SILC_PARAM_SI64_INT:
350       {
351         SilcInt64 *x = va_arg(ap, SilcInt64 *);
352         UNFORMAT_HAS_SPACE(src, sizeof(SilcInt64));
353         if (x)
354           SILC_GET64_MSB(*x, src->data);
355         silc_buffer_pull(src, sizeof(SilcInt64));
356         break;
357       }
358     case SILC_PARAM_UI64_INT:
359       {
360         SilcUInt64 *x = va_arg(ap, SilcUInt64 *);
361         UNFORMAT_HAS_SPACE(src, sizeof(SilcUInt64));
362         if (x)
363           SILC_GET64_MSB(*x, src->data);
364         silc_buffer_pull(src, sizeof(SilcUInt64));
365         break;
366       }
367     case SILC_PARAM_UI8_STRING:
368       {
369         SilcUInt8 len2;
370         unsigned char **x = va_arg(ap, unsigned char **);
371         UNFORMAT_HAS_SPACE(src, 1);
372         len2 = (SilcUInt8)src->data[0];
373         silc_buffer_pull(src, 1);
374         UNFORMAT_HAS_SPACE(src, len2);
375         if (x)
376           *x = src->data;
377         silc_buffer_pull(src, len2);
378         break;
379       }
380     case SILC_PARAM_UI16_STRING:
381       {
382         SilcUInt16 len2;
383         unsigned char **x = va_arg(ap, unsigned char **);
384         UNFORMAT_HAS_SPACE(src, 2);
385         SILC_GET16_MSB(len2, src->data);
386         silc_buffer_pull(src, 2);
387         UNFORMAT_HAS_SPACE(src, len2);
388         if (x)
389           *x = src->data;
390         silc_buffer_pull(src, len2);
391         break;
392       }
393     case SILC_PARAM_UI8_STRING_ALLOC:
394       {
395         SilcUInt8 len2;
396         unsigned char **x = va_arg(ap, unsigned char **);
397         UNFORMAT_HAS_SPACE(src, 1);
398         len2 = (SilcUInt8)src->data[0];
399         silc_buffer_pull(src, 1);
400         UNFORMAT_HAS_SPACE(src, len2);
401         if (x && len2) {
402           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
403           memcpy(*x, src->data, len2);
404         }
405         silc_buffer_pull(src, len2);
406         break;
407       }
408     case SILC_PARAM_UI16_STRING_ALLOC:
409       {
410         SilcUInt16 len2;
411         unsigned char **x = va_arg(ap, unsigned char **);
412         UNFORMAT_HAS_SPACE(src, 2);
413         SILC_GET16_MSB(len2, src->data);
414         silc_buffer_pull(src, 2);
415         UNFORMAT_HAS_SPACE(src, len2);
416         if (x && len2) {
417           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
418           memcpy(*x, src->data, len2);
419         }
420         silc_buffer_pull(src, len2);
421         break;
422       }
423     case SILC_PARAM_UI32_STRING:
424       {
425         SilcUInt32 len2;
426         unsigned char **x = va_arg(ap, unsigned char **);
427         UNFORMAT_HAS_SPACE(src, 4);
428         SILC_GET32_MSB(len2, src->data);
429         silc_buffer_pull(src, 4);
430         UNFORMAT_HAS_SPACE(src, len2);
431         if (x)
432           *x = src->data;
433         silc_buffer_pull(src, len2);
434         break;
435       }
436     case SILC_PARAM_UI32_STRING_ALLOC:
437       {
438         SilcUInt32 len2;
439         unsigned char **x = va_arg(ap, unsigned char **);
440         UNFORMAT_HAS_SPACE(src, 4);
441         SILC_GET32_MSB(len2, src->data);
442         silc_buffer_pull(src, 4);
443         UNFORMAT_HAS_SPACE(src, len2);
444         if (x && len2) {
445           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
446           memcpy(*x, src->data, len2);
447         }
448         silc_buffer_pull(src, len2);
449         break;
450       }
451     case SILC_PARAM_UI8_NSTRING:
452       {
453         SilcUInt8 len2;
454         unsigned char **x = va_arg(ap, unsigned char **);
455         SilcUInt8 *len3 = va_arg(ap, SilcUInt8 *);
456         UNFORMAT_HAS_SPACE(src, 1);
457         len2 = (SilcUInt8)src->data[0];
458         silc_buffer_pull(src, 1);
459         UNFORMAT_HAS_SPACE(src, len2);
460         if (len3)
461           *len3 = len2;
462         if (x)
463           *x = src->data;
464         silc_buffer_pull(src, len2);
465         break;
466       }
467     case SILC_PARAM_UI16_NSTRING:
468       {
469         SilcUInt16 len2;
470         unsigned char **x = va_arg(ap, unsigned char **);
471         SilcUInt16 *len3 = va_arg(ap, SilcUInt16 *);
472         UNFORMAT_HAS_SPACE(src, 2);
473         SILC_GET16_MSB(len2, src->data);
474         silc_buffer_pull(src, 2);
475         UNFORMAT_HAS_SPACE(src, len2);
476         if (len3)
477           *len3 = len2;
478         if (x)
479           *x = src->data;
480         silc_buffer_pull(src, len2);
481         break;
482       }
483     case SILC_PARAM_UI8_NSTRING_ALLOC:
484       {
485         SilcUInt8 len2;
486         unsigned char **x = va_arg(ap, unsigned char **);
487         SilcUInt8 *len3 = va_arg(ap, SilcUInt8 *);
488         UNFORMAT_HAS_SPACE(src, 1);
489         len2 = (SilcUInt8)src->data[0];
490         silc_buffer_pull(src, 1);
491         UNFORMAT_HAS_SPACE(src, len2);
492         if (len3)
493           *len3 = len2;
494         if (x && len2) {
495           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
496           memcpy(*x, src->data, len2);
497         }
498         silc_buffer_pull(src, len2);
499         break;
500       }
501     case SILC_PARAM_UI16_NSTRING_ALLOC:
502       {
503         SilcUInt16 len2;
504         unsigned char **x = va_arg(ap, unsigned char **);
505         SilcUInt16 *len3 = va_arg(ap, SilcUInt16 *);
506         UNFORMAT_HAS_SPACE(src, 2);
507         SILC_GET16_MSB(len2, src->data);
508         silc_buffer_pull(src, 2);
509         UNFORMAT_HAS_SPACE(src, len2);
510         if (len3)
511           *len3 = len2;
512         if (x && len2) {
513           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
514           memcpy(*x, src->data, len2);
515         }
516         silc_buffer_pull(src, len2);
517         break;
518       }
519     case SILC_PARAM_UI32_NSTRING:
520       {
521         SilcUInt32 len2;
522         unsigned char **x = va_arg(ap, unsigned char **);
523         SilcUInt32 *len3 = va_arg(ap, SilcUInt32 *);
524         UNFORMAT_HAS_SPACE(src, 4);
525         SILC_GET32_MSB(len2, src->data);
526         silc_buffer_pull(src, 4);
527         UNFORMAT_HAS_SPACE(src, len2);
528         if (len3)
529           *len3 = len2;
530         if (x)
531           *x = src->data;
532         silc_buffer_pull(src, len2);
533         break;
534       }
535     case SILC_PARAM_UI_XNSTRING:
536     case SILC_PARAM_DATA:
537       {
538         unsigned char **x = va_arg(ap, unsigned char **);
539         SilcUInt32 len2 = va_arg(ap, SilcUInt32);
540         UNFORMAT_HAS_SPACE(src, len2);
541         if (len2 && x)
542           *x = src->data;
543         silc_buffer_pull(src, len2);
544         break;
545       }
546     case SILC_PARAM_UI_XNSTRING_ALLOC:
547     case SILC_PARAM_DATA_ALLOC:
548       {
549         unsigned char **x = va_arg(ap, unsigned char **);
550         SilcUInt32 len2 = va_arg(ap, SilcUInt32);
551         UNFORMAT_HAS_SPACE(src, len2);
552         if (len2 && x) {
553           *x = silc_calloc(len2 + 1, sizeof(unsigned char));
554           memcpy(*x, src->data, len2);
555         }
556         silc_buffer_pull(src, len2);
557         break;
558       }
559     case SILC_PARAM_BUFFER:
560       {
561         SilcBuffer x = va_arg(ap, SilcBuffer);
562         SilcUInt32 len2;
563         UNFORMAT_HAS_SPACE(src, 4);
564         SILC_GET32_MSB(len2, src->data);
565         silc_buffer_pull(src, 4);
566         UNFORMAT_HAS_SPACE(src, len2);
567         silc_buffer_set(x, src->data, len2);
568         silc_buffer_pull(src, len2);
569       }
570       break;
571     case SILC_PARAM_BUFFER_ALLOC:
572       {
573         SilcBuffer x = va_arg(ap, SilcBuffer);
574         SilcUInt32 len2;
575         UNFORMAT_HAS_SPACE(src, 4);
576         SILC_GET32_MSB(len2, src->data);
577         silc_buffer_pull(src, 4);
578         UNFORMAT_HAS_SPACE(src, len2);
579         silc_buffer_format(x,
580                            SILC_STR_DATA(src->data, len2),
581                            SILC_STR_END);
582         silc_buffer_pull(src, len2);
583       }
584       break;
585     case SILC_PARAM_END:
586       goto ok;
587       break;
588     case SILC_PARAM_ADVANCE:
589       break;
590     default:
591       SILC_LOG_DEBUG(("Bad buffer formatting type `%d'. Could not "
592                       "format the data.", fmt));
593       goto fail;
594       break;
595     }
596   }
597
598  fail:
599   SILC_LOG_DEBUG(("Error occured while unformatting buffer"));
600   len = src->data - start_ptr;
601   silc_buffer_push(src, len);
602   return -1;
603
604  ok:
605   /* Push the buffer back to the start. */
606   len = src->data - start_ptr;
607   silc_buffer_push(src, len);
608   return len;
609 }
610
611
612 /**************************** Utility functions *****************************/
613
614 /* Formats strings into a buffer */
615
616 int silc_buffer_strformat(SilcBuffer dst, ...)
617 {
618   int len = silc_buffer_truelen(dst);
619   int hlen = silc_buffer_headlen(dst);
620   va_list va;
621
622   va_start(va, dst);
623
624   /* Parse the arguments by formatting type. */
625   while(1) {
626     char *string = va_arg(va, char *);
627     unsigned char *d;
628     SilcInt32 slen;
629
630     if (!string)
631       continue;
632     if (string == (char *)SILC_PARAM_END)
633       goto ok;
634
635     slen = strlen(string);
636     d = silc_realloc(dst->head, sizeof(*dst->head) * (slen + len + 1));
637     if (!d)
638       return -1;
639     dst->head = d;
640     memcpy(dst->head + len, string, slen);
641     len += slen;
642     dst->head[len] = '\0';
643   }
644
645   SILC_LOG_DEBUG(("Error occured while formatting buffer"));
646   va_end(va);
647   return -1;
648
649  ok:
650   dst->end = dst->head + len;
651   dst->data = dst->head + hlen;
652   dst->tail = dst->end;
653
654   va_end(va);
655   return len;
656 }
657
658 /* Formats strings into a buffer.  Allocates memory from SilcStack. */
659
660 int silc_buffer_sstrformat(SilcStack stack, SilcBuffer dst, ...)
661 {
662   int len = silc_buffer_truelen(dst);
663   int hlen = silc_buffer_headlen(dst);
664   va_list va;
665
666   va_start(va, dst);
667
668   /* Parse the arguments by formatting type. */
669   while(1) {
670     char *string = va_arg(va, char *);
671     unsigned char *d;
672     SilcInt32 slen;
673
674     if (!string)
675       continue;
676     if (string == (char *)SILC_PARAM_END)
677       goto ok;
678
679     slen = strlen(string);
680     d = silc_srealloc_ua(stack, len + 1, dst->head,
681                          sizeof(*dst->head) * (slen + len + 1));
682     if (!d)
683       return -1;
684     dst->head = d;
685     memcpy(dst->head + len, string, slen);
686     len += slen;
687     dst->head[len] = '\0';
688   }
689
690   SILC_LOG_DEBUG(("Error occured while formatting buffer"));
691   va_end(va);
692   return -1;
693
694  ok:
695   dst->end = dst->head + len;
696   dst->data = dst->head + hlen;
697   dst->tail = dst->end;
698
699   va_end(va);
700   return len;
701 }