infix
A JIT-Powered FFI Library for C
Loading...
Searching...
No Matches
type_registry.c
Go to the documentation of this file.
1
39#include <ctype.h>
40#include <string.h>
49#define INITIAL_REGISTRY_BUCKETS 61
64// Hash Table Implementation
72static uint64_t _registry_hash_string(const char * str) {
73 uint64_t hash = 5381;
74 int c;
75 while ((c = *str++))
76 hash = ((hash << 5) + hash) + c; // hash * 33 + c
77 return hash;
78}
87 if (!registry || !name)
88 return nullptr;
89 uint64_t hash = _registry_hash_string(name);
90 size_t index = hash % registry->num_buckets;
91 // Traverse the linked list (chain) at the computed bucket index.
92 for (_infix_registry_entry_t * current = registry->buckets[index]; current; current = current->next)
93 if (current->hash == hash && strcmp(current->name, name) == 0)
94 return current;
95 return nullptr;
96}
97
105 // Roughly double the size. Keep it odd to slightly help with distribution.
106 size_t new_num_buckets = registry->num_buckets * 2 + 1;
107
108 // Allocate new buckets from the heap.
109 _infix_registry_entry_t ** new_buckets = infix_calloc(new_num_buckets, sizeof(_infix_registry_entry_t *));
110
111 if (!new_buckets) {
112 // If allocation fails, we simply return and continue using the existing buckets.
113 // Performance will degrade, but correctness is preserved.
114 return;
115 }
116
117 // Rehash all existing entries.
118 for (size_t i = 0; i < registry->num_buckets; ++i) {
120 while (entry) {
121 _infix_registry_entry_t * next_entry = entry->next; // Save next pointer
122
123 // Use the pre-calculated hash
124 size_t new_index = entry->hash % new_num_buckets;
125
126 // Insert into new bucket (prepend)
127 entry->next = new_buckets[new_index];
128 new_buckets[new_index] = entry;
129
130 entry = next_entry;
131 }
132 }
133
134 // Update the registry to use the new table and free the old one.
136 registry->buckets = new_buckets;
137 registry->num_buckets = new_num_buckets;
138}
139
154 // Check load factor. If num_items > num_buckets * 0.75, resize.
155 // Equivalent integer math: num_items * 4 > num_buckets * 3
156 if (registry->num_items * 4 > registry->num_buckets * 3)
158
159 uint64_t hash = _registry_hash_string(name);
160 size_t index = hash % registry->num_buckets;
161 _infix_registry_entry_t * new_entry =
163 if (!new_entry) {
165 return nullptr;
166 }
167 size_t name_len = strlen(name) + 1;
168 char * name_copy = infix_arena_alloc(registry->arena, name_len, 1);
169 if (!name_copy) {
171 return nullptr;
172 }
173 infix_memcpy((void *)name_copy, name, name_len);
174 new_entry->name = name_copy;
175 new_entry->hash = hash;
176 new_entry->type = nullptr;
177 new_entry->is_forward_declaration = false;
178 // Prepend to the linked list in the bucket.
179 new_entry->next = registry->buckets[index];
180 registry->buckets[index] = new_entry;
182 return new_entry;
183}
184// Public API: Registry Lifecycle
262 if (!src)
263 return nullptr;
264
266 if (!dest)
267 return nullptr;
268
269 // Iterate over all buckets in the source registry
270 for (size_t i = 0; i < src->num_buckets; ++i) {
271 _infix_registry_entry_t * entry = src->buckets[i];
272 while (entry) {
273 // Create a new entry in the destination registry.
274 // _registry_insert handles allocating the entry and copying the name string.
275 _infix_registry_entry_t * new_entry = _registry_insert(dest, entry->name);
276 if (!new_entry) {
278 return nullptr;
279 }
280
281 // Copy flags
283
284 // Deep copy the type graph into the new registry's arena
285 if (entry->type) {
286 new_entry->type = _copy_type_graph_to_arena(dest->arena, entry->type);
287 if (!new_entry->type) {
289 return nullptr;
290 }
291 }
292
293 entry = entry->next;
294 }
295 }
296 return dest;
297}
308 if (!registry)
309 return;
310 // Only destroy the arena if it was created internally by `infix_registry_create`.
313 // Free the bucket array which is now allocated from the heap.
315 // Always free the registry struct itself.
317}
318// Internal Type Graph Resolution
338 infix_type ** type_ptr,
340 resolve_memo_node_t ** memo_head) {
341 if (!type_ptr || !*type_ptr || !(*type_ptr)->is_arena_allocated)
342 return INFIX_SUCCESS;
343 infix_type * type = *type_ptr;
344 // Cycle detection: If we've seen this node before, we're in a cycle.
345 // Return success to break the loop.
346 for (resolve_memo_node_t * node = *memo_head; node != nullptr; node = node->next)
347 if (node->src == type)
348 return INFIX_SUCCESS;
349 // Allocate the memoization node from the stable temporary arena.
350 resolve_memo_node_t * memo_node =
351 infix_arena_alloc(temp_arena, sizeof(resolve_memo_node_t), _Alignof(resolve_memo_node_t));
352 if (!memo_node) {
355 }
356 memo_node->src = type;
357 memo_node->next = *memo_head;
358 *memo_head = memo_node;
360 if (!registry) {
363 }
364 const char * name = type->meta.named_reference.name;
366 if (!entry || !entry->type) {
369 }
370 *type_ptr = entry->type;
371 return INFIX_SUCCESS;
372 }
374 switch (type->category) {
377 temp_arena, &type->meta.pointer_info.pointee_type, registry, memo_head);
378 break;
379 case INFIX_TYPE_ARRAY:
380 status =
382 break;
384 case INFIX_TYPE_UNION:
385 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
387 temp_arena, &type->meta.aggregate_info.members[i].type, registry, memo_head);
388 if (status != INFIX_SUCCESS)
389 break;
390 }
391 break;
394 temp_arena, &type->meta.func_ptr_info.return_type, registry, memo_head);
395 if (status != INFIX_SUCCESS)
396 break;
397 for (size_t i = 0; i < type->meta.func_ptr_info.num_args; ++i) {
399 temp_arena, &type->meta.func_ptr_info.args[i].type, registry, memo_head);
400 if (status != INFIX_SUCCESS)
401 break;
402 }
403 break;
404 case INFIX_TYPE_ENUM:
406 temp_arena, &type->meta.enum_info.underlying_type, registry, memo_head);
407 break;
409 status =
411 break;
414 temp_arena, &type->meta.vector_info.element_type, registry, memo_head);
415 break;
416 default:
417 break;
418 }
419 return status;
420}
429 // Create a temporary arena solely for the visited list's lifetime.
430 infix_arena_t * temp_arena = infix_arena_create(1024);
431 if (!temp_arena) {
434 }
435 resolve_memo_node_t * memo_head = nullptr;
436 infix_status status = _resolve_type_graph_inplace_recursive(temp_arena, type_ptr, registry, &memo_head);
437 infix_arena_destroy(temp_arena);
438 return status;
439}
440// Public API: Type Registration
449typedef struct {
450 const char * p;
451 const char * start;
459 while (1) {
460 while (isspace((unsigned char)*state->p))
461 state->p++;
462 if (*state->p == '#') // Skip comments
463 while (*state->p != '\n' && *state->p != '\0')
464 state->p++;
465 else
466 break;
467 }
468}
477static char * _registry_parser_parse_name(_registry_parser_state_t * state, char * buffer, size_t buf_size) {
479 const char * name_start = state->p;
480 if (!isalpha((unsigned char)*name_start) && *name_start != '_')
481 return nullptr;
482 while (isalnum((unsigned char)*state->p) || *state->p == '_' || *state->p == ':') {
483 if (*state->p == ':' && state->p[1] != ':')
484 break; // Handle single colon as non-identifier char.
485 if (*state->p == ':')
486 state->p++; // Skip the first ':' of '::'
487 state->p++;
488 }
489 size_t len = state->p - name_start;
490 if (len == 0 || len >= buf_size)
491 return nullptr;
492 infix_memcpy(buffer, name_start, len);
493 buffer[len] = '\0';
494 return buffer;
495}
525 if (!registry || !definitions) {
528 }
529 _registry_parser_state_t state = {.p = definitions, .start = definitions};
531 // A temporary structure to hold information about each definition found in Pass 1.
532 struct def_info {
534 const char * def_body_start;
535 size_t def_body_len;
536 };
537 size_t defs_capacity = 64; // Start with an initial capacity.
538 struct def_info * defs_found = infix_malloc(sizeof(struct def_info) * defs_capacity);
539 if (!defs_found) {
542 }
543 size_t num_defs_found = 0;
544 infix_status final_status = INFIX_SUCCESS;
545
546 // Pass 1: Scan & Index all names and their definition bodies.
547 while (*state.p != '\0') {
549 if (*state.p == '\0')
550 break;
551 if (*state.p != '@') {
553 final_status = INFIX_ERROR_INVALID_ARGUMENT;
554 goto cleanup;
555 }
556 state.p++;
557 char name_buffer[256];
558 if (!_registry_parser_parse_name(&state, name_buffer, sizeof(name_buffer))) {
560 final_status = INFIX_ERROR_INVALID_ARGUMENT;
561 goto cleanup;
562 }
563 _infix_registry_entry_t * entry = _registry_lookup(registry, name_buffer);
565 if (*state.p == '=') { // This is a full definition.
566 state.p++;
568 // It's an error to redefine a type that wasn't a forward declaration.
569 if (entry && !entry->is_forward_declaration) {
571 final_status = INFIX_ERROR_INVALID_ARGUMENT;
572 goto cleanup;
573 }
574 if (!entry) { // If it doesn't exist, create it.
575 entry = _registry_insert(registry, name_buffer);
576 if (!entry) {
577 final_status = INFIX_ERROR_ALLOCATION_FAILED;
578 goto cleanup;
579 }
580 }
581 if (num_defs_found >= defs_capacity) {
582 size_t new_capacity = defs_capacity * 2;
583 struct def_info * new_defs = infix_realloc(defs_found, sizeof(struct def_info) * new_capacity);
584 if (!new_defs) {
586 final_status = INFIX_ERROR_ALLOCATION_FAILED;
587 goto cleanup;
588 }
589 defs_found = new_defs;
590 defs_capacity = new_capacity;
591 }
592 // Find the end of the type definition body (the next top-level ';').
593 defs_found[num_defs_found].entry = entry;
594 defs_found[num_defs_found].def_body_start = state.p;
595 int nest_level = 0;
596 const char * body_end = state.p;
597 while (*body_end != '\0' &&
598 !(*body_end == ';' &&
599 nest_level == 0)) { // Explicitly check for and skip over the '->' token as a single unit.
600 if (*body_end == '-' && body_end[1] == '>') {
601 body_end += 2; // Advance the pointer past the entire token.
602 continue; // Continue to the next character in the loop.
603 }
604 if (*body_end == '{' || *body_end == '<' || *body_end == '(' || *body_end == '[')
605 nest_level++;
606 if (*body_end == '}' || *body_end == '>' || *body_end == ')' || *body_end == ']')
607 nest_level--;
608 body_end++;
609 }
610 defs_found[num_defs_found].def_body_len = body_end - state.p;
611 state.p = body_end;
612 num_defs_found++;
613 }
614 else if (*state.p == ';') { // This is a forward declaration.
615 if (entry) {
616 // If the type is already fully defined, re-declaring it as a forward decl is an error.
617 if (!entry->is_forward_declaration) {
619 final_status = INFIX_ERROR_INVALID_ARGUMENT;
620 goto cleanup;
621 }
622 // If it's already a forward declaration, this is a no-op.
623 }
624 else {
625 entry = _registry_insert(registry, name_buffer);
626 if (!entry) {
627 final_status = INFIX_ERROR_ALLOCATION_FAILED;
628 goto cleanup;
629 }
630 }
631 // Ensure a placeholder type exists so other types can reference it immediately.
632 // We create an opaque struct (size 0) as the placeholder and mark it incomplete.
633 if (!entry->type) {
634 entry->type = infix_arena_calloc(registry->arena, 1, sizeof(infix_type), _Alignof(infix_type));
635 if (!entry->type) {
636 final_status = INFIX_ERROR_ALLOCATION_FAILED;
637 goto cleanup;
638 }
639 entry->type->is_arena_allocated = true;
640 entry->type->is_incomplete = true; // Mark as incomplete
641 entry->type->arena = registry->arena;
643 entry->type->size = 0;
644 entry->type->alignment = 1; // Minimal alignment to pass basic validation
645 entry->type->name = entry->name;
646 }
647 entry->is_forward_declaration = true;
648 }
649 else {
651 final_status = INFIX_ERROR_INVALID_ARGUMENT;
652 goto cleanup;
653 }
654 if (*state.p == ';')
655 state.p++;
656 }
657
658 // Use a single temporary arena for ALL definitions in this batch to reduce malloc pressure.
659 infix_arena_t * parser_arena = infix_arena_create(65536);
660 if (!parser_arena) {
662 final_status = INFIX_ERROR_ALLOCATION_FAILED;
663 goto cleanup;
664 }
665
666 // Pass 2: Parse the bodies of all found definitions into the registry.
667 for (size_t i = 0; i < num_defs_found; ++i) {
668 _infix_registry_entry_t * entry = defs_found[i].entry;
669
670 // FAST-PATH: Check if the definition body is just a single primitive keyword.
671 // This avoids the overhead of the recursive-descent parser for simple aliases.
672 infix_type * fast_primitive = nullptr;
673 char body_buffer[64];
674 if (defs_found[i].def_body_len > 0 && defs_found[i].def_body_len < sizeof(body_buffer)) {
675 infix_memcpy(body_buffer, defs_found[i].def_body_start, defs_found[i].def_body_len);
676 body_buffer[defs_found[i].def_body_len] = '\0';
677
678 // Use a temporary parser state for parse_primitive.
679 parser_state fast_state = {.p = body_buffer, .start = body_buffer, .arena = parser_arena, .depth = 0};
680 fast_primitive = parse_primitive(&fast_state);
681
682 // Ensure no trailing junk even in fast-path.
683 if (fast_primitive) {
684 skip_whitespace(&fast_state);
685 if (*fast_state.p != '\0')
686 fast_primitive = nullptr;
687 }
688 }
689
690 infix_type * raw_type = nullptr;
691 if (fast_primitive) {
692 raw_type = fast_primitive;
693 }
694 else {
695 // SLOW-PATH: Fall back to full signature parser.
696 // Make a temporary, null-terminated copy of the definition body substring.
697 char * body_copy = infix_malloc(defs_found[i].def_body_len + 1);
698 if (!body_copy) {
700 final_status = INFIX_ERROR_ALLOCATION_FAILED;
701 infix_arena_destroy(parser_arena);
702 goto cleanup;
703 }
704 infix_memcpy(body_copy, defs_found[i].def_body_start, defs_found[i].def_body_len);
705 body_copy[defs_found[i].def_body_len] = '\0';
706
707 parser_state state = {.p = body_copy, .start = body_copy, .arena = parser_arena, .depth = 0};
708 raw_type = parse_type(&state);
709
710 if (raw_type) {
711 skip_whitespace(&state);
712 if (*state.p != '\0') {
714 raw_type = nullptr;
715 }
716 }
717
718 if (!raw_type) {
720 _infix_set_error(err.category, err.code, (defs_found[i].def_body_start - definitions) + err.position);
721 final_status = INFIX_ERROR_INVALID_ARGUMENT;
722 infix_free(body_copy);
723 infix_arena_destroy(parser_arena);
724 goto cleanup;
725 }
726 infix_free(body_copy);
727 }
728
729 const infix_type * type_to_alias = raw_type;
730 // If the RHS is another named type (e.g., @MyAlias = @ExistingType),
731 // we need to resolve it first to get the actual type we're aliasing.
732 if (raw_type->category == INFIX_TYPE_NAMED_REFERENCE) {
734 if (existing_entry && existing_entry->type)
735 type_to_alias = existing_entry->type;
736 else {
737 size_t relative_pos = raw_type->source_offset;
740 (size_t)(defs_found[i].def_body_start - definitions) + relative_pos);
741 final_status = INFIX_ERROR_INVALID_ARGUMENT;
742 infix_arena_destroy(parser_arena);
743 goto cleanup;
744 }
745 }
746 // Prepare the new definition.
747 infix_type * new_def = nullptr;
748 if (!type_to_alias->is_arena_allocated) {
749 // This is a static type (e.g., primitive). We MUST create a mutable
750 // copy in the registry's arena before we can attach a name to it.
751 // This prevents corrupting the global static singletons.
752 new_def = infix_arena_alloc(registry->arena, sizeof(infix_type), _Alignof(infix_type));
753 if (new_def) {
754 *new_def = *type_to_alias;
755 new_def->is_arena_allocated = true;
756 new_def->arena = registry->arena;
757 }
758 }
759 else // Dynamic type: deep copy.
760 new_def = _copy_type_graph_to_arena(registry->arena, type_to_alias);
761
762 if (!new_def) {
764 final_status = INFIX_ERROR_ALLOCATION_FAILED;
765 infix_arena_destroy(parser_arena);
766 goto cleanup;
767 }
768
769 // Update the entry. If a placeholder already exists (from forward decl), we MUST update it in-place
770 // to preserve pointers from other types that have already resolved to it.
771 if (entry->type) {
772 // Struct-copy the new definition into the existing placeholder memory.
773 *entry->type = *new_def;
774 // Restore the self-reference if the copy logic pointed the arena elsewhere.
775 entry->type->arena = registry->arena;
776 // The new definition is complete, so ensure the flag is cleared.
777 entry->type->is_incomplete = false;
778 }
779 else {
780 entry->type = new_def;
781 entry->type->is_incomplete = false;
782 }
783
784 // Ensure the name is attached and the flag is cleared.
785 entry->type->name = entry->name;
786 entry->is_forward_declaration = false;
787 }
788
789 infix_arena_destroy(parser_arena);
790
791 // Pass 3: Resolve and layout all the newly defined types.
792 for (size_t i = 0; i < num_defs_found; ++i) {
793 _infix_registry_entry_t * entry = defs_found[i].entry;
794 if (entry->type) {
795 // "Resolve" and "Layout" steps.
797 // The error was set inside resolve (relative to body).
798 // We need to re-base it to the full definitions string.
800 size_t body_offset = defs_found[i].def_body_start - definitions;
801 _infix_set_error(err.category, err.code, body_offset + err.position);
802 final_status = INFIX_ERROR_INVALID_ARGUMENT;
803 goto cleanup;
804 }
806 }
807 }
808cleanup:
809 infix_free(defs_found);
810 return final_status;
811}
812// Registry Introspection API Implementation
833 // Return an iterator positioned before the first element.
834 // The first call to next() will advance it to the first valid element.
836 it.registry = registry;
837 it._bucket_index = 0;
838 it._current_entry = nullptr;
839 return it;
840}
841
850 if (!iter || !iter->registry)
851 return false;
852
853 // Cast the opaque void* back to the internal entry type
855
856 // If we have a current entry, start from the next one in the chain.
857 if (entry)
858 entry = entry->next;
859 // Otherwise, if we are starting (or moved buckets), begin with the head of the current bucket.
860 else if (iter->_bucket_index < iter->registry->num_buckets)
861 entry = iter->registry->buckets[iter->_bucket_index];
862
863 while (true) {
864 // Traverse the current chain looking for a valid entry.
865 while (entry) {
866 if (entry->type && !entry->is_forward_declaration) {
867 // Found one. Update the iterator and return successfully.
868 iter->_current_entry = (void *)entry;
869 return true;
870 }
871 entry = entry->next;
872 }
873 // If we're here, the current chain is exhausted. Move to the next bucket.
874 iter->_bucket_index++;
875 // If there are no more buckets, we're done.
876 if (iter->_bucket_index >= iter->registry->num_buckets) {
877 iter->_current_entry = nullptr;
878 return false;
879 }
880 // Start the search from the head of the new bucket.
881 entry = iter->registry->buckets[iter->_bucket_index];
882 }
883}
893 if (!iter || !iter->_current_entry)
894 return nullptr;
896 return entry->name;
897}
907 if (!iter || !iter->_current_entry)
908 return nullptr;
910 return entry->type;
911}
923 if (!registry || !name)
924 return false;
926 // It's defined if an entry exists, it has a type, and it's not a lingering forward declaration.
927 return entry != nullptr && entry->type != nullptr && !entry->is_forward_declaration;
928}
939 const char * name) {
940 if (!registry || !name)
941 return nullptr;
943 if (entry && entry->type && !entry->is_forward_declaration)
944 return entry->type;
945 return nullptr;
946}
infix_arena_t * arena
Definition 005_layouts.c:62
const char * definitions
Definition 008_registry_introspection.c:36
infix_registry_t * registry
Definition 008_registry_introspection.c:33
infix_status status
Definition 103_unions.c:61
size_t buf_size
Definition 904_registry_benchmark.c:23
#define c23_nodiscard
Internal alias for the public INFIX_NODISCARD macro.
Definition compat_c23.h:92
#define INFIX_TLS
Definition error.c:68
INFIX_API infix_error_details_t infix_get_last_error(void)
Retrieves detailed information about the last error that occurred on the current thread.
Definition error.c:281
@ INFIX_CODE_UNRESOLVED_NAMED_TYPE
Definition infix.h:1387
@ INFIX_CODE_UNEXPECTED_TOKEN
Definition infix.h:1375
@ INFIX_CODE_NULL_POINTER
Definition infix.h:1364
@ INFIX_CODE_OUT_OF_MEMORY
Definition infix.h:1369
@ INFIX_CATEGORY_ALLOCATION
Definition infix.h:1353
@ INFIX_CATEGORY_GENERAL
Definition infix.h:1352
@ INFIX_CATEGORY_PARSER
Definition infix.h:1354
size_t source_offset
Definition infix.h:282
void * _current_entry
Definition infix.h:520
struct infix_type_t::@0::@1 pointer_info
Metadata for INFIX_TYPE_POINTER.
size_t position
Definition infix.h:1403
union infix_type_t::@0 meta
A union containing metadata specific to the type's category.
struct infix_type_t::@0::@7 vector_info
Metadata for INFIX_TYPE_VECTOR.
infix_error_category_t category
Definition infix.h:1401
const infix_registry_t * registry
Definition infix.h:518
bool is_incomplete
Definition infix.h:280
infix_type * type
Definition infix.h:348
struct infix_type_t::@0::@4 func_ptr_info
Metadata for INFIX_TYPE_REVERSE_TRAMPOLINE.
infix_arena_t * arena
Definition infix.h:281
size_t size
Definition infix.h:277
size_t alignment
Definition infix.h:278
infix_struct_member * members
Definition infix.h:293
struct infix_type_t::@0::@6 complex_info
Metadata for INFIX_TYPE_COMPLEX.
const char * name
Definition infix.h:275
infix_function_argument * args
Definition infix.h:306
infix_status
Enumerates the possible status codes returned by infix API functions.
Definition infix.h:434
infix_type_category category
Definition infix.h:276
size_t _bucket_index
Definition infix.h:519
struct infix_type_t::@0::@2 aggregate_info
Metadata for INFIX_TYPE_STRUCT and INFIX_TYPE_UNION.
struct infix_type_t::@0::@3 array_info
Metadata for INFIX_TYPE_ARRAY.
struct infix_type_t * pointee_type
Definition infix.h:289
infix_type * type
Definition infix.h:336
struct infix_type_t * element_type
Definition infix.h:299
struct infix_type_t * return_type
Definition infix.h:305
struct infix_type_t::@0::@5 enum_info
Metadata for INFIX_TYPE_ENUM.
struct infix_type_t * base_type
Definition infix.h:316
infix_error_code_t code
Definition infix.h:1402
size_t num_members
Definition infix.h:294
struct infix_type_t * underlying_type
Definition infix.h:312
struct infix_type_t::@0::@8 named_reference
Metadata for INFIX_TYPE_NAMED_REFERENCE.
size_t num_args
Definition infix.h:307
bool is_arena_allocated
Definition infix.h:279
@ INFIX_ERROR_ALLOCATION_FAILED
Definition infix.h:436
@ INFIX_SUCCESS
Definition infix.h:435
@ INFIX_ERROR_INVALID_ARGUMENT
Definition infix.h:437
#define infix_free
A macro that can be defined to override the default free function.
Definition infix.h:383
INFIX_API INFIX_NODISCARD void * infix_arena_alloc(infix_arena_t *, size_t, size_t)
Allocates a block of memory from an arena.
Definition arena.c:117
#define infix_memcpy
A macro that can be defined to override the default memcpy function.
Definition infix.h:387
#define infix_calloc
A macro that can be defined to override the default calloc function.
Definition infix.h:375
#define infix_realloc
A macro that can be defined to override the default realloc function.
Definition infix.h:379
INFIX_API INFIX_NODISCARD infix_arena_t * infix_arena_create(size_t)
Creates a new memory arena.
Definition arena.c:52
INFIX_API INFIX_NODISCARD void * infix_arena_calloc(infix_arena_t *, size_t, size_t, size_t)
Allocates and zero-initializes a block of memory from an arena.
Definition arena.c:188
#define infix_malloc
A macro that can be defined to override the default malloc function.
Definition infix.h:371
INFIX_API void infix_arena_destroy(infix_arena_t *)
Destroys an arena and frees all memory allocated from it.
Definition arena.c:83
INFIX_API void infix_registry_destroy(infix_registry_t *registry)
Destroys a type registry and frees all associated memory.
Definition type_registry.c:307
INFIX_API c23_nodiscard infix_status infix_register_types(infix_registry_t *registry, const char *definitions)
Parses a string of type definitions and adds them to a registry.
Definition type_registry.c:523
INFIX_API c23_nodiscard infix_registry_t * infix_registry_create(void)
Creates a new, empty named type registry.
Definition type_registry.c:196
INFIX_API c23_nodiscard infix_registry_t * infix_registry_clone(const infix_registry_t *src)
Creates a deep copy of an existing registry.
Definition type_registry.c:261
INFIX_API c23_nodiscard const infix_type * infix_registry_lookup_type(const infix_registry_t *registry, const char *name)
Retrieves the canonical infix_type object for a given name from the registry.
Definition type_registry.c:938
INFIX_API c23_nodiscard infix_registry_t * infix_registry_create_in_arena(infix_arena_t *arena)
Creates a new, empty named type registry that allocates from a user-provided arena.
Definition type_registry.c:233
INFIX_API c23_nodiscard bool infix_registry_iterator_next(infix_registry_iterator_t *iter)
Advances the iterator to the next defined type in the registry.
Definition type_registry.c:849
INFIX_API c23_nodiscard infix_registry_iterator_t infix_registry_iterator_begin(const infix_registry_t *registry)
Initializes an iterator for traversing the types in a registry.
Definition type_registry.c:832
INFIX_API c23_nodiscard const infix_type * infix_registry_iterator_get_type(const infix_registry_iterator_t *iter)
Gets the infix_type object of the type at the iterator's current position.
Definition type_registry.c:906
INFIX_API c23_nodiscard bool infix_registry_is_defined(const infix_registry_t *registry, const char *name)
Checks if a type with the given name is fully defined in the registry.
Definition type_registry.c:922
INFIX_API c23_nodiscard const char * infix_registry_iterator_get_name(const infix_registry_iterator_t *iter)
Gets the name of the type at the iterator's current position.
Definition type_registry.c:892
@ INFIX_TYPE_UNION
Definition infix.h:231
@ INFIX_TYPE_COMPLEX
Definition infix.h:235
@ INFIX_TYPE_ARRAY
Definition infix.h:232
@ INFIX_TYPE_VECTOR
Definition infix.h:236
@ INFIX_TYPE_POINTER
Definition infix.h:229
@ INFIX_TYPE_NAMED_REFERENCE
Definition infix.h:237
@ INFIX_TYPE_REVERSE_TRAMPOLINE
Definition infix.h:233
@ INFIX_TYPE_ENUM
Definition infix.h:234
@ INFIX_TYPE_STRUCT
Definition infix.h:230
#define INFIX_API
Symbol visibility macro.
Definition infix.h:114
Internal data structures, function prototypes, and constants.
INFIX_INTERNAL infix_type * parse_primitive(parser_state *state)
Definition signature.c:463
INFIX_INTERNAL void skip_whitespace(parser_state *state)
Definition signature.c:70
INFIX_INTERNAL infix_type * _copy_type_graph_to_arena(infix_arena_t *, const infix_type *)
Performs a deep copy of a type graph into a destination arena.
Definition types.c:1112
INFIX_INTERNAL void _infix_set_parser_error(parser_state *state, infix_error_code_t code)
Definition signature.c:60
INFIX_INTERNAL void _infix_type_recalculate_layout(infix_type *type)
Recalculates the layout of a fully resolved type graph.
Definition types.c:924
INFIX_INTERNAL void _infix_clear_error(void)
Clears the thread-local error state.
Definition error.c:268
INFIX_INTERNAL void _infix_set_error(infix_error_category_t category, infix_error_code_t code, size_t position)
Sets the thread-local error state with detailed information.
Definition error.c:175
INFIX_INTERNAL infix_type * parse_type(parser_state *state)
Definition signature.c:592
A single entry in the registry's hash table.
Definition infix_internals.h:174
bool is_forward_declaration
Definition infix_internals.h:178
const char * name
Definition infix_internals.h:175
uint64_t hash
Definition infix_internals.h:176
infix_type * type
Definition infix_internals.h:177
struct _infix_registry_entry_t * next
Definition infix_internals.h:179
Definition type_registry.c:449
const char * start
Definition type_registry.c:451
const char * p
Definition type_registry.c:450
Internal definition of a memory arena.
Definition infix_internals.h:143
Provides detailed, thread-local information about the last error that occurred.
Definition infix.h:1400
An iterator for traversing a type registry.
Definition infix.h:517
Internal definition of a named type registry.
Definition infix_internals.h:188
size_t num_items
Definition infix_internals.h:192
bool is_external_arena
Definition infix_internals.h:190
infix_arena_t * arena
Definition infix_internals.h:189
_infix_registry_entry_t ** buckets
Definition infix_internals.h:193
size_t num_buckets
Definition infix_internals.h:191
A semi-opaque structure that describes a C type.
Definition infix.h:274
Holds the complete state of the recursive descent parser during a single parse operation.
Definition infix_internals.h:199
const char * p
Definition infix_internals.h:200
Definition type_registry.c:60
struct resolve_memo_node_t * next
Definition type_registry.c:62
infix_type * src
Definition type_registry.c:61
static uint64_t _registry_hash_string(const char *str)
Definition type_registry.c:72
#define INITIAL_REGISTRY_BUCKETS
Definition type_registry.c:49
c23_nodiscard infix_status _infix_resolve_type_graph_inplace(infix_type **type_ptr, infix_registry_t *registry)
Resolves all named type references in a type graph in-place.
Definition type_registry.c:428
static void _registry_rehash(infix_registry_t *registry)
Definition type_registry.c:104
static _infix_registry_entry_t * _registry_lookup(infix_registry_t *registry, const char *name)
Definition type_registry.c:86
static _infix_registry_entry_t * _registry_insert(infix_registry_t *registry, const char *name)
Definition type_registry.c:153
static char * _registry_parser_parse_name(_registry_parser_state_t *state, char *buffer, size_t buf_size)
Definition type_registry.c:477
static void _registry_parser_skip_whitespace(_registry_parser_state_t *state)
Definition type_registry.c:458
static infix_status _resolve_type_graph_inplace_recursive(infix_arena_t *temp_arena, infix_type **type_ptr, infix_registry_t *registry, resolve_memo_node_t **memo_head)
Definition type_registry.c:337
INFIX_TLS const char * g_infix_last_signature_context
A thread-local pointer to the full signature string being parsed.
Definition error.c:99