1/*
2 * dynbuf.c
3 *
4 * Copyright (C) 2008-2014 Aerospike, Inc.
5 *
6 * Portions may be licensed to Aerospike, Inc. under one or more contributor
7 * license agreements.
8 *
9 * This program is free software: you can redistribute it and/or modify it under
10 * the terms of the GNU Affero General Public License as published by the Free
11 * Software Foundation, either version 3 of the License, or (at your option) any
12 * later version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see http://www.gnu.org/licenses/
21 */
22
23#include "dynbuf.h"
24
25#include <stdarg.h>
26#include <stdbool.h>
27#include <stddef.h>
28#include <stdint.h>
29#include <string.h>
30#include <arpa/inet.h>
31#include <asm/byteorder.h>
32
33#include <citrusleaf/alloc.h>
34
35#include "cf_str.h"
36
37
38#define MAX_BACKOFF (1024 * 256)
39#define MAX_FORMAT 100
40
41size_t
42get_new_size(int alloc, int used, int requested)
43{
44 if (alloc - used > requested) {
45 return alloc;
46 }
47
48 size_t new_sz = alloc + requested + sizeof(cf_buf_builder);
49 int backoff;
50
51 if (new_sz < 1024 * 8) {
52 backoff = 1024;
53 }
54 else if (new_sz < 1024 * 32) {
55 backoff = 1024 * 4;
56 }
57 else if (new_sz < 1024 * 128) {
58 backoff = 1024 * 32;
59 }
60 else {
61 backoff = MAX_BACKOFF;
62 }
63
64 return new_sz + (backoff - (new_sz % backoff));
65}
66
67void
68cf_dyn_buf_reserve_internal(cf_dyn_buf *db, size_t sz)
69{
70 size_t new_sz = get_new_size(db->alloc_sz, db->used_sz, sz);
71
72 if (new_sz > db->alloc_sz) {
73 uint8_t *_t;
74
75 if (db->is_stack) {
76 _t = cf_malloc(new_sz);
77 memcpy(_t, db->buf, db->used_sz);
78 db->is_stack = false;
79 }
80 else {
81 _t = cf_realloc(db->buf, new_sz);
82 }
83
84 db->buf = _t;
85 db->alloc_sz = new_sz;
86 }
87}
88
89#define DB_RESERVE(_n) \
90 if (db->alloc_sz - db->used_sz < _n) { \
91 cf_dyn_buf_reserve_internal(db, _n); \
92 }
93
94void
95cf_dyn_buf_init_heap(cf_dyn_buf *db, size_t sz)
96{
97 db->buf = cf_malloc(sz);
98 db->is_stack = false;
99 db->alloc_sz = sz;
100 db->used_sz = 0;
101}
102
103void
104cf_dyn_buf_reserve(cf_dyn_buf *db, size_t sz, uint8_t **from)
105{
106 DB_RESERVE(sz);
107
108 if (from) {
109 *from = &db->buf[db->used_sz];
110 }
111
112 db->used_sz += sz;
113}
114
115void
116cf_dyn_buf_append_buf(cf_dyn_buf *db, uint8_t *buf, size_t sz)
117{
118 DB_RESERVE(sz);
119 memcpy(&db->buf[db->used_sz], buf, sz);
120 db->used_sz += sz;
121}
122
123void
124cf_dyn_buf_append_string(cf_dyn_buf *db, const char *s)
125{
126 size_t len = strlen(s);
127
128 DB_RESERVE(len);
129 memcpy(&db->buf[db->used_sz], s, len);
130 db->used_sz += len;
131}
132
133void
134cf_dyn_buf_append_char(cf_dyn_buf *db, char c)
135{
136 DB_RESERVE(1);
137 db->buf[db->used_sz] = (uint8_t)c;
138 db->used_sz++;
139}
140
141void
142cf_dyn_buf_append_bool(cf_dyn_buf *db, bool b)
143{
144 if (b) {
145 DB_RESERVE(4);
146 memcpy(&db->buf[db->used_sz], "true", 4);
147 db->used_sz += 4;
148 }
149 else {
150 DB_RESERVE(5);
151 memcpy(&db->buf[db->used_sz], "false", 5);
152 db->used_sz += 5;
153 }
154}
155
156void
157cf_dyn_buf_append_int(cf_dyn_buf *db, int i)
158{
159 DB_RESERVE(12);
160 db->used_sz += cf_str_itoa(i, (char *)&db->buf[db->used_sz], 10);
161}
162
163void
164cf_dyn_buf_append_uint64_x(cf_dyn_buf *db, uint64_t i)
165{
166 DB_RESERVE(18);
167 db->used_sz += cf_str_itoa_u64(i, (char *)&db->buf[db->used_sz], 16);
168}
169
170void
171cf_dyn_buf_append_uint64(cf_dyn_buf *db, uint64_t i)
172{
173 DB_RESERVE(22);
174 db->used_sz += cf_str_itoa_u64(i, (char *)&db->buf[db->used_sz], 10);
175}
176
177void
178cf_dyn_buf_append_uint32(cf_dyn_buf *db, uint32_t i)
179{
180 DB_RESERVE(12);
181 db->used_sz += cf_str_itoa_u32(i, (char *)&db->buf[db->used_sz], 10);
182}
183
184void
185cf_dyn_buf_append_format_va(cf_dyn_buf *db, const char *form, va_list va)
186{
187 DB_RESERVE(MAX_FORMAT + 1);
188 int32_t len = vsnprintf((char *)&db->buf[db->used_sz], MAX_FORMAT + 1, form,
189 va);
190
191 if (len > MAX_FORMAT) {
192 len = MAX_FORMAT;
193 }
194
195 db->used_sz += len;
196}
197
198void
199cf_dyn_buf_append_format(cf_dyn_buf *db, const char *form, ...)
200{
201 va_list va;
202 va_start(va, form);
203
204 cf_dyn_buf_append_format_va(db, form, va);
205
206 va_end(va);
207}
208
209void
210cf_dyn_buf_chomp(cf_dyn_buf *db)
211{
212 if (db->used_sz > 0) {
213 db->used_sz--;
214 }
215}
216
217char *
218cf_dyn_buf_strdup(cf_dyn_buf *db)
219{
220 if (db->used_sz == 0) {
221 return NULL;
222 }
223
224 char *s = cf_malloc(db->used_sz + 1);
225
226 memcpy(s, db->buf, db->used_sz);
227 s[db->used_sz] = 0;
228
229 return s;
230}
231
232void
233cf_dyn_buf_free(cf_dyn_buf *db)
234{
235 if (! db->is_stack && db->buf) {
236 cf_free(db->buf);
237 }
238}
239
240// Helpers to append name value pairs to a cf_dyn_buf in pattern: name=value;
241
242void
243info_append_bool(cf_dyn_buf *db, const char *name, bool value)
244{
245 cf_dyn_buf_append_string(db, name);
246 cf_dyn_buf_append_char(db, '=');
247 cf_dyn_buf_append_bool(db, value);
248 cf_dyn_buf_append_char(db, ';');
249}
250
251void
252info_append_int(cf_dyn_buf *db, const char *name, int value)
253{
254 cf_dyn_buf_append_string(db, name);
255 cf_dyn_buf_append_char(db, '=');
256 cf_dyn_buf_append_int(db, value);
257 cf_dyn_buf_append_char(db, ';');
258}
259
260void
261info_append_string(cf_dyn_buf *db, const char *name, const char *value)
262{
263 cf_dyn_buf_append_string(db, name);
264 cf_dyn_buf_append_char(db, '=');
265 cf_dyn_buf_append_string(db, value);
266 cf_dyn_buf_append_char(db, ';');
267}
268
269void
270info_append_string_safe(cf_dyn_buf *db, const char *name, const char *value)
271{
272 cf_dyn_buf_append_string(db, name);
273 cf_dyn_buf_append_char(db, '=');
274 cf_dyn_buf_append_string(db, value ? value : "null");
275 cf_dyn_buf_append_char(db, ';');
276}
277
278void
279info_append_uint32(cf_dyn_buf *db, const char *name, uint32_t value)
280{
281 cf_dyn_buf_append_string(db, name);
282 cf_dyn_buf_append_char(db, '=');
283 cf_dyn_buf_append_uint32(db, value);
284 cf_dyn_buf_append_char(db, ';');
285}
286
287void
288info_append_uint64(cf_dyn_buf *db, const char *name, uint64_t value)
289{
290 cf_dyn_buf_append_string(db, name);
291 cf_dyn_buf_append_char(db, '=');
292 cf_dyn_buf_append_uint64(db, value);
293 cf_dyn_buf_append_char(db, ';');
294}
295
296void
297info_append_uint64_x(cf_dyn_buf *db, const char *name, uint64_t value)
298{
299 cf_dyn_buf_append_string(db, name);
300 cf_dyn_buf_append_char(db, '=');
301 cf_dyn_buf_append_uint64_x(db, value);
302 cf_dyn_buf_append_char(db, ';');
303}
304
305void
306info_append_format(cf_dyn_buf *db, const char *name, const char *form, ...)
307{
308 va_list va;
309 va_start(va, form);
310
311 cf_dyn_buf_append_string(db, name);
312 cf_dyn_buf_append_char(db, '=');
313 cf_dyn_buf_append_format_va(db, form, va);
314 cf_dyn_buf_append_char(db, ';');
315
316 va_end(va);
317}
318
319static inline void
320append_indexed_name(cf_dyn_buf *db, const char *name, uint32_t ix,
321 const char *attr)
322{
323 cf_dyn_buf_append_string(db, name);
324 cf_dyn_buf_append_char(db, '[');
325 cf_dyn_buf_append_uint32(db, ix);
326 cf_dyn_buf_append_char(db, ']');
327
328 if (attr) {
329 cf_dyn_buf_append_char(db, '.');
330 cf_dyn_buf_append_string(db, attr);
331 }
332
333 cf_dyn_buf_append_char(db, '=');
334}
335
336void
337info_append_indexed_string(cf_dyn_buf *db, const char *name, uint32_t ix,
338 const char *attr, const char *value)
339{
340 append_indexed_name(db, name, ix, attr);
341 cf_dyn_buf_append_string(db, value);
342 cf_dyn_buf_append_char(db, ';');
343}
344
345void
346info_append_indexed_int(cf_dyn_buf *db, const char *name, uint32_t ix,
347 const char *attr, int value)
348{
349 append_indexed_name(db, name, ix, attr);
350 cf_dyn_buf_append_int(db, value);
351 cf_dyn_buf_append_char(db, ';');
352}
353
354void
355info_append_indexed_uint32(cf_dyn_buf *db, const char *name, uint32_t ix,
356 const char *attr, uint32_t value)
357{
358 append_indexed_name(db, name, ix, attr);
359 cf_dyn_buf_append_uint32(db, value);
360 cf_dyn_buf_append_char(db, ';');
361}
362
363void
364info_append_indexed_uint64(cf_dyn_buf *db, const char *name, uint32_t ix,
365 const char *attr, uint64_t value)
366{
367 append_indexed_name(db, name, ix, attr);
368 cf_dyn_buf_append_uint64(db, value);
369 cf_dyn_buf_append_char(db, ';');
370}
371
372
373
374void
375cf_buf_builder_reserve_internal(cf_buf_builder **bb_r, size_t sz)
376{
377 cf_buf_builder *bb = *bb_r;
378 size_t new_sz = get_new_size(bb->alloc_sz, bb->used_sz, sz);
379
380 if (new_sz > bb->alloc_sz) {
381 if (bb->alloc_sz - bb->used_sz < MAX_BACKOFF) {
382 bb = cf_realloc(bb, new_sz);
383 }
384 else {
385 // Only possible if buffer was reset. Avoids potential expensive
386 // copy within realloc.
387 cf_buf_builder *_t = cf_malloc(new_sz);
388
389 memcpy(_t->buf, bb->buf, bb->used_sz);
390 _t->used_sz = bb->used_sz;
391 cf_free(bb);
392 bb = _t;
393 }
394
395 bb->alloc_sz = new_sz - sizeof(cf_buf_builder);
396 *bb_r = bb;
397 }
398}
399
400#define BB_RESERVE(_n) \
401 if ((*bb_r)->alloc_sz - (*bb_r)->used_sz < _n) { \
402 cf_buf_builder_reserve_internal(bb_r, _n); \
403 }
404
405void
406cf_buf_builder_append_buf(cf_buf_builder **bb_r, uint8_t *buf, size_t sz)
407{
408 BB_RESERVE(sz);
409 cf_buf_builder *bb = *bb_r;
410 memcpy(&bb->buf[bb->used_sz], buf, sz);
411 bb->used_sz += sz;
412}
413
414void
415cf_buf_builder_append_string(cf_buf_builder **bb_r, const char *s)
416{
417 size_t len = strlen(s);
418 BB_RESERVE(len);
419 cf_buf_builder *bb = *bb_r;
420 memcpy(&bb->buf[bb->used_sz], s, len);
421 bb->used_sz += len;
422}
423
424void
425cf_buf_builder_append_char(cf_buf_builder **bb_r, char c)
426{
427 BB_RESERVE(1);
428 cf_buf_builder *bb = *bb_r;
429 bb->buf[bb->used_sz] = (uint8_t)c;
430 bb->used_sz++;
431}
432
433void
434cf_buf_builder_append_ascii_int(cf_buf_builder **bb_r, int i)
435{
436 BB_RESERVE(12);
437 cf_buf_builder *bb = *bb_r;
438 bb->used_sz += cf_str_itoa(i, (char *)&bb->buf[bb->used_sz], 10);
439}
440
441void
442cf_buf_builder_append_ascii_uint64_x(cf_buf_builder **bb_r, uint64_t i)
443{
444 BB_RESERVE(18);
445 cf_buf_builder *bb = *bb_r;
446 bb->used_sz += cf_str_itoa_u64(i, (char *)&bb->buf[bb->used_sz], 16);
447}
448
449void
450cf_buf_builder_append_ascii_uint64(cf_buf_builder **bb_r, uint64_t i)
451{
452 BB_RESERVE(12);
453 cf_buf_builder *bb = *bb_r;
454 bb->used_sz += cf_str_itoa_u64(i, (char *)&bb->buf[bb->used_sz], 10);
455}
456
457void
458cf_buf_builder_append_ascii_uint32(cf_buf_builder **bb_r, uint32_t i)
459{
460 BB_RESERVE(12);
461 cf_buf_builder *bb = *bb_r;
462 bb->used_sz += cf_str_itoa_u32(i, (char *)&bb->buf[bb->used_sz], 10);
463}
464
465void
466cf_buf_builder_append_uint64(cf_buf_builder **bb_r, uint64_t i)
467{
468 BB_RESERVE(8);
469 cf_buf_builder *bb = *bb_r;
470 uint64_t *i_p = (uint64_t *)&bb->buf[bb->used_sz];
471 *i_p = __swab64(i);
472 bb->used_sz += 8;
473}
474
475void
476cf_buf_builder_append_uint32(cf_buf_builder **bb_r, uint32_t i)
477{
478 BB_RESERVE(4);
479 cf_buf_builder *bb = *bb_r;
480 uint32_t *i_p = (uint32_t *)&bb->buf[bb->used_sz];
481 *i_p = htonl(i);
482 bb->used_sz += 4;
483}
484
485void
486cf_buf_builder_append_uint16(cf_buf_builder **bb_r, uint16_t i)
487{
488 BB_RESERVE(2);
489 cf_buf_builder *bb = *bb_r;
490 uint16_t *i_p = (uint16_t *)&bb->buf[bb->used_sz];
491 *i_p = htons(i);
492 bb->used_sz += 2;
493}
494
495void
496cf_buf_builder_append_uint8(cf_buf_builder **bb_r, uint8_t i)
497{
498 BB_RESERVE(1);
499 cf_buf_builder *bb = *bb_r;
500 bb->buf[bb->used_sz] = i;
501 bb->used_sz ++;
502}
503
504void
505cf_buf_builder_reserve(cf_buf_builder **bb_r, int sz, uint8_t **buf)
506{
507 BB_RESERVE(sz);
508 cf_buf_builder *bb = *bb_r;
509
510 if (buf) {
511 *buf = &bb->buf[bb->used_sz];
512 }
513
514 bb->used_sz += sz;
515}
516
517int
518cf_buf_builder_size(cf_buf_builder *bb)
519{
520 return bb->alloc_sz + sizeof(cf_buf_builder);
521}
522
523void
524cf_buf_builder_chomp(cf_buf_builder *bb)
525{
526 if (bb->used_sz > 0) {
527 bb->used_sz--;
528 }
529}
530
531cf_buf_builder *
532cf_buf_builder_create(size_t sz)
533{
534 size_t malloc_sz = (sz < 1024) ? 1024 : sz;
535 cf_buf_builder *bb = cf_malloc(malloc_sz);
536
537 bb->alloc_sz = malloc_sz - sizeof(cf_buf_builder);
538 bb->used_sz = 0;
539
540 return bb;
541}
542
543void
544cf_buf_builder_free(cf_buf_builder *bb)
545{
546 cf_free(bb);
547}
548
549void
550cf_buf_builder_reset(cf_buf_builder *bb)
551{
552 bb->used_sz = 0;
553}
554
555
556
557// TODO - We've only implemented a few cf_ll_buf methods for now. We'll add more
558// functionality if and when it's needed.
559
560void
561cf_ll_buf_grow(cf_ll_buf *llb, size_t sz)
562{
563 size_t buf_sz = sz > llb->head->buf_sz ? sz : llb->head->buf_sz;
564 cf_ll_buf_stage *new_tail = cf_malloc(sizeof(cf_ll_buf_stage) + buf_sz);
565
566 new_tail->next = NULL;
567 new_tail->buf_sz = buf_sz;
568 new_tail->used_sz = 0;
569
570 llb->tail->next = new_tail;
571 llb->tail = new_tail;
572}
573
574#define LLB_RESERVE(_n) \
575 if (_n > llb->tail->buf_sz - llb->tail->used_sz) { \
576 cf_ll_buf_grow(llb, _n); \
577 }
578
579void
580cf_ll_buf_reserve(cf_ll_buf *llb, size_t sz, uint8_t **from)
581{
582 LLB_RESERVE(sz);
583
584 if (from) {
585 *from = llb->tail->buf + llb->tail->used_sz;
586 }
587
588 llb->tail->used_sz += sz;
589}
590
591void
592cf_ll_buf_free(cf_ll_buf *llb)
593{
594 cf_ll_buf_stage *cur = llb->head_is_stack ? llb->head->next : llb->head;
595
596 while (cur) {
597 cf_ll_buf_stage *temp = cur;
598
599 cur = cur->next;
600 cf_free(temp);
601 }
602}
603