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 | |
41 | size_t |
42 | get_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 | |
67 | void |
68 | cf_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 | |
94 | void |
95 | cf_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 | |
103 | void |
104 | cf_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 | |
115 | void |
116 | cf_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 | |
123 | void |
124 | cf_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 | |
133 | void |
134 | cf_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 | |
141 | void |
142 | cf_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 | |
156 | void |
157 | cf_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 | |
163 | void |
164 | cf_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 | |
170 | void |
171 | cf_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 | |
177 | void |
178 | cf_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 | |
184 | void |
185 | cf_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 | |
198 | void |
199 | cf_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 | |
209 | void |
210 | cf_dyn_buf_chomp(cf_dyn_buf *db) |
211 | { |
212 | if (db->used_sz > 0) { |
213 | db->used_sz--; |
214 | } |
215 | } |
216 | |
217 | char * |
218 | cf_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 | |
232 | void |
233 | cf_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 | |
242 | void |
243 | info_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 | |
251 | void |
252 | info_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 | |
260 | void |
261 | info_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 | |
269 | void |
270 | info_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 | |
278 | void |
279 | info_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 | |
287 | void |
288 | info_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 | |
296 | void |
297 | info_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 | |
305 | void |
306 | info_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 | |
319 | static inline void |
320 | append_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 | |
336 | void |
337 | info_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 | |
345 | void |
346 | info_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 | |
354 | void |
355 | info_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 | |
363 | void |
364 | info_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 | |
374 | void |
375 | cf_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 | |
405 | void |
406 | cf_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 | |
414 | void |
415 | cf_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 | |
424 | void |
425 | cf_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 | |
433 | void |
434 | cf_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 | |
441 | void |
442 | cf_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 | |
449 | void |
450 | cf_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 | |
457 | void |
458 | cf_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 | |
465 | void |
466 | cf_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 | |
475 | void |
476 | cf_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 | |
485 | void |
486 | cf_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 | |
495 | void |
496 | cf_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 | |
504 | void |
505 | cf_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 | |
517 | int |
518 | cf_buf_builder_size(cf_buf_builder *bb) |
519 | { |
520 | return bb->alloc_sz + sizeof(cf_buf_builder); |
521 | } |
522 | |
523 | void |
524 | cf_buf_builder_chomp(cf_buf_builder *bb) |
525 | { |
526 | if (bb->used_sz > 0) { |
527 | bb->used_sz--; |
528 | } |
529 | } |
530 | |
531 | cf_buf_builder * |
532 | cf_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 | |
543 | void |
544 | cf_buf_builder_free(cf_buf_builder *bb) |
545 | { |
546 | cf_free(bb); |
547 | } |
548 | |
549 | void |
550 | cf_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 | |
560 | void |
561 | cf_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 | |
579 | void |
580 | cf_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 | |
591 | void |
592 | cf_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 | |