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 size_t index = _registry_hash_string(name) % registry->num_buckets;
90 // Traverse the linked list (chain) at the computed bucket index.
91 for (_infix_registry_entry_t * current = registry->buckets[index]; current; current = current->next)
92 if (strcmp(current->name, name) == 0)
93 return current;
94 return nullptr;
95}
110 size_t index = _registry_hash_string(name) % registry->num_buckets;
111 _infix_registry_entry_t * new_entry =
113 if (!new_entry) {
115 return nullptr;
116 }
117 size_t name_len = strlen(name) + 1;
118 char * name_copy = infix_arena_alloc(registry->arena, name_len, 1);
119 if (!name_copy) {
121 return nullptr;
122 }
123 infix_memcpy((void *)name_copy, name, name_len);
124 new_entry->name = name_copy;
125 new_entry->type = nullptr;
126 new_entry->is_forward_declaration = false;
127 // Prepend to the linked list in the bucket.
128 new_entry->next = registry->buckets[index];
129 registry->buckets[index] = new_entry;
131 return new_entry;
132}
133// Public API: Registry Lifecycle
217 if (!registry)
218 return;
219 // Only destroy the arena if it was created internally by `infix_registry_create`.
222 // Always free the registry struct itself.
224}
225// Internal Type Graph Resolution
245 infix_type ** type_ptr,
247 resolve_memo_node_t ** memo_head) {
248 if (!type_ptr || !*type_ptr || !(*type_ptr)->is_arena_allocated)
249 return INFIX_SUCCESS;
250 infix_type * type = *type_ptr;
251 // Cycle detection: If we've seen this node before, we're in a cycle.
252 // Return success to break the loop.
253 for (resolve_memo_node_t * node = *memo_head; node != nullptr; node = node->next)
254 if (node->src == type)
255 return INFIX_SUCCESS;
256 // Allocate the memoization node from the stable temporary arena.
257 resolve_memo_node_t * memo_node =
258 infix_arena_alloc(temp_arena, sizeof(resolve_memo_node_t), _Alignof(resolve_memo_node_t));
259 if (!memo_node) {
262 }
263 memo_node->src = type;
264 memo_node->next = *memo_head;
265 *memo_head = memo_node;
267 if (!registry) {
270 }
271 const char * name = type->meta.named_reference.name;
273 if (!entry || !entry->type) {
276 }
277 *type_ptr = entry->type;
278 return INFIX_SUCCESS;
279 }
281 switch (type->category) {
284 temp_arena, &type->meta.pointer_info.pointee_type, registry, memo_head);
285 break;
286 case INFIX_TYPE_ARRAY:
287 status =
289 break;
291 case INFIX_TYPE_UNION:
292 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
294 temp_arena, &type->meta.aggregate_info.members[i].type, registry, memo_head);
295 if (status != INFIX_SUCCESS)
296 break;
297 }
298 break;
301 temp_arena, &type->meta.func_ptr_info.return_type, registry, memo_head);
302 if (status != INFIX_SUCCESS)
303 break;
304 for (size_t i = 0; i < type->meta.func_ptr_info.num_args; ++i) {
306 temp_arena, &type->meta.func_ptr_info.args[i].type, registry, memo_head);
307 if (status != INFIX_SUCCESS)
308 break;
309 }
310 break;
311 case INFIX_TYPE_ENUM:
313 temp_arena, &type->meta.enum_info.underlying_type, registry, memo_head);
314 break;
316 status =
318 break;
321 temp_arena, &type->meta.vector_info.element_type, registry, memo_head);
322 break;
323 default:
324 break;
325 }
326 return status;
327}
336 // Create a temporary arena solely for the visited list's lifetime.
337 infix_arena_t * temp_arena = infix_arena_create(1024);
338 if (!temp_arena) {
341 }
342 resolve_memo_node_t * memo_head = nullptr;
343 infix_status status = _resolve_type_graph_inplace_recursive(temp_arena, type_ptr, registry, &memo_head);
344 infix_arena_destroy(temp_arena);
345 return status;
346}
347// Public API: Type Registration
356typedef struct {
357 const char * p;
358 const char * start;
366 while (1) {
367 while (isspace((unsigned char)*state->p))
368 state->p++;
369 if (*state->p == '#') // Skip comments
370 while (*state->p != '\n' && *state->p != '\0')
371 state->p++;
372 else
373 break;
374 }
375}
384static char * _registry_parser_parse_name(_registry_parser_state_t * state, char * buffer, size_t buf_size) {
386 const char * name_start = state->p;
387 while (isalnum((unsigned char)*state->p) || *state->p == '_' || *state->p == ':') {
388 if (*state->p == ':' && state->p[1] != ':')
389 break; // Handle single colon as non-identifier char.
390 if (*state->p == ':')
391 state->p++; // Skip the first ':' of '::'
392 state->p++;
393 }
394 size_t len = state->p - name_start;
395 if (len == 0 || len >= buf_size)
396 return nullptr;
397 infix_memcpy(buffer, name_start, len);
398 buffer[len] = '\0';
399 return buffer;
400}
432 if (!registry || !definitions) {
435 }
436 _registry_parser_state_t state = {.p = definitions, .start = definitions};
438 // A temporary structure to hold information about each definition found in Pass 1.
439 struct def_info {
441 const char * def_body_start;
442 size_t def_body_len;
443 };
444 size_t defs_capacity = 64; // Start with an initial capacity.
445 struct def_info * defs_found = infix_malloc(sizeof(struct def_info) * defs_capacity);
446 if (!defs_found) {
449 }
450 size_t num_defs_found = 0;
451 infix_status final_status = INFIX_SUCCESS;
452 // Pass 1: Scan & Index all names and their definition bodies.
453 while (*state.p != '\0') {
455 if (*state.p == '\0')
456 break;
457 if (*state.p != '@') {
459 final_status = INFIX_ERROR_INVALID_ARGUMENT;
460 goto cleanup;
461 }
462 state.p++;
463 char name_buffer[256];
464 if (!_registry_parser_parse_name(&state, name_buffer, sizeof(name_buffer))) {
466 final_status = INFIX_ERROR_INVALID_ARGUMENT;
467 goto cleanup;
468 }
469 _infix_registry_entry_t * entry = _registry_lookup(registry, name_buffer);
471 if (*state.p == '=') { // This is a full definition.
472 state.p++;
474 // It's an error to redefine a type that wasn't a forward declaration.
475 if (entry && !entry->is_forward_declaration) {
477 final_status = INFIX_ERROR_INVALID_ARGUMENT;
478 goto cleanup;
479 }
480 if (!entry) { // If it doesn't exist, create it.
481 entry = _registry_insert(registry, name_buffer);
482 if (!entry) {
483 final_status = INFIX_ERROR_ALLOCATION_FAILED;
484 goto cleanup;
485 }
486 }
487 if (num_defs_found >= defs_capacity) {
488 size_t new_capacity = defs_capacity * 2;
489 struct def_info * new_defs = infix_realloc(defs_found, sizeof(struct def_info) * new_capacity);
490 if (!new_defs) {
492 final_status = INFIX_ERROR_ALLOCATION_FAILED;
493 goto cleanup;
494 }
495 defs_found = new_defs;
496 defs_capacity = new_capacity;
497 }
498 // Find the end of the type definition body (the next top-level ';').
499 defs_found[num_defs_found].entry = entry;
500 defs_found[num_defs_found].def_body_start = state.p;
501 int nest_level = 0;
502 const char * body_end = state.p;
503 while (*body_end != '\0' &&
504 !(*body_end == ';' &&
505 nest_level == 0)) { // Explicitly check for and skip over the '->' token as a single unit.
506 if (*body_end == '-' && body_end[1] == '>') {
507 body_end += 2; // Advance the pointer past the entire token.
508 continue; // Continue to the next character in the loop.
509 }
510 if (*body_end == '{' || *body_end == '<' || *body_end == '(' || *body_end == '[')
511 nest_level++;
512 if (*body_end == '}' || *body_end == '>' || *body_end == ')' || *body_end == ']')
513 nest_level--;
514 body_end++;
515 }
516 defs_found[num_defs_found].def_body_len = body_end - state.p;
517 state.p = body_end;
518 num_defs_found++;
519 }
520 else if (*state.p == ';') { // This is a forward declaration.
521 if (entry) { // Cannot forward-declare an already-defined type.
523 final_status = INFIX_ERROR_INVALID_ARGUMENT;
524 goto cleanup;
525 }
526 entry = _registry_insert(registry, name_buffer);
527 if (!entry) {
528 final_status = INFIX_ERROR_ALLOCATION_FAILED;
529 goto cleanup;
530 }
531 entry->is_forward_declaration = true;
532 }
533 else {
535 final_status = INFIX_ERROR_INVALID_ARGUMENT;
536 goto cleanup;
537 }
538 if (*state.p == ';')
539 state.p++;
540 }
541 // Pass 2: Parse the bodies of all found definitions into the registry.
542 for (size_t i = 0; i < num_defs_found; ++i) {
543 _infix_registry_entry_t * entry = defs_found[i].entry;
544 // Make a temporary, null-terminated copy of the definition body substring.
545 char * body_copy = infix_malloc(defs_found[i].def_body_len + 1);
546 if (!body_copy) {
548 final_status = INFIX_ERROR_ALLOCATION_FAILED;
549 goto cleanup;
550 }
551 infix_memcpy(body_copy, defs_found[i].def_body_start, defs_found[i].def_body_len);
552 body_copy[defs_found[i].def_body_len] = '\0';
553 // "Parse" step: parse into a temporary arena.
554 infix_type * raw_type = nullptr;
555 infix_arena_t * parser_arena = nullptr;
556 infix_status status = _infix_parse_type_internal(&raw_type, &parser_arena, body_copy);
557 infix_free(body_copy);
558 if (status != INFIX_SUCCESS) {
559 // Adjust the error position to be relative to the full definition string.
561 _infix_set_error(err.category, err.code, (defs_found[i].def_body_start - definitions) + err.position);
562 final_status = INFIX_ERROR_INVALID_ARGUMENT;
563 infix_arena_destroy(parser_arena);
564 goto cleanup;
565 }
566 const infix_type * type_to_alias = raw_type;
567 // If the RHS is another named type (e.g., @MyAlias = @ExistingType),
568 // we need to resolve it first to get the actual type we're aliasing.
569 if (raw_type->category == INFIX_TYPE_NAMED_REFERENCE) {
571 if (existing_entry && existing_entry->type)
572 type_to_alias = existing_entry->type;
573 else {
576 (size_t)(defs_found[i].def_body_start - definitions));
577 final_status = INFIX_ERROR_INVALID_ARGUMENT;
578 infix_arena_destroy(parser_arena);
579 goto cleanup;
580 }
581 }
582 if (!type_to_alias->is_arena_allocated) {
583 // This is a static type (e.g., primitive). We MUST create a mutable
584 // copy in the registry's arena before we can attach a name to it.
585 // This prevents corrupting the global static singletons.
586 infix_type * prim_copy = infix_arena_alloc(registry->arena, sizeof(infix_type), _Alignof(infix_type));
587 if (!prim_copy) {
588 final_status = INFIX_ERROR_ALLOCATION_FAILED;
589 infix_arena_destroy(parser_arena);
590 goto cleanup;
591 }
592 *prim_copy = *type_to_alias;
593 prim_copy->is_arena_allocated = true;
594 prim_copy->arena = registry->arena;
595 entry->type = prim_copy;
596 }
597 else
598 entry->type = _copy_type_graph_to_arena(registry->arena, type_to_alias);
599 infix_arena_destroy(parser_arena); // The temporary raw_type is no longer needed.
600 if (!entry->type) {
602 final_status = INFIX_ERROR_ALLOCATION_FAILED;
603 goto cleanup;
604 }
605 // Attach the new alias name to the copied type. This is the key to preserving semantic names.
606 entry->type->name = entry->name;
607 entry->is_forward_declaration = false;
608 }
609 // Pass 3: Resolve and layout all the newly defined types.
610 for (size_t i = 0; i < num_defs_found; ++i) {
611 _infix_registry_entry_t * entry = defs_found[i].entry;
612 if (entry->type) {
613 // "Resolve" and "Layout" steps.
615 final_status = INFIX_ERROR_INVALID_ARGUMENT;
616 goto cleanup;
617 }
619 }
620 }
621cleanup:
622 infix_free(defs_found);
623 return final_status;
624}
625// Registry Introspection API Implementation
646 // Return an iterator positioned before the first element.
647 // The first call to next() will advance it to the first valid element.
648 return (infix_registry_iterator_t){registry, 0, nullptr};
649}
658 if (!iter || !iter->registry)
659 return false;
660 const _infix_registry_entry_t * entry = iter->current_entry;
661 // If we have a current entry, start from the next one in the chain.
662 if (iter->current_entry)
663 entry = entry->next;
664 // Otherwise, if we are starting, begin with the head of the current bucket.
665 else if (iter->current_bucket < iter->registry->num_buckets)
666 entry = iter->registry->buckets[iter->current_bucket];
667 while (true) {
668 // Traverse the current chain looking for a valid entry.
669 while (entry) {
670 if (entry->type && !entry->is_forward_declaration) {
671 // Found one. Update the iterator and return successfully.
672 iter->current_entry = entry;
673 return true;
674 }
675 entry = entry->next;
676 }
677 // If we're here, the current chain is exhausted. Move to the next bucket.
678 iter->current_bucket++;
679 // If there are no more buckets, we're done.
680 if (iter->current_bucket >= iter->registry->num_buckets) {
681 iter->current_entry = nullptr;
682 return false;
683 }
684 // Start the search from the head of the new bucket.
685 entry = iter->registry->buckets[iter->current_bucket];
686 }
687}
697 if (!iter || !iter->current_entry)
698 return nullptr;
699 return iter->current_entry->name;
700}
710 if (!iter || !iter->current_entry)
711 return nullptr;
712 return iter->current_entry->type;
713}
725 if (!registry || !name)
726 return false;
728 // It's defined if an entry exists, it has a type, and it's not a lingering forward declaration.
729 return entry != nullptr && entry->type != nullptr && !entry->is_forward_declaration;
730}
741 if (!registry || !name)
742 return nullptr;
744 if (entry && entry->type && !entry->is_forward_declaration)
745 return entry->type;
746 return nullptr;
747}
infix_arena_t * arena
Definition 005_layouts.c:60
const char * definitions
Definition 008_registry_introspection.c:35
infix_registry_t * registry
Definition 008_registry_introspection.c:32
infix_status status
Definition 103_unions.c:59
#define c23_nodiscard
A compatibility macro for the C23 [[nodiscard]] attribute.
Definition compat_c23.h:106
#define INFIX_TLS
Definition error.c:68
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:271
@ INFIX_CODE_UNRESOLVED_NAMED_TYPE
Definition infix.h:1242
@ INFIX_CODE_UNEXPECTED_TOKEN
Definition infix.h:1232
@ INFIX_CODE_UNKNOWN
Definition infix.h:1226
@ INFIX_CODE_OUT_OF_MEMORY
Definition infix.h:1228
@ INFIX_CATEGORY_ALLOCATION
Definition infix.h:1216
@ INFIX_CATEGORY_GENERAL
Definition infix.h:1215
@ INFIX_CATEGORY_PARSER
Definition infix.h:1217
struct infix_type_t::@0::@1 pointer_info
Metadata for INFIX_TYPE_POINTER.
size_t position
Definition infix.h:1257
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:1255
infix_type * type
Definition infix.h:266
struct infix_type_t::@0::@4 func_ptr_info
Metadata for INFIX_TYPE_REVERSE_TRAMPOLINE.
infix_arena_t * arena
Definition infix.h:200
infix_struct_member * members
Definition infix.h:211
struct infix_type_t::@0::@6 complex_info
Metadata for INFIX_TYPE_COMPLEX.
const char * name
Definition infix.h:195
infix_function_argument * args
Definition infix.h:224
infix_status
Enumerates the possible status codes returned by infix API functions.
Definition infix.h:352
infix_type_category category
Definition infix.h:196
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:207
infix_type * type
Definition infix.h:254
struct infix_type_t * element_type
Definition infix.h:217
struct infix_type_t * return_type
Definition infix.h:223
struct infix_type_t::@0::@5 enum_info
Metadata for INFIX_TYPE_ENUM.
struct infix_type_t * base_type
Definition infix.h:234
infix_error_code_t code
Definition infix.h:1256
size_t num_members
Definition infix.h:212
struct infix_type_t * underlying_type
Definition infix.h:230
struct infix_type_t::@0::@8 named_reference
Metadata for INFIX_TYPE_NAMED_REFERENCE.
size_t num_args
Definition infix.h:225
bool is_arena_allocated
Definition infix.h:199
@ INFIX_ERROR_ALLOCATION_FAILED
Definition infix.h:354
@ INFIX_SUCCESS
Definition infix.h:353
@ INFIX_ERROR_INVALID_ARGUMENT
Definition infix.h:355
#define infix_free
A macro that can be defined to override the default free function.
Definition infix.h:301
void infix_arena_destroy(infix_arena_t *)
Destroys an arena and frees all memory allocated from it.
Definition arena.c:83
#define infix_memcpy
A macro that can be defined to override the default memcpy function.
Definition infix.h:305
c23_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:179
#define infix_realloc
A macro that can be defined to override the default realloc function.
Definition infix.h:297
c23_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_malloc
A macro that can be defined to override the default malloc function.
Definition infix.h:289
c23_nodiscard infix_arena_t * infix_arena_create(size_t)
Creates a new memory arena.
Definition arena.c:52
void infix_registry_destroy(infix_registry_t *registry)
Destroys a type registry and frees all associated memory.
Definition type_registry.c:216
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:430
c23_nodiscard infix_registry_t * infix_registry_create(void)
Creates a new, empty named type registry.
Definition type_registry.c:145
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:740
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:183
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:709
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:645
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:657
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:696
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:724
@ INFIX_TYPE_UNION
Definition infix.h:152
@ INFIX_TYPE_COMPLEX
Definition infix.h:156
@ INFIX_TYPE_ARRAY
Definition infix.h:153
@ INFIX_TYPE_VECTOR
Definition infix.h:157
@ INFIX_TYPE_POINTER
Definition infix.h:150
@ INFIX_TYPE_NAMED_REFERENCE
Definition infix.h:158
@ INFIX_TYPE_REVERSE_TRAMPOLINE
Definition infix.h:154
@ INFIX_TYPE_ENUM
Definition infix.h:155
@ INFIX_TYPE_STRUCT
Definition infix.h:151
Internal data structures, function prototypes, and constants.
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:165
void _infix_type_recalculate_layout(infix_type *type)
Recalculates the layout of a fully resolved type graph.
Definition types.c:938
c23_nodiscard infix_status _infix_parse_type_internal(infix_type **, infix_arena_t **, const char *)
The internal core of the signature parser.
Definition signature.c:963
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:1125
void _infix_clear_error(void)
Clears the thread-local error state.
Definition error.c:258
A single entry in the registry's hash table.
Definition infix_internals.h:152
bool is_forward_declaration
Definition infix_internals.h:155
const char * name
Definition infix_internals.h:153
infix_type * type
Definition infix_internals.h:154
struct _infix_registry_entry_t * next
Definition infix_internals.h:156
Definition type_registry.c:356
const char * start
Definition type_registry.c:358
const char * p
Definition type_registry.c:357
Internal definition of a memory arena.
Definition infix_internals.h:138
Provides detailed, thread-local information about the last error that occurred.
Definition infix.h:1254
Internal definition of a registry iterator.
Definition infix_internals.h:179
const _infix_registry_entry_t * current_entry
Definition infix_internals.h:182
const infix_registry_t * registry
Definition infix_internals.h:180
size_t current_bucket
Definition infix_internals.h:181
Internal definition of a named type registry.
Definition infix_internals.h:165
size_t num_items
Definition infix_internals.h:169
bool is_external_arena
Definition infix_internals.h:167
infix_arena_t * arena
Definition infix_internals.h:166
_infix_registry_entry_t ** buckets
Definition infix_internals.h:170
size_t num_buckets
Definition infix_internals.h:168
A semi-opaque structure that describes a C type.
Definition infix.h:194
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:335
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:109
static char * _registry_parser_parse_name(_registry_parser_state_t *state, char *buffer, size_t buf_size)
Definition type_registry.c:384
static void _registry_parser_skip_whitespace(_registry_parser_state_t *state)
Definition type_registry.c:365
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:244
INFIX_TLS const char * g_infix_last_signature_context
A thread-local pointer to the full signature string being parsed.
Definition error.c:99