1 #include "private.h"
2
3 #include <assert.h>
4 #include <stdlib.h>
5 #include <stddef.h>
6 #include <string.h>
7
8 #include "sb.h"
9
10 #if defined(BINARY_TYTEST)
11 #include "unit_tests.h"
12 #endif
13
14 int
ty_sb_add(struct ty_sb * sb,const char * s,size_t len)15 ty_sb_add(struct ty_sb *sb, const char *s, size_t len)
16 {
17 size_t new_len = sb->len + len;
18
19 if ((new_len + sb->gap >= sb->alloc) || !sb->buf)
20 {
21 size_t new_alloc = ((new_len + sb->gap + 15) / 16) * 24;
22 char *new_buf;
23 char *buf = sb->buf;
24
25 if (buf && sb->gap)
26 buf -= sb->gap;
27 new_buf = realloc(buf, new_alloc);
28 if (new_buf == NULL)
29 return -1;
30 sb->buf = new_buf + sb->gap;
31 sb->alloc = new_alloc;
32 }
33 memcpy(sb->buf + sb->len, s, len);
34 sb->len += len;
35 sb->buf[sb->len] = '\0';
36 return 0;
37 }
38
39 int
ty_sb_prepend(struct ty_sb * sb,const char * s,size_t len)40 ty_sb_prepend(struct ty_sb *sb, const char *s, size_t len)
41 {
42 if (len >= sb->gap)
43 {
44 size_t aligned_gap = ((len + 15) / 16) * 24;
45 size_t third_of_alloc = (((sb->alloc / 3) + 15) / 16) * 16;
46 size_t new_gap = MAX(aligned_gap, third_of_alloc);
47 size_t new_alloc = sb->alloc + new_gap;
48 char *new_buf;
49
50 new_buf = calloc(new_alloc, 1);
51 if (new_buf == NULL)
52 return -1;
53
54 memcpy(new_buf + new_gap, sb->buf, sb->len);
55 free(sb->buf - sb->gap);
56 sb->buf = new_buf + new_gap;
57 sb->gap = new_gap;
58 sb->alloc = new_alloc;
59 }
60
61 sb->buf -= len;
62 sb->gap -= len;
63 sb->len += len;
64 memcpy(sb->buf, s, len);
65 return 0;
66 }
67
68 /* unlike eina_strbuf_rtrim, only trims \t, \f, ' ' */
69 void
ty_sb_spaces_ltrim(struct ty_sb * sb)70 ty_sb_spaces_ltrim(struct ty_sb *sb)
71 {
72 if (!sb->buf)
73 return;
74
75 while (sb->len > 0)
76 {
77 char c = sb->buf[0];
78 if ((c != ' ') && (c != '\t') && (c != '\f'))
79 break;
80 sb->len--;
81 sb->buf++;
82 sb->gap++;
83 }
84 sb->buf[sb->len] = '\0';
85 }
86
87 /* unlike eina_strbuf_rtrim, only trims \t, \f, ' ' */
88 void
ty_sb_spaces_rtrim(struct ty_sb * sb)89 ty_sb_spaces_rtrim(struct ty_sb *sb)
90 {
91 if (!sb->buf)
92 return;
93
94 while (sb->len > 0)
95 {
96 char c = sb->buf[sb->len - 1];
97 if ((c != ' ') && (c != '\t') && (c != '\f'))
98 break;
99 sb->len--;
100 }
101 sb->buf[sb->len] = '\0';
102 }
103
104 char *
ty_sb_steal_buf(struct ty_sb * sb)105 ty_sb_steal_buf(struct ty_sb *sb)
106 {
107 char *buf;
108
109 if (!sb->len)
110 return NULL;
111
112 if (sb->gap != 0)
113 {
114 size_t i;
115
116 sb->buf -= sb->gap;
117 for (i = 0; i <= sb->len; i++)
118 {
119 sb->buf[i] = sb->buf[i + sb->gap];
120 }
121 sb->gap = 0;
122 }
123
124 sb->alloc = 0;
125 sb->gap = 0;
126 sb->len = 0;
127
128 buf = sb->buf;
129
130 sb->buf = NULL;
131
132 return buf;
133 }
134
135 void
ty_sb_lskip(struct ty_sb * sb,size_t len)136 ty_sb_lskip(struct ty_sb *sb, size_t len)
137 {
138 if (len >= sb->len)
139 len = sb->len;
140 sb->len -= len;
141 if (sb->len)
142 {
143 sb->gap += len;
144 sb->buf += len;
145 }
146 else
147 {
148 /* buffer is empty, get rid of gap */
149 if (sb->buf)
150 sb->buf -= sb->gap;
151 sb->gap = 0;
152 }
153 }
154
155 void
ty_sb_rskip(struct ty_sb * sb,size_t len)156 ty_sb_rskip(struct ty_sb *sb, size_t len)
157 {
158 if (len >= sb->len)
159 len = sb->len;
160 sb->len -= len;
161 if (sb->alloc)
162 sb->buf[sb->len] = '\0';
163 }
164
165 void
ty_sb_free(struct ty_sb * sb)166 ty_sb_free(struct ty_sb *sb)
167 {
168 char *buf = sb->buf;
169 if (buf && sb->gap)
170 buf -= sb->gap;
171 free(buf);
172 sb->gap = sb->len = sb->alloc = 0;
173 sb->buf = NULL;
174 }
175
176 #if defined(BINARY_TYTEST)
177 int
tytest_sb_skip(void)178 tytest_sb_skip(void)
179 {
180 struct ty_sb sb = {};
181 const char *data = "foobar";
182
183 /* lskip normal */
184 assert(ty_sb_add(&sb, data, strlen(data)) == 0);
185 ty_sb_lskip(&sb, 3);
186 assert(sb.len == strlen(data) - 3);
187 assert(sb.gap == 3);
188 assert(strncmp(sb.buf, data+3, sb.len) == 0);
189 /* lskip too large */
190 ty_sb_lskip(&sb, 30);
191 assert(sb.len == 0);
192 assert(sb.gap == 0);
193 ty_sb_free(&sb);
194
195 /* lskip all */
196 assert(ty_sb_add(&sb, data, strlen(data)) == 0);
197 ty_sb_lskip(&sb, strlen(data));
198 assert(sb.len == 0);
199 assert(sb.gap == 0);
200 ty_sb_free(&sb);
201 /* lskip empty */
202 ty_sb_lskip(&sb, 3);
203 assert(sb.len == 0);
204 assert(sb.gap == 0);
205
206 /* rskip normal */
207 assert(ty_sb_add(&sb, data, strlen(data)) == 0);
208 ty_sb_rskip(&sb, 3);
209 assert(sb.len == strlen(data) - 3);
210 assert(sb.gap == 0);
211 assert(strncmp(sb.buf, data, sb.len) == 0);
212 /* rskip too large */
213 ty_sb_rskip(&sb, 30);
214 assert(sb.len == 0);
215 assert(sb.gap == 0);
216 ty_sb_free(&sb);
217
218 /* rskip all */
219 assert(ty_sb_add(&sb, data, strlen(data)) == 0);
220 ty_sb_rskip(&sb, strlen(data));
221 assert(sb.len == 0);
222 assert(sb.gap == 0);
223 ty_sb_free(&sb);
224 /* rskip empty */
225 ty_sb_rskip(&sb, 3);
226 assert(sb.len == 0);
227 assert(sb.gap == 0);
228
229 return 0;
230 }
231
232 int
tytest_sb_trim(void)233 tytest_sb_trim(void)
234 {
235 struct ty_sb sb = {};
236
237 assert(ty_sb_add(&sb,
238 " \f \t sb_trim_spaces \t ",
239 strlen(" \f \t sb_trim_spaces \t ")) == 0);
240
241 ty_sb_spaces_ltrim(&sb);
242 assert(sb.gap == 5);
243 assert(strncmp(sb.buf, "sb", 2) == 0);
244 ty_sb_spaces_rtrim(&sb);
245 assert(sb.gap == 5);
246 assert(sb.len == strlen("sb_trim_spaces"));
247 assert(strncmp(sb.buf, "sb_trim_spaces", sb.len) == 0);
248
249 ty_sb_free(&sb);
250 /* on empty */
251 ty_sb_spaces_rtrim(&sb);
252 ty_sb_spaces_ltrim(&sb);
253 return 0;
254 }
255
256 int
tytest_sb_gap(void)257 tytest_sb_gap(void)
258 {
259 struct ty_sb sb = {};
260 const char *data = "alpha bravo charlie delta";
261
262 assert(ty_sb_add(&sb, data, strlen(data)) == 0);
263 /* prepend with no gap */
264 assert(ty_sb_prepend(&sb, ">>>", strlen(">>>")) == 0);
265 assert(sb.len == strlen(data) + 3);
266 assert(strncmp(sb.buf,">>>alpha", 3+5) == 0);
267 /* make gap */
268 ty_sb_lskip(&sb, 3);
269 /* prepend with enough gap */
270 assert(ty_sb_prepend(&sb, "!!", strlen("!!")) == 0);
271 assert(strncmp(sb.buf,"!!alpha", 2+5) == 0);
272 /* make gap larger */
273 ty_sb_lskip(&sb, 2);
274 /* prepend with not enough gap */
275 assert(ty_sb_prepend(&sb, ">>>>>>", strlen(">>>>>>")) == 0);
276 assert(sb.len == strlen(data) + 6);
277 assert(strncmp(sb.buf,">>>>>>alpha", 6+5) == 0);
278 ty_sb_free(&sb);
279
280 /** add that realloc, with gap */
281 assert(ty_sb_add(&sb, "foobar", strlen("foobar")) == 0);
282 ty_sb_lskip(&sb, 3);
283 assert(ty_sb_add(&sb, data, strlen(data)) == 0);
284
285 ty_sb_free(&sb);
286 return 0;
287 }
288
289 int
tytest_sb_steal(void)290 tytest_sb_steal(void)
291 {
292 struct ty_sb sb = {};
293 const char *data = "foobar foobar";
294 char *buf;
295 assert(ty_sb_steal_buf(&sb) == NULL);
296
297 /* with gap */
298 assert(ty_sb_add(&sb, data, strlen(data)) == 0);
299 ty_sb_lskip(&sb, 7);
300 buf = ty_sb_steal_buf(&sb);
301 assert(strlen(buf) == strlen("foobar"));
302 assert(sb.buf == NULL);
303 assert(sb.len == 0);
304 assert(sb.alloc == 0);
305 assert(sb.gap == 0);
306 free(buf);
307 ty_sb_free(&sb);
308 return 0;
309 }
310 #endif
311