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/configuration.h"
19 : #include "ccc/flat_buffer.h"
20 : #include "ccc/private/private_flat_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_Flat_buffer const *, size_t);
30 : static size_t max(size_t, size_t);
31 :
32 : /*========================== Interface ================================*/
33 :
34 : CCC_Result
35 63 : CCC_flat_buffer_allocate(
36 : CCC_Flat_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_flat_buffer_reserve(
61 : CCC_Flat_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_flat_buffer_clear(
90 : CCC_Flat_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_flat_buffer_begin(buffer);
100 9 : i != CCC_flat_buffer_end(buffer);
101 8 : i = CCC_flat_buffer_next(buffer, i)) {
102 24 : destructor->destroy((CCC_Arguments){
103 8 : .type = i,
104 8 : .context = destructor->context,
105 : });
106 8 : }
107 1 : buffer->count = 0;
108 1 : return CCC_RESULT_OK;
109 4 : }
110 :
111 : CCC_Result
112 20 : CCC_flat_buffer_clear_and_free(
113 : CCC_Flat_buffer *const buffer,
114 : CCC_Destructor const *const destructor,
115 : CCC_Allocator const *const allocator
116 : ) {
117 20 : if (!buffer || !allocator || !destructor || !allocator->allocate) {
118 1 : return CCC_RESULT_ARGUMENT_ERROR;
119 : }
120 19 : if (destructor->destroy) {
121 9 : for (void *i = CCC_flat_buffer_begin(buffer);
122 9 : i != CCC_flat_buffer_end(buffer);
123 8 : i = CCC_flat_buffer_next(buffer, i)) {
124 24 : destructor->destroy((CCC_Arguments){
125 8 : .type = i,
126 8 : .context = destructor->context,
127 : });
128 8 : }
129 1 : }
130 57 : (void)allocator->allocate((CCC_Allocator_arguments){
131 19 : .input = buffer->data,
132 : .bytes = 0,
133 19 : .context = allocator->context,
134 : });
135 19 : buffer->data = NULL;
136 19 : buffer->count = 0;
137 19 : buffer->capacity = 0;
138 19 : return CCC_RESULT_OK;
139 20 : }
140 :
141 : void *
142 3696 : CCC_flat_buffer_at(CCC_Flat_buffer const *const buffer, size_t const i) {
143 3696 : if (!buffer || i >= buffer->capacity) {
144 3 : return NULL;
145 : }
146 3693 : return ((char *)buffer->data + (i * buffer->sizeof_type));
147 3696 : }
148 :
149 : void *
150 90 : CCC_flat_buffer_back(CCC_Flat_buffer const *const buffer) {
151 90 : return CCC_flat_buffer_at(buffer, buffer->count - 1);
152 : }
153 :
154 : void *
155 7 : CCC_flat_buffer_front(CCC_Flat_buffer const *const buffer) {
156 7 : return CCC_flat_buffer_at(buffer, 0);
157 : }
158 :
159 : void *
160 4877 : CCC_flat_buffer_allocate_back(
161 : CCC_Flat_buffer *const buffer, CCC_Allocator const *const allocator
162 : ) {
163 4877 : if (!buffer || !allocator) {
164 4 : return NULL;
165 : }
166 4873 : if (buffer->count == buffer->capacity) {
167 40 : CCC_Result const resize_res = CCC_flat_buffer_allocate(
168 20 : buffer, max(buffer->capacity * 2, START_CAPACITY), allocator
169 : );
170 20 : if (resize_res != CCC_RESULT_OK) {
171 6 : return NULL;
172 : }
173 20 : }
174 9734 : void *const ret
175 4867 : = ((char *)buffer->data + (buffer->sizeof_type * buffer->count));
176 4867 : ++buffer->count;
177 4867 : return ret;
178 4877 : }
179 :
180 : void *
181 149 : CCC_flat_buffer_push_back(
182 : CCC_Flat_buffer *const buffer,
183 : void const *const data,
184 : CCC_Allocator const *const allocator
185 : ) {
186 149 : if (!data) {
187 1 : return NULL;
188 : }
189 148 : void *const slot = CCC_flat_buffer_allocate_back(buffer, allocator);
190 148 : if (slot) {
191 142 : (void)memcpy(slot, data, buffer->sizeof_type);
192 142 : }
193 148 : return slot;
194 149 : }
195 :
196 : CCC_Result
197 5 : CCC_flat_buffer_swap(
198 : CCC_Flat_buffer const *const buffer,
199 : void *const temp,
200 : size_t const index,
201 : size_t const swap_index
202 : ) {
203 5 : if (!buffer || !temp || index >= buffer->capacity
204 5 : || swap_index >= buffer->capacity || swap_index == index) {
205 1 : return CCC_RESULT_ARGUMENT_ERROR;
206 : }
207 4 : (void)memcpy(temp, at(buffer, index), buffer->sizeof_type);
208 4 : (void)memcpy(
209 4 : at(buffer, index), at(buffer, swap_index), buffer->sizeof_type
210 : );
211 4 : (void)memcpy(at(buffer, swap_index), temp, buffer->sizeof_type);
212 4 : return CCC_RESULT_OK;
213 5 : }
214 :
215 : void *
216 3 : CCC_flat_buffer_move(
217 : CCC_Flat_buffer const *const buffer,
218 : size_t const destination,
219 : size_t const source
220 : ) {
221 3 : if (!buffer || destination >= buffer->capacity
222 3 : || source >= buffer->capacity) {
223 1 : return NULL;
224 : }
225 2 : if (destination == source) {
226 1 : return at(buffer, destination);
227 : }
228 1 : return memcpy(
229 1 : at(buffer, destination), at(buffer, source), buffer->sizeof_type
230 : );
231 3 : }
232 :
233 : CCC_Result
234 10 : CCC_flat_buffer_write(
235 : CCC_Flat_buffer const *const buffer, size_t const i, void const *const data
236 : ) {
237 10 : if (!buffer || !buffer->data || !data) {
238 1 : return CCC_RESULT_ARGUMENT_ERROR;
239 : }
240 9 : void *const pos = CCC_flat_buffer_at(buffer, i);
241 9 : if (!pos || data == pos) {
242 2 : return CCC_RESULT_ARGUMENT_ERROR;
243 : }
244 7 : (void)memcpy(pos, data, buffer->sizeof_type);
245 7 : return CCC_RESULT_OK;
246 10 : }
247 :
248 : CCC_Result
249 6 : CCC_flat_buffer_erase(CCC_Flat_buffer *const buffer, size_t const i) {
250 6 : if (!buffer || !buffer->count || i >= buffer->count) {
251 2 : return CCC_RESULT_ARGUMENT_ERROR;
252 : }
253 4 : if (1 == buffer->count) {
254 1 : buffer->count = 0;
255 1 : return CCC_RESULT_OK;
256 : }
257 3 : if (i == buffer->count - 1) {
258 1 : --buffer->count;
259 1 : return CCC_RESULT_OK;
260 : }
261 2 : (void)memmove(
262 2 : at(buffer, i),
263 2 : at(buffer, i + 1),
264 2 : buffer->sizeof_type * (buffer->count - (i + 1))
265 : );
266 2 : --buffer->count;
267 2 : return CCC_RESULT_OK;
268 6 : }
269 :
270 : void *
271 11 : CCC_flat_buffer_insert(
272 : CCC_Flat_buffer *const buffer,
273 : size_t const i,
274 : void const *const data,
275 : CCC_Allocator const *const allocator
276 : ) {
277 11 : if (!buffer || !buffer->data || i > buffer->count || !allocator) {
278 4 : return NULL;
279 : }
280 7 : if (i == buffer->count) {
281 1 : return CCC_flat_buffer_push_back(buffer, data, allocator);
282 : }
283 6 : if (buffer->count == buffer->capacity) {
284 2 : CCC_Result const r = CCC_flat_buffer_allocate(
285 1 : buffer, max(buffer->count * 2, START_CAPACITY), allocator
286 : );
287 1 : if (r != CCC_RESULT_OK) {
288 1 : return NULL;
289 : }
290 1 : }
291 5 : (void)memmove(
292 5 : at(buffer, i + 1),
293 5 : at(buffer, i),
294 5 : buffer->sizeof_type * (buffer->count - i)
295 : );
296 5 : ++buffer->count;
297 5 : return memcpy(at(buffer, i), data, buffer->sizeof_type);
298 11 : }
299 :
300 : CCC_Result
301 66 : CCC_flat_buffer_pop_back_n(CCC_Flat_buffer *const buffer, size_t count) {
302 66 : if (!buffer || count > buffer->count) {
303 4 : return CCC_RESULT_ARGUMENT_ERROR;
304 : }
305 62 : buffer->count -= count;
306 62 : return CCC_RESULT_OK;
307 66 : }
308 :
309 : CCC_Result
310 64 : CCC_flat_buffer_pop_back(CCC_Flat_buffer *const buffer) {
311 64 : return CCC_flat_buffer_pop_back_n(buffer, 1);
312 : }
313 :
314 : CCC_Count
315 1078 : CCC_flat_buffer_count(CCC_Flat_buffer const *const buffer) {
316 1078 : if (!buffer) {
317 1 : return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
318 : }
319 1077 : return (CCC_Count){.count = buffer->count};
320 1078 : }
321 :
322 : CCC_Count
323 29 : CCC_flat_buffer_capacity(CCC_Flat_buffer const *const buffer) {
324 29 : if (!buffer) {
325 1 : return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
326 : }
327 28 : return (CCC_Count){.count = buffer->capacity};
328 29 : }
329 :
330 : CCC_Count
331 128 : CCC_flat_buffer_sizeof_type(CCC_Flat_buffer const *const buffer) {
332 128 : if (!buffer) {
333 1 : return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
334 : }
335 127 : return (CCC_Count){.count = buffer->sizeof_type};
336 128 : }
337 :
338 : CCC_Tribool
339 2066 : CCC_flat_buffer_is_empty(CCC_Flat_buffer const *const buffer) {
340 2066 : if (!buffer) {
341 1 : return CCC_TRIBOOL_ERROR;
342 : }
343 2065 : return !buffer->count;
344 2066 : }
345 :
346 : CCC_Tribool
347 29 : CCC_flat_buffer_is_full(CCC_Flat_buffer const *const buffer) {
348 29 : if (!buffer) {
349 1 : return CCC_TRIBOOL_ERROR;
350 : }
351 28 : if (!buffer->capacity) {
352 1 : return CCC_FALSE;
353 : }
354 27 : return buffer->count == buffer->capacity ? CCC_TRUE : CCC_FALSE;
355 29 : }
356 :
357 : void *
358 2156 : CCC_flat_buffer_begin(CCC_Flat_buffer const *const buffer) {
359 2156 : return buffer ? buffer->data : NULL;
360 : }
361 :
362 : void *
363 64 : CCC_flat_buffer_reverse_begin(CCC_Flat_buffer const *const buffer) {
364 64 : if (!buffer || !buffer->data) {
365 2 : return NULL;
366 : }
367 : /* OK if count is 0. Negative offset puts at reverse_end anyway. */
368 124 : return (unsigned char *)buffer->data
369 62 : + ((buffer->count - 1) * buffer->sizeof_type);
370 64 : }
371 :
372 : void *
373 1415 : CCC_flat_buffer_next(
374 : CCC_Flat_buffer const *const buffer, void const *const iterator
375 : ) {
376 1415 : if (!buffer || !buffer->capacity || !iterator) {
377 1 : return NULL;
378 : }
379 1414 : if ((char *)iterator
380 1414 : >= (char *)buffer->data + ((buffer->count - 1) * buffer->sizeof_type)) {
381 78 : return CCC_flat_buffer_end(buffer);
382 : }
383 1336 : return (unsigned char *)iterator + buffer->sizeof_type;
384 1415 : }
385 :
386 : void *
387 384 : CCC_flat_buffer_reverse_next(
388 : CCC_Flat_buffer const *const buffer, void const *const iterator
389 : ) {
390 384 : if (!buffer || !buffer->data || !iterator) {
391 1 : return NULL;
392 : }
393 383 : if (iterator <= CCC_flat_buffer_reverse_end(buffer)) {
394 1 : return CCC_flat_buffer_reverse_end(buffer);
395 : }
396 382 : return (char *)iterator - buffer->sizeof_type;
397 384 : }
398 :
399 : void *
400 1251 : CCC_flat_buffer_end(CCC_Flat_buffer const *const buffer) {
401 1251 : if (!buffer || !buffer->data) {
402 2 : return NULL;
403 : }
404 2498 : return (unsigned char *)buffer->data
405 1249 : + (buffer->count * buffer->sizeof_type);
406 1251 : }
407 :
408 : /** We accept that reverse_end is out of bounds and the address before start.
409 : Even if the array base was somehow 0 and wrapping occurred upon subtraction the
410 : iterator would eventually reach this same address through reverse_next and be
411 : compared to it in the main user loop. */
412 : void *
413 738 : CCC_flat_buffer_reverse_end(CCC_Flat_buffer const *const buffer) {
414 738 : if (!buffer || !buffer->data) {
415 1 : return NULL;
416 : }
417 737 : return (unsigned char *)buffer->data - buffer->sizeof_type;
418 738 : }
419 :
420 : CCC_Count
421 91 : CCC_flat_buffer_index(
422 : CCC_Flat_buffer const *const buffer, void const *const slot
423 : ) {
424 91 : if (!buffer || !buffer->data || !slot || slot < buffer->data
425 89 : || (char *)slot
426 176 : >= ((char *)buffer->data
427 88 : + (buffer->capacity * buffer->sizeof_type))) {
428 3 : return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
429 : }
430 0 : assert(
431 88 : slot >= buffer->data && "positive pointer difference is caught at entry"
432 : );
433 176 : return (CCC_Count){
434 : .count
435 88 : = ((size_t)((char *)slot - ((char *)buffer->data))
436 88 : / buffer->sizeof_type),
437 : };
438 91 : }
439 :
440 : CCC_Result
441 3 : CCC_flat_buffer_count_plus(CCC_Flat_buffer *const buffer, size_t const count) {
442 3 : if (!buffer) {
443 1 : return CCC_RESULT_ARGUMENT_ERROR;
444 : }
445 2 : size_t const new_count = buffer->count + count;
446 2 : if (new_count > buffer->capacity) {
447 1 : buffer->count = buffer->capacity;
448 1 : return CCC_RESULT_ARGUMENT_ERROR;
449 : }
450 1 : buffer->count = new_count;
451 1 : return CCC_RESULT_OK;
452 3 : }
453 :
454 : CCC_Result
455 3 : CCC_flat_buffer_count_minus(CCC_Flat_buffer *const buffer, size_t const count) {
456 3 : if (!buffer) {
457 1 : return CCC_RESULT_ARGUMENT_ERROR;
458 : }
459 2 : if (count > buffer->count) {
460 1 : buffer->count = 0;
461 1 : return CCC_RESULT_ARGUMENT_ERROR;
462 : }
463 1 : buffer->count -= count;
464 1 : return CCC_RESULT_OK;
465 3 : }
466 :
467 : CCC_Result
468 3 : CCC_flat_buffer_count_set(CCC_Flat_buffer *const buffer, size_t const count) {
469 3 : if (!buffer) {
470 1 : return CCC_RESULT_ARGUMENT_ERROR;
471 : }
472 2 : if (count > buffer->capacity) {
473 1 : buffer->count = buffer->capacity;
474 1 : return CCC_RESULT_ARGUMENT_ERROR;
475 : }
476 1 : buffer->count = count;
477 1 : return CCC_RESULT_OK;
478 3 : }
479 :
480 : CCC_Count
481 3 : CCC_flat_buffer_count_bytes(CCC_Flat_buffer const *buffer) {
482 3 : if (!buffer) {
483 1 : return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
484 : }
485 2 : return (CCC_Count){.count = buffer->count * buffer->sizeof_type};
486 3 : }
487 :
488 : CCC_Count
489 2 : CCC_flat_buffer_capacity_bytes(CCC_Flat_buffer const *buffer) {
490 2 : if (!buffer) {
491 1 : return (CCC_Count){.error = CCC_RESULT_ARGUMENT_ERROR};
492 : }
493 2 : return (CCC_Count){
494 1 : .count = buffer->capacity * buffer->sizeof_type,
495 : };
496 2 : }
497 :
498 : CCC_Result
499 18 : CCC_flat_buffer_copy(
500 : CCC_Flat_buffer *const destination,
501 : CCC_Flat_buffer const *const source,
502 : CCC_Allocator const *const allocator
503 : ) {
504 18 : if (!destination || !source || source == destination || !allocator
505 16 : || (destination->capacity < source->capacity && !allocator->allocate)) {
506 6 : return CCC_RESULT_ARGUMENT_ERROR;
507 : }
508 12 : if (!source->capacity) {
509 1 : return CCC_RESULT_OK;
510 : }
511 11 : if (destination->capacity < source->capacity) {
512 16 : CCC_Result const r = CCC_flat_buffer_allocate(
513 8 : destination, source->capacity, allocator
514 : );
515 8 : if (r != CCC_RESULT_OK) {
516 1 : return r;
517 : }
518 7 : destination->capacity = source->capacity;
519 8 : }
520 10 : if (!source->data || !destination->data) {
521 1 : return CCC_RESULT_ARGUMENT_ERROR;
522 : }
523 9 : destination->count = source->count;
524 9 : (void)memcpy(
525 9 : destination->data, source->data, source->capacity * source->sizeof_type
526 : );
527 9 : return CCC_RESULT_OK;
528 18 : }
529 :
530 : void *
531 3 : CCC_flat_buffer_data(CCC_Flat_buffer const *const buffer) {
532 3 : return buffer ? buffer->data : NULL;
533 : }
534 :
535 : /*====================== Static Helpers ==================================*/
536 :
537 : static inline void *
538 38 : at(struct CCC_Flat_buffer const *const buffer, size_t const i) {
539 38 : return ((char *)buffer->data + (i * buffer->sizeof_type));
540 : }
541 :
542 : static inline size_t
543 21 : max(size_t const a, size_t const b) {
544 21 : return a > b ? a : b;
545 : }
|