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