GCC Code Coverage Report


Directory: ./
File: buffer.c
Date: 2025-04-06 13:22:55
Exec Total Coverage
Lines: 253 262 96.6%
Functions: 24 24 100.0%
Branches: 121 144 84.0%

Line Branch Exec Source
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 */
28
29 #include "cx/buffer.h"
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <errno.h>
34
35 860 static int buffer_copy_on_write(CxBuffer* buffer) {
36
2/2
✓ Branch 0 taken 856 times.
✓ Branch 1 taken 4 times.
860 if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) return 0;
37 4 void *newspace = cxMalloc(buffer->allocator, buffer->capacity);
38
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (NULL == newspace) return -1;
39 4 memcpy(newspace, buffer->space, buffer->size);
40 4 buffer->space = newspace;
41 4 buffer->flags &= ~CX_BUFFER_COPY_ON_WRITE;
42 4 buffer->flags |= CX_BUFFER_FREE_CONTENTS;
43 4 return 0;
44 }
45
46 209 int cxBufferInit(
47 CxBuffer *buffer,
48 void *space,
49 size_t capacity,
50 const CxAllocator *allocator,
51 int flags
52 ) {
53
2/2
✓ Branch 0 taken 125 times.
✓ Branch 1 taken 84 times.
209 if (allocator == NULL) {
54 125 allocator = cxDefaultAllocator;
55 }
56
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 205 times.
209 if (flags & CX_BUFFER_COPY_ON_EXTEND) {
57 4 flags |= CX_BUFFER_AUTO_EXTEND;
58 }
59 209 buffer->allocator = allocator;
60 209 buffer->flags = flags;
61
2/2
✓ Branch 0 taken 81 times.
✓ Branch 1 taken 128 times.
209 if (!space) {
62 81 buffer->bytes = cxMalloc(allocator, capacity);
63
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 81 times.
81 if (buffer->bytes == NULL) {
64 return -1; // LCOV_EXCL_LINE
65 }
66 81 buffer->flags |= CX_BUFFER_FREE_CONTENTS;
67 } else {
68 128 buffer->bytes = space;
69 }
70 209 buffer->capacity = capacity;
71 209 buffer->size = 0;
72 209 buffer->pos = 0;
73
74 209 buffer->flush = NULL;
75
76 209 return 0;
77 }
78
79 8 int cxBufferEnableFlushing(
80 CxBuffer *buffer,
81 CxBufferFlushConfig config
82 ) {
83 8 buffer->flush = malloc(sizeof(CxBufferFlushConfig));
84 if (buffer->flush == NULL) return -1; // LCOV_EXCL_LINE
85 8 memcpy(buffer->flush, &config, sizeof(CxBufferFlushConfig));
86 8 return 0;
87 }
88
89 315 void cxBufferDestroy(CxBuffer *buffer) {
90
2/2
✓ Branch 0 taken 94 times.
✓ Branch 1 taken 221 times.
315 if (buffer->flags & CX_BUFFER_FREE_CONTENTS) {
91 94 cxFree(buffer->allocator, buffer->bytes);
92 }
93 315 free(buffer->flush);
94 315 memset(buffer, 0, sizeof(CxBuffer));
95 315 }
96
97 1 CxBuffer *cxBufferCreate(
98 void *space,
99 size_t capacity,
100 const CxAllocator *allocator,
101 int flags
102 ) {
103
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (allocator == NULL) {
104 allocator = cxDefaultAllocator;
105 }
106 1 CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer));
107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (buf == NULL) return NULL;
108
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (0 == cxBufferInit(buf, space, capacity, allocator, flags)) {
109 1 return buf;
110 } else {
111 // LCOV_EXCL_START
112 cxFree(allocator, buf);
113 return NULL;
114 // LCOV_EXCL_STOP
115 }
116 }
117
118 1 void cxBufferFree(CxBuffer *buffer) {
119
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (buffer == NULL) return;
120 1 const CxAllocator *allocator = buffer->allocator;
121 1 cxBufferDestroy(buffer);
122 1 cxFree(allocator, buffer);
123 }
124
125 14 int cxBufferSeek(
126 CxBuffer *buffer,
127 off_t offset,
128 int whence
129 ) {
130 size_t npos;
131
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 times.
14 switch (whence) {
132 5 case SEEK_CUR:
133 5 npos = buffer->pos;
134 5 break;
135 3 case SEEK_END:
136 3 npos = buffer->size;
137 3 break;
138 5 case SEEK_SET:
139 5 npos = 0;
140 5 break;
141 1 default:
142 1 errno = EINVAL;
143 1 return -1;
144 }
145
146 13 size_t opos = npos;
147 13 npos += offset;
148
149
7/8
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 2 times.
13 if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) {
150 1 errno = EOVERFLOW;
151 1 return -1;
152 }
153
154
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 9 times.
12 if (npos > buffer->size) {
155 3 return -1;
156 } else {
157 9 buffer->pos = npos;
158 9 return 0;
159 }
160
161 }
162
163 3 void cxBufferClear(CxBuffer *buffer) {
164
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) {
165 2 memset(buffer->bytes, 0, buffer->size);
166 }
167 3 buffer->size = 0;
168 3 buffer->pos = 0;
169 3 }
170
171 39 void cxBufferReset(CxBuffer *buffer) {
172 39 buffer->size = 0;
173 39 buffer->pos = 0;
174 39 }
175
176 668 bool cxBufferEof(const CxBuffer *buffer) {
177 668 return buffer->pos >= buffer->size;
178 }
179
180 53 int cxBufferMinimumCapacity(
181 CxBuffer *buffer,
182 size_t newcap
183 ) {
184
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 52 times.
53 if (newcap <= buffer->capacity) {
185 1 return 0;
186 }
187
188 52 const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND;
189
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 45 times.
52 if (buffer->flags & force_copy_flags) {
190 7 void *newspace = cxMalloc(buffer->allocator, newcap);
191
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (NULL == newspace) return -1;
192 7 memcpy(newspace, buffer->space, buffer->size);
193 7 buffer->space = newspace;
194 7 buffer->capacity = newcap;
195 7 buffer->flags &= ~force_copy_flags;
196 7 buffer->flags |= CX_BUFFER_FREE_CONTENTS;
197 7 return 0;
198
1/2
✓ Branch 1 taken 45 times.
✗ Branch 2 not taken.
45 } else if (cxReallocate(buffer->allocator,
199 (void **) &buffer->bytes, newcap) == 0) {
200 45 buffer->capacity = newcap;
201 45 return 0;
202 } else {
203 return -1; // LCOV_EXCL_LINE
204 }
205 }
206
207 26 static size_t cx_buffer_flush_helper(
208 const CxBuffer *buffer,
209 const unsigned char *src,
210 size_t size,
211 size_t nitems
212 ) {
213 // flush data from an arbitrary source
214 // does not need to be the buffer's contents
215 26 size_t max_items = buffer->flush->blksize / size;
216 26 size_t fblocks = 0;
217 26 size_t flushed_total = 0;
218
4/4
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 30 times.
✓ Branch 3 taken 11 times.
54 while (nitems > 0 && fblocks < buffer->flush->blkmax) {
219 30 fblocks++;
220 30 size_t items = nitems > max_items ? max_items : nitems;
221 30 size_t flushed = buffer->flush->wfunc(
222 30 src, size, items, buffer->flush->target);
223
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 2 times.
30 if (flushed > 0) {
224 28 flushed_total += flushed;
225 28 src += flushed * size;
226 28 nitems -= flushed;
227 } else {
228 // if no bytes can be flushed out anymore, we give up
229 2 break;
230 }
231 }
232 26 return flushed_total;
233 }
234
235 21 static size_t cx_buffer_flush_impl(CxBuffer *buffer, size_t size) {
236 // flush the current contents of the buffer
237 21 unsigned char *space = buffer->bytes;
238 21 size_t remaining = buffer->pos / size;
239 21 size_t flushed_total = cx_buffer_flush_helper(
240 buffer, space, size, remaining);
241
242 // shift the buffer left after flushing
243 // IMPORTANT: up to this point, copy on write must have been
244 // performed already, because we can't do error handling here
245 21 cxBufferShiftLeft(buffer, flushed_total*size);
246
247 21 return flushed_total;
248 }
249
250 2 size_t cxBufferFlush(CxBuffer *buffer) {
251
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (buffer_copy_on_write(buffer)) return 0;
252 2 return cx_buffer_flush_impl(buffer, 1);
253 }
254
255 819 size_t cxBufferWrite(
256 const void *ptr,
257 size_t size,
258 size_t nitems,
259 CxBuffer *buffer
260 ) {
261 // optimize for easy case
262
4/4
✓ Branch 0 taken 798 times.
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 738 times.
✓ Branch 3 taken 60 times.
819 if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) {
263
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 738 times.
738 if (buffer_copy_on_write(buffer)) return 0;
264 738 memcpy(buffer->bytes + buffer->pos, ptr, nitems);
265 738 buffer->pos += nitems;
266
2/2
✓ Branch 0 taken 734 times.
✓ Branch 1 taken 4 times.
738 if (buffer->pos > buffer->size) {
267 734 buffer->size = buffer->pos;
268 }
269 738 return nitems;
270 }
271
272 81 size_t len, total_flushed = 0;
273 22 cx_buffer_write_retry:
274
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 102 times.
103 if (cx_szmul(size, nitems, &len)) {
275 1 errno = EOVERFLOW;
276 1 return total_flushed;
277 }
278
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 101 times.
102 if (buffer->pos > SIZE_MAX - len) {
279 1 errno = EOVERFLOW;
280 1 return total_flushed;
281 }
282
283 101 size_t required = buffer->pos + len;
284 101 bool perform_flush = false;
285
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 17 times.
101 if (required > buffer->capacity) {
286
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 33 times.
84 if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
287
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 49 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
51 if (buffer->flush != NULL && required > buffer->flush->threshold) {
288 1 perform_flush = true;
289 } else {
290
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 50 times.
50 if (cxBufferMinimumCapacity(buffer, required)) {
291 return total_flushed; // LCOV_EXCL_LINE
292 }
293 }
294 } else {
295
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 10 times.
33 if (buffer->flush != NULL) {
296 23 perform_flush = true;
297 } else {
298 // truncate data, if we can neither extend nor flush
299 10 len = buffer->capacity - buffer->pos;
300
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
10 if (size > 1) {
301 1 len -= len % size;
302 }
303 10 nitems = len / size;
304 }
305 }
306 }
307
308 // check here and not above because of possible truncation
309
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 96 times.
101 if (len == 0) {
310 5 return total_flushed;
311 }
312
313 // check if we need to copy
314
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 96 times.
96 if (buffer_copy_on_write(buffer)) return 0;
315
316 // perform the operation
317
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 72 times.
96 if (perform_flush) {
318 size_t items_flushed;
319
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 19 times.
24 if (buffer->pos == 0) {
320 // if we don't have data in the buffer, but are instructed
321 // to flush, it means that we are supposed to relay the data
322 5 items_flushed = cx_buffer_flush_helper(buffer, ptr, size, nitems);
323
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (items_flushed == 0) {
324 // we needed to relay data, but could not flush anything
325 // i.e. we have to give up to avoid endless trying
326 return 0;
327 }
328 5 nitems -= items_flushed;
329 5 total_flushed += items_flushed;
330
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 if (nitems > 0) {
331 4 ptr = ((unsigned char*)ptr) + items_flushed * size;
332 4 goto cx_buffer_write_retry;
333 }
334 1 return total_flushed;
335 } else {
336 19 items_flushed = cx_buffer_flush_impl(buffer, size);
337
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 16 times.
19 if (items_flushed == 0) {
338 // flush target is full, let's try to truncate
339 size_t remaining_space;
340
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
341 remaining_space = buffer->flush->threshold > buffer->pos
342 ? buffer->flush->threshold - buffer->pos
343 : 0;
344 } else {
345 3 remaining_space = buffer->capacity > buffer->pos
346 2 ? buffer->capacity - buffer->pos
347
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 : 0;
348 }
349 3 nitems = remaining_space / size;
350
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (nitems == 0) {
351 1 return total_flushed;
352 }
353 }
354 18 goto cx_buffer_write_retry;
355 }
356 } else {
357 72 memcpy(buffer->bytes + buffer->pos, ptr, len);
358 72 buffer->pos += len;
359
2/2
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 1 times.
72 if (buffer->pos > buffer->size) {
360 71 buffer->size = buffer->pos;
361 }
362 72 return total_flushed + nitems;
363 }
364 }
365
366 49 size_t cxBufferAppend(
367 const void *ptr,
368 size_t size,
369 size_t nitems,
370 CxBuffer *buffer
371 ) {
372 49 size_t pos = buffer->pos;
373 49 size_t append_pos = buffer->size;
374 49 buffer->pos = append_pos;
375 49 size_t written = cxBufferWrite(ptr, size, nitems, buffer);
376 // the buffer might have been flushed
377 // we must compute a possible delta for the position
378 // expected: pos = append_pos + written
379 // -> if this is not the case, there is a delta
380 49 size_t delta = append_pos + written*size - buffer->pos;
381
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 48 times.
49 if (delta > pos) {
382 1 buffer->pos = 0;
383 } else {
384 48 buffer->pos = pos - delta;
385 }
386 49 return written;
387 }
388
389 120 int cxBufferPut(
390 CxBuffer *buffer,
391 int c
392 ) {
393 120 c &= 0xFF;
394 120 unsigned char const ch = c;
395
2/2
✓ Branch 1 taken 117 times.
✓ Branch 2 taken 3 times.
120 if (cxBufferWrite(&ch, 1, 1, buffer) == 1) {
396 117 return c;
397 } else {
398 3 return EOF;
399 }
400 }
401
402 6 int cxBufferTerminate(CxBuffer *buffer) {
403 6 bool success = 0 == cxBufferPut(buffer, 0);
404
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
6 if (success) {
405 5 buffer->pos--;
406 5 buffer->size--;
407 5 return 0;
408 } else {
409 1 return -1;
410 }
411 }
412
413 18 size_t cxBufferPutString(
414 CxBuffer *buffer,
415 const char *str
416 ) {
417 18 return cxBufferWrite(str, 1, strlen(str), buffer);
418 }
419
420 14 size_t cxBufferRead(
421 void *ptr,
422 size_t size,
423 size_t nitems,
424 CxBuffer *buffer
425 ) {
426 size_t len;
427
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
14 if (cx_szmul(size, nitems, &len)) {
428 errno = EOVERFLOW;
429 return 0;
430 }
431
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
14 if (buffer->pos + len > buffer->size) {
432 7 len = buffer->size - buffer->pos;
433
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if (size > 1) len -= len % size;
434 }
435
436
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 11 times.
14 if (len <= 0) {
437 3 return len;
438 }
439
440 11 memcpy(ptr, buffer->bytes + buffer->pos, len);
441 11 buffer->pos += len;
442
443 11 return len / size;
444 }
445
446 5 int cxBufferGet(CxBuffer *buffer) {
447
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
5 if (cxBufferEof(buffer)) {
448 1 return EOF;
449 } else {
450 4 int c = buffer->bytes[buffer->pos];
451 4 buffer->pos++;
452 4 return c;
453 }
454 }
455
456 27 int cxBufferShiftLeft(
457 CxBuffer *buffer,
458 size_t shift
459 ) {
460
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 18 times.
27 if (shift >= buffer->size) {
461 9 buffer->pos = buffer->size = 0;
462 } else {
463
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
18 if (buffer_copy_on_write(buffer)) return -1;
464 18 memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift);
465 18 buffer->size -= shift;
466
467
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 1 times.
18 if (buffer->pos >= shift) {
468 17 buffer->pos -= shift;
469 } else {
470 1 buffer->pos = 0;
471 }
472 }
473 27 return 0;
474 }
475
476 6 int cxBufferShiftRight(
477 CxBuffer *buffer,
478 size_t shift
479 ) {
480
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (buffer->size > SIZE_MAX - shift) {
481 errno = EOVERFLOW;
482 return -1;
483 }
484 6 size_t req_capacity = buffer->size + shift;
485 size_t movebytes;
486
487 // auto extend buffer, if required and enabled
488
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
6 if (buffer->capacity < req_capacity) {
489
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
490
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 if (cxBufferMinimumCapacity(buffer, req_capacity)) {
491 return -1; // LCOV_EXCL_LINE
492 }
493 1 movebytes = buffer->size;
494 } else {
495 1 movebytes = buffer->capacity - shift;
496 }
497 } else {
498 4 movebytes = buffer->size;
499 }
500
501
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (movebytes > 0) {
502
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if (buffer_copy_on_write(buffer)) return -1;
503 6 memmove(buffer->bytes + shift, buffer->bytes, movebytes);
504 6 buffer->size = shift + movebytes;
505 }
506
507 6 buffer->pos += shift;
508
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (buffer->pos > buffer->size) {
509 1 buffer->pos = buffer->size;
510 }
511
512 6 return 0;
513 }
514
515 4 int cxBufferShift(
516 CxBuffer *buffer,
517 off_t shift
518 ) {
519
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if (shift < 0) {
520 1 return cxBufferShiftLeft(buffer, (size_t) (-shift));
521
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 } else if (shift > 0) {
522 1 return cxBufferShiftRight(buffer, (size_t) shift);
523 } else {
524 2 return 0;
525 }
526 }
527