GCC Code Coverage Report


Directory: ./
File: buffer.c
Date: 2025-12-31 16:19:05
Exec Total Coverage
Lines: 256 256 100.0%
Functions: 25 25 100.0%
Branches: 132 154 85.7%

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 3728 static int buffer_copy_on_write(CxBuffer* buffer) {
36
2/2
✓ Branch 0 (2→3) taken 3723 times.
✓ Branch 1 (2→4) taken 5 times.
3728 if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) return 0;
37 5 void *newspace = cxMalloc(buffer->allocator, buffer->capacity);
38 if (NULL == newspace) return -1; // LCOV_EXCL_LINE
39 5 memcpy(newspace, buffer->space, buffer->size);
40 5 buffer->space = newspace;
41 5 buffer->flags &= ~CX_BUFFER_COPY_ON_WRITE;
42 5 buffer->flags |= CX_BUFFER_FREE_CONTENTS;
43 5 return 0;
44 }
45
46 368 int cxBufferInit(
47 CxBuffer *buffer,
48 const CxAllocator *allocator,
49 void *space,
50 size_t capacity,
51 int flags
52 ) {
53
2/2
✓ Branch 0 (2→3) taken 228 times.
✓ Branch 1 (2→4) taken 140 times.
368 if (allocator == NULL) {
54 228 allocator = cxDefaultAllocator;
55 }
56
2/2
✓ Branch 0 (4→5) taken 9 times.
✓ Branch 1 (4→6) taken 359 times.
368 if (flags & CX_BUFFER_COPY_ON_EXTEND) {
57 9 flags |= CX_BUFFER_AUTO_EXTEND;
58 }
59 368 buffer->allocator = allocator;
60 368 buffer->flags = flags;
61
2/2
✓ Branch 0 (6→7) taken 138 times.
✓ Branch 1 (6→11) taken 230 times.
368 if (!space) {
62 138 buffer->bytes = cxMalloc(allocator, capacity);
63 if (buffer->bytes == NULL) return -1; // LCOV_EXCL_LINE
64 138 buffer->flags |= CX_BUFFER_FREE_CONTENTS;
65 } else {
66 230 buffer->bytes = space;
67 }
68 368 buffer->capacity = capacity;
69 368 buffer->max_capacity = SIZE_MAX;
70 368 buffer->size = 0;
71 368 buffer->pos = 0;
72
73 368 return 0;
74 }
75
76 832 void cxBufferDestroy(CxBuffer *buffer) {
77
2/2
✓ Branch 0 (2→3) taken 91 times.
✓ Branch 1 (2→4) taken 741 times.
832 if ((buffer->flags & (CX_BUFFER_FREE_CONTENTS | CX_BUFFER_DO_NOT_FREE))
78 == CX_BUFFER_FREE_CONTENTS) {
79 91 cxFree(buffer->allocator, buffer->bytes);
80 }
81 832 memset(buffer, 0, sizeof(CxBuffer));
82 832 }
83
84 2 CxBuffer *cxBufferCreate(
85 const CxAllocator *allocator,
86 void *space,
87 size_t capacity,
88 int flags
89 ) {
90
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 1 times.
2 if (allocator == NULL) {
91 1 allocator = cxDefaultAllocator;
92 }
93 2 CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer));
94 if (buf == NULL) return NULL; // LCOV_EXCL_LINE
95
1/2
✓ Branch 0 (8→9) taken 2 times.
✗ Branch 1 (8→10) not taken.
2 if (0 == cxBufferInit(buf, allocator, space, capacity, flags)) {
96 2 return buf;
97 } else {
98 // LCOV_EXCL_START
99 cxFree(allocator, buf);
100 return NULL;
101 // LCOV_EXCL_STOP
102 }
103 }
104
105 2 void cxBufferFree(CxBuffer *buffer) {
106
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 2 times.
2 if (buffer == NULL) return;
107 2 const CxAllocator *allocator = buffer->allocator;
108 2 cxBufferDestroy(buffer);
109 2 cxFree(allocator, buffer);
110 }
111
112 14 int cxBufferSeek(
113 CxBuffer *buffer,
114 off_t offset,
115 int whence
116 ) {
117 size_t npos;
118
4/4
✓ Branch 0 (2→3) taken 5 times.
✓ Branch 1 (2→4) taken 3 times.
✓ Branch 2 (2→5) taken 5 times.
✓ Branch 3 (2→6) taken 1 times.
14 switch (whence) {
119 5 case SEEK_CUR:
120 5 npos = buffer->pos;
121 5 break;
122 3 case SEEK_END:
123 3 npos = buffer->size;
124 3 break;
125 5 case SEEK_SET:
126 5 npos = 0;
127 5 break;
128 1 default:
129 1 errno = EINVAL;
130 1 return -1;
131 }
132
133 13 size_t opos = npos;
134 13 npos += offset;
135
136
7/8
✓ Branch 0 (7→8) taken 5 times.
✓ Branch 1 (7→9) taken 8 times.
✓ Branch 2 (8→9) taken 5 times.
✗ Branch 3 (8→11) not taken.
✓ Branch 4 (9→10) taken 3 times.
✓ Branch 5 (9→12) taken 10 times.
✓ Branch 6 (10→11) taken 1 times.
✓ Branch 7 (10→12) taken 2 times.
13 if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) {
137 // to be compliant with fseek() specification
138 // we return EINVAL on underflow
139 1 errno = EINVAL;
140 1 return -1;
141 }
142
143
2/2
✓ Branch 0 (12→13) taken 3 times.
✓ Branch 1 (12→14) taken 9 times.
12 if (npos > buffer->size) {
144 // not compliant with fseek() specification
145 // but this is the better behavior for CxBuffer
146 3 errno = EINVAL;
147 3 return -1;
148 } else {
149 9 buffer->pos = npos;
150 9 return 0;
151 }
152
153 }
154
155 6 size_t cxBufferPop(CxBuffer *buffer, size_t size, size_t nitems) {
156 size_t len;
157
1/2
✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 6 times.
6 if (cx_szmul(size, nitems, &len)) {
158 // LCOV_EXCL_START
159 errno = EOVERFLOW;
160 return 0;
161 // LCOV_EXCL_STOP
162 }
163
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 6 times.
6 if (len == 0) return 0;
164
2/2
✓ Branch 0 (7→8) taken 2 times.
✓ Branch 1 (7→11) taken 4 times.
6 if (len > buffer->size) {
165
2/2
✓ Branch 0 (8→9) taken 1 times.
✓ Branch 1 (8→10) taken 1 times.
2 if (size == 1) {
166 // simple case: everything can be discarded
167 1 len = buffer->size;
168 } else {
169 // complicated case: misaligned bytes must stay
170 1 size_t misalignment = buffer->size % size;
171 1 len = buffer->size - misalignment;
172 }
173 }
174 6 buffer->size -= len;
175
176 // adjust position, if required
177
2/2
✓ Branch 0 (11→12) taken 5 times.
✓ Branch 1 (11→13) taken 1 times.
6 if (buffer->pos > buffer->size) {
178 5 buffer->pos = buffer->size;
179 }
180
181 6 return len / size;
182 }
183
184 3 void cxBufferClear(CxBuffer *buffer) {
185
2/2
✓ Branch 0 (2→3) taken 2 times.
✓ Branch 1 (2→4) taken 1 times.
3 if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) {
186 2 memset(buffer->bytes, 0, buffer->size);
187 }
188 3 buffer->size = 0;
189 3 buffer->pos = 0;
190 3 }
191
192 66 void cxBufferReset(CxBuffer *buffer) {
193 66 buffer->size = 0;
194 66 buffer->pos = 0;
195 66 }
196
197 1796 bool cxBufferEof(const CxBuffer *buffer) {
198 1796 return buffer->pos >= buffer->size;
199 }
200
201 51 int cxBufferReserve(CxBuffer *buffer, size_t newcap) {
202
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 50 times.
51 if (newcap == buffer->capacity) {
203 1 return 0;
204 }
205
2/2
✓ Branch 0 (4→5) taken 1 times.
✓ Branch 1 (4→6) taken 49 times.
50 if (newcap > buffer->max_capacity) {
206 1 return -1;
207 }
208 49 const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND;
209
2/2
✓ Branch 0 (6→7) taken 9 times.
✓ Branch 1 (6→11) taken 40 times.
49 if (buffer->flags & force_copy_flags) {
210 9 void *newspace = cxMalloc(buffer->allocator, newcap);
211
1/2
✗ Branch 0 (8→9) not taken.
✓ Branch 1 (8→10) taken 9 times.
9 if (NULL == newspace) return -1;
212 9 memcpy(newspace, buffer->space, buffer->size);
213 9 buffer->space = newspace;
214 9 buffer->capacity = newcap;
215 9 buffer->flags &= ~force_copy_flags;
216 9 buffer->flags |= CX_BUFFER_FREE_CONTENTS;
217 9 return 0;
218
1/2
✓ Branch 0 (12→13) taken 40 times.
✗ Branch 1 (12→16) not taken.
40 } else if (cxReallocate(buffer->allocator,
219 (void **) &buffer->bytes, newcap) == 0) {
220 40 buffer->flags |= CX_BUFFER_FREE_CONTENTS;
221 40 buffer->capacity = newcap;
222
2/2
✓ Branch 0 (13→14) taken 2 times.
✓ Branch 1 (13→15) taken 38 times.
40 if (buffer->size > newcap) {
223 2 buffer->size = newcap;
224 }
225 40 return 0;
226 } else {
227 return -1; // LCOV_EXCL_LINE
228 }
229 }
230
231 8 int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity) {
232
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 7 times.
8 if (capacity < buffer->capacity) {
233 1 return -1;
234 }
235 7 buffer->max_capacity = capacity;
236 7 return 0;
237 }
238
239 46 int cxBufferMinimumCapacity(CxBuffer *buffer, size_t newcap) {
240
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 45 times.
46 if (newcap <= buffer->capacity) {
241 1 return 0;
242 }
243
2/2
✓ Branch 0 (4→5) taken 1 times.
✓ Branch 1 (4→6) taken 44 times.
45 if (newcap > buffer->max_capacity) {
244 1 return -1;
245 }
246
2/2
✓ Branch 0 (6→7) taken 42 times.
✓ Branch 1 (6→15) taken 2 times.
44 if (newcap < buffer->max_capacity) {
247 42 unsigned long pagesize = cx_system_page_size();
248 // if page size is larger than 64 KB - for some reason - truncate to 64 KB
249
1/2
✗ Branch 0 (8→9) not taken.
✓ Branch 1 (8→10) taken 42 times.
42 if (pagesize > 65536) pagesize = 65536;
250
2/2
✓ Branch 0 (10→11) taken 41 times.
✓ Branch 1 (10→12) taken 1 times.
42 if (newcap < pagesize) {
251 // when smaller as one page, map to the next power of two
252 41 newcap--;
253 41 newcap |= newcap >> 1;
254 41 newcap |= newcap >> 2;
255 41 newcap |= newcap >> 4;
256 // last operation only needed for pages larger 4096 bytes
257 // but if/else would be more expensive than just doing this
258 41 newcap |= newcap >> 8;
259 41 newcap++;
260 } else {
261 // otherwise, map to a multiple of the page size
262 1 newcap -= newcap % pagesize;
263 1 newcap += pagesize;
264 // note: if newcap is already page aligned,
265 // this gives a full additional page (which is good)
266 }
267
2/2
✓ Branch 0 (13→14) taken 2 times.
✓ Branch 1 (13→15) taken 40 times.
42 if (newcap > buffer->max_capacity) {
268 2 newcap = buffer->max_capacity;
269 }
270 }
271 44 return cxBufferReserve(buffer, newcap);
272 }
273
274 71 void cxBufferShrink(
275 CxBuffer *buffer,
276 size_t reserve
277 ) {
278 // Ensure buffer is in a reallocatable state
279 71 const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND;
280
2/2
✓ Branch 0 (2→3) taken 3 times.
✓ Branch 1 (2→4) taken 68 times.
71 if (buffer->flags & force_copy_flags) {
281 // do nothing when we are not allowed to reallocate
282 3 return;
283 }
284
285 // calculate new capacity
286 68 size_t newCapacity = buffer->size + reserve;
287
288 // If new capacity is smaller than current capacity, resize the buffer
289
1/2
✓ Branch 0 (4→5) taken 68 times.
✗ Branch 1 (4→8) not taken.
68 if (newCapacity < buffer->capacity) {
290
1/2
✓ Branch 0 (6→7) taken 68 times.
✗ Branch 1 (6→8) not taken.
68 if (0 == cxReallocate(buffer->allocator, &buffer->bytes, newCapacity)) {
291 68 buffer->capacity = newCapacity;
292 }
293 }
294 }
295
296 3656 size_t cxBufferWrite(
297 const void *ptr,
298 size_t size,
299 size_t nitems,
300 CxBuffer *buffer
301 ) {
302 // trivial case
303
3/4
✓ Branch 0 (2→3) taken 3656 times.
✗ Branch 1 (2→4) not taken.
✓ Branch 2 (3→4) taken 4 times.
✓ Branch 3 (3→5) taken 3652 times.
3656 if (size == 0 || nitems == 0) return 0;
304
305 // optimize for easy case
306
4/4
✓ Branch 0 (5→6) taken 3646 times.
✓ Branch 1 (5→13) taken 6 times.
✓ Branch 2 (6→7) taken 3603 times.
✓ Branch 3 (6→13) taken 43 times.
3652 if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) {
307
1/2
✗ Branch 0 (8→9) not taken.
✓ Branch 1 (8→10) taken 3603 times.
3603 if (buffer_copy_on_write(buffer)) return 0;
308 3603 memcpy(buffer->bytes + buffer->pos, ptr, nitems);
309 3603 buffer->pos += nitems;
310
2/2
✓ Branch 0 (10→11) taken 3599 times.
✓ Branch 1 (10→12) taken 4 times.
3603 if (buffer->pos > buffer->size) {
311 3599 buffer->size = buffer->pos;
312 }
313 3603 return nitems;
314 }
315
316 size_t len;
317
2/2
✓ Branch 0 (14→15) taken 1 times.
✓ Branch 1 (14→16) taken 48 times.
49 if (cx_szmul(size, nitems, &len)) {
318 1 errno = EOVERFLOW;
319 1 return 0;
320 }
321
2/2
✓ Branch 0 (16→17) taken 1 times.
✓ Branch 1 (16→18) taken 47 times.
48 if (buffer->pos > SIZE_MAX - len) {
322 1 errno = EOVERFLOW;
323 1 return 0;
324 }
325 47 const size_t required = buffer->pos + len;
326
327 // check if we need to auto-extend
328
2/2
✓ Branch 0 (18→19) taken 45 times.
✓ Branch 1 (18→23) taken 2 times.
47 if (required > buffer->capacity) {
329
2/2
✓ Branch 0 (19→20) taken 39 times.
✓ Branch 1 (19→23) taken 6 times.
45 if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
330 39 size_t newcap = required < buffer->max_capacity
331 ? required : buffer->max_capacity;
332
1/2
✗ Branch 0 (21→22) not taken.
✓ Branch 1 (21→23) taken 39 times.
39 if (cxBufferMinimumCapacity(buffer, newcap)) {
333 return 0; // LCOV_EXCL_LINE
334 }
335 }
336 }
337
338 // check again and truncate data if capacity is still not enough
339
2/2
✓ Branch 0 (23→24) taken 8 times.
✓ Branch 1 (23→27) taken 39 times.
47 if (required > buffer->capacity) {
340 8 len = buffer->capacity - buffer->pos;
341
2/2
✓ Branch 0 (24→25) taken 2 times.
✓ Branch 1 (24→26) taken 6 times.
8 if (size > 1) {
342 2 len -= len % size;
343 }
344 8 nitems = len / size;
345 }
346
347 // check here and not above because of possible truncation
348
2/2
✓ Branch 0 (27→28) taken 2 times.
✓ Branch 1 (27→29) taken 45 times.
47 if (len == 0) {
349 2 return 0;
350 }
351
352 // check if we need to copy
353
1/2
✗ Branch 0 (30→31) not taken.
✓ Branch 1 (30→32) taken 45 times.
45 if (buffer_copy_on_write(buffer)) return 0;
354
355 // perform the operation
356 45 memcpy(buffer->bytes + buffer->pos, ptr, len);
357 45 buffer->pos += len;
358
2/2
✓ Branch 0 (32→33) taken 44 times.
✓ Branch 1 (32→34) taken 1 times.
45 if (buffer->pos > buffer->size) {
359 44 buffer->size = buffer->pos;
360 }
361 45 return nitems;
362 }
363
364 76 size_t cxBufferAppend(
365 const void *ptr,
366 size_t size,
367 size_t nitems,
368 CxBuffer *buffer
369 ) {
370 // trivial case
371
2/4
✓ Branch 0 (2→3) taken 76 times.
✗ Branch 1 (2→4) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 76 times.
76 if (size == 0 || nitems == 0) return 0;
372
373 76 const size_t pos = buffer->pos;
374 76 buffer->pos = buffer->size;
375 76 const size_t written = cxBufferWrite(ptr, size, nitems, buffer);
376 76 buffer->pos = pos;
377 76 return written;
378 }
379
380 115 int cxBufferPut(
381 CxBuffer *buffer,
382 int c
383 ) {
384 115 c &= 0xFF;
385 115 unsigned char const ch = c;
386
2/2
✓ Branch 0 (3→4) taken 113 times.
✓ Branch 1 (3→5) taken 2 times.
115 if (cxBufferWrite(&ch, 1, 1, buffer) == 1) {
387 113 return c;
388 } else {
389 2 return EOF;
390 }
391 }
392
393 70 int cxBufferTerminate(CxBuffer *buffer) {
394 // try to extend / shrink the buffer
395
2/2
✓ Branch 0 (2→3) taken 2 times.
✓ Branch 1 (2→8) taken 68 times.
70 if (buffer->pos >= buffer->capacity) {
396
2/2
✓ Branch 0 (3→4) taken 1 times.
✓ Branch 1 (3→5) taken 1 times.
2 if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == 0) {
397 1 return -1;
398 }
399
1/2
✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→10) taken 1 times.
1 if (cxBufferReserve(buffer, buffer->pos + 1)) {
400 return -1; // LCOV_EXCL_LINE
401 }
402 } else {
403 68 buffer->size = buffer->pos;
404 68 cxBufferShrink(buffer, 1);
405 // set the capacity explicitly, in case shrink was skipped due to CoW
406 68 buffer->capacity = buffer->size + 1;
407 }
408
409 // check if we are still on read-only memory
410
1/2
✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→13) taken 69 times.
69 if (buffer_copy_on_write(buffer)) return -1;
411
412 // write the terminator and exit
413 69 buffer->space[buffer->pos] = '\0';
414 69 return 0;
415 }
416
417 16 size_t cx_buffer_put_string(CxBuffer *buffer, cxstring str) {
418 16 return cxBufferWrite(str.ptr, 1, str.length, buffer);
419 }
420
421 1 size_t cx_buffer_append_string(CxBuffer *buffer, cxstring str) {
422 1 return cxBufferAppend(str.ptr, 1, str.length, buffer);
423 }
424
425 15 size_t cxBufferRead(
426 void *ptr,
427 size_t size,
428 size_t nitems,
429 CxBuffer *buffer
430 ) {
431 size_t len;
432
2/2
✓ Branch 0 (3→4) taken 1 times.
✓ Branch 1 (3→5) taken 14 times.
15 if (cx_szmul(size, nitems, &len)) {
433 1 errno = EOVERFLOW;
434 1 return 0;
435 }
436
2/2
✓ Branch 0 (5→6) taken 7 times.
✓ Branch 1 (5→8) taken 7 times.
14 if (buffer->pos + len > buffer->size) {
437 7 len = buffer->size - buffer->pos;
438
2/2
✓ Branch 0 (6→7) taken 1 times.
✓ Branch 1 (6→8) taken 6 times.
7 if (size > 1) len -= len % size;
439 }
440
441
2/2
✓ Branch 0 (8→9) taken 3 times.
✓ Branch 1 (8→10) taken 11 times.
14 if (len <= 0) {
442 3 return len;
443 }
444
445 11 memcpy(ptr, buffer->bytes + buffer->pos, len);
446 11 buffer->pos += len;
447
448 11 return len / size;
449 }
450
451 5 int cxBufferGet(CxBuffer *buffer) {
452
2/2
✓ Branch 0 (3→4) taken 1 times.
✓ Branch 1 (3→5) taken 4 times.
5 if (cxBufferEof(buffer)) {
453 1 return EOF;
454 } else {
455 4 int c = buffer->bytes[buffer->pos];
456 4 buffer->pos++;
457 4 return c;
458 }
459 }
460
461 6 int cxBufferShiftLeft(
462 CxBuffer *buffer,
463 size_t shift
464 ) {
465
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 5 times.
6 if (shift >= buffer->size) {
466 1 buffer->pos = buffer->size = 0;
467 } else {
468
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 5 times.
5 if (buffer_copy_on_write(buffer)) return -1;
469 5 memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift);
470 5 buffer->size -= shift;
471
472
2/2
✓ Branch 0 (7→8) taken 4 times.
✓ Branch 1 (7→9) taken 1 times.
5 if (buffer->pos >= shift) {
473 4 buffer->pos -= shift;
474 } else {
475 1 buffer->pos = 0;
476 }
477 }
478 6 return 0;
479 }
480
481 7 int cxBufferShiftRight(
482 CxBuffer *buffer,
483 size_t shift
484 ) {
485
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→4) taken 6 times.
7 if (buffer->size > SIZE_MAX - shift) {
486 1 errno = EOVERFLOW;
487 1 return -1;
488 }
489 6 size_t req_capacity = buffer->size + shift;
490 size_t movebytes;
491
492 // auto extend buffer, if required and enabled
493
2/2
✓ Branch 0 (4→5) taken 2 times.
✓ Branch 1 (4→11) taken 4 times.
6 if (buffer->capacity < req_capacity) {
494
2/2
✓ Branch 0 (5→6) taken 1 times.
✓ Branch 1 (5→10) taken 1 times.
2 if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
495
1/2
✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→9) taken 1 times.
1 if (cxBufferMinimumCapacity(buffer, req_capacity)) {
496 return -1; // LCOV_EXCL_LINE
497 }
498 1 movebytes = buffer->size;
499 } else {
500 1 movebytes = buffer->capacity - shift;
501 }
502 } else {
503 4 movebytes = buffer->size;
504 }
505
506
1/2
✓ Branch 0 (12→13) taken 6 times.
✗ Branch 1 (12→17) not taken.
6 if (movebytes > 0) {
507
1/2
✗ Branch 0 (14→15) not taken.
✓ Branch 1 (14→16) taken 6 times.
6 if (buffer_copy_on_write(buffer)) return -1;
508 6 memmove(buffer->bytes + shift, buffer->bytes, movebytes);
509 6 buffer->size = shift + movebytes;
510 }
511
512 6 buffer->pos += shift;
513
2/2
✓ Branch 0 (17→18) taken 1 times.
✓ Branch 1 (17→19) taken 5 times.
6 if (buffer->pos > buffer->size) {
514 1 buffer->pos = buffer->size;
515 }
516
517 6 return 0;
518 }
519
520 4 int cxBufferShift(
521 CxBuffer *buffer,
522 off_t shift
523 ) {
524
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→5) taken 3 times.
4 if (shift < 0) {
525 1 return cxBufferShiftLeft(buffer, (size_t) (-shift));
526
2/2
✓ Branch 0 (5→6) taken 1 times.
✓ Branch 1 (5→8) taken 2 times.
3 } else if (shift > 0) {
527 1 return cxBufferShiftRight(buffer, (size_t) shift);
528 } else {
529 2 return 0;
530 }
531 }
532