Merge branch 'release/1.3'
[reservoir_sample] / buf.c
1 #include <stdlib.h>
2 #include <string.h>
3 #include <errno.h>
4 #include <assert.h>
5
6 #include "buf.h"
7 #include "notify.h"
8 #include "test_suite.h"
9
10 /* If these ever need to be overridden... */
11 void *(*buf_malloc_)(size_t) = malloc;
12 void *(*buf_realloc_)(void *, size_t) = realloc;
13 void (*buf_free_)(void *) = free;
14
15 inline
16 buf_t buf_new(size_t sz) {
17 buf_t buf = buf_malloc_(sz + sizeof *buf);
18 if (buf) {
19 buf->buf_sz = sz;
20 buf->buf_start = buf->buf_used = 0;
21 memset(buf->buf, 0, sz);
22 }
23 return buf;
24 }
25
26 inline
27 void buf_del(buf_t *pbuf) {
28 if (pbuf) {
29 if (*pbuf) {
30 buf_free_(*pbuf);
31 *pbuf = NULL;
32 }
33 }
34 }
35
36 inline
37 void buf_rebase(buf_t buf) {
38 if (buf->buf_start == 0)
39 return;
40 memmove(buf->buf, buf->buf + buf->buf_start, buf->buf_used);
41 buf->buf_start = 0;
42 }
43
44 inline
45 int buf_makeroom(buf_t *pbuf, size_t roomfor) {
46 size_t new_sz;
47 void *tmp_ptr;
48
49 assert(pbuf != NULL);
50
51 if (*pbuf == NULL) {
52 *pbuf = buf_new(roomfor);
53 if (*pbuf == NULL) {
54 return -1;
55 }
56 }
57
58 buf_rebase(*pbuf);
59
60 if (BUF_ROOM(*pbuf) >= roomfor)
61 return 0;
62
63 new_sz = (*pbuf)->buf_used + roomfor;
64 tmp_ptr = buf_realloc_(*pbuf, new_sz + sizeof **pbuf);
65 if (tmp_ptr == NULL) {
66 NOTIFY_ERROR("%s:%s", "realloc", strerror(errno));
67 return -1;
68 }
69 *pbuf = tmp_ptr;
70 (*pbuf)->buf_sz = new_sz;
71
72 return 0;
73 }
74
75 inline
76 int buf_range_dup_or_append(buf_t src, size_t src_offset, size_t n, buf_t *pdst) {
77 assert(src != NULL);
78 assert(pdst != NULL);
79 assert(src_offset + n <= src->buf_used);
80
81 if (buf_makeroom(pdst, n)) {
82 return -1;
83 }
84
85 memcpy((*pdst)->buf + (*pdst)->buf_used, src->buf + src->buf_start + src_offset, n);
86 (*pdst)->buf_used += n;
87
88 return 0;
89 }
90
91 /* Room for improvement:
92 Depending on ratio of flensed portion to buffer use, try to do less
93 copying-around of buffer ranges, by swapping buf pointers instead.
94 */
95 ssize_t buf_flense(buf_t *psrc, size_t src_offset, int delimiter, buf_t *pdst) {
96 const size_t delimiter_len = 1;
97 size_t i;
98
99 assert(psrc != NULL);
100 assert(src_offset <= (*psrc)->buf_used);
101
102 NOTIFY_DEBUG("src_offset:%zu", src_offset);
103 D_BUF("src ", *psrc);
104 D_BUF("dst ", pdst ? *pdst : NULL);
105
106 for (i = src_offset; i < (*psrc)->buf_used; i++) {
107 if ((*psrc)->buf[(*psrc)->buf_start + i] == delimiter) {
108
109 if (pdst != NULL) {
110 if (buf_range_dup_or_append((*psrc), 0, i, pdst)) {
111 return -1;
112 }
113 }
114
115 (*psrc)->buf_start += i + delimiter_len;
116 (*psrc)->buf_used -= i + delimiter_len;
117
118 D_BUF("src ", *psrc);
119 D_BUF("dst ", pdst ? *pdst : NULL);
120 return i + delimiter_len;
121 }
122 }
123
124 return 0;
125 }
126
127 #ifdef TEST
128
129 static const char buf_flense_testdata1_[] = "a\nbc\ndef\nghij\nklmno\npqr\ns\ntuv\nwxyz0\n1234\n567\n89\n0leftovers";
130 static const char buf_flense_testdata2_[] = "\n\nfoo\nbar\n\n";
131
132 struct buf_flense_expected_result_ {
133 ssize_t r;
134 const char *buf;
135 } buf_flense_expected1[] = {
136 { 1 + 1, "a" },
137 { 2 + 1, "bc" },
138 { 3 + 1, "def" },
139 { 4 + 1, "ghij" },
140 { 5 + 1, "klmno" },
141 { 3 + 1, "pqr" },
142 { 1 + 1, "s" },
143 { 3 + 1, "tuv" },
144 { 5 + 1, "wxyz0" },
145 { 4 + 1, "1234" },
146 { 3 + 1, "567" },
147 { 2 + 1, "89" },
148 { 0, "0leftovers" },
149 }, buf_flense_expected2[] = {
150 { 0 + 1, "" },
151 { 0 + 1, "" },
152 { 3 + 1, "foo" },
153 { 3 + 1, "bar" },
154 { 0 + 1, "" },
155 { 0, "" },
156 };
157
158 struct test_buf_flense_data_ {
159 const char *src;
160 const struct buf_flense_expected_result_ *expected;
161 } test_buf_flense_data1 = {
162 .src = buf_flense_testdata1_,
163 .expected = buf_flense_expected1,
164 }, test_buf_flense_data2 = {
165 .src = buf_flense_testdata2_,
166 .expected = buf_flense_expected2,
167 };
168
169 static
170 int test_buf_flense_(void *test_arg, void *suite_arg) {
171 (void)suite_arg;
172 struct test_buf_flense_data_ *test_data = (struct test_buf_flense_data_ *)test_arg;
173 const char testdata_len = strlen(test_data->src);
174 const struct buf_flense_expected_result_ *next_result = test_data->expected;
175 int retval = 0;
176 buf_t src;
177
178 TEST_INFO("initializing src buffer with %zu characters", testdata_len);
179 src = buf_new(testdata_len);
180 if (src == NULL) {
181 TEST_ERROR("%s:%s", "new_buf_", "failed");
182 return -1;
183 }
184 memcpy(src->buf, test_data->src, testdata_len);
185 src->buf_used += testdata_len;
186
187 D_BUF("src ", src);
188
189 for (;;) {
190 ssize_t r;
191 buf_t dst = NULL;
192
193 r = buf_flense(&src, 0, '\n', &dst);
194 if (r != next_result->r) {
195 TEST_ERROR("result '%zd' does not match expected result '%zd'", r, next_result->r);
196 retval = -1;
197 }
198 if (r == 0) {
199 TEST_INFO("finished flensing");
200 break;
201 }
202
203 if (strlen(next_result->buf) > dst->buf_used) {
204 TEST_ERROR("flensed buffer smaller than expected result '%s'", next_result->buf);
205 D_BUF("dst ", dst);
206 retval = -1;
207 } else if (memcmp(next_result->buf, dst->buf + dst->buf_start, strlen(next_result->buf))) {
208 TEST_ERROR("flensed buffer does not match expected result '%s'", next_result->buf);
209 D_BUF("dst ", dst);
210 retval = -1;
211 }
212
213 TEST_INFO("flensed: '%.*s'", (int)dst->buf_used, dst->buf + dst->buf_start);
214
215 memset(dst, 0, dst->buf_sz + sizeof *dst);
216 buf_del(&dst);
217
218 next_result++;
219 }
220 if (strlen(next_result->buf) > src->buf_used) {
221 TEST_ERROR("remaining buffer smaller than expected result '%s'", next_result->buf);
222 D_BUF("src ", src);
223 retval = -1;
224 } else if (memcmp(next_result->buf, src->buf + src->buf_start, strlen(next_result->buf))) {
225 TEST_ERROR("remaining buffer does not match expected result '%s'", next_result->buf);
226 D_BUF("src ", src);
227 retval = -1;
228 }
229
230 TEST_INFO("remains: '%.*s'", (int)src->buf_used, src->buf + src->buf_start);
231
232 memset(src, 0, src->buf_sz + sizeof *src);
233 buf_del(&src);
234
235 return retval;
236 }
237
238 void *test_suite_data;
239
240 int test_suite_pre(void *suite_data) {
241 (void)suite_data;
242 return 0;
243 }
244
245 int test_suite_post(void *suite_data) {
246 (void)suite_data;
247 return 0;
248 }
249
250 test_t test_suite[] = {
251 { "test_buf_flense_ 1", test_buf_flense_, NULL, NULL, &test_buf_flense_data1 },
252 { "test_buf_flense_ 2", test_buf_flense_, NULL, NULL, &test_buf_flense_data2 },
253 { NULL, NULL, NULL, NULL, NULL },
254 };
255
256 #endif /* TEST */