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