Line data Source code
1 : /** Copyright 2025 Alexander G. Lopez
2 :
3 : Licensed under the Apache License, Version 2.0 (the "License");
4 : you may not use this file except in compliance with the License.
5 : You may obtain a copy of the License at
6 :
7 : http://www.apache.org/licenses/LICENSE-2.0
8 :
9 : Unless required by applicable law or agreed to in writing, software
10 : distributed under the License is distributed on an "AS IS" BASIS,
11 : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : See the License for the specific language governing permissions and
13 : limitations under the License. */
14 : /** C23 provided headers. */
15 : #include <stddef.h>
16 :
17 : /** CCC provided headers. */
18 : #include "ccc/buffer.h"
19 : #include "ccc/configuration.h"
20 : #include "ccc/private/private_buffer.h"
21 : #include "ccc/types.h"
22 :
23 : enum : size_t {
24 : START_CAPACITY = 8,
25 : };
26 :
27 : /*========================== Prototypes ================================*/
28 :
29 : static void *at(CCC_Buffer const *, size_t);
30 : static size_t max(size_t, size_t);
31 :
32 : /*========================== Interface ================================*/
33 :
34 : CCC_Result
35 63 : CCC_buffer_allocate(
36 : CCC_Buffer *const buffer,
37 : size_t const capacity,
38 : CCC_Allocator const *const allocator
39 : ) {
40 63 : if (!buffer || !allocator) {
41 2 : return CCC_RESULT_ARGUMENT_ERROR;
42 : }
43 61 : if (!allocator->allocate) {
44 9 : return CCC_RESULT_NO_ALLOCATION_FUNCTION;
45 : }
46 208 : void *const new_data = allocator->allocate((CCC_Allocator_arguments){
47 52 : .input = buffer->data,
48 52 : .bytes = buffer->sizeof_type * capacity,
49 52 : .context = allocator->context,
50 : });
51 52 : if (capacity && !new_data) {
52 4 : return CCC_RESULT_ALLOCATOR_ERROR;
53 : }
54 48 : buffer->data = new_data;
55 48 : buffer->capacity = capacity;
56 48 : return CCC_RESULT_OK;
57 63 : }
58 :
59 : CCC_Result
60 51 : CCC_buffer_reserve(
61 : CCC_Buffer *const buffer,
62 : size_t const to_add,
63 : CCC_Allocator const *const allocator
64 : ) {
65 51 : if (!buffer || !allocator || !allocator->allocate || !to_add) {
66 9 : return CCC_RESULT_ARGUMENT_ERROR;
67 : }
68 42 : size_t needed = buffer->count + to_add;
69 42 : if (needed <= buffer->capacity) {
70 1 : return CCC_RESULT_OK;
71 : }
72 41 : if (needed < START_CAPACITY) {
73 3 : needed = START_CAPACITY;
74 3 : }
75 164 : void *const new_data = allocator->allocate((CCC_Allocator_arguments){
76 41 : .input = buffer->data,
77 41 : .bytes = buffer->sizeof_type * needed,
78 41 : .context = allocator->context,
79 : });
80 41 : if (!new_data) {
81 1 : return CCC_RESULT_ALLOCATOR_ERROR;
82 : }
83 40 : buffer->data = new_data;
84 40 : buffer->capacity = needed;
85 40 : return CCC_RESULT_OK;
86 51 : }
87 :
88 : CCC_Result
89 4 : CCC_buffer_clear(
90 : CCC_Buffer *const buffer, CCC_Destructor const *const destructor
91 : ) {
92 4 : if (!buffer || !destructor) {
93 2 : return CCC_RESULT_ARGUMENT_ERROR;
94 : }
95 2 : if (!destructor->destroy) {
96 1 : buffer->count = 0;
97 1 : return CCC_RESULT_OK;
98 : }
99 9 : for (void *i = CCC_buffer_begin(buffer); i != CCC_buffer_end(buffer);
100 8 : i = CCC_buffer_next(buffer, i)) {
101 24 : destructor->destroy((CCC_Arguments){
102 8 : .type = i,
103 8 : .context = destructor->context,
104 : });
105 8 : }
106 1 : buffer->count = 0;
107 1 : return CCC_RESULT_OK;
108 4 : }
109 :
110 : CCC_Result
111 20 : CCC_buffer_clear_and_free(
112 : CCC_Buffer *const buffer,
113 : CCC_Destructor const *const destructor,
114 : CCC_Allocator const *const allocator
115 : ) {
116 20 : if (!buffer || !allocator || !destructor || !allocator->allocate) {
117 1 : return CCC_RESULT_ARGUMENT_ERROR;
118 : }
119 19 : if (destructor->destroy) {
120 9 : for (void *i = CCC_buffer_begin(buffer); i != CCC_buffer_end(buffer);
121 8 : i = CCC_buffer_next(buffer, i)) {
122 24 : destructor->destroy((CCC_Arguments){
123 8 : .type = i,
124 8 : .context = destructor->context,
125 : });
126 8 : }
127 1 : }
128 57 : (void)allocator->allocate((CCC_Allocator_arguments){
129 19 : .input = buffer->data,
130 : .bytes = 0,
131 19 : .context = allocator->context,
132 : });
133 19 : buffer->data = NULL;
134 19 : buffer->count = 0;
135 19 : buffer->capacity = 0;
136 19 : return CCC_RESULT_OK;
137 20 : }
138 :
139 : void *
140 2952531 : CCC_buffer_at(CCC_Buffer const *const buffer, size_t const i) {
141 2952531 : if (!buffer || i >= buffer->capacity) {
142 3 : return NULL;
143 : }
144 2952528 : return ((char *)buffer->data + (i * buffer->sizeof_type));
145 2952531 : }
146 :
147 : void *
148 90 : CCC_buffer_back(CCC_Buffer const *const buffer) {
149 90 : return CCC_buffer_at(buffer, buffer->count - 1);
150 : }
151 :
152 : void *
153 7 : CCC_buffer_front(CCC_Buffer const *const buffer) {
154 7 : return CCC_buffer_at(buffer, 0);
155 : }
156 :
157 : void *
158 4877 : CCC_buffer_allocate_back(
159 : CCC_Buffer *const buffer, CCC_Allocator const *const allocator
160 : ) {
161 4877 : if (!buffer || !allocator) {
162 4 : return NULL;
163 : }
164 4873 : if (buffer->count == buffer->capacity) {
165 40 : CCC_Result const resize_res = CCC_buffer_allocate(
166 20 : buffer, max(buffer->capacity * 2, START_CAPACITY), allocator
167 : );
168 20 : if (resize_res != CCC_RESULT_OK) {
169 6 : return NULL;
170 : }
171 20 : }
172 9734 : void *const ret
173 4867 : = ((char *)buffer->data + (buffer->sizeof_type * buffer->count));
174 4867 : ++buffer->count;
175 4867 : return ret;
176 4877 : }
177 :
178 : void *
179 149 : CCC_buffer_push_back(
180 : CCC_Buffer *const buffer,
181 : void const *const data,
182 : CCC_Allocator const *const allocator
183 : ) {
184 149 : if (!data) {
185 1 : return NULL;
186 : }
187 148 : void *const slot = CCC_buffer_allocate_back(buffer, allocator);
188 148 : if (slot) {
189 142 : (void)memcpy(slot, data, buffer->sizeof_type);
190 142 : }
191 148 : return slot;
192 149 : }
193 :
194 : CCC_Result
195 14814 : CCC_buffer_swap(
196 : CCC_Buffer const *const buffer,
197 : void *const temp,
198 : size_t const index,
199 : size_t const swap_index
200 : ) {
201 14814 : if (!buffer || !temp || index >= buffer->capacity
202 14814 : || swap_index >= buffer->capacity || swap_index == index) {
203 1 : return CCC_RESULT_ARGUMENT_ERROR;
204 : }
205 14813 : (void)memcpy(temp, at(buffer, index), buffer->sizeof_type);
206 14813 : (void)memcpy(
207 14813 : at(buffer, index), at(buffer, swap_index), buffer->sizeof_type
208 : );
209 14813 : (void)memcpy(at(buffer, swap_index), temp, buffer->sizeof_type);
210 14813 : return CCC_RESULT_OK;
211 14814 : }
212 :
213 : void *
214 3 : CCC_buffer_move(
215 : CCC_Buffer const *const buffer,
216 : size_t const destination,
217 : size_t const source
218 : ) {
219 3 : if (!buffer || destination >= buffer->capacity
220 3 : || source >= buffer->capacity) {
221 1 : return NULL;
222 : }
223 2 : if (destination == source) {
224 1 : return at(buffer, destination);
225 : }
226 1 : return memcpy(
227 1 : at(buffer, destination), at(buffer, source), buffer->sizeof_type
228 : );
229 3 : }
230 :
231 : CCC_Result
232 10 : CCC_buffer_write(
233 : CCC_Buffer const *const buffer, size_t const i, void const *const data
234 : ) {
235 10 : if (!buffer || !buffer->data || !data) {
236 1 : return CCC_RESULT_ARGUMENT_ERROR;
237 : }
238 9 : void *const pos = CCC_buffer_at(buffer, i);
239 9 : if (!pos || data == pos) {
240 2 : return CCC_RESULT_ARGUMENT_ERROR;
241 : }
242 7 : (void)memcpy(pos, data, buffer->sizeof_type);
243 7 : return CCC_RESULT_OK;
244 10 : }
245 :
246 : CCC_Result
247 6 : CCC_buffer_erase(CCC_Buffer *const buffer, size_t const i) {
248 6 : if (!buffer || !buffer->count || i >= buffer->count) {
249 2 : return CCC_RESULT_ARGUMENT_ERROR;
250 : }
251 4 : if (1 == buffer->count) {
252 1 : buffer->count = 0;
253 1 : return CCC_RESULT_OK;
254 : }
255 3 : if (i == buffer->count - 1) {
256 1 : --buffer->count;
257 1 : return CCC_RESULT_OK;
258 : }
259 2 : (void)memmove(
260 2 : at(buffer, i),
261 2 : at(buffer, i + 1),
262 2 : buffer->sizeof_type * (buffer->count - (i + 1))
263 : );
264 2 : --buffer->count;
265 2 : return CCC_RESULT_OK;
266 6 : }
267 :
268 : void *
269 11 : CCC_buffer_insert(
270 : CCC_Buffer *const buffer,
271 : size_t const i,
272 : void const *const data,
273 : CCC_Allocator const *const allocator
274 : ) {
275 11 : if (!buffer || !buffer->data || i > buffer->count || !allocator) {
276 4 : return NULL;
277 : }
278 7 : if (i == buffer->count) {
279 1 : return CCC_buffer_push_back(buffer, data, allocator);
280 : }
281 6 : if (buffer->count == buffer->capacity) {
282 2 : CCC_Result const r = CCC_buffer_allocate(
283 1 : buffer, max(buffer->count * 2, START_CAPACITY), allocator
284 : );
285 1 : if (r != CCC_RESULT_OK) {
286 1 : return NULL;
287 : }
288 1 : }
289 5 : (void)memmove(
290 5 : at(buffer, i + 1),
291 5 : at(buffer, i),
292 5 : buffer->sizeof_type * (buffer->count - i)
293 : );
294 5 : ++buffer->count;
295 5 : return memcpy(at(buffer, i), data, buffer->sizeof_type);
296 11 : }
297 :
298 : CCC_Result
299 66 : CCC_buffer_pop_back_n(CCC_Buffer *const buffer, size_t count) {
300 66 : if (!buffer || count > buffer->count) {
301 4 : return CCC_RESULT_ARGUMENT_ERROR;
302 : }
303 62 : buffer->count -= count;
304 62 : return CCC_RESULT_OK;
305 66 : }
306 :
307 : CCC_Result
308 64 : CCC_buffer_pop_back(CCC_Buffer *const buffer) {
309 64 : return CCC_buffer_pop_back_n(buffer, 1);
310 : }
311 :
312 : CCC_Count
313 1078 : CCC_buffer_count(CCC_Buffer const *const buffer) {
314 1078 : if (!buffer) {
315 1 : return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
316 : }
317 1077 : return (CCC_Count){.count = buffer->count};
318 1078 : }
319 :
320 : CCC_Count
321 29 : CCC_buffer_capacity(CCC_Buffer const *const buffer) {
322 29 : if (!buffer) {
323 1 : return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
324 : }
325 28 : return (CCC_Count){.count = buffer->capacity};
326 29 : }
327 :
328 : CCC_Count
329 128 : CCC_buffer_sizeof_type(CCC_Buffer const *const buffer) {
330 128 : if (!buffer) {
331 1 : return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
332 : }
333 127 : return (CCC_Count){.count = buffer->sizeof_type};
334 128 : }
335 :
336 : CCC_Tribool
337 2038 : CCC_buffer_is_empty(CCC_Buffer const *const buffer) {
338 2038 : if (!buffer) {
339 1 : return CCC_TRIBOOL_ERROR;
340 : }
341 2037 : return !buffer->count;
342 2038 : }
343 :
344 : CCC_Tribool
345 29 : CCC_buffer_is_full(CCC_Buffer const *const buffer) {
346 29 : if (!buffer) {
347 1 : return CCC_TRIBOOL_ERROR;
348 : }
349 28 : if (!buffer->capacity) {
350 1 : return CCC_FALSE;
351 : }
352 27 : return buffer->count == buffer->capacity ? CCC_TRUE : CCC_FALSE;
353 29 : }
354 :
355 : void *
356 2156 : CCC_buffer_begin(CCC_Buffer const *const buffer) {
357 2156 : return buffer ? buffer->data : NULL;
358 : }
359 :
360 : void *
361 64 : CCC_buffer_reverse_begin(CCC_Buffer const *const buffer) {
362 64 : if (!buffer || !buffer->data) {
363 2 : return NULL;
364 : }
365 : /* OK if count is 0. Negative offset puts at reverse_end anyway. */
366 124 : return (unsigned char *)buffer->data
367 62 : + ((buffer->count - 1) * buffer->sizeof_type);
368 64 : }
369 :
370 : void *
371 1458 : CCC_buffer_next(CCC_Buffer const *const buffer, void const *const iterator) {
372 1458 : if (!buffer || !buffer->capacity || !iterator) {
373 1 : return NULL;
374 : }
375 1457 : if ((char *)iterator
376 1457 : >= (char *)buffer->data + ((buffer->count - 1) * buffer->sizeof_type)) {
377 77 : return CCC_buffer_end(buffer);
378 : }
379 1380 : return (unsigned char *)iterator + buffer->sizeof_type;
380 1458 : }
381 :
382 : void *
383 385 : CCC_buffer_reverse_next(
384 : CCC_Buffer const *const buffer, void const *const iterator
385 : ) {
386 385 : if (!buffer || !buffer->data || !iterator) {
387 1 : return NULL;
388 : }
389 384 : if (iterator <= CCC_buffer_reverse_end(buffer)) {
390 1 : return CCC_buffer_reverse_end(buffer);
391 : }
392 383 : return (char *)iterator - buffer->sizeof_type;
393 385 : }
394 :
395 : /** We accept that end may be the address past Buffer capacity. */
396 : void *
397 1250 : CCC_buffer_end(CCC_Buffer const *const buffer) {
398 1250 : if (!buffer || !buffer->data) {
399 2 : return NULL;
400 : }
401 2496 : return (unsigned char *)buffer->data
402 1248 : + (buffer->count * buffer->sizeof_type);
403 1250 : }
404 :
405 : /** We accept that reverse_end is out of bounds and the address before start.
406 : Even if the array base was somehow 0 and wrapping occurred upon subtraction the
407 : iterator would eventually reach this same address through reverse_next and be
408 : compared to it in the main user loop. */
409 : void *
410 739 : CCC_buffer_reverse_end(CCC_Buffer const *const buffer) {
411 739 : if (!buffer || !buffer->data) {
412 1 : return NULL;
413 : }
414 738 : return (unsigned char *)buffer->data - buffer->sizeof_type;
415 739 : }
416 :
417 : CCC_Count
418 91 : CCC_buffer_index(CCC_Buffer const *const buffer, void const *const slot) {
419 91 : if (!buffer || !buffer->data || !slot || slot < buffer->data
420 89 : || (char *)slot
421 176 : >= ((char *)buffer->data
422 88 : + (buffer->capacity * buffer->sizeof_type))) {
423 3 : return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
424 : }
425 0 : assert(
426 88 : slot >= buffer->data && "positive pointer difference is caught at entry"
427 : );
428 176 : return (CCC_Count){
429 : .count
430 88 : = ((size_t)((char *)slot - ((char *)buffer->data))
431 88 : / buffer->sizeof_type),
432 : };
433 91 : }
434 :
435 : CCC_Result
436 3 : CCC_buffer_count_plus(CCC_Buffer *const buffer, size_t const count) {
437 3 : if (!buffer) {
438 1 : return CCC_RESULT_ARGUMENT_ERROR;
439 : }
440 2 : size_t const new_count = buffer->count + count;
441 2 : if (new_count > buffer->capacity) {
442 1 : buffer->count = buffer->capacity;
443 1 : return CCC_RESULT_ARGUMENT_ERROR;
444 : }
445 1 : buffer->count = new_count;
446 1 : return CCC_RESULT_OK;
447 3 : }
448 :
449 : CCC_Result
450 3 : CCC_buffer_count_minus(CCC_Buffer *const buffer, size_t const count) {
451 3 : if (!buffer) {
452 1 : return CCC_RESULT_ARGUMENT_ERROR;
453 : }
454 2 : if (count > buffer->count) {
455 1 : buffer->count = 0;
456 1 : return CCC_RESULT_ARGUMENT_ERROR;
457 : }
458 1 : buffer->count -= count;
459 1 : return CCC_RESULT_OK;
460 3 : }
461 :
462 : CCC_Result
463 4 : CCC_buffer_count_set(CCC_Buffer *const buffer, size_t const count) {
464 4 : if (!buffer) {
465 1 : return CCC_RESULT_ARGUMENT_ERROR;
466 : }
467 3 : if (count > buffer->capacity) {
468 1 : buffer->count = buffer->capacity;
469 1 : return CCC_RESULT_ARGUMENT_ERROR;
470 : }
471 2 : buffer->count = count;
472 2 : return CCC_RESULT_OK;
473 4 : }
474 :
475 : CCC_Count
476 3 : CCC_buffer_count_bytes(CCC_Buffer const *buffer) {
477 3 : if (!buffer) {
478 1 : return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
479 : }
480 2 : return (CCC_Count){.count = buffer->count * buffer->sizeof_type};
481 3 : }
482 :
483 : CCC_Count
484 2 : CCC_buffer_capacity_bytes(CCC_Buffer const *buffer) {
485 2 : if (!buffer) {
486 1 : return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
487 : }
488 2 : return (CCC_Count){
489 1 : .count = buffer->capacity * buffer->sizeof_type,
490 : };
491 2 : }
492 :
493 : CCC_Result
494 18 : CCC_buffer_copy(
495 : CCC_Buffer *const destination,
496 : CCC_Buffer const *const source,
497 : CCC_Allocator const *const allocator
498 : ) {
499 18 : if (!destination || !source || source == destination || !allocator
500 16 : || (destination->capacity < source->capacity && !allocator->allocate)) {
501 6 : return CCC_RESULT_ARGUMENT_ERROR;
502 : }
503 12 : if (!source->capacity) {
504 1 : return CCC_RESULT_OK;
505 : }
506 11 : if (destination->capacity < source->capacity) {
507 16 : CCC_Result const r
508 8 : = CCC_buffer_allocate(destination, source->capacity, allocator);
509 8 : if (r != CCC_RESULT_OK) {
510 1 : return r;
511 : }
512 7 : destination->capacity = source->capacity;
513 8 : }
514 10 : if (!source->data || !destination->data) {
515 1 : return CCC_RESULT_ARGUMENT_ERROR;
516 : }
517 9 : destination->count = source->count;
518 9 : (void)memcpy(
519 9 : destination->data, source->data, source->capacity * source->sizeof_type
520 : );
521 9 : return CCC_RESULT_OK;
522 18 : }
523 :
524 : void *
525 3 : CCC_buffer_data(CCC_Buffer const *const buffer) {
526 3 : return buffer ? buffer->data : NULL;
527 : }
528 :
529 : /*====================== Static Helpers ==================================*/
530 :
531 : static inline void *
532 59274 : at(struct CCC_Buffer const *const buffer, size_t const i) {
533 59274 : return ((char *)buffer->data + (i * buffer->sizeof_type));
534 : }
535 :
536 : static inline size_t
537 21 : max(size_t const a, size_t const b) {
538 21 : return a > b ? a : b;
539 : }
|