infix
A JIT-Powered FFI Library for C
Loading...
Searching...
No Matches
signature.c
Go to the documentation of this file.
1
37#include <ctype.h>
38#include <stdarg.h>
39#include <stdbool.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
47#define MAX_RECURSION_DEPTH 32
49 infix_type ** out_ret_type,
50 infix_function_argument ** out_args,
51 size_t * out_num_args,
52 size_t * out_num_fixed_args);
53// Parser Helper Functions
64
71 while (true) {
72 while (isspace((unsigned char)*state->p))
73 state->p++;
74 if (*state->p == '#') // C-style line comments
75 while (*state->p != '\n' && *state->p != '\0')
76 state->p++;
77 else
78 break;
79 }
80}
88static bool parse_size_t(parser_state * state, size_t * out_val) {
89 const char * start = state->p;
90 char * end;
91 errno = 0; // Reset errno before call
92 unsigned long long val = strtoull(start, &end, 10);
93
94 // Check for no conversion (end==start) OR overflow (ERANGE)
95 if (end == start || errno == ERANGE) {
96 // Use INTEGER_OVERFLOW code for range errors
98 return false;
99 }
100
101 // Check for truncation if size_t is smaller than unsigned long long (e.g. 32-bit builds)
102 if (val > SIZE_MAX) {
104 return false;
105 }
106 *out_val = (size_t)val;
107 state->p = end;
108 return true;
109}
118static const char * parse_identifier(parser_state * state) {
119 skip_whitespace(state);
120 const char * start = state->p;
121 if (!isalpha((unsigned char)*start) && *start != '_')
122 return nullptr;
123 while (isalnum((unsigned char)*state->p) || *state->p == '_' || *state->p == ':') {
124 if (*state->p == ':' && state->p[1] != ':')
125 break; // A single ':' is not part of an identifier.
126 if (*state->p == ':')
127 state->p++; // Consume first ':' of '::'
128 state->p++;
129 }
130 size_t len = state->p - start;
131 if (len == 0)
132 return nullptr;
133 char * name = infix_arena_calloc(state->arena, 1, len + 1, 1);
134 if (!name) {
136 return nullptr;
137 }
138 infix_memcpy((void *)name, start, len);
139 name[len] = '\0';
140 return name;
141}
152static bool consume_keyword(parser_state * state, const char * keyword) {
153 skip_whitespace(state);
154 size_t len = strlen(keyword);
155 if (strncmp(state->p, keyword, len) == 0) {
156 // Ensure it's not a prefix of a longer word (e.g., "int" vs "integer").
157 if (isalnum((unsigned char)state->p[len]) || state->p[len] == '_')
158 return false;
159 state->p += len;
160 skip_whitespace(state);
161 return true;
162 }
163 return false;
164}
174static const char * parse_optional_name_prefix(parser_state * state) {
175 skip_whitespace(state);
176 // Save the current position in case we need to backtrack.
177 const char * p_before = state->p;
178 const char * name = parse_identifier(state);
179 if (name) {
180 skip_whitespace(state);
181 if (*state->p == ':') { // Found "identifier:", so consume the colon and return the name.
182 state->p++;
183 return name;
184 }
185 }
186 // If it wasn't a `name:`, backtrack to the original position.
187 state->p = p_before;
188 return nullptr;
189}
204static bool is_function_signature_ahead(const parser_state * state) {
205 const char * p = state->p;
206 if (*p != '(')
207 return false;
208 p++;
209 // Find the matching ')' by tracking nesting depth.
210 int depth = 1;
211 while (*p != '\0' && depth > 0) {
212 if (*p == '(')
213 depth++;
214 else if (*p == ')')
215 depth--;
216 p++;
217 }
218 if (depth != 0)
219 return false; // Mismatched parentheses.
220 // Skip any whitespace or comments after the ')'
221 while (isspace((unsigned char)*p) || *p == '#') {
222 if (*p == '#')
223 while (*p != '\n' && *p != '\0')
224 p++;
225 else
226 p++;
227 }
228 // Check for the '->' arrow.
229 return (p[0] == '-' && p[1] == '>');
230}
231// Aggregate Parsing Logic
243static infix_struct_member * parse_aggregate_members(parser_state * state, char end_char, size_t * out_num_members) {
244 // Use a temporary linked list to collect members, as the count is unknown in a single pass.
245 typedef struct member_node {
247 struct member_node * next;
248 } member_node;
249 member_node *head = nullptr, *tail = nullptr;
250 size_t num_members = 0;
251 skip_whitespace(state);
252 if (*state->p != end_char) {
253 while (1) {
254 const char * p_before_member = state->p;
255 const char * name = parse_optional_name_prefix(state);
256 // Disallow an empty member definition like `name,` without a type.
257 if (name && (*state->p == ',' || *state->p == end_char)) {
258 state->p = p_before_member + strlen(name); // Position error at end of name
260 return nullptr;
261 }
262 infix_type * member_type = parse_type(state);
263 if (!member_type)
264 return nullptr;
265 // Structs and unions cannot have `void` members.
266 if (member_type->category == INFIX_TYPE_VOID) {
268 return nullptr;
269 }
270
271 // Check for bitfield syntax: "name: type : width"
272 uint8_t bit_width = 0;
273 bool is_bitfield = false;
274 const char * p_before_colon = state->p;
275 skip_whitespace(state);
276 if (*state->p == ':') {
277 state->p++; // Consume ':'
278 skip_whitespace(state);
279 if (!isdigit((unsigned char)*state->p)) {
280 // Not a bitfield width, backtrack. This handles "name: type" where ':' is part of name prefix.
281 state->p = p_before_colon;
282 }
283 else {
284 size_t width_val = 0;
285 if (!parse_size_t(state, &width_val))
286 return nullptr; // Error set by parse_size_t
287 if (width_val > 255) {
289 return nullptr;
290 }
291 bit_width = (uint8_t)width_val;
292 is_bitfield = true;
293 }
294 }
295
296 member_node * node = infix_arena_calloc(state->arena, 1, sizeof(member_node), _Alignof(member_node));
297 if (!node) {
299 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
300 return nullptr;
301 }
302 // The member offset is not calculated here; it will be done later
303 // by `infix_type_create_struct` or `_infix_type_recalculate_layout`.
304 if (is_bitfield)
305 node->m = infix_type_create_bitfield_member(name, member_type, 0, bit_width);
306 else
307 node->m = infix_type_create_member(name, member_type, 0);
308
309 node->next = nullptr;
310
311 if (!head)
312 head = tail = node;
313 else {
314 tail->next = node;
315 tail = node;
316 }
317 num_members++;
318 // Check for next token: ',' or end_char
319 skip_whitespace(state);
320 if (*state->p == ',') {
321 state->p++; // Consume comma.
322 skip_whitespace(state);
323 // A trailing comma like `{int,}` is a syntax error.
324 if (*state->p == end_char) {
326 return nullptr;
327 }
328 }
329 else if (*state->p == end_char)
330 break;
331 else { // Unexpected token (e.g., missing comma).
332 if (*state->p == '\0') {
334 return nullptr;
335 }
337 return nullptr;
338 }
339 }
340 }
341 *out_num_members = num_members;
342 if (num_members == 0)
343 return nullptr;
344 // Convert the temporary linked list to a flat array in the arena.
346 infix_arena_calloc(state->arena, num_members, sizeof(infix_struct_member), _Alignof(infix_struct_member));
347 if (!members) {
349 return nullptr;
350 }
351 member_node * current = head;
352 for (size_t i = 0; i < num_members; i++) {
353 members[i] = current->m;
354 current = current->next;
355 }
356 return members;
357}
366static infix_type * parse_aggregate(parser_state * state, char start_char, char end_char) {
367 if (state->depth >= MAX_RECURSION_DEPTH) {
369 return nullptr;
370 }
371 state->depth++;
372 if (*state->p != start_char) {
374 state->depth--;
375 return nullptr;
376 }
377 state->p++;
378 size_t num_members = 0;
379 infix_struct_member * members = parse_aggregate_members(state, end_char, &num_members);
380 // If member parsing failed, an error is already set. Propagate the failure.
382 state->depth--;
383 return nullptr;
384 }
385 if (*state->p != end_char) {
387 state->depth--;
388 return nullptr;
389 }
390 state->p++;
391 infix_type * agg_type = nullptr;
392 infix_status status = (start_char == '{') ? infix_type_create_struct(state->arena, &agg_type, members, num_members)
393 : infix_type_create_union(state->arena, &agg_type, members, num_members);
394 if (status != INFIX_SUCCESS) {
395 state->depth--;
396 return nullptr;
397 }
398 state->depth--;
399 return agg_type;
400}
408 size_t alignment = 1; // Default alignment for `!{...}` is 1.
409 if (*state->p == '!') {
410 state->p++;
411 if (isdigit((unsigned char)*state->p)) {
412 // This is the `!N:{...}` form with an explicit alignment.
413 if (!parse_size_t(state, &alignment))
414 return nullptr;
415 if (*state->p != ':') {
417 return nullptr;
418 }
419 state->p++;
420 }
421 }
422 skip_whitespace(state);
423 if (*state->p != '{') {
425 return nullptr;
426 }
427 state->p++;
428 size_t num_members = 0;
429 infix_struct_member * members = parse_aggregate_members(state, '}', &num_members);
431 return nullptr;
432 if (*state->p != '}') {
434 return nullptr;
435 }
436 state->p++;
437 infix_type * packed_type = nullptr;
438 // For packed structs, the total size is simply the sum of member sizes without padding.
439 // The user of `infix_type_create_packed_struct` must provide pre-calculated offsets.
440 // Since our parser doesn't know the offsets, we pass a preliminary size. The final
441 // layout pass will fix this if needed, but for packed structs, the user's offsets
442 // are king.
443 size_t total_size = 0;
444 for (size_t i = 0; i < num_members; ++i)
445 total_size += members[i].type->size;
447 infix_type_create_packed_struct(state->arena, &packed_type, total_size, alignment, members, num_members);
448 if (status != INFIX_SUCCESS)
449 return nullptr;
450 return packed_type;
451}
452// Main Parser Logic
464 if (consume_keyword(state, "sint8") || consume_keyword(state, "int8"))
466 if (consume_keyword(state, "uint8"))
468 if (consume_keyword(state, "sint16") || consume_keyword(state, "int16"))
470 if (consume_keyword(state, "uint16"))
472 if (consume_keyword(state, "sint32") || consume_keyword(state, "int32"))
474 if (consume_keyword(state, "uint32"))
476 if (consume_keyword(state, "sint64") || consume_keyword(state, "int64"))
478 if (consume_keyword(state, "uint64"))
480 if (consume_keyword(state, "sint128") || consume_keyword(state, "int128"))
482 if (consume_keyword(state, "uint128"))
484 if (consume_keyword(state, "float16"))
486 if (consume_keyword(state, "float32"))
488 if (consume_keyword(state, "float64"))
490 if (consume_keyword(state, "bool"))
492 if (consume_keyword(state, "void"))
493 return infix_type_create_void();
494 // C-style convenience aliases
495 if (consume_keyword(state, "uchar"))
497 if (consume_keyword(state, "char"))
499 if (consume_keyword(state, "ushort"))
501 if (consume_keyword(state, "short"))
503 if (consume_keyword(state, "uint"))
505 if (consume_keyword(state, "int"))
507 if (consume_keyword(state, "ulonglong"))
509 if (consume_keyword(state, "longlong"))
511 // `long` is platform-dependent, so we use `sizeof` to pick the correct size.
512 if (consume_keyword(state, "ulong"))
513 return infix_type_create_primitive(sizeof(unsigned long) == 8 ? INFIX_PRIMITIVE_UINT64
515 if (consume_keyword(state, "long"))
517 if (consume_keyword(state, "double"))
519 if (consume_keyword(state, "float"))
521 if (consume_keyword(state, "longdouble"))
523 if (consume_keyword(state, "size_t"))
525 if (consume_keyword(state, "ssize_t"))
527 // uchar.h types
528 if (consume_keyword(state, "char8_t"))
530 if (consume_keyword(state, "char16_t"))
532 if (consume_keyword(state, "char32_t"))
534 // AVX convenience aliases
535 if (consume_keyword(state, "m256d")) {
536 infix_type * type = nullptr;
539 if (status != INFIX_SUCCESS)
540 return nullptr; // Propagate failure
541 type->alignment = 32; // YMM registers require 32-byte alignment
542 return type;
543 }
544 if (consume_keyword(state, "m256")) {
545 infix_type * type = nullptr;
548 if (status != INFIX_SUCCESS)
549 return nullptr; // Propagate failure
550 type->alignment = 32; // YMM registers require 32-byte alignment
551 return type;
552 }
553 if (consume_keyword(state, "m512d")) {
554 infix_type * type = nullptr;
557 if (status != INFIX_SUCCESS)
558 return nullptr;
559 type->alignment = 64; // ZMM registers have 64-byte alignment
560 return type;
561 }
562 if (consume_keyword(state, "m512")) {
563 infix_type * type = nullptr;
566 if (status != INFIX_SUCCESS)
567 return nullptr;
568 type->alignment = 64;
569 return type;
570 }
571 if (consume_keyword(state, "m512i")) {
572 infix_type * type = nullptr;
575 if (status != INFIX_SUCCESS)
576 return nullptr;
577 type->alignment = 64;
578 return type;
579 }
580 return nullptr;
581}
593 if (state->depth >= MAX_RECURSION_DEPTH) {
595 return nullptr;
596 }
597 state->depth++;
598 skip_whitespace(state);
599 // Capture the offset from the start of the signature string *before* parsing the type.
600 size_t current_offset = state->p - state->start;
601 infix_type * result_type = nullptr;
602 const char * p_before_type = state->p;
603 if (*state->p == '@') { // Named type reference: `@MyStruct`
604 state->p++;
605 const char * name = parse_identifier(state);
606 if (!name) {
608 state->depth--;
609 return nullptr;
610 }
611 if (infix_type_create_named_reference(state->arena, &result_type, name, INFIX_AGGREGATE_STRUCT) !=
613 result_type = nullptr;
614 }
615 else if (*state->p == '*') { // Pointer type: `*int`
616 state->p++;
617 skip_whitespace(state);
618 infix_type * pointee_type = parse_type(state);
619 if (!pointee_type) {
620 state->depth--;
621 return nullptr;
622 }
623 if (infix_type_create_pointer_to(state->arena, &result_type, pointee_type) != INFIX_SUCCESS)
624 result_type = nullptr;
625 }
626 else if (*state->p == '(') { // Grouped type `(type)` or function pointer `(...) -> type`
627 if (is_function_signature_ahead(state)) {
628 infix_type * ret_type = nullptr;
629 infix_function_argument * args = nullptr;
630 size_t num_args = 0, num_fixed = 0;
631 if (parse_function_signature_details(state, &ret_type, &args, &num_args, &num_fixed) != INFIX_SUCCESS) {
632 state->depth--;
633 return nullptr;
634 }
635 // Manually construct a function pointer type object.
636 // This is represented internally as a pointer-like type with extra metadata.
637 infix_type * func_type = infix_arena_calloc(state->arena, 1, sizeof(infix_type), _Alignof(infix_type));
638 if (!func_type) {
640 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
641 state->depth--;
642 return nullptr;
643 }
644 func_type->size = sizeof(void *);
645 func_type->alignment = _Alignof(void *);
646 func_type->is_arena_allocated = true;
647 func_type->category = INFIX_TYPE_REVERSE_TRAMPOLINE; // Special category for function types.
649 func_type->meta.func_ptr_info.args = args;
650 func_type->meta.func_ptr_info.num_args = num_args;
651 func_type->meta.func_ptr_info.num_fixed_args = num_fixed;
652 result_type = func_type;
653 }
654 else { // Grouped type: `(type)`
655 state->p++;
656 skip_whitespace(state);
657 result_type = parse_type(state);
658 if (!result_type) {
659 state->depth--;
660 return nullptr;
661 }
662 skip_whitespace(state);
663 if (*state->p != ')') {
665 result_type = nullptr;
666 }
667 else
668 state->p++;
669 }
670 }
671 else if (*state->p == '[') { // Array type: `[size:type]`
672 state->p++;
673 skip_whitespace(state);
674 size_t num_elements = 0;
675 bool is_flexible = false;
676
677 if (*state->p == '?') {
678 // Flexible array member: `[?:type]`
679 is_flexible = true;
680 state->p++;
681 }
682 else if (!parse_size_t(state, &num_elements)) {
683 state->depth--;
684 return nullptr;
685 }
686
687 skip_whitespace(state);
688 if (*state->p != ':') {
690 state->depth--;
691 return nullptr;
692 }
693 state->p++;
694 skip_whitespace(state);
695 infix_type * element_type = parse_type(state);
696 if (!element_type) {
697 state->depth--;
698 return nullptr;
699 }
700 if (element_type->category == INFIX_TYPE_VOID) { // An array of `void` is illegal in C.
702 state->depth--;
703 return nullptr;
704 }
705 skip_whitespace(state);
706 if (*state->p != ']') {
708 state->depth--;
709 return nullptr;
710 }
711 state->p++;
712
713 if (is_flexible) {
714 if (infix_type_create_flexible_array(state->arena, &result_type, element_type) != INFIX_SUCCESS)
715 result_type = nullptr;
716 }
717 else {
718 if (infix_type_create_array(state->arena, &result_type, element_type, num_elements) != INFIX_SUCCESS)
719 result_type = nullptr;
720 }
721 }
722 else if (*state->p == '!') // Packed struct
723 result_type = parse_packed_struct(state);
724 else if (*state->p == '{') // Struct
725 result_type = parse_aggregate(state, '{', '}');
726 else if (*state->p == '<') // Union
727 result_type = parse_aggregate(state, '<', '>');
728 else if (*state->p == 'e' && state->p[1] == ':') { // Enum: `e:type`
729 state->p += 2;
730 skip_whitespace(state);
731 infix_type * underlying_type = parse_type(state);
732 if (!underlying_type || underlying_type->category != INFIX_TYPE_PRIMITIVE) {
734 state->depth--;
735 return nullptr;
736 }
737 if (infix_type_create_enum(state->arena, &result_type, underlying_type) != INFIX_SUCCESS)
738 result_type = nullptr;
739 }
740 else if (*state->p == 'c' && state->p[1] == '[') { // Complex: `c[type]`
741 state->p += 2;
742 skip_whitespace(state);
743 infix_type * base_type = parse_type(state);
744 if (!base_type) {
745 state->depth--;
746 return nullptr;
747 }
748 skip_whitespace(state);
749 if (*state->p != ']') {
751 state->depth--;
752 return nullptr;
753 }
754 state->p++;
755 if (infix_type_create_complex(state->arena, &result_type, base_type) != INFIX_SUCCESS)
756 result_type = nullptr;
757 }
758 else if (*state->p == 'v' && state->p[1] == '[') { // Vector: `v[size:type]`
759 state->p += 2;
760 skip_whitespace(state);
761 size_t num_elements;
762 if (!parse_size_t(state, &num_elements)) {
763 state->depth--;
764 return nullptr;
765 }
766 if (*state->p != ':') {
768 state->depth--;
769 return nullptr;
770 }
771 state->p++;
772 infix_type * element_type = parse_type(state);
773 if (!element_type) {
774 state->depth--;
775 return nullptr;
776 }
777 if (*state->p != ']') {
779 state->depth--;
780 return nullptr;
781 }
782 state->p++;
783 if (infix_type_create_vector(state->arena, &result_type, element_type, num_elements) != INFIX_SUCCESS)
784 result_type = nullptr;
785 }
786 else { // Primitive type or error
787 result_type = parse_primitive(state);
788 if (!result_type) {
789 // If no error was set by a failed `consume_keyword`, set a generic one.
791 state->p = p_before_type;
792 if (isalpha((unsigned char)*state->p) || *state->p == '_')
794 else
796 }
797 }
798 }
799 // Only set source offset for dynamically allocated types (primitives are static singletons).
800 if (result_type && result_type->is_arena_allocated)
801 result_type->source_offset = current_offset;
802 state->depth--;
803 return result_type;
804}
819 infix_type ** out_ret_type,
820 infix_function_argument ** out_args,
821 size_t * out_num_args,
822 size_t * out_num_fixed_args) {
823 if (*state->p != '(') {
826 }
827 state->p++;
828 skip_whitespace(state);
829 // Use a temporary linked list to collect arguments.
830 typedef struct arg_node {
832 struct arg_node * next;
833 } arg_node;
834 arg_node *head = nullptr, *tail = nullptr;
835 size_t num_args = 0;
836 // Parse Fixed Arguments
837 if (*state->p != ')' && *state->p != ';') {
838 while (1) {
839 skip_whitespace(state);
840 if (*state->p == ')' || *state->p == ';')
841 break;
842 const char * name = parse_optional_name_prefix(state);
843 infix_type * arg_type = parse_type(state);
844 if (!arg_type)
846 arg_node * node = infix_arena_calloc(state->arena, 1, sizeof(arg_node), _Alignof(arg_node));
847 if (!node) {
849 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
851 }
852 node->arg.type = arg_type;
853 node->arg.name = name;
854 node->next = nullptr;
855 if (!head)
856 head = tail = node;
857 else {
858 tail->next = node;
859 tail = node;
860 }
861 num_args++;
862 skip_whitespace(state);
863 if (*state->p == ',') {
864 state->p++;
865 skip_whitespace(state);
866 if (*state->p == ')' || *state->p == ';') { // Trailing comma error.
869 }
870 }
871 else if (*state->p != ')' && *state->p != ';') {
874 }
875 else
876 break;
877 }
878 }
879 *out_num_fixed_args = num_args;
880 // Parse Variadic Arguments
881 if (*state->p == ';') {
882 state->p++;
883 if (*state->p != ')') {
884 while (1) {
885 skip_whitespace(state);
886 if (*state->p == ')')
887 break;
888 const char * name = parse_optional_name_prefix(state);
889 infix_type * arg_type = parse_type(state);
890 if (!arg_type)
892 arg_node * node = infix_arena_calloc(state->arena, 1, sizeof(arg_node), _Alignof(arg_node));
893 if (!node) {
895 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
897 }
898 node->arg.type = arg_type;
899 node->arg.name = name;
900 node->next = nullptr;
901 if (!head)
902 head = tail = node;
903 else {
904 tail->next = node;
905 tail = node;
906 }
907 num_args++;
908 skip_whitespace(state);
909 if (*state->p == ',') {
910 state->p++;
911 skip_whitespace(state);
912 if (*state->p == ')') { // Trailing comma error.
915 }
916 }
917 else if (*state->p != ')') {
920 }
921 else
922 break;
923 }
924 }
925 }
926 skip_whitespace(state);
927 if (*state->p != ')') {
930 }
931 state->p++;
932 // Parse Return Type
933 skip_whitespace(state);
934 if (state->p[0] != '-' || state->p[1] != '>') {
937 }
938 state->p += 2;
939 *out_ret_type = parse_type(state);
940 if (!*out_ret_type)
942 // Convert linked list of args to a flat array.
943 infix_function_argument * args = (num_args > 0)
944 ? infix_arena_calloc(state->arena, num_args, sizeof(infix_function_argument), _Alignof(infix_function_argument))
945 : nullptr;
946 if (num_args > 0 && !args) {
949 }
950 arg_node * current = head;
951 for (size_t i = 0; i < num_args; i++) {
952 args[i] = current->arg;
953 current = current->next;
954 }
955 *out_args = args;
956 *out_num_args = num_args;
957 return INFIX_SUCCESS;
958}
959// High-Level API Implementation
977 infix_arena_t ** out_arena,
978 const char * signature) {
979 if (!out_type || !out_arena) {
982 }
983 if (!signature || *signature == '\0') {
986 }
987 // The top-level public API is responsible for setting g_infix_last_signature_context.
988 *out_arena = infix_arena_create(4096);
989 if (!*out_arena) {
992 }
993 parser_state state = {.p = signature, .start = signature, .arena = *out_arena, .depth = 0};
994 infix_type * type = parse_type(&state);
995 if (type) {
996 skip_whitespace(&state);
997 // After successfully parsing a type, ensure there is no trailing junk.
998 if (state.p[0] != '\0') {
1000 type = nullptr;
1001 }
1002 }
1003 if (!type) {
1004 // If parsing failed at any point, clean up the temporary arena.
1005 infix_arena_destroy(*out_arena);
1006 *out_arena = nullptr;
1007 *out_type = nullptr;
1009 }
1010 *out_type = type;
1011 return INFIX_SUCCESS;
1012}
1027 infix_arena_t ** out_arena,
1028 const char * signature,
1031 g_infix_last_signature_context = signature; // Set context for rich error reporting.
1032 // "Parse" stage: Create a raw, unresolved type graph in a temporary arena.
1033 infix_type * raw_type = nullptr;
1034 infix_arena_t * parser_arena = nullptr;
1035 infix_status status = _infix_parse_type_internal(&raw_type, &parser_arena, signature);
1036 if (status != INFIX_SUCCESS)
1037 return status;
1038 // Create the final arena that will be returned to the caller.
1039 *out_arena = infix_arena_create(4096);
1040 if (!*out_arena) {
1041 infix_arena_destroy(parser_arena);
1044 }
1045 // "Copy" stage: Deep copy the raw graph into the final arena.
1046 infix_type * final_type = _copy_type_graph_to_arena(*out_arena, raw_type);
1047 infix_arena_destroy(parser_arena); // The temporary graph is no longer needed.
1048 if (!final_type) {
1049 infix_arena_destroy(*out_arena);
1050 *out_arena = nullptr;
1053 }
1054 // "Resolve" stage: Replace all named references (`@Name`) with concrete types.
1056 if (status != INFIX_SUCCESS) {
1057 infix_arena_destroy(*out_arena);
1058 *out_arena = nullptr;
1059 *out_type = nullptr;
1060 }
1061 else {
1062 // "Layout" stage: Calculate the final size, alignment, and member offsets.
1064 *out_type = final_type;
1065 }
1066 return status;
1067}
1086 infix_arena_t ** out_arena,
1087 infix_type ** out_ret_type,
1088 infix_function_argument ** out_args,
1089 size_t * out_num_args,
1090 size_t * out_num_fixed_args,
1093
1094 //
1095 if (!signature) {
1098 }
1099 if (*signature == '\0') {
1102 }
1103 if (!out_arena || !out_ret_type || !out_args || !out_num_args || !out_num_fixed_args) {
1106 }
1107
1109
1110 // Parse stage
1111 infix_type * raw_func_type = nullptr;
1112 infix_arena_t * parser_arena = nullptr;
1113 infix_status status = _infix_parse_type_internal(&raw_func_type, &parser_arena, signature);
1114 if (status != INFIX_SUCCESS)
1115 return status;
1116
1117 if (raw_func_type->category != INFIX_TYPE_REVERSE_TRAMPOLINE) {
1118 infix_arena_destroy(parser_arena);
1121 }
1122
1123 // Create final arena
1124 *out_arena = infix_arena_create(8192);
1125 if (!*out_arena) {
1126 infix_arena_destroy(parser_arena);
1129 }
1130
1131 // "Copy" stage
1132 infix_type * final_func_type = _copy_type_graph_to_arena(*out_arena, raw_func_type);
1133 infix_arena_destroy(parser_arena);
1134 if (!final_func_type) {
1135 infix_arena_destroy(*out_arena);
1136 *out_arena = nullptr;
1139 }
1140
1141 // Resolve and layout stages
1143 if (status != INFIX_SUCCESS) {
1144 infix_arena_destroy(*out_arena);
1145 *out_arena = nullptr;
1147 }
1148 _infix_type_recalculate_layout(final_func_type);
1149
1150 // Unpack the results for the caller from the final, processed function type object.
1151 *out_ret_type = final_func_type->meta.func_ptr_info.return_type;
1152 *out_args = final_func_type->meta.func_ptr_info.args;
1153 *out_num_args = final_func_type->meta.func_ptr_info.num_args;
1154 *out_num_fixed_args = final_func_type->meta.func_ptr_info.num_fixed_args;
1155 return INFIX_SUCCESS;
1156}
1157
1158// Type Printing Logic
1164typedef struct {
1165 char * p;
1166 size_t remaining;
1168 // Itanium mangling state
1169 const void * itanium_subs[64];
1171 // MSVC mangling state
1172 const infix_type * msvc_types[10];
1183static void _print(printer_state * state, const char * fmt, ...) {
1184 if (state->status != INFIX_SUCCESS)
1185 return;
1186 va_list args;
1187 va_start(args, fmt);
1188 int written = vsnprintf(state->p, state->remaining, fmt, args);
1189 va_end(args);
1190 if (written < 0 || (size_t)written >= state->remaining)
1191 // If snprintf failed or would have overflowed, mark an error.
1193 else {
1194 state->p += written;
1195 state->remaining -= written;
1196 }
1197}
1198// Forward declaration for mutual recursion in printers.
1199static void _infix_type_print_signature_recursive(printer_state * state, const infix_type * type);
1200static void _infix_type_print_itanium_recursive(printer_state * state, const infix_type * type);
1201static void _infix_type_print_msvc_recursive(printer_state * state, const infix_type * type);
1202
1203// Itanium Mangling Helpers
1204static bool _find_itanium_sub(printer_state * state, const void * component, size_t * index) {
1205 for (size_t i = 0; i < state->itanium_sub_count; i++) {
1206 if (state->itanium_subs[i] == component) {
1207 *index = i;
1208 return true;
1209 }
1210 }
1211 return false;
1212}
1213
1214static void _add_itanium_sub(printer_state * state, const void * component) {
1215 if (state->itanium_sub_count < 64)
1216 state->itanium_subs[state->itanium_sub_count++] = component;
1217}
1218
1219static void _print_itanium_sub(printer_state * state, size_t index) {
1220 if (index == 0) {
1221 _print(state, "S_");
1222 }
1223 else {
1224 index--; // S0_ is index 1
1225 _print(state, "S");
1226 if (index == 0) {
1227 _print(state, "0");
1228 }
1229 else {
1230 char buf[16];
1231 int pos = 0;
1232 size_t val = index;
1233 while (val > 0) {
1234 int digit = val % 36;
1235 buf[pos++] = (digit < 10) ? (char)('0' + digit) : (char)('A' + digit - 10);
1236 val /= 36;
1237 }
1238 while (pos > 0)
1239 _print(state, "%c", buf[--pos]);
1240 }
1241 _print(state, "_");
1242 }
1243}
1244
1258 if (state->status != INFIX_SUCCESS || !type) {
1259 if (state->status == INFIX_SUCCESS)
1261 return;
1262 }
1263 // If the type has a semantic name, always prefer printing it.
1264 if (type->name) {
1265 _print(state, "@%s", type->name);
1266 return;
1267 }
1268 switch (type->category) {
1269 case INFIX_TYPE_VOID:
1270 _print(state, "void");
1271 break;
1273 // This case should ideally not be hit with a fully resolved type, but we handle it for robustness.
1274 _print(state, "@%s", type->meta.named_reference.name);
1275 break;
1276 case INFIX_TYPE_POINTER:
1277 _print(state, "*");
1278 // Special handling for `void*` or recursive pointers to avoid infinite recursion.
1279 if (type->meta.pointer_info.pointee_type == type || type->meta.pointer_info.pointee_type == nullptr ||
1281 _print(state, "void");
1282 else
1284 break;
1285 case INFIX_TYPE_ARRAY:
1286 if (type->meta.array_info.is_flexible)
1287 _print(state, "[?:");
1288 else
1289 _print(state, "[%zu:", type->meta.array_info.num_elements);
1291 _print(state, "]");
1292 break;
1293 case INFIX_TYPE_STRUCT:
1294 if (type->meta.aggregate_info.is_packed) {
1295 _print(state, "!");
1296 if (type->alignment != 1)
1297 _print(state, "%zu:", type->alignment);
1298 }
1299 _print(state, "{");
1300 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
1301 if (i > 0)
1302 _print(state, ",");
1303 const infix_struct_member * member = &type->meta.aggregate_info.members[i];
1304 if (member->name)
1305 _print(state, "%s:", member->name);
1307 if (member->bit_width > 0)
1308 _print(state, ":%u", member->bit_width);
1309 }
1310 _print(state, "}");
1311 break;
1312 case INFIX_TYPE_UNION:
1313 _print(state, "<");
1314 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
1315 if (i > 0)
1316 _print(state, ",");
1317 const infix_struct_member * member = &type->meta.aggregate_info.members[i];
1318 if (member->name)
1319 _print(state, "%s:", member->name);
1321 // Bitfields in unions are rare but syntactically valid in C.
1322 if (member->bit_width > 0)
1323 _print(state, ":%u", member->bit_width);
1324 }
1325 _print(state, ">");
1326 break;
1328 _print(state, "(");
1329 for (size_t i = 0; i < type->meta.func_ptr_info.num_fixed_args; ++i) {
1330 if (i > 0)
1331 _print(state, ",");
1332 const infix_function_argument * arg = &type->meta.func_ptr_info.args[i];
1333 if (arg->name)
1334 _print(state, "%s:", arg->name);
1336 }
1338 _print(state, ";");
1339 for (size_t i = type->meta.func_ptr_info.num_fixed_args; i < type->meta.func_ptr_info.num_args; ++i) {
1340 if (i > type->meta.func_ptr_info.num_fixed_args)
1341 _print(state, ",");
1342 const infix_function_argument * arg = &type->meta.func_ptr_info.args[i];
1343 if (arg->name)
1344 _print(state, "%s:", arg->name);
1346 }
1347 }
1348 _print(state, ")->");
1350 break;
1351 case INFIX_TYPE_ENUM:
1352 _print(state, "e:");
1354 break;
1355 case INFIX_TYPE_COMPLEX:
1356 _print(state, "c[");
1358 _print(state, "]");
1359 break;
1360 case INFIX_TYPE_VECTOR:
1361 {
1362 const infix_type * element_type = type->meta.vector_info.element_type;
1363 size_t num_elements = type->meta.vector_info.num_elements;
1364 bool printed_alias = false;
1365 if (element_type->category == INFIX_TYPE_PRIMITIVE) {
1366 if (num_elements == 8 && is_double(element_type)) {
1367 _print(state, "m512d");
1368 printed_alias = true;
1369 }
1370 else if (num_elements == 16 && is_float(element_type)) {
1371 _print(state, "m512");
1372 printed_alias = true;
1373 }
1374 else if (num_elements == 8 && element_type->meta.primitive_id == INFIX_PRIMITIVE_SINT64) {
1375 _print(state, "m512i");
1376 printed_alias = true;
1377 }
1378 else if (num_elements == 4 && is_double(element_type)) {
1379 _print(state, "m256d");
1380 printed_alias = true;
1381 }
1382 else if (num_elements == 8 && is_float(element_type)) {
1383 _print(state, "m256");
1384 printed_alias = true;
1385 }
1386 }
1387 if (!printed_alias) {
1388 _print(state, "v[%zu:", num_elements);
1389 _infix_type_print_signature_recursive(state, element_type);
1390 _print(state, "]");
1391 }
1392 }
1393 break;
1395 switch (type->meta.primitive_id) {
1397 _print(state, "bool");
1398 break;
1400 _print(state, "sint8");
1401 break;
1403 _print(state, "uint8");
1404 break;
1406 _print(state, "sint16");
1407 break;
1409 _print(state, "uint16");
1410 break;
1412 _print(state, "sint32");
1413 break;
1415 _print(state, "uint32");
1416 break;
1418 _print(state, "sint64");
1419 break;
1421 _print(state, "uint64");
1422 break;
1424 _print(state, "sint128");
1425 break;
1427 _print(state, "uint128");
1428 break;
1430 _print(state, "float16");
1431 break;
1433 _print(state, "float");
1434 break;
1436 _print(state, "double");
1437 break;
1439 _print(state, "longdouble");
1440 break;
1441 }
1442 break;
1443 default:
1445 break;
1446 }
1447}
1455 if (state->status != INFIX_SUCCESS || !type) {
1456 if (state->status == INFIX_SUCCESS)
1458 return;
1459 }
1460
1461 size_t sub_index;
1462 bool is_builtin = (type->category == INFIX_TYPE_VOID || type->category == INFIX_TYPE_PRIMITIVE);
1463
1464 if (!is_builtin && _find_itanium_sub(state, type, &sub_index)) {
1465 _print_itanium_sub(state, sub_index);
1466 return;
1467 }
1468
1469 switch (type->category) {
1470 case INFIX_TYPE_VOID:
1471 _print(state, "v");
1472 break;
1473 case INFIX_TYPE_POINTER:
1474 _print(state, "P");
1476 _add_itanium_sub(state, type);
1477 break;
1479 switch (type->meta.primitive_id) {
1481 _print(state, "b");
1482 break;
1484 _print(state, "a");
1485 break; // signed char
1487 _print(state, "h");
1488 break; // unsigned char
1490 _print(state, "s");
1491 break; // short
1493 _print(state, "t");
1494 break; // unsigned short
1496 _print(state, "i");
1497 break; // int
1499 _print(state, "j");
1500 break; // unsigned int
1502 _print(state, "x");
1503 break; // long long
1505 _print(state, "y");
1506 break; // unsigned long long
1508 _print(state, "n");
1509 break; // __int128
1511 _print(state, "o");
1512 break; // unsigned __int128
1514 _print(state, "Dh");
1515 break; // half-precision float (IEEE 754)
1517 _print(state, "f");
1518 break;
1520 _print(state, "d");
1521 break;
1523 _print(state, "e");
1524 break;
1525 }
1526 break;
1528 {
1529 const char * name = type->meta.named_reference.name;
1530 size_t len = strlen(name);
1531 _print(state, "%zu%s", len, name);
1532 _add_itanium_sub(state, type);
1533 }
1534 break;
1535 case INFIX_TYPE_STRUCT:
1536 case INFIX_TYPE_UNION:
1537 if (type->name) {
1538 // Check for namespaced type (e.g. "Namespace::Class")
1539 // Itanium mangling for namespaces is N...E
1540 const char * p = type->name;
1541 int parts = 0;
1542 // Count parts
1543 while (*p) {
1544 if (p[0] == ':' && p[1] == ':') {
1545 parts++;
1546 p += 2;
1547 }
1548 else {
1549 p++;
1550 }
1551 }
1552 parts++; // Last part
1553
1554 if (parts > 1) {
1555 _print(state, "N");
1556 p = type->name;
1557 while (*p) {
1558 const char * end = strstr(p, "::");
1559 size_t part_len = end ? (size_t)(end - p) : strlen(p);
1560 _print(state, "%zu", part_len);
1561 // Print part_len chars
1562 for (size_t i = 0; i < part_len; i++)
1563 _print(state, "%c", p[i]);
1564 if (end)
1565 p = end + 2;
1566 else
1567 break;
1568 }
1569 _print(state, "E");
1570 }
1571 else {
1572 // Simple name
1573 size_t len = strlen(type->name);
1574 _print(state, "%zu%s", len, type->name);
1575 }
1576 _add_itanium_sub(state, type);
1577 }
1578 else {
1579 // Mangling for anonymous structs isn't standardized.
1580 // Emitting 'void' as a safe placeholder for "unknown type".
1581 _print(state, "v");
1582 }
1583 break;
1584 case INFIX_TYPE_COMPLEX:
1585 _print(state, "C");
1587 _add_itanium_sub(state, type);
1588 break;
1589 case INFIX_TYPE_VECTOR:
1590 _print(state, "Dv%zu_", type->size / type->meta.vector_info.element_type->size);
1592 _add_itanium_sub(state, type);
1593 break;
1594 default:
1595 // Fallback for types that don't map cleanly to Itanium mangling
1596 _print(state, "v");
1597 break;
1598 }
1599}
1607 if (state->status != INFIX_SUCCESS || !type) {
1608 if (state->status == INFIX_SUCCESS)
1610 return;
1611 }
1612
1613 // Check for type back-references (0-9)
1614 // MSVC only back-references complex types or pointers to them.
1615 bool can_backref =
1616 (type->category == INFIX_TYPE_POINTER || type->category == INFIX_TYPE_STRUCT ||
1617 type->category == INFIX_TYPE_UNION || type->category == INFIX_TYPE_ENUM || type->name != nullptr);
1618
1619 if (can_backref) {
1620 for (size_t i = 0; i < state->msvc_type_count; i++) {
1621 if (state->msvc_types[i] == type) {
1622 _print(state, "%zu", i);
1623 return;
1624 }
1625 }
1626 }
1627
1628 // Handle named types (Struct/Union/Enum or aliases)
1629 if (type->name) {
1630 // MSVC encoding:
1631 // U = Struct
1632 // T = Union
1633 // W = Enum
1634 char prefix = 'U';
1635 if (type->category == INFIX_TYPE_UNION)
1636 prefix = 'T';
1637 else if (type->category == INFIX_TYPE_ENUM)
1638 prefix = 'W';
1639
1640 // Check for namespaces (e.g. "Namespace::Class")
1641 // MSVC format: <Prefix><Name>@<Namespace>@@
1642 // Reverse order of namespaces!
1643 if (strstr(type->name, "::")) {
1644 _print(state, "%c", prefix);
1645
1646 // We need to split and reverse. Since we can't allocate easily here,
1647 // we'll scan the string multiple times or use recursion.
1648 // Let's use a simple stack-based approach for small depth.
1649 const char * parts[MAX_RECURSION_DEPTH];
1650 size_t lens[MAX_RECURSION_DEPTH];
1651 int count = 0;
1652
1653 const char * p = type->name;
1654 while (*p && count < MAX_RECURSION_DEPTH) {
1655 parts[count] = p;
1656 const char * end = strstr(p, "::");
1657 if (end) {
1658 lens[count] = end - p;
1659 p = end + 2;
1660 }
1661 else {
1662 lens[count] = strlen(p);
1663 p += lens[count];
1664 }
1665 count++;
1666 }
1667
1668 // Print in reverse order
1669 for (int i = count - 1; i >= 0; i--) {
1670 for (size_t j = 0; j < lens[i]; j++)
1671 _print(state, "%c", parts[i][j]);
1672 _print(state, "@");
1673 }
1674 _print(state, "@"); // Terminator
1675 }
1676 else {
1677 _print(state, "%c%s@@", prefix, type->name);
1678 }
1679
1680 if (can_backref && state->msvc_type_count < 10)
1681 state->msvc_types[state->msvc_type_count++] = type;
1682 return;
1683 }
1684
1685 switch (type->category) {
1686 case INFIX_TYPE_VOID:
1687 _print(state, "X");
1688 break;
1689 case INFIX_TYPE_POINTER:
1690 // Standard MSVC pointer encoding for x64:
1691 // P = Pointer
1692 // E = __ptr64
1693 // A = const/volatile qualifiers (A = none)
1694 // Then the pointee type.
1695 _print(state, "PEA");
1697 if (can_backref && state->msvc_type_count < 10)
1698 state->msvc_types[state->msvc_type_count++] = type;
1699 break;
1700 case INFIX_TYPE_REVERSE_TRAMPOLINE: // Function Pointer
1701 // P6 = Pointer to Function
1702 // A = __cdecl
1703 _print(state, "P6A");
1704 // Return type
1706 // Arguments
1707 if (type->meta.func_ptr_info.num_args == 0)
1708 _print(state, "X");
1709 else
1710 for (size_t i = 0; i < type->meta.func_ptr_info.num_args; ++i)
1712 _print(state, "@Z");
1713 if (can_backref && state->msvc_type_count < 10)
1714 state->msvc_types[state->msvc_type_count++] = type;
1715 break;
1717 switch (type->meta.primitive_id) {
1719 _print(state, "_N");
1720 break;
1722 _print(state, "C");
1723 break;
1725 _print(state, "E");
1726 break;
1728 _print(state, "F");
1729 break;
1731 _print(state, "G");
1732 break;
1734 _print(state, "H");
1735 break;
1737 _print(state, "I");
1738 break;
1740 _print(state, "_J");
1741 break;
1743 _print(state, "_K");
1744 break;
1746 _print(state, "_L");
1747 break;
1749 _print(state, "_M");
1750 break;
1752 _print(state, "_T");
1753 break;
1755 _print(state, "M");
1756 break;
1758 _print(state, "N");
1759 break;
1761 _print(state, "O");
1762 break;
1763 }
1764 break;
1765 case INFIX_TYPE_COMPLEX:
1766 // MSVC doesn't have a built-in complex type, it uses structs.
1767 _print(state, "U_Complex@@");
1768 if (can_backref && state->msvc_type_count < 10)
1769 state->msvc_types[state->msvc_type_count++] = type;
1770 break;
1771 case INFIX_TYPE_VECTOR:
1772 _print(state, "T__m%zu@@", type->size * 8);
1773 if (can_backref && state->msvc_type_count < 10)
1774 state->msvc_types[state->msvc_type_count++] = type;
1775 break;
1777 // Unresolved references, treat as Struct for mangling purposes.
1778 _print(state, "U%s@@", type->meta.named_reference.name);
1779 if (can_backref && state->msvc_type_count < 10)
1780 state->msvc_types[state->msvc_type_count++] = type;
1781 break;
1782 default:
1783 _print(state, "X");
1784 break;
1785 }
1786}
1800 if (state->status != INFIX_SUCCESS || !type) {
1801 if (state->status == INFIX_SUCCESS)
1803 return;
1804 }
1805 // This is the key difference from the main printer: we skip the `if (type->name)` check
1806 // and immediately print the underlying structure of the type.
1807 switch (type->category) {
1808 case INFIX_TYPE_STRUCT:
1809 if (type->meta.aggregate_info.is_packed) {
1810 _print(state, "!");
1811 if (type->alignment != 1)
1812 _print(state, "%zu:", type->alignment);
1813 }
1814 _print(state, "{");
1815 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
1816 if (i > 0)
1817 _print(state, ",");
1818 const infix_struct_member * member = &type->meta.aggregate_info.members[i];
1819 if (member->name)
1820 _print(state, "%s:", member->name);
1821 // For nested members, we can use the standard printer, which IS allowed
1822 // to use the `@Name` shorthand for brevity.
1824 if (member->bit_width > 0)
1825 _print(state, ":%u", member->bit_width);
1826 }
1827 _print(state, "}");
1828 break;
1829 case INFIX_TYPE_UNION:
1830 _print(state, "<");
1831 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
1832 if (i > 0)
1833 _print(state, ",");
1834 const infix_struct_member * member = &type->meta.aggregate_info.members[i];
1835 if (member->name)
1836 _print(state, "%s:", member->name);
1838 if (member->bit_width > 0)
1839 _print(state, ":%u", member->bit_width);
1840 }
1841 _print(state, ">");
1842 break;
1843 // For all other types, we replicate the printing logic from the main printer
1844 // to ensure we print the structure, not a potential top-level alias name.
1845 case INFIX_TYPE_VOID:
1846 _print(state, "void");
1847 break;
1848 case INFIX_TYPE_POINTER:
1849 _print(state, "*");
1850 if (type->meta.pointer_info.pointee_type == type || type->meta.pointer_info.pointee_type == nullptr ||
1852 _print(state, "void");
1853 else
1855 break;
1856 case INFIX_TYPE_ARRAY:
1857 if (type->meta.array_info.is_flexible)
1858 _print(state, "[?:");
1859 else
1860 _print(state, "[%zu:", type->meta.array_info.num_elements);
1862 _print(state, "]");
1863 break;
1864 case INFIX_TYPE_ENUM:
1865 _print(state, "e:");
1867 break;
1868 case INFIX_TYPE_COMPLEX:
1869 _print(state, "c[");
1871 _print(state, "]");
1872 break;
1874 // This block is now a full copy from the main printer.
1875 switch (type->meta.primitive_id) {
1877 _print(state, "bool");
1878 break;
1880 _print(state, "sint8");
1881 break;
1883 _print(state, "uint8");
1884 break;
1886 _print(state, "sint16");
1887 break;
1889 _print(state, "uint16");
1890 break;
1892 _print(state, "sint32");
1893 break;
1895 _print(state, "uint32");
1896 break;
1898 _print(state, "sint64");
1899 break;
1901 _print(state, "uint64");
1902 break;
1904 _print(state, "sint128");
1905 break;
1907 _print(state, "uint128");
1908 break;
1910 _print(state, "float16");
1911 break;
1913 _print(state, "float");
1914 break;
1916 _print(state, "double");
1917 break;
1919 _print(state, "longdouble");
1920 break;
1921 }
1922 break;
1923 // We can safely delegate the remaining complex cases to the main printer, as they
1924 // do not have a top-level `name` field themselves.
1927 case INFIX_TYPE_VECTOR:
1929 break;
1930 default:
1932 break;
1933 }
1934}
1940 size_t buffer_size,
1941 const infix_type * type,
1942 infix_print_dialect_t dialect) {
1943 if (!buffer || buffer_size == 0 || !type || dialect != INFIX_DIALECT_SIGNATURE)
1945 printer_state state = {buffer, buffer_size, INFIX_SUCCESS, {0}, 0, {0}, 0};
1946 *buffer = '\0';
1948 if (state.remaining > 0)
1949 *state.p = '\0';
1950 else
1951 buffer[buffer_size - 1] = '\0';
1952 return state.status;
1953}
1963 size_t buffer_size,
1964 const infix_type * type,
1965 infix_print_dialect_t dialect) {
1967 if (!buffer || buffer_size == 0 || !type) {
1970 }
1971 printer_state state = {buffer, buffer_size, INFIX_SUCCESS, {0}, 0, {0}, 0};
1972 *buffer = '\0';
1973 if (dialect == INFIX_DIALECT_SIGNATURE)
1975 else if (dialect == INFIX_DIALECT_ITANIUM_MANGLING)
1977 else if (dialect == INFIX_DIALECT_MSVC_MANGLING)
1979 else {
1980 _print(&state, "unsupported_dialect");
1982 }
1983 if (state.status == INFIX_SUCCESS) {
1984 if (state.remaining > 0)
1985 *state.p = '\0'; // Null-terminate if there is space.
1986 else {
1987 // Buffer was exactly full. Ensure null termination at the very end.
1988 buffer[buffer_size - 1] = '\0';
1989 return INFIX_ERROR_INVALID_ARGUMENT; // Indicate truncation.
1990 }
1991 }
1992 else if (buffer_size > 0)
1993 // Ensure null termination even on error (e.g., buffer too small).
1994 buffer[buffer_size - 1] = '\0';
1995 return state.status;
1996}
2010 size_t buffer_size,
2011 const char * function_name,
2012 const infix_type * ret_type,
2014 size_t num_args,
2015 size_t num_fixed_args,
2016 infix_print_dialect_t dialect) {
2018 if (!buffer || buffer_size == 0 || !ret_type || (num_args > 0 && !args)) {
2021 }
2022 printer_state state = {buffer, buffer_size, INFIX_SUCCESS, {0}, 0, {0}, 0};
2023 *buffer = '\0';
2024 if (dialect == INFIX_DIALECT_SIGNATURE) {
2025 (void)function_name; // Unused
2026 _print(&state, "(");
2027 for (size_t i = 0; i < num_fixed_args; ++i) {
2028 if (i > 0)
2029 _print(&state, ",");
2031 }
2032 if (num_args > num_fixed_args) {
2033 _print(&state, ";");
2034 for (size_t i = num_fixed_args; i < num_args; ++i) {
2035 if (i > num_fixed_args)
2036 _print(&state, ",");
2038 }
2039 }
2040 _print(&state, ")->");
2042 }
2043 else if (dialect == INFIX_DIALECT_ITANIUM_MANGLING) {
2044 // _Z <name_len> <name> <ret_type?> <args...>
2045 // Note: Itanium mangling usually omits return type for standard functions unless it's a template or special
2046 // case. We omit it here for simplicity to match extern "C" -> C++ linking expectations for simple functions.
2047 _print(&state, "_Z");
2048 if (function_name) {
2049 // Check for namespace in function name (e.g., "MyNS::my_func")
2050 const char * p = function_name;
2051 int parts = 0;
2052 while (*p)
2053 if (p[0] == ':' && p[1] == ':') {
2054 parts++;
2055 p += 2;
2056 }
2057 else
2058 p++;
2059 parts++;
2060
2061 if (parts > 1) {
2062 _print(&state, "N");
2063 p = function_name;
2064 while (*p) {
2065 const char * end = strstr(p, "::");
2066 size_t part_len = end ? (size_t)(end - p) : strlen(p);
2067 _print(&state, "%zu", part_len);
2068 for (size_t i = 0; i < part_len; i++)
2069 _print(&state, "%c", p[i]);
2070 if (end)
2071 p = end + 2;
2072 else
2073 break;
2074 }
2075 _print(&state, "E");
2076 }
2077 else {
2078 size_t name_len = strlen(function_name);
2079 _print(&state, "%zu%s", name_len, function_name);
2080 }
2081 }
2082 else
2083 _print(&state, "4func"); // Default name if NULL
2084
2085 if (num_args == 0)
2086 _print(&state, "v"); // void (no args)
2087 else
2088 for (size_t i = 0; i < num_args; ++i)
2090 }
2091 else if (dialect == INFIX_DIALECT_MSVC_MANGLING) {
2092 // MSVC: ?<name>@@YA<ret><args...>@Z
2093 _print(&state, "?");
2094 if (function_name) {
2095 // MSVC namespace handling: reverse order
2096 if (strstr(function_name, "::")) {
2097 const char * parts[MAX_RECURSION_DEPTH];
2098 size_t lens[MAX_RECURSION_DEPTH];
2099 int count = 0;
2100 const char * p = function_name;
2101 while (*p && count < MAX_RECURSION_DEPTH) {
2102 parts[count] = p;
2103 const char * end = strstr(p, "::");
2104 if (end) {
2105 lens[count] = end - p;
2106 p = end + 2;
2107 }
2108 else {
2109 lens[count] = strlen(p);
2110 p += lens[count];
2111 }
2112 count++;
2113 }
2114 // Print in reverse order
2115 for (int i = count - 1; i >= 0; i--) {
2116 for (size_t j = 0; j < lens[i]; j++)
2117 _print(&state, "%c", parts[i][j]);
2118 _print(&state, "@");
2119 }
2120 }
2121 else {
2122 _print(&state, "%s@", function_name);
2123 }
2124 }
2125 else {
2126 _print(&state, "func@");
2127 }
2128 _print(&state, "@YA"); // __cdecl (default)
2130
2131 if (num_args == 0)
2132 _print(&state, "X"); // void argument list
2133 else
2134 for (size_t i = 0; i < num_args; ++i)
2135 _infix_type_print_msvc_recursive(&state, args[i].type);
2136 _print(&state, "@Z");
2137 }
2138 else {
2139 _print(&state, "unsupported_dialect");
2141 }
2142 if (state.status == INFIX_SUCCESS) {
2143 if (state.remaining > 0)
2144 *state.p = '\0';
2145 else {
2146 if (buffer_size > 0)
2147 buffer[buffer_size - 1] = '\0';
2148 return INFIX_ERROR_INVALID_ARGUMENT; // Indicate truncation.
2149 }
2150 }
2151 else if (buffer_size > 0)
2152 buffer[buffer_size - 1] = '\0';
2153 return state.status;
2154}
2169c23_nodiscard infix_status infix_registry_print(char * buffer, size_t buffer_size, const infix_registry_t * registry) {
2170 if (!buffer || buffer_size == 0 || !registry)
2172 printer_state state = {buffer, buffer_size, INFIX_SUCCESS, {0}, 0, {0}, 0};
2173 *state.p = '\0';
2174 // Iterate through all buckets and their chains.
2175 for (size_t i = 0; i < registry->num_buckets; ++i) {
2176 for (const _infix_registry_entry_t * entry = registry->buckets[i]; entry != nullptr; entry = entry->next) {
2177 // Only print fully defined types, not forward declarations.
2178 if (entry->type && !entry->is_forward_declaration) {
2179 char type_body_buffer[1024];
2181 type_body_buffer, sizeof(type_body_buffer), entry->type, INFIX_DIALECT_SIGNATURE) !=
2182 INFIX_SUCCESS) {
2184 goto end_print_loop;
2185 }
2186 _print(&state, "@%s = %s;\n", entry->name, type_body_buffer);
2187 }
2188 else if (entry->is_forward_declaration) // Explicitly print forward declarations
2189 _print(&state, "@%s;\n", entry->name);
2190 if (state.status != INFIX_SUCCESS)
2191 goto end_print_loop;
2192 }
2193 }
2194end_print_loop:;
2195 return state.status;
2196}
infix_registry_t * registry
Definition 008_registry_introspection.c:33
infix_status status
Definition 103_unions.c:61
infix_struct_member * members
Definition 103_unions.c:55
void * args[]
Definition 202_in_structs.c:59
clock_t start
Definition 901_call_overhead.c:48
infix_type * ret_type
Definition 901_call_overhead.c:61
clock_t end
Definition 901_call_overhead.c:48
char * p
Definition 904_registry_benchmark.c:25
#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_error_code_t
Enumerates specific error codes.
Definition infix.h:1360
@ INFIX_CODE_SUCCESS
Definition infix.h:1362
@ INFIX_CODE_INVALID_MEMBER_TYPE
Definition infix.h:1388
@ INFIX_CODE_INTEGER_OVERFLOW
Definition infix.h:1379
@ INFIX_CODE_TYPE_TOO_LARGE
Definition infix.h:1386
@ INFIX_CODE_EMPTY_SIGNATURE
Definition infix.h:1382
@ INFIX_CODE_UNEXPECTED_TOKEN
Definition infix.h:1375
@ INFIX_CODE_MISSING_RETURN_TYPE
Definition infix.h:1378
@ INFIX_CODE_RECURSION_DEPTH_EXCEEDED
Definition infix.h:1380
@ INFIX_CODE_UNTERMINATED_AGGREGATE
Definition infix.h:1376
@ INFIX_CODE_NULL_POINTER
Definition infix.h:1364
@ INFIX_CODE_INVALID_KEYWORD
Definition infix.h:1377
@ 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
struct infix_type_t::@0::@1 pointer_info
Metadata for INFIX_TYPE_POINTER.
union infix_type_t::@0 meta
A union containing metadata specific to the type's category.
bool is_packed
Definition infix.h:295
struct infix_type_t::@0::@7 vector_info
Metadata for INFIX_TYPE_VECTOR.
infix_type * type
Definition infix.h:348
c23_nodiscard infix_status infix_signature_parse(const char *signature, infix_arena_t **out_arena, infix_type **out_ret_type, infix_function_argument **out_args, size_t *out_num_args, size_t *out_num_fixed_args, infix_registry_t *registry)
Parses a full function signature string into its constituent parts.
Definition signature.c:1085
struct infix_type_t::@0::@4 func_ptr_info
Metadata for INFIX_TYPE_REVERSE_TRAMPOLINE.
size_t num_elements
Definition infix.h:300
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.
c23_nodiscard infix_status infix_type_from_signature(infix_type **out_type, infix_arena_t **out_arena, const char *signature, infix_registry_t *registry)
Parses a signature string representing a single data type.
Definition signature.c:1026
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
const char * name
Definition infix.h:347
const char * name
Definition infix.h:335
infix_type_category category
Definition infix.h:276
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
bool is_flexible
Definition infix.h:301
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
uint8_t bit_width
Definition infix.h:338
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_fixed_args
Definition infix.h:308
infix_primitive_type_id primitive_id
Metadata for INFIX_TYPE_PRIMITIVE.
Definition infix.h:286
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
INFIX_API c23_nodiscard infix_status infix_function_print(char *buffer, size_t buffer_size, const char *function_name, const infix_type *ret_type, const infix_function_argument *args, size_t num_args, size_t num_fixed_args, infix_print_dialect_t dialect)
Serializes a function signature's components into a string.
Definition signature.c:2009
INFIX_API c23_nodiscard infix_status infix_type_print(char *buffer, size_t buffer_size, const infix_type *type, infix_print_dialect_t dialect)
Serializes an infix_type object graph back into a signature string.
Definition signature.c:1962
infix_print_dialect_t
Specifies the output format for printing types and function signatures.
Definition infix.h:1175
@ INFIX_DIALECT_SIGNATURE
Definition infix.h:1176
@ INFIX_DIALECT_ITANIUM_MANGLING
Definition infix.h:1177
@ INFIX_DIALECT_MSVC_MANGLING
Definition infix.h:1178
#define infix_memcpy
A macro that can be defined to override the default memcpy function.
Definition infix.h:387
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
INFIX_API void infix_arena_destroy(infix_arena_t *)
Destroys an arena and frees all memory allocated from it.
Definition arena.c:83
c23_nodiscard infix_status infix_registry_print(char *buffer, size_t buffer_size, const infix_registry_t *registry)
Serializes all defined types within a registry into a single, human-readable string.
Definition signature.c:2169
INFIX_API INFIX_NODISCARD infix_status infix_type_create_pointer_to(infix_arena_t *, infix_type **, infix_type *)
Creates a new pointer type that points to a specific type.
Definition types.c:393
INFIX_API infix_struct_member infix_type_create_member(const char *, infix_type *, size_t)
A factory function to create an infix_struct_member.
Definition types.c:208
INFIX_API INFIX_NODISCARD infix_status infix_type_create_packed_struct(infix_arena_t *, infix_type **, size_t, size_t, infix_struct_member *, size_t)
Creates a new packed struct type with a user-specified layout.
Definition types.c:719
INFIX_API INFIX_NODISCARD infix_status infix_type_create_complex(infix_arena_t *, infix_type **, infix_type *)
Creates a new _Complex number type.
Definition types.c:542
INFIX_API INFIX_NODISCARD infix_type * infix_type_create_void(void)
Creates a static descriptor for the void type.
Definition types.c:200
INFIX_API INFIX_NODISCARD infix_status infix_type_create_union(infix_arena_t *, infix_type **, infix_struct_member *, size_t)
Creates a new union type from an array of members.
Definition types.c:612
INFIX_API infix_struct_member infix_type_create_bitfield_member(const char *, infix_type *, size_t, uint8_t)
A factory function to create a bitfield infix_struct_member.
Definition types.c:219
INFIX_API INFIX_NODISCARD infix_status infix_type_create_named_reference(infix_arena_t *, infix_type **, const char *, infix_aggregate_category_t)
Creates a placeholder for a named type to be resolved by a registry.
Definition types.c:777
INFIX_API INFIX_NODISCARD infix_type * infix_type_create_primitive(infix_primitive_type_id)
Creates a static descriptor for a primitive C type.
Definition types.c:144
INFIX_API INFIX_NODISCARD infix_status infix_type_create_vector(infix_arena_t *, infix_type **, infix_type *, size_t)
Creates a new SIMD vector type.
Definition types.c:573
INFIX_API INFIX_NODISCARD infix_status infix_type_create_struct(infix_arena_t *, infix_type **, infix_struct_member *, size_t)
Creates a new struct type from an array of members, calculating layout automatically.
Definition types.c:660
INFIX_API INFIX_NODISCARD infix_status infix_type_create_enum(infix_arena_t *, infix_type **, infix_type *)
Creates a new enum type with a specified underlying integer type.
Definition types.c:507
INFIX_API infix_status infix_type_create_flexible_array(infix_arena_t *, infix_type **, infix_type *)
Creates a flexible array member type ([?:type]).
Definition types.c:463
INFIX_API INFIX_NODISCARD infix_status infix_type_create_array(infix_arena_t *, infix_type **, infix_type *, size_t)
Creates a new fixed-size array type.
Definition types.c:424
@ INFIX_PRIMITIVE_UINT16
Definition infix.h:247
@ INFIX_PRIMITIVE_UINT32
Definition infix.h:249
@ INFIX_PRIMITIVE_LONG_DOUBLE
Definition infix.h:258
@ INFIX_PRIMITIVE_FLOAT
Definition infix.h:256
@ INFIX_PRIMITIVE_DOUBLE
Definition infix.h:257
@ INFIX_PRIMITIVE_SINT16
Definition infix.h:248
@ INFIX_PRIMITIVE_SINT64
Definition infix.h:252
@ INFIX_PRIMITIVE_SINT32
Definition infix.h:250
@ INFIX_PRIMITIVE_UINT8
Definition infix.h:245
@ INFIX_PRIMITIVE_UINT128
Definition infix.h:253
@ INFIX_PRIMITIVE_BOOL
Definition infix.h:244
@ INFIX_PRIMITIVE_UINT64
Definition infix.h:251
@ INFIX_PRIMITIVE_FLOAT16
Definition infix.h:255
@ INFIX_PRIMITIVE_SINT128
Definition infix.h:254
@ INFIX_PRIMITIVE_SINT8
Definition infix.h:246
@ INFIX_TYPE_UNION
Definition infix.h:231
@ INFIX_TYPE_PRIMITIVE
Definition infix.h:228
@ INFIX_TYPE_COMPLEX
Definition infix.h:235
@ INFIX_TYPE_ARRAY
Definition infix.h:232
@ INFIX_TYPE_VECTOR
Definition infix.h:236
@ INFIX_TYPE_VOID
Definition infix.h:238
@ 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
@ INFIX_AGGREGATE_STRUCT
Definition infix.h:264
#define INFIX_API
Symbol visibility macro.
Definition infix.h:114
#define INFIX_INTERNAL
When compiling with -fvisibility=hidden, we use this to explicitly mark internal-but-shared functions...
Definition infix_config.h:220
Internal data structures, function prototypes, and constants.
INFIX_INTERNAL 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 bool is_double(const infix_type *type)
A fast inline check to determine if an infix_type is a double.
Definition infix_internals.h:791
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_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
static bool is_float(const infix_type *type)
A fast inline check to determine if an infix_type is a float (32-bit).
Definition infix_internals.h:783
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
static void _print(printer_state *state, const char *fmt,...)
Definition signature.c:1183
static void _add_itanium_sub(printer_state *state, const void *component)
Definition signature.c:1214
static infix_struct_member * parse_aggregate_members(parser_state *state, char end_char, size_t *out_num_members)
Definition signature.c:243
static bool is_function_signature_ahead(const parser_state *state)
Definition signature.c:204
static void _infix_type_print_body_only_recursive(printer_state *state, const infix_type *type)
Definition signature.c:1799
c23_nodiscard infix_status _infix_type_print_body_only(char *buffer, size_t buffer_size, const infix_type *type, infix_print_dialect_t dialect)
An internal-only function to serialize a type's body without its registered name.
Definition signature.c:1939
INFIX_INTERNAL infix_type * parse_primitive(parser_state *state)
Definition signature.c:463
#define MAX_RECURSION_DEPTH
Definition signature.c:47
static bool _find_itanium_sub(printer_state *state, const void *component, size_t *index)
Definition signature.c:1204
INFIX_INTERNAL void skip_whitespace(parser_state *state)
Definition signature.c:70
static bool parse_size_t(parser_state *state, size_t *out_val)
Definition signature.c:88
c23_nodiscard infix_status _infix_parse_type_internal(infix_type **out_type, infix_arena_t **out_arena, const char *signature)
The internal core of the signature parser.
Definition signature.c:976
static void _print_itanium_sub(printer_state *state, size_t index)
Definition signature.c:1219
static infix_status parse_function_signature_details(parser_state *state, infix_type **out_ret_type, infix_function_argument **out_args, size_t *out_num_args, size_t *out_num_fixed_args)
Definition signature.c:818
INFIX_INTERNAL void _infix_set_parser_error(parser_state *state, infix_error_code_t code)
Definition signature.c:60
static bool consume_keyword(parser_state *state, const char *keyword)
Definition signature.c:152
static infix_type * parse_packed_struct(parser_state *state)
Definition signature.c:407
static infix_type * parse_aggregate(parser_state *state, char start_char, char end_char)
Definition signature.c:366
static const char * parse_identifier(parser_state *state)
Definition signature.c:118
INFIX_INTERNAL infix_type * parse_type(parser_state *state)
Definition signature.c:592
static void _infix_type_print_itanium_recursive(printer_state *state, const infix_type *type)
Definition signature.c:1454
static void _infix_type_print_signature_recursive(printer_state *state, const infix_type *type)
Definition signature.c:1257
static const char * parse_optional_name_prefix(parser_state *state)
Definition signature.c:174
INFIX_TLS const char * g_infix_last_signature_context
A thread-local pointer to the full signature string being parsed.
Definition error.c:99
static void _infix_type_print_msvc_recursive(printer_state *state, const infix_type *type)
Definition signature.c:1606
A single entry in the registry's hash table.
Definition infix_internals.h:174
Internal definition of a memory arena.
Definition infix_internals.h:143
Describes a single argument to a C function.
Definition infix.h:346
Internal definition of a named type registry.
Definition infix_internals.h:188
_infix_registry_entry_t ** buckets
Definition infix_internals.h:193
size_t num_buckets
Definition infix_internals.h:191
Describes a single member of a C struct or union.
Definition infix.h:334
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 * start
Definition infix_internals.h:201
infix_arena_t * arena
Definition infix_internals.h:202
int depth
Definition infix_internals.h:203
const char * p
Definition infix_internals.h:200
Definition signature.c:1164
char * p
Definition signature.c:1165
size_t remaining
Definition signature.c:1166
infix_status status
Definition signature.c:1167
const void * itanium_subs[64]
Definition signature.c:1169
size_t msvc_type_count
Definition signature.c:1173
const infix_type * msvc_types[10]
Definition signature.c:1172
size_t itanium_sub_count
Definition signature.c:1170