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