aboutsummaryrefslogtreecommitdiffstats
path: root/include/stringbuilder.h
blob: e695082ad2bbadc2fdf407a49fb1b4eb7321be85 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#ifndef JAC_STRINGBUILDER
#define JAC_STRINGBUILDER

#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct jac_sb {
	char *data;
	size_t size;
	size_t cap;
} jac_sb;

jac_sb jac_sb_empty(void);
jac_sb jac_sb_new(size_t size);
jac_sb jac_sb_from_buf(const char* buf);
jac_sb jac_sb_from_buf_n(const char* buf, size_t size);

void jac_sb_append_buf(jac_sb *sb, const char *c);
void jac_sb_append_buf_n(jac_sb *sb, const char *c, size_t n);
void jac_sb_putc(jac_sb *sb, char c);
void jac_sb_snprintf(jac_sb *sb, const char* fmt, ...);
void jac_sb_vsnprintf(jac_sb *sb, const char* fmt, va_list list);

void jac_sb_free(jac_sb sb);

#define STRINGBUILDER_IMPLEMENTATION
#ifdef STRINGBUILDER_IMPLEMENTATION

#define __SB_REALLOC(sb, n)							\
do {	 									\
	if (sb->size + n >= sb->cap) {						\
		size_t rsz = 1UL <<						\
			(64 - __builtin_clzl((unsigned long)(sb->size+n)));	\
		sb->data = realloc(sb->data, rsz + 1);				\
		sb->cap = rsz;							\
	}									\
	sb->size += n;								\
} while(0)									\

jac_sb jac_sb_empty(void) {
	return (jac_sb){.data = calloc(1, 1), .size = 0, .cap = 0};
}
jac_sb jac_sb_new(size_t size) {
	return (jac_sb){.data = calloc(size + 1, 1), .size = 0, .cap = size};
}
jac_sb jac_sb_from_buf(const char* buf) {
	size_t ln = strlen(buf);
	jac_sb ret = {.data = malloc(ln + 1), .size = ln, .cap = ln};
	memcpy(ret.data, buf, ln);
	ret.data[ret.size] = '\0';
	return ret;
}
jac_sb jac_sb_from_buf_n(const char* buf, size_t size) {
	jac_sb ret = {.data = malloc(size + 1), .size = size, .cap = size};
	memcpy(ret.data, buf, size);
	ret.data[size] = '\0';
	return ret;
}

void jac_sb_append_buf(jac_sb *sb, const char *c) {
	jac_sb_append_buf_n(sb, c, strlen(c));
}
void jac_sb_append_buf_n(jac_sb *sb, const char *c, size_t n) {
	__SB_REALLOC(sb, n);
	memcpy(sb->data + sb->size - n, c, n);
	sb->data[sb->size] = '\0';
}
void jac_sb_putc(jac_sb *sb, char c) {
	if (sb->size + 1 >= sb->cap) {
		sb->cap <<= 1;
		sb->data = realloc(sb->data, sb->cap + 1);
	}
	sb->data[sb->size++] = c;
	sb->data[sb->size] = '\0';
}

void jac_sb_snprintf(jac_sb *sb, const char* fmt, ...) {
	va_list l;
	va_start(l, fmt);
	jac_sb_vsnprintf(sb, fmt, l);
}
void jac_sb_vsnprintf(jac_sb *sb, const char* fmt, va_list list) {
	va_list ls1;
	size_t ln;
try_write:
	/*cap: 4, size: 2 
	 * | a | b | \0 | \0 | \0 |
	 */
	va_copy(ls1, list);
		ln = vsnprintf(sb->data + sb->size, sb->cap - sb->size + 1, fmt, ls1);
	va_end(ls1);
	if (ln >= sb->cap - sb->size + 1) {
		sb->cap <<= 1;
		sb->data = realloc(sb->data, sb->cap + 1);
		goto try_write;
	}
	va_end(list);
	sb->size += ln;
}

void jac_sb_free(jac_sb sb) {
	free(sb.data);
}


#undef __SB_REALLOC
#endif /* STRINGBUILDER_IMPLEMENTATION */

#endif /* JAC_STRINGBUILDER */

/* vim: set ts=8 noet: */