Tue, 04 Oct 2022 19:25:07 +0200
fix over-optimization of strstr
1. it's actually less performant to frequently read bytes
from an array instead of using the native word length
2. the SBO buffer should be local and not static to allow
multi-threading usage
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
29 #include "cx/buffer.h"
31 #include <gtest/gtest.h>
32 #include "util_allocator.h"
34 class BufferFixture : public ::testing::Test {
35 protected:
36 void SetUp() override {
37 cxBufferInit(&buf, nullptr, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
38 buf.size = 6;
39 buf.pos = 3;
40 }
42 void TearDown() override {
43 cxBufferDestroy(&buf);
44 }
46 CxBuffer buf{};
47 };
49 static void expect_default_flush_config(CxBuffer *buf) {
50 EXPECT_EQ(buf->flush_blkmax, 0);
51 EXPECT_EQ(buf->flush_blksize, 4096);
52 EXPECT_EQ(buf->flush_threshold, SIZE_MAX);
53 EXPECT_EQ(buf->flush_func, nullptr);
54 EXPECT_EQ(buf->flush_target, nullptr);
55 }
57 TEST(BufferInit, WrapSpace) {
58 CxTestingAllocator alloc;
59 CxBuffer buf;
60 void *space = cxMalloc(&alloc, 16);
61 cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_DEFAULT);
62 expect_default_flush_config(&buf);
63 EXPECT_EQ(buf.space, space);
64 EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0);
65 EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, 0);
66 EXPECT_EQ(buf.pos, 0);
67 EXPECT_EQ(buf.size, 0);
68 EXPECT_EQ(buf.capacity, 16);
69 EXPECT_EQ(buf.allocator, &alloc);
70 cxBufferDestroy(&buf);
71 EXPECT_FALSE(alloc.verify());
72 cxFree(&alloc, space);
73 EXPECT_TRUE(alloc.verify());
74 }
76 TEST(BufferInit, WrapSpaceAutoExtend) {
77 CxTestingAllocator alloc;
78 CxBuffer buf;
79 void *space = cxMalloc(&alloc, 16);
80 cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_AUTO_EXTEND);
81 expect_default_flush_config(&buf);
82 EXPECT_EQ(buf.space, space);
83 EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, CX_BUFFER_AUTO_EXTEND);
84 EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, 0);
85 EXPECT_EQ(buf.pos, 0);
86 EXPECT_EQ(buf.size, 0);
87 EXPECT_EQ(buf.capacity, 16);
88 EXPECT_EQ(buf.allocator, &alloc);
89 cxBufferDestroy(&buf);
90 EXPECT_FALSE(alloc.verify());
91 cxFree(&alloc, space);
92 EXPECT_TRUE(alloc.verify());
93 }
95 TEST(BufferInit, WrapSpaceAutoFree) {
96 CxTestingAllocator alloc;
97 CxBuffer buf;
98 void *space = cxMalloc(&alloc, 16);
99 cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_FREE_CONTENTS);
100 expect_default_flush_config(&buf);
101 EXPECT_EQ(buf.space, space);
102 EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0);
103 EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS);
104 EXPECT_EQ(buf.pos, 0);
105 EXPECT_EQ(buf.size, 0);
106 EXPECT_EQ(buf.capacity, 16);
107 EXPECT_EQ(buf.allocator, &alloc);
108 EXPECT_FALSE(alloc.verify());
109 cxBufferDestroy(&buf);
110 EXPECT_TRUE(alloc.verify());
111 }
113 TEST(BufferInit, FreshSpace) {
114 CxTestingAllocator alloc;
115 CxBuffer buf;
116 cxBufferInit(&buf, nullptr, 8, &alloc, CX_BUFFER_DEFAULT);
117 expect_default_flush_config(&buf);
118 EXPECT_NE(buf.space, nullptr);
119 EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0);
120 EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS);
121 EXPECT_EQ(buf.pos, 0);
122 EXPECT_EQ(buf.size, 0);
123 EXPECT_EQ(buf.capacity, 8);
124 EXPECT_EQ(buf.allocator, &alloc);
125 EXPECT_FALSE(alloc.verify()); // space is still allocated
126 cxBufferDestroy(&buf);
127 EXPECT_TRUE(alloc.verify());
128 }
130 class BufferShiftFixture : public ::testing::Test {
131 protected:
132 void SetUp() override {
133 ASSERT_TRUE(alloc.verify());
134 cxBufferInit(&buf, nullptr, 16, &alloc, CX_BUFFER_DEFAULT);
135 memcpy(buf.space, "test____________", 16);
136 buf.capacity = 8; // purposely pretend that the buffer has less capacity s.t. we can test beyond the range
137 buf.pos = 4;
138 buf.size = 4;
139 }
141 void TearDown() override {
142 cxBufferDestroy(&buf);
143 EXPECT_TRUE(alloc.verify());
144 }
146 CxTestingAllocator alloc;
147 CxBuffer buf{};
148 };
150 class BufferShiftLeft : public BufferShiftFixture {
151 };
153 TEST_F(BufferShiftLeft, Zero) {
154 ASSERT_EQ(buf.pos, 4);
155 ASSERT_EQ(buf.size, 4);
156 int ret = cxBufferShiftLeft(&buf, 0);
157 EXPECT_EQ(ret, 0);
158 EXPECT_EQ(buf.pos, 4);
159 EXPECT_EQ(buf.size, 4);
160 EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
161 }
163 TEST_F(BufferShiftLeft, ZeroOffsetInterface) {
164 ASSERT_EQ(buf.pos, 4);
165 ASSERT_EQ(buf.size, 4);
166 int ret = cxBufferShift(&buf, -0);
167 EXPECT_EQ(ret, 0);
168 EXPECT_EQ(buf.pos, 4);
169 EXPECT_EQ(buf.size, 4);
170 EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
171 }
173 TEST_F(BufferShiftLeft, Standard) {
174 ASSERT_EQ(buf.pos, 4);
175 ASSERT_EQ(buf.size, 4);
176 int ret = cxBufferShiftLeft(&buf, 2);
177 EXPECT_EQ(ret, 0);
178 EXPECT_EQ(buf.pos, 2);
179 EXPECT_EQ(buf.size, 2);
180 EXPECT_TRUE(memcmp(buf.space, "stst________", 8) == 0);
181 }
183 TEST_F(BufferShiftLeft, Overshift) {
184 ASSERT_LT(buf.pos, 6);
185 ASSERT_LT(buf.size, 6);
186 int ret = cxBufferShiftLeft(&buf, 6);
187 EXPECT_EQ(ret, 0);
188 EXPECT_EQ(buf.pos, 0);
189 EXPECT_EQ(buf.size, 0);
190 EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
191 }
193 TEST_F(BufferShiftLeft, OvershiftPosOnly) {
194 buf.pos = 2;
195 ASSERT_EQ(buf.size, 4);
196 int ret = cxBufferShiftLeft(&buf, 3);
197 EXPECT_EQ(ret, 0);
198 EXPECT_EQ(buf.pos, 0);
199 EXPECT_EQ(buf.size, 1);
200 EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
201 }
203 TEST_F(BufferShiftLeft, OffsetInterface) {
204 buf.pos = 3;
205 ASSERT_EQ(buf.size, 4);
206 int ret = cxBufferShift(&buf, -2);
207 EXPECT_EQ(ret, 0);
208 EXPECT_EQ(buf.pos, 1);
209 EXPECT_EQ(buf.size, 2);
210 EXPECT_TRUE(memcmp(buf.space, "stst________", 8) == 0);
211 }
213 class BufferShiftRight : public BufferShiftFixture {
214 };
216 TEST_F(BufferShiftRight, Zero) {
217 ASSERT_EQ(buf.pos, 4);
218 ASSERT_EQ(buf.size, 4);
219 int ret = cxBufferShiftRight(&buf, 0);
220 EXPECT_EQ(ret, 0);
221 EXPECT_EQ(buf.pos, 4);
222 EXPECT_EQ(buf.size, 4);
223 EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
224 }
226 TEST_F(BufferShiftRight, ZeroOffsetInterface) {
227 ASSERT_EQ(buf.pos, 4);
228 ASSERT_EQ(buf.size, 4);
229 int ret = cxBufferShift(&buf, +0);
230 EXPECT_EQ(ret, 0);
231 EXPECT_EQ(buf.pos, 4);
232 EXPECT_EQ(buf.size, 4);
233 EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
234 }
236 TEST_F(BufferShiftRight, Standard) {
237 ASSERT_EQ(buf.pos, 4);
238 ASSERT_EQ(buf.size, 4);
239 int ret = cxBufferShiftRight(&buf, 3);
240 EXPECT_EQ(ret, 0);
241 EXPECT_EQ(buf.pos, 7);
242 EXPECT_EQ(buf.size, 7);
243 EXPECT_TRUE(memcmp(buf.space, "testest_____", 8) == 0);
244 }
246 TEST_F(BufferShiftRight, OvershiftDiscard) {
247 ASSERT_EQ(buf.pos, 4);
248 ASSERT_EQ(buf.size, 4);
249 ASSERT_EQ(buf.capacity, 8);
250 int ret = cxBufferShiftRight(&buf, 6);
251 EXPECT_EQ(ret, 0);
252 EXPECT_EQ(buf.pos, 8);
253 EXPECT_EQ(buf.size, 8);
254 EXPECT_EQ(buf.capacity, 8);
255 EXPECT_TRUE(memcmp(buf.space, "test__te____", 8) == 0);
256 }
258 TEST_F(BufferShiftRight, OvershiftExtend) {
259 ASSERT_EQ(buf.pos, 4);
260 ASSERT_EQ(buf.size, 4);
261 ASSERT_EQ(buf.capacity, 8);
262 buf.flags |= CX_BUFFER_AUTO_EXTEND;
263 int ret = cxBufferShiftRight(&buf, 6);
264 EXPECT_EQ(ret, 0);
265 EXPECT_EQ(buf.pos, 10);
266 EXPECT_EQ(buf.size, 10);
267 EXPECT_GE(buf.capacity, 10);
268 EXPECT_TRUE(memcmp(buf.space, "test__test__", 8) == 0);
269 }
271 TEST_F(BufferShiftRight, OffsetInterface) {
272 buf.pos = 3;
273 ASSERT_EQ(buf.size, 4);
274 int ret = cxBufferShift(&buf, 2);
275 EXPECT_EQ(ret, 0);
276 EXPECT_EQ(buf.pos, 5);
277 EXPECT_EQ(buf.size, 6);
278 EXPECT_TRUE(memcmp(buf.space, "tetest______", 8) == 0);
279 }
281 TEST(BufferMinimumCapacity, Sufficient) {
282 CxTestingAllocator alloc;
283 auto space = cxMalloc(&alloc, 8);
284 CxBuffer buf;
285 cxBufferInit(&buf, space, 8, &alloc, CX_BUFFER_FREE_CONTENTS);
286 memcpy(space, "Testing", 8);
287 buf.size = 8;
288 cxBufferMinimumCapacity(&buf, 6);
289 EXPECT_EQ(buf.capacity, 8);
290 EXPECT_EQ(buf.size, 8);
291 EXPECT_TRUE(memcmp(buf.space, "Testing", 8) == 0);
292 cxBufferDestroy(&buf);
293 EXPECT_TRUE(alloc.verify());
294 }
296 TEST(BufferMinimumCapacity, Extend) {
297 CxTestingAllocator alloc;
298 auto space = cxMalloc(&alloc, 8);
299 CxBuffer buf;
300 cxBufferInit(&buf, space, 8, &alloc, CX_BUFFER_FREE_CONTENTS); // NO auto extend!
301 memcpy(space, "Testing", 8);
302 buf.size = 8;
303 cxBufferMinimumCapacity(&buf, 16);
304 EXPECT_EQ(buf.capacity, 16);
305 EXPECT_EQ(buf.size, 8);
306 EXPECT_TRUE(memcmp(buf.space, "Testing", 8) == 0);
307 cxBufferDestroy(&buf);
308 EXPECT_TRUE(alloc.verify());
309 }
311 TEST(BufferClear, Test) {
312 char space[16];
313 strcpy(space, "clear test");
314 CxBuffer buf;
315 cxBufferInit(&buf, space, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
316 ASSERT_EQ(buf.size, 0);
317 // only clear the used part of the buffer
318 cxBufferClear(&buf);
319 EXPECT_EQ(memcmp(space, "clear test", 10), 0);
320 buf.size = 5;
321 buf.pos = 3;
322 cxBufferClear(&buf);
323 EXPECT_EQ(memcmp(space, "\0\0\0\0\0 test", 10), 0);
324 EXPECT_EQ(buf.size, 0);
325 EXPECT_EQ(buf.pos, 0);
326 cxBufferDestroy(&buf);
327 }
329 class BufferWrite : public ::testing::Test {
330 protected:
331 CxBuffer buf{}, target{};
333 void SetUp() override {
334 cxBufferInit(&target, nullptr, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND);
335 cxBufferInit(&buf, nullptr, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
336 buf.capacity = 8; // artificially reduce capacity to check OOB writes
337 memset(buf.space, 0, 16);
338 memcpy(buf.space, "prep", 4);
339 buf.size = buf.pos = 4;
340 }
342 void TearDown() override {
343 cxBufferDestroy(&buf);
344 cxBufferDestroy(&target);
345 }
347 void enableFlushing() {
348 buf.flush_target = ⌖
349 buf.flush_func = reinterpret_cast<cx_write_func>(cxBufferWrite);
350 buf.flush_blkmax = 1;
351 }
352 };
354 static size_t mock_write_limited_rate(
355 void const *ptr,
356 size_t size,
357 __attribute__((unused)) size_t nitems,
358 CxBuffer *buffer
359 ) {
360 // simulate limited target drain capacity
361 static bool full = false;
362 if (full) {
363 full = false;
364 return 0;
365 } else {
366 full = true;
367 return cxBufferWrite(ptr, size, nitems > 2 ? 2 : nitems, buffer);
368 }
369 }
371 TEST_F(BufferWrite, SizeOneFit) {
372 const char *data = "test";
373 ASSERT_EQ(buf.capacity, 8);
374 ASSERT_EQ(buf.pos, 4);
375 ASSERT_EQ(buf.size, 4);
376 size_t written = cxBufferWrite(data, 1, 4, &buf);
377 EXPECT_EQ(written, 4);
378 EXPECT_EQ(buf.size, 8);
379 EXPECT_EQ(buf.pos, 8);
380 EXPECT_EQ(buf.capacity, 8);
381 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
382 }
384 TEST_F(BufferWrite, SizeOneDiscard) {
385 const char *data = "testing";
386 ASSERT_EQ(buf.capacity, 8);
387 ASSERT_EQ(buf.pos, 4);
388 ASSERT_EQ(buf.size, 4);
389 size_t written = cxBufferWrite(data, 1, 7, &buf);
390 EXPECT_EQ(written, 4);
391 EXPECT_EQ(buf.size, 8);
392 EXPECT_EQ(buf.pos, 8);
393 EXPECT_EQ(buf.capacity, 8);
394 EXPECT_EQ(memcmp(buf.space, "preptest\0", 9), 0);
395 }
397 TEST_F(BufferWrite, SizeOneExtend) {
398 buf.flags |= CX_BUFFER_AUTO_EXTEND;
399 const char *data = "testing";
400 ASSERT_EQ(buf.capacity, 8);
401 ASSERT_EQ(buf.pos, 4);
402 ASSERT_EQ(buf.size, 4);
403 size_t written = cxBufferWrite(data, 1, 7, &buf);
404 EXPECT_EQ(written, 7);
405 EXPECT_EQ(buf.size, 11);
406 EXPECT_EQ(buf.pos, 11);
407 EXPECT_GE(buf.capacity, 11);
408 EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0);
409 }
411 TEST_F(BufferWrite, MultibyteFit) {
412 const char *data = "test";
413 ASSERT_EQ(buf.capacity, 8);
414 ASSERT_EQ(buf.pos, 4);
415 ASSERT_EQ(buf.size, 4);
416 size_t written = cxBufferWrite(data, 2, 2, &buf);
417 EXPECT_EQ(written, 2);
418 EXPECT_EQ(buf.size, 8);
419 EXPECT_EQ(buf.pos, 8);
420 EXPECT_EQ(buf.capacity, 8);
421 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
422 }
424 TEST_F(BufferWrite, MultibyteDiscard) {
425 const char *data = "testing";
426 ASSERT_EQ(buf.capacity, 8);
427 ASSERT_EQ(buf.size, 4);
428 buf.pos = 3;
429 size_t written = cxBufferWrite(data, 2, 4, &buf);
430 // remember: whole elements are discarded if they do not fit
431 EXPECT_EQ(written, 2);
432 EXPECT_EQ(buf.size, 7);
433 EXPECT_EQ(buf.pos, 7);
434 EXPECT_EQ(buf.capacity, 8);
435 EXPECT_EQ(memcmp(buf.space, "pretest\0", 8), 0);
436 }
438 TEST_F(BufferWrite, MultibyteExtend) {
439 buf.flags |= CX_BUFFER_AUTO_EXTEND;
440 const char *data = "tester";
441 ASSERT_EQ(buf.capacity, 8);
442 ASSERT_EQ(buf.size, 4);
443 buf.pos = 3;
444 size_t written = cxBufferWrite(data, 2, 3, &buf);
445 // remember: whole elements are discarded if they do not fit
446 EXPECT_EQ(written, 3);
447 EXPECT_EQ(buf.size, 9);
448 EXPECT_EQ(buf.pos, 9);
449 EXPECT_GE(buf.capacity, 9);
450 EXPECT_EQ(memcmp(buf.space, "pretester", 9), 0);
451 }
453 TEST_F(BufferWrite, PutcWrapperFit) {
454 ASSERT_EQ(buf.capacity, 8);
455 ASSERT_EQ(buf.pos, 4);
456 ASSERT_EQ(buf.size, 4);
457 int c = cxBufferPut(&buf, 0x200 | 'a');
458 EXPECT_EQ(c, 'a');
459 EXPECT_EQ(buf.size, 5);
460 EXPECT_EQ(buf.pos, 5);
461 EXPECT_EQ(buf.capacity, 8);
462 EXPECT_EQ(memcmp(buf.space, "prepa\0", 6), 0);
463 }
465 TEST_F(BufferWrite, PutcWrapperDiscard) {
466 ASSERT_EQ(buf.capacity, 8);
467 ASSERT_EQ(buf.size, 4);
468 buf.pos = 8;
469 int c = cxBufferPut(&buf, 0x200 | 'a');
470 EXPECT_EQ(c, EOF);
471 EXPECT_EQ(buf.size, 4);
472 EXPECT_EQ(buf.pos, 8);
473 EXPECT_EQ(buf.capacity, 8);
474 EXPECT_EQ(memcmp(buf.space, "prep\0\0\0\0\0", 9), 0);
475 }
477 TEST_F(BufferWrite, PutcWrapperExtend) {
478 buf.flags |= CX_BUFFER_AUTO_EXTEND;
479 ASSERT_EQ(buf.capacity, 8);
480 ASSERT_EQ(buf.size, 4);
481 buf.pos = 8;
482 int c = cxBufferPut(&buf, 0x200 | 'a');
483 EXPECT_EQ(c, 'a');
484 EXPECT_EQ(buf.size, 9);
485 EXPECT_EQ(buf.pos, 9);
486 EXPECT_GE(buf.capacity, 9);
487 EXPECT_EQ(memcmp(buf.space, "prep\0\0\0\0a", 9), 0);
488 }
490 TEST_F(BufferWrite, PutStringWrapperFit) {
491 const char *data = "test";
492 ASSERT_EQ(buf.capacity, 8);
493 ASSERT_EQ(buf.pos, 4);
494 ASSERT_EQ(buf.size, 4);
495 size_t written = cxBufferPutString(&buf, data);
496 EXPECT_EQ(written, 4);
497 EXPECT_EQ(buf.size, 8);
498 EXPECT_EQ(buf.pos, 8);
499 EXPECT_EQ(buf.capacity, 8);
500 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
501 }
503 TEST_F(BufferWrite, PutStringWrapperDiscard) {
504 const char *data = "testing";
505 ASSERT_EQ(buf.capacity, 8);
506 ASSERT_EQ(buf.pos, 4);
507 ASSERT_EQ(buf.size, 4);
508 size_t written = cxBufferPutString(&buf, data);
509 EXPECT_EQ(written, 4);
510 EXPECT_EQ(buf.size, 8);
511 EXPECT_EQ(buf.pos, 8);
512 EXPECT_EQ(buf.capacity, 8);
513 EXPECT_EQ(memcmp(buf.space, "preptest\0", 9), 0);
514 }
516 TEST_F(BufferWrite, PutStringWrapperExtend) {
517 buf.flags |= CX_BUFFER_AUTO_EXTEND;
518 const char *data = "testing";
519 ASSERT_EQ(buf.capacity, 8);
520 ASSERT_EQ(buf.pos, 4);
521 ASSERT_EQ(buf.size, 4);
522 size_t written = cxBufferPutString(&buf, data);
523 EXPECT_EQ(written, 7);
524 EXPECT_EQ(buf.size, 11);
525 EXPECT_EQ(buf.pos, 11);
526 EXPECT_GE(buf.capacity, 11);
527 EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0);
528 }
530 TEST_F(BufferWrite, MultOverflow) {
531 const char *data = "testing";
532 ASSERT_EQ(buf.capacity, 8);
533 ASSERT_EQ(buf.pos, 4);
534 ASSERT_EQ(buf.size, 4);
535 size_t written = cxBufferWrite(data, 8, SIZE_MAX / 4, &buf);
536 EXPECT_EQ(written, 0);
537 EXPECT_EQ(buf.capacity, 8);
538 EXPECT_EQ(buf.pos, 4);
539 EXPECT_EQ(buf.size, 4);
540 EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0);
541 }
543 TEST_F(BufferWrite, MaxCapaOverflow) {
544 buf.flags |= CX_BUFFER_AUTO_EXTEND;
545 const char *data = "testing";
546 ASSERT_EQ(buf.capacity, 8);
547 ASSERT_EQ(buf.pos, 4);
548 ASSERT_EQ(buf.size, 4);
549 size_t written = cxBufferWrite(data, 1, SIZE_MAX - 2, &buf);
550 EXPECT_EQ(written, 0);
551 EXPECT_EQ(buf.capacity, 8);
552 EXPECT_EQ(buf.pos, 4);
553 EXPECT_EQ(buf.size, 4);
554 EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0);
555 }
557 TEST_F(BufferWrite, OnlyOverwrite) {
558 buf.flags |= CX_BUFFER_AUTO_EXTEND;
559 ASSERT_EQ(buf.capacity, 8);
560 memcpy(buf.space, "preptest", 8);
561 buf.pos = 3;
562 buf.size = 8;
563 size_t written = cxBufferWrite("XXX", 2, 2, &buf);
564 EXPECT_EQ(written, 2);
565 EXPECT_EQ(buf.capacity, 8);
566 EXPECT_EQ(buf.size, 8);
567 EXPECT_EQ(buf.pos, 7);
568 EXPECT_EQ(memcmp(buf.space, "preXXX\0t", 8), 0);
569 }
571 TEST_F(BufferWrite, FlushAtCapacity) {
572 enableFlushing();
573 ASSERT_EQ(buf.capacity, 8);
574 ASSERT_EQ(buf.pos, 4);
575 size_t written = cxBufferWrite("foo", 1, 3, &buf);
576 EXPECT_EQ(written, 3);
577 ASSERT_EQ(buf.pos, 7);
578 ASSERT_EQ(buf.size, 7);
579 ASSERT_EQ(target.pos, 0);
580 ASSERT_EQ(target.size, 0);
581 written = cxBufferWrite("hello", 1, 5, &buf);
582 EXPECT_EQ(written, 5);
583 EXPECT_EQ(buf.pos, 0);
584 EXPECT_EQ(buf.size, 0);
585 EXPECT_EQ(buf.capacity, 8);
586 EXPECT_EQ(target.pos, 12);
587 ASSERT_EQ(target.size, 12);
588 EXPECT_EQ(memcmp(target.space, "prepfoohello", 12), 0);
589 }
591 TEST_F(BufferWrite, FlushAtThreshold) {
592 enableFlushing();
593 buf.flush_threshold = 12;
594 buf.flags |= CX_BUFFER_AUTO_EXTEND;
595 ASSERT_EQ(buf.capacity, 8);
596 ASSERT_EQ(buf.pos, 4);
597 size_t written = cxBufferWrite("foobar", 1, 6, &buf);
598 EXPECT_EQ(written, 6);
599 ASSERT_EQ(buf.pos, 10);
600 ASSERT_EQ(buf.size, 10);
601 ASSERT_GE(buf.capacity, 10);
602 ASSERT_LE(buf.capacity, 12);
603 ASSERT_EQ(target.pos, 0);
604 ASSERT_EQ(target.size, 0);
605 written = cxBufferWrite("hello", 1, 5, &buf);
606 EXPECT_EQ(written, 5);
607 EXPECT_EQ(buf.pos, 0);
608 EXPECT_EQ(buf.size, 0);
609 EXPECT_LE(buf.capacity, 12);
610 EXPECT_EQ(target.pos, 15);
611 ASSERT_EQ(target.size, 15);
612 EXPECT_EQ(memcmp(target.space, "prepfoobarhello", 15), 0);
613 }
615 TEST_F(BufferWrite, FlushRateLimited) {
616 enableFlushing();
617 // limit the rate of the flush function and the capacity of the target
618 target.capacity = 16;
619 target.flags &= ~CX_BUFFER_AUTO_EXTEND;
620 buf.flush_func = (cx_write_func) mock_write_limited_rate;
621 ASSERT_EQ(buf.capacity, 8);
622 ASSERT_EQ(buf.pos, 4);
623 size_t written = cxBufferWrite("foo", 1, 3, &buf);
624 EXPECT_EQ(written, 3);
625 ASSERT_EQ(buf.pos, 7);
626 ASSERT_EQ(buf.size, 7);
627 ASSERT_EQ(target.pos, 0);
628 ASSERT_EQ(target.size, 0);
629 written = cxBufferWrite("hello, world!", 1, 13, &buf);
630 // " world!" fits into this buffer, the remaining stuff is flushed out
631 EXPECT_EQ(written, 13);
632 EXPECT_EQ(buf.pos, 7);
633 EXPECT_EQ(buf.size, 7);
634 EXPECT_EQ(buf.capacity, 8);
635 EXPECT_EQ(memcmp(buf.space, " world!", 7), 0);
636 EXPECT_EQ(target.pos, 13);
637 ASSERT_EQ(target.size, 13);
638 EXPECT_EQ(target.capacity, 16);
639 EXPECT_EQ(memcmp(target.space, "prepfoohello,", 13), 0);
640 }
642 class BufferSeek : public BufferFixture {
643 };
645 TEST_F(BufferSeek, SetZero) {
646 int result = cxBufferSeek(&buf, 0, SEEK_SET);
647 EXPECT_EQ(result, 0);
648 EXPECT_EQ(buf.pos, 0);
649 }
651 TEST_F(BufferSeek, SetValid) {
652 int result = cxBufferSeek(&buf, 5, SEEK_SET);
653 EXPECT_EQ(result, 0);
654 EXPECT_EQ(buf.pos, 5);
655 }
657 TEST_F(BufferSeek, SetInvalid) {
658 ASSERT_EQ(buf.pos, 3);
659 int result = cxBufferSeek(&buf, 6, SEEK_SET);
660 EXPECT_NE(result, 0);
661 EXPECT_EQ(buf.pos, 3);
662 }
664 TEST_F(BufferSeek, CurZero) {
665 ASSERT_EQ(buf.pos, 3);
666 int result = cxBufferSeek(&buf, 0, SEEK_CUR);
667 EXPECT_EQ(result, 0);
668 EXPECT_EQ(buf.pos, 3);
669 }
671 TEST_F(BufferSeek, CurValidPositive) {
672 ASSERT_EQ(buf.pos, 3);
673 int result = cxBufferSeek(&buf, 2, SEEK_CUR);
674 EXPECT_EQ(result, 0);
675 EXPECT_EQ(buf.pos, 5);
676 }
678 TEST_F(BufferSeek, CurValidNegative) {
679 ASSERT_EQ(buf.pos, 3);
680 int result = cxBufferSeek(&buf, -3, SEEK_CUR);
681 EXPECT_EQ(result, 0);
682 EXPECT_EQ(buf.pos, 0);
683 }
685 TEST_F(BufferSeek, CurInvalidPositive) {
686 ASSERT_EQ(buf.pos, 3);
687 int result = cxBufferSeek(&buf, 3, SEEK_CUR);
688 EXPECT_NE(result, 0);
689 EXPECT_EQ(buf.pos, 3);
690 }
692 TEST_F(BufferSeek, CurInvalidNegative) {
693 ASSERT_EQ(buf.pos, 3);
694 int result = cxBufferSeek(&buf, -4, SEEK_CUR);
695 EXPECT_NE(result, 0);
696 EXPECT_EQ(buf.pos, 3);
697 }
699 TEST_F(BufferSeek, EndZero) {
700 ASSERT_EQ(buf.size, 6);
701 int result = cxBufferSeek(&buf, 0, SEEK_END);
702 // the (past-the-)end position is always invalid
703 EXPECT_NE(result, 0);
704 EXPECT_EQ(buf.pos, 3);
705 }
707 TEST_F(BufferSeek, EndValid) {
708 ASSERT_EQ(buf.size, 6);
709 int result = cxBufferSeek(&buf, -6, SEEK_END);
710 EXPECT_EQ(result, 0);
711 EXPECT_EQ(buf.pos, 0);
712 }
714 TEST_F(BufferSeek, EndInvalid) {
715 ASSERT_EQ(buf.size, 6);
716 int result = cxBufferSeek(&buf, 1, SEEK_END);
717 EXPECT_NE(result, 0);
718 EXPECT_EQ(buf.pos, 3);
719 }
721 TEST_F(BufferSeek, WhenceInvalid) {
722 ASSERT_EQ(buf.size, 6);
723 ASSERT_EQ(buf.pos, 3);
724 int result = cxBufferSeek(&buf, 2, 9000);
725 EXPECT_NE(result, 0);
726 EXPECT_EQ(buf.size, 6);
727 EXPECT_EQ(buf.pos, 3);
728 }
730 class BufferEof : public BufferFixture {
731 };
733 TEST_F(BufferEof, Reached) {
734 buf.pos = buf.size;
735 EXPECT_TRUE(cxBufferEof(&buf));
736 buf.pos = buf.size - 1;
737 ASSERT_FALSE(cxBufferEof(&buf));
738 cxBufferPut(&buf, 'a');
739 EXPECT_TRUE(cxBufferEof(&buf));
740 }
742 TEST_F(BufferEof, NotReached) {
743 buf.pos = buf.size - 1;
744 EXPECT_FALSE(cxBufferEof(&buf));
745 buf.pos = 0;
746 cxBufferWrite("test", 1, 5, &buf);
747 EXPECT_FALSE(cxBufferEof(&buf));
748 }
750 class BufferRead : public ::testing::Test {
751 protected:
752 CxBuffer buf{};
754 void SetUp() override {
755 cxBufferInit(&buf, nullptr, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
756 buf.capacity = 8; // artificially reduce capacity to check OOB writes
757 memset(buf.space, 0, 16);
758 memcpy(buf.space, "some data", 9);
759 buf.size = 9;
760 }
762 void TearDown() override {
763 cxBufferDestroy(&buf);
764 }
765 };
767 TEST_F(BufferRead, GetByte) {
768 buf.pos = 2;
769 EXPECT_EQ(cxBufferGet(&buf), 'm');
770 EXPECT_EQ(cxBufferGet(&buf), 'e');
771 EXPECT_EQ(cxBufferGet(&buf), ' ');
772 EXPECT_EQ(cxBufferGet(&buf), 'd');
773 EXPECT_EQ(buf.pos, 6);
774 }
776 TEST_F(BufferRead, GetEof) {
777 buf.pos = buf.size;
778 EXPECT_EQ(cxBufferGet(&buf), EOF);
779 }
781 TEST_F(BufferRead, ReadWithinBounds) {
782 buf.pos = 2;
783 char target[4];
784 auto read = cxBufferRead(&target, 1, 4, &buf);
785 ASSERT_EQ(read, 4);
786 EXPECT_EQ(memcmp(&target, "me d", 4), 0);
787 EXPECT_EQ(buf.pos, 6);
788 }
790 TEST_F(BufferRead, ReadOutOfBounds) {
791 buf.pos = 6;
792 char target[4];
793 auto read = cxBufferRead(&target, 1, 4, &buf);
794 ASSERT_EQ(read, 3);
795 EXPECT_EQ(memcmp(&target, "ata", 3), 0);
796 EXPECT_EQ(buf.pos, 9);
797 }
799 TEST_F(BufferRead, ReadOutOfBoundsMultibyte) {
800 buf.pos = 6;
801 char target[4];
802 target[2] = '\0';
803 auto read = cxBufferRead(&target, 2, 2, &buf);
804 ASSERT_EQ(read, 1);
805 EXPECT_EQ(memcmp(&target, "at\0", 3), 0);
806 EXPECT_EQ(buf.pos, 8);
807 }
809 TEST_F(BufferRead, ReadEof) {
810 buf.pos = 9;
811 char target[4];
812 auto read = cxBufferRead(&target, 1, 1, &buf);
813 ASSERT_EQ(read, 0);
814 EXPECT_EQ(buf.pos, 9);
815 }