infix
A JIT-Powered FFI Library for C
Loading...
Searching...
No Matches
type_registry.c
Go to the documentation of this file.
1
33#include <ctype.h>
34#include <string.h>
35
42#define INITIAL_REGISTRY_BUCKETS 61
43
44// Internal Hash Table Implementation
51static uint64_t _registry_hash_string(const char * str) {
52 uint64_t hash = 5381;
53 int c;
54 while ((c = *str++)) {
55 hash = ((hash << 5) + hash) + c; // hash * 33 + c
56 }
57 return hash;
58}
59
67static _infix_registry_entry_t * _registry_lookup(infix_registry_t * registry, const char * name) {
68 if (!registry || !name)
69 return nullptr;
70
71 // Calculate the hash to find the correct bucket.
72 uint64_t hash = _registry_hash_string(name);
73 size_t index = hash % registry->num_buckets;
74
75 // Traverse the linked list (chain) at that bucket.
76 _infix_registry_entry_t * current = registry->buckets[index];
77 while (current) {
78 if (strcmp(current->name, name) == 0) {
79 return current; // Found a match.
80 }
81 current = current->next;
82 }
83 return nullptr; // Not found.
84}
85
96static _infix_registry_entry_t * _registry_insert(infix_registry_t * registry, const char * name) {
97 uint64_t hash = _registry_hash_string(name);
98 size_t index = hash % registry->num_buckets;
99
100 // Allocate a new entry node from the registry's main arena.
101 _infix_registry_entry_t * new_entry =
103 if (!new_entry)
104 return nullptr;
105
106 // Allocate a permanent copy of the name string into the arena. This is critical
107 // for making the registry self-contained.
108 size_t name_len = strlen(name) + 1;
109 char * name_copy = infix_arena_alloc(registry->arena, name_len, 1);
110 if (!name_copy)
111 return nullptr;
112 infix_memcpy(name_copy, name, name_len);
113
114 // Populate the new entry as a placeholder. The type will be linked later.
115 new_entry->name = name_copy;
116 new_entry->type = nullptr;
117 new_entry->is_forward_declaration = false;
118
119 // Insert the new entry at the head of the bucket's linked list (chain).
120 new_entry->next = registry->buckets[index];
121 registry->buckets[index] = new_entry;
122
123 registry->num_items++;
124 return new_entry;
125}
126
127// Public API: Registry Management
133 // 1. Allocate the main registry struct itself.
134 infix_registry_t * registry = infix_malloc(sizeof(infix_registry_t));
135 if (!registry) {
137 return nullptr;
138 }
139
140 // 2. Create the arena that will manage all internal memory for this registry.
141 registry->arena = infix_arena_create(16384); // Start with 16KB
142 if (!registry->arena) {
143 infix_free(registry);
145 return nullptr;
146 }
147
148 // 3. Allocate the bucket array from the arena. Use calloc to zero-initialize.
150 registry->buckets = infix_arena_calloc(
151 registry->arena, registry->num_buckets, sizeof(_infix_registry_entry_t *), _Alignof(_infix_registry_entry_t *));
152 if (!registry->buckets) {
153 infix_arena_destroy(registry->arena);
154 infix_free(registry);
156 return nullptr;
157 }
158
159 registry->num_items = 0;
160 return registry;
161}
162
167 if (!registry)
168 return;
169
170 // The arena allocator handles all complex cleanup. This one call frees the
171 // hash table, all entry nodes, all copied strings, and all infix_type graphs.
172 infix_arena_destroy(registry->arena);
173
174 // Finally, free the registry handle itself.
175 infix_free(registry);
176}
177
178// Type Definition Parser for infix_register_types
183typedef struct {
184 const char * p;
185 const char * start;
188
191 while (1) {
192 while (isspace((unsigned char)*state->p))
193 state->p++;
194 if (*state->p == '#') {
195 while (*state->p != '\n' && *state->p != '\0')
196 state->p++;
197 }
198 else
199 break;
200 }
201}
202
211static char * _registry_parser_parse_name(_registry_parser_state_t * state, char * buffer, size_t buf_size) {
213 const char * name_start = state->p;
214 while (isalnum((unsigned char)*state->p) || *state->p == '_' || *state->p == ':') {
215 if (*state->p == ':' && state->p[1] != ':')
216 break; // Allow '::' but not single ':'
217 if (*state->p == ':')
218 state->p++; // Skip the first ':' of '::'
219 state->p++;
220 }
221 size_t len = state->p - name_start;
222 if (len == 0 || len >= buf_size)
223 return nullptr;
224
225 infix_memcpy(buffer, name_start, len);
226 buffer[len] = '\0';
227 return buffer;
228}
229
235 if (!registry || !definitions) {
238 }
239
240 _registry_parser_state_t state = {.p = definitions, .start = definitions, .registry = registry};
241
242 // This parser uses a three-pass strategy to correctly handle forward
243 // declarations and mutually recursive types.
244 struct def_info {
246 const char * def_body_start;
247 size_t def_body_len;
248 };
249 struct def_info defs_found[256]; // Support up to 256 definitions in one call
250 size_t num_defs_found = 0;
251
252 // PASS 1: NAME DISCOVERY
253 // Find all type names (@Name), check for redefinitions, and create placeholder entries.
254 // This allows definitions to appear in any order.
255 while (*state.p != '\0') {
257 if (*state.p == '\0')
258 break;
259
260 if (*state.p != '@') {
263 }
264 state.p++;
265
266 char name_buffer[256];
267 if (!_registry_parser_parse_name(&state, name_buffer, sizeof(name_buffer))) {
270 }
271
272 _infix_registry_entry_t * entry = _registry_lookup(registry, name_buffer);
274
275 if (*state.p == '=') { // This is a full definition.
276 state.p++;
278 if (entry && !entry->is_forward_declaration) {
279 // Error: Redefining an already fully-defined type.
282 }
283 if (!entry) { // First time we see this name, create the entry.
284 entry = _registry_insert(registry, name_buffer);
285 if (!entry) {
288 }
289 }
290 // Record this definition to be parsed in Pass 2.
291 if (num_defs_found >= 256) {
294 }
295 defs_found[num_defs_found].entry = entry;
296 defs_found[num_defs_found].def_body_start = state.p;
297
298 // Scan to the end of the current definition, respecting nested delimiters.
299 int nest_level = 0;
300 const char * body_end = state.p;
301 while (*body_end != '\0' && !(*body_end == ';' && nest_level == 0)) {
302 if (*body_end == '{' || *body_end == '<' || *body_end == '(' || *body_end == '[')
303 nest_level++;
304 if (*body_end == '}' || *body_end == '>' || *body_end == ')' || *body_end == ']')
305 nest_level--;
306 body_end++;
307 }
308 defs_found[num_defs_found].def_body_len = body_end - state.p;
309 state.p = body_end;
310 num_defs_found++;
311 }
312 else if (*state.p == ';') { // This is a forward declaration.
313 if (entry) {
314 // Error: Duplicate declaration or trying to forward-declare an already defined type.
317 }
318 entry = _registry_insert(registry, name_buffer);
319 if (!entry) {
322 }
323 entry->is_forward_declaration = true;
324 }
325 else {
328 }
329
330 if (*state.p == ';')
331 state.p++;
332 }
333
334 // PASS 2: PARSE DEFINITIONS
335 // Parse all definition bodies into unresolved type graphs in the main arena.
336 for (size_t i = 0; i < num_defs_found; ++i) {
337 _infix_registry_entry_t * entry = defs_found[i].entry;
338
339 // Create a temporary, null-terminated string for the definition body.
340 char * body_copy = infix_malloc(defs_found[i].def_body_len + 1);
341 if (!body_copy)
343 infix_memcpy(body_copy, defs_found[i].def_body_start, defs_found[i].def_body_len);
344 body_copy[defs_found[i].def_body_len] = '\0';
345
346 infix_type * parsed_type = nullptr;
347 infix_arena_t * temp_arena = nullptr;
348 // Use the internal parser which does not perform resolution. This creates a raw type graph.
349 infix_status status = _infix_parse_type_internal(&parsed_type, &temp_arena, body_copy, registry);
350
351 infix_free(body_copy);
352
353 if (status != INFIX_SUCCESS) {
354 infix_arena_destroy(temp_arena);
356 }
357
358 // Deep-copy the raw graph from the temporary arena to the registry's permanent arena.
359 infix_type * permanent_type = _copy_type_graph_to_arena(registry->arena, parsed_type);
360 infix_arena_destroy(temp_arena); // The temp arena is no longer needed.
361
362 if (!permanent_type)
364
365 entry->type = permanent_type;
366 entry->is_forward_declaration = false;
367 }
368
369 // PASS 3: RESOLVE TYPES
370 // Now that all types have been parsed and stored, resolve all named references.
371 for (size_t i = 0; i < num_defs_found; ++i) {
372 _infix_registry_entry_t * entry = defs_found[i].entry;
373 if (entry->type) {
374 if (_infix_resolve_type_graph(&entry->type, registry) != INFIX_SUCCESS) {
376 }
377 }
378 }
379
380 return INFIX_SUCCESS;
381}
382
383
384// Type Graph Resolver
385
386
399 if (!type_ptr || !*type_ptr)
400 return INFIX_SUCCESS;
401 infix_type * type = *type_ptr;
402
403 // The base case: we found a named reference.
405 const char * name = type->meta.named_reference.name;
406 _infix_registry_entry_t * entry = _registry_lookup(registry, name);
407
408 // Fail if the name is not in the registry or if it's an undefined forward declaration.
409 if (!entry || !entry->type) {
412 }
413
414 // The magic: replace the reference node with the pointer to the real type.
415 *type_ptr = entry->type;
416 // Do NOT recurse further down this branch. The type from the registry is
417 // assumed to be on its own path to resolution. This is the key to
418 // breaking the infinite loop in recursive type definitions.
419 return INFIX_SUCCESS;
420 }
421
422 // The recursive cases: traverse into composite types.
423 switch (type->category) {
426 case INFIX_TYPE_ARRAY:
427 return _infix_resolve_type_graph(&type->meta.array_info.element_type, registry);
429 case INFIX_TYPE_UNION:
430 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
433 }
434 }
435 break;
439 }
440 for (size_t i = 0; i < type->meta.func_ptr_info.num_args; ++i) {
443 }
444 }
445 break;
446 default:
447 // Primitives and other simple types have no children to resolve.
448 break;
449 }
450 return INFIX_SUCCESS;
451}
infix_status status
Definition 103_unions.c:74
#define c23_nodiscard
Definition compat_c23.h:93
@ INFIX_CODE_UNRESOLVED_NAMED_TYPE
The type graph that contains an unresolved named reference.
Definition infix.h:1078
@ INFIX_CODE_UNEXPECTED_TOKEN
Parser ran into an invalid character at a given position.
Definition infix.h:1066
@ INFIX_CODE_UNKNOWN
An unknown or unspecified error occurred. This is a fallback code.
Definition infix.h:1058
@ INFIX_CODE_RECURSION_DEPTH_EXCEEDED
The parser exceeded the max nesting depth (e.g., {{{{...}}}}).
Definition infix.h:1072
@ INFIX_CODE_OUT_OF_MEMORY
Failure to allocate memory. Likely due to lack of system resources.
Definition infix.h:1061
@ INFIX_CATEGORY_ALLOCATION
An error related to memory allocation.
Definition infix.h:1042
@ INFIX_CATEGORY_GENERAL
A general or miscellaneous error.
Definition infix.h:1041
@ INFIX_CATEGORY_PARSER
An error that occurred while parsing a signature string.
Definition infix.h:1043
#define infix_free
A macro for the memory deallocation function.
Definition infix.h:280
void infix_arena_destroy(infix_arena_t *)
Frees an entire memory arena and all objects allocated within it.
Definition arena.c:68
#define infix_memcpy
A macro for copying memory from a source to a destination pointer.
Definition infix.h:290
c23_nodiscard void * infix_arena_calloc(infix_arena_t *, size_t, size_t, size_t)
Allocates a zero-initialized block of memory from the arena.
Definition arena.c:129
c23_nodiscard void * infix_arena_alloc(infix_arena_t *, size_t, size_t)
Allocates a block of memory from the arena with a specific alignment.
Definition arena.c:86
#define infix_malloc
A macro for the memory allocation function used by the library.
Definition infix.h:250
c23_nodiscard infix_arena_t * infix_arena_create(size_t)
Creates and initializes a new memory arena.
Definition arena.c:41
infix_status
An enumeration of all possible success or failure codes from the public API.
Definition infix.h:351
@ INFIX_ERROR_ALLOCATION_FAILED
A memory allocation request failed.
Definition infix.h:353
@ INFIX_SUCCESS
The operation completed successfully.
Definition infix.h:352
@ INFIX_ERROR_INVALID_ARGUMENT
An invalid argument was provided to a function.
Definition infix.h:354
void infix_registry_destroy(infix_registry_t *registry)
Implementation for the public infix_registry_destroy function.
Definition type_registry.c:166
c23_nodiscard infix_status infix_register_types(infix_registry_t *registry, const char *definitions)
Implementation of the public infix_register_types function.
Definition type_registry.c:233
c23_nodiscard infix_registry_t * infix_registry_create(void)
Implementation for the public infix_registry_create function.
Definition type_registry.c:131
@ INFIX_TYPE_UNION
A user-defined union (union).
Definition infix.h:118
@ INFIX_TYPE_ARRAY
A fixed-size array.
Definition infix.h:119
@ INFIX_TYPE_POINTER
A generic void* pointer type.
Definition infix.h:116
@ INFIX_TYPE_NAMED_REFERENCE
A reference to a named type (e.g., struct<Node>).
Definition infix.h:124
@ INFIX_TYPE_REVERSE_TRAMPOLINE
A callback wrapper.
Definition infix.h:120
@ INFIX_TYPE_STRUCT
A user-defined structure (struct).
Definition infix.h:117
Declarations for internal-only functions, types, and constants.
void _infix_set_error(infix_error_category_t, infix_error_code_t, size_t)
Sets the thread-local error details for a library-internal error.
Definition error.c:44
c23_nodiscard infix_status _infix_parse_type_internal(infix_type **, infix_arena_t **, const char *, infix_registry_t *)
The core, non-resolving entry point for the signature parser.
Definition signature.c:1040
infix_type * _copy_type_graph_to_arena(infix_arena_t *, const infix_type *)
Performs a deep copy of a type graph from one arena to another.
Definition types.c:610
void _infix_clear_error(void)
Resets the thread-local error state. Called at the start of every public API function.
Definition error.c:76
Definition infix_internals.h:144
bool is_forward_declaration
Definition infix_internals.h:147
const char * name
Definition infix_internals.h:145
infix_type * type
Definition infix_internals.h:146
struct _infix_registry_entry_t * next
Definition infix_internals.h:148
Definition type_registry.c:183
infix_registry_t * registry
Definition type_registry.c:186
const char * start
Definition type_registry.c:185
const char * p
Definition type_registry.c:184
Definition infix_internals.h:130
infix_type * type
An infix_type describing the argument's type.
Definition infix.h:231
Definition infix_internals.h:158
size_t num_items
Definition infix_internals.h:161
infix_arena_t * arena
Definition infix_internals.h:159
_infix_registry_entry_t ** buckets
Definition infix_internals.h:162
size_t num_buckets
Definition infix_internals.h:160
infix_type * type
An infix_type describing the member's type.
Definition infix.h:221
The central structure for describing any data type in the FFI system.
Definition infix.h:161
struct infix_type_t::@0::@1 pointer_info
For INFIX_TYPE_POINTER.
union infix_type_t::@0 meta
Type-specific metadata.
struct infix_type_t::@0::@4 func_ptr_info
For INFIX_TYPE_REVERSE_TRAMPOLINE.
infix_struct_member * members
Array of members for the aggregate.
Definition infix.h:177
const char * name
Optional name of the aggregate.
Definition infix.h:176
infix_function_argument * args
Array of function arguments (name and type).
Definition infix.h:188
infix_type_category category
The fundamental category of the type.
Definition infix.h:162
struct infix_type_t::@0::@2 aggregate_info
For INFIX_TYPE_STRUCT and INFIX_TYPE_UNION.
struct infix_type_t::@0::@3 array_info
For INFIX_TYPE_ARRAY.
struct infix_type_t * pointee_type
The type this pointer points to.
Definition infix.h:172
struct infix_type_t * element_type
The type of elements in the array.
Definition infix.h:182
struct infix_type_t * return_type
Reverse trampoline return value.
Definition infix.h:187
size_t num_members
Number of members in the aggregate.
Definition infix.h:178
struct infix_type_t::@0::@8 named_reference
For INFIX_TYPE_NAMED_REFERENCE. This is an internal placeholder for a named type like @Point.
size_t num_args
The total number of fixed and variadic arguments.
Definition infix.h:189
static uint64_t _registry_hash_string(const char *str)
Definition type_registry.c:51
#define INITIAL_REGISTRY_BUCKETS
Definition type_registry.c:42
static _infix_registry_entry_t * _registry_lookup(infix_registry_t *registry, const char *name)
Definition type_registry.c:67
static _infix_registry_entry_t * _registry_insert(infix_registry_t *registry, const char *name)
Definition type_registry.c:96
static char * _registry_parser_parse_name(_registry_parser_state_t *state, char *buffer, size_t buf_size)
Definition type_registry.c:211
static void _registry_parser_skip_whitespace(_registry_parser_state_t *state)
Definition type_registry.c:190
c23_nodiscard infix_status _infix_resolve_type_graph(infix_type **type_ptr, infix_registry_t *registry)
Walks a type graph, replacing all @Name placeholders with their concrete definitions from a registry.
Definition type_registry.c:398