xref: /linux/tools/perf/util/demangle-rust-v0.c (revision 0939bd2fcf337243133b0271335a2838857c319f)
1*60869b22SIan Rogers // SPDX-License-Identifier: Apache-2.0 OR MIT
2*60869b22SIan Rogers 
3*60869b22SIan Rogers // The contents of this file come from the Rust rustc-demangle library, hosted
4*60869b22SIan Rogers // in the <https://github.com/rust-lang/rustc-demangle> repository, licensed
5*60869b22SIan Rogers // under "Apache-2.0 OR MIT". For copyright details, see
6*60869b22SIan Rogers // <https://github.com/rust-lang/rustc-demangle/blob/main/README.md>.
7*60869b22SIan Rogers // Please note that the file should be kept as close as possible to upstream.
8*60869b22SIan Rogers 
9*60869b22SIan Rogers // Code for demangling Rust symbols. This code is mostly
10*60869b22SIan Rogers // a line-by-line translation of the Rust code in `rustc-demangle`.
11*60869b22SIan Rogers 
12*60869b22SIan Rogers // you can find the latest version of this code in https://github.com/rust-lang/rustc-demangle
13*60869b22SIan Rogers 
14*60869b22SIan Rogers #include <stdint.h>
15*60869b22SIan Rogers #include <stddef.h>
16*60869b22SIan Rogers #include <string.h>
17*60869b22SIan Rogers #include <stdbool.h>
18*60869b22SIan Rogers #include <sys/param.h>
19*60869b22SIan Rogers #include <stdio.h>
20*60869b22SIan Rogers 
21*60869b22SIan Rogers #include "demangle-rust-v0.h"
22*60869b22SIan Rogers 
23*60869b22SIan Rogers #if defined(__GNUC__) || defined(__clang__)
24*60869b22SIan Rogers #define NODISCARD __attribute__((warn_unused_result))
25*60869b22SIan Rogers #else
26*60869b22SIan Rogers #define NODISCARD
27*60869b22SIan Rogers #endif
28*60869b22SIan Rogers 
29*60869b22SIan Rogers #define MAX_DEPTH 500
30*60869b22SIan Rogers 
31*60869b22SIan Rogers typedef enum {
32*60869b22SIan Rogers     DemangleOk,
33*60869b22SIan Rogers     DemangleInvalid,
34*60869b22SIan Rogers     DemangleRecursed,
35*60869b22SIan Rogers     DemangleBug,
36*60869b22SIan Rogers } demangle_status;
37*60869b22SIan Rogers 
38*60869b22SIan Rogers struct demangle_v0 {
39*60869b22SIan Rogers     const char *mangled;
40*60869b22SIan Rogers     size_t mangled_len;
41*60869b22SIan Rogers };
42*60869b22SIan Rogers 
43*60869b22SIan Rogers struct demangle_legacy {
44*60869b22SIan Rogers     const char *mangled;
45*60869b22SIan Rogers     size_t mangled_len;
46*60869b22SIan Rogers     size_t elements;
47*60869b22SIan Rogers };
48*60869b22SIan Rogers 
49*60869b22SIan Rogers // private version of memrchr to avoid _GNU_SOURCE
50*60869b22SIan Rogers static void *demangle_memrchr(const void *s, int c, size_t n) {
51*60869b22SIan Rogers     const uint8_t *s_ = s;
52*60869b22SIan Rogers     for (; n != 0; n--) {
53*60869b22SIan Rogers         if (s_[n-1] == c) {
54*60869b22SIan Rogers             return (void*)&s_[n-1];
55*60869b22SIan Rogers         }
56*60869b22SIan Rogers     }
57*60869b22SIan Rogers     return NULL;
58*60869b22SIan Rogers }
59*60869b22SIan Rogers 
60*60869b22SIan Rogers 
61*60869b22SIan Rogers static bool unicode_iscontrol(uint32_t ch) {
62*60869b22SIan Rogers     // this is *technically* a unicode table, but
63*60869b22SIan Rogers     // some unicode properties are simpler than you might think
64*60869b22SIan Rogers     return ch < 0x20 || (ch >= 0x7f && ch < 0xa0);
65*60869b22SIan Rogers }
66*60869b22SIan Rogers 
67*60869b22SIan Rogers // "good enough" tables, the only consequence is that when printing
68*60869b22SIan Rogers // *constant strings*, some characters are printed as `\u{abcd}` rather than themselves.
69*60869b22SIan Rogers //
70*60869b22SIan Rogers // I'm leaving these here to allow easily replacing them with actual
71*60869b22SIan Rogers // tables if desired.
72*60869b22SIan Rogers static bool unicode_isprint(uint32_t ch) {
73*60869b22SIan Rogers     if (ch < 0x20) {
74*60869b22SIan Rogers         return false;
75*60869b22SIan Rogers     }
76*60869b22SIan Rogers     if (ch < 0x7f) {
77*60869b22SIan Rogers         return true;
78*60869b22SIan Rogers     }
79*60869b22SIan Rogers     return false;
80*60869b22SIan Rogers }
81*60869b22SIan Rogers 
82*60869b22SIan Rogers static bool unicode_isgraphemextend(uint32_t ch) {
83*60869b22SIan Rogers     (void)ch;
84*60869b22SIan Rogers     return false;
85*60869b22SIan Rogers }
86*60869b22SIan Rogers 
87*60869b22SIan Rogers static bool str_isascii(const char *s, size_t s_len) {
88*60869b22SIan Rogers     for (size_t i = 0; i < s_len; i++) {
89*60869b22SIan Rogers         if (s[i] & 0x80) {
90*60869b22SIan Rogers             return false;
91*60869b22SIan Rogers         }
92*60869b22SIan Rogers     }
93*60869b22SIan Rogers 
94*60869b22SIan Rogers     return true;
95*60869b22SIan Rogers }
96*60869b22SIan Rogers 
97*60869b22SIan Rogers typedef enum {
98*60869b22SIan Rogers     PunycodeOk,
99*60869b22SIan Rogers     PunycodeError
100*60869b22SIan Rogers } punycode_status;
101*60869b22SIan Rogers 
102*60869b22SIan Rogers struct parser {
103*60869b22SIan Rogers     // the parser assumes that `sym` has a safe "terminating byte". It might be NUL,
104*60869b22SIan Rogers     // but it might also be something else if a symbol is "truncated".
105*60869b22SIan Rogers     const char *sym;
106*60869b22SIan Rogers     size_t sym_len;
107*60869b22SIan Rogers     size_t next;
108*60869b22SIan Rogers     uint32_t depth;
109*60869b22SIan Rogers };
110*60869b22SIan Rogers 
111*60869b22SIan Rogers struct printer {
112*60869b22SIan Rogers     demangle_status status; // if status == 0 parser is valid
113*60869b22SIan Rogers     struct parser parser;
114*60869b22SIan Rogers     char *out; // NULL for no output [in which case out_len is not decremented]
115*60869b22SIan Rogers     size_t out_len;
116*60869b22SIan Rogers     uint32_t bound_lifetime_depth;
117*60869b22SIan Rogers     bool alternate;
118*60869b22SIan Rogers };
119*60869b22SIan Rogers 
120*60869b22SIan Rogers static NODISCARD overflow_status printer_print_path(struct printer *printer, bool in_value);
121*60869b22SIan Rogers static NODISCARD overflow_status printer_print_type(struct printer *printer);
122*60869b22SIan Rogers static NODISCARD overflow_status printer_print_const(struct printer *printer, bool in_value);
123*60869b22SIan Rogers 
124*60869b22SIan Rogers static NODISCARD demangle_status try_parse_path(struct parser *parser) {
125*60869b22SIan Rogers     struct printer printer = {
126*60869b22SIan Rogers         DemangleOk,
127*60869b22SIan Rogers         *parser,
128*60869b22SIan Rogers         NULL,
129*60869b22SIan Rogers         SIZE_MAX,
130*60869b22SIan Rogers         0,
131*60869b22SIan Rogers         false
132*60869b22SIan Rogers     };
133*60869b22SIan Rogers     overflow_status ignore = printer_print_path(&printer, false); // can't fail since no output
134*60869b22SIan Rogers     (void)ignore;
135*60869b22SIan Rogers     *parser = printer.parser;
136*60869b22SIan Rogers     return printer.status;
137*60869b22SIan Rogers }
138*60869b22SIan Rogers 
139*60869b22SIan Rogers NODISCARD static demangle_status rust_demangle_v0_demangle(const char *s, size_t s_len, struct demangle_v0 *res, const char **rest) {
140*60869b22SIan Rogers     if (s_len > strlen(s)) {
141*60869b22SIan Rogers         // s_len only exists to shorten the string, this is not a buffer API
142*60869b22SIan Rogers         return DemangleInvalid;
143*60869b22SIan Rogers     }
144*60869b22SIan Rogers 
145*60869b22SIan Rogers     const char *inner;
146*60869b22SIan Rogers     size_t inner_len;
147*60869b22SIan Rogers     if (s_len >= 2 && !strncmp(s, "_R", strlen("_R"))) {
148*60869b22SIan Rogers         inner = s+2;
149*60869b22SIan Rogers         inner_len = s_len - 2;
150*60869b22SIan Rogers     } else if (s_len >= 1 && !strncmp(s, "R", strlen("R"))) {
151*60869b22SIan Rogers         // On Windows, dbghelp strips leading underscores, so we accept "R..."
152*60869b22SIan Rogers         // form too.
153*60869b22SIan Rogers         inner = s+1;
154*60869b22SIan Rogers         inner_len = s_len - 1;
155*60869b22SIan Rogers     } else if (s_len >= 3 && !strncmp(s, "__R", strlen("__R"))) {
156*60869b22SIan Rogers         // On OSX, symbols are prefixed with an extra _
157*60869b22SIan Rogers         inner = s+3;
158*60869b22SIan Rogers         inner_len = s_len - 3;
159*60869b22SIan Rogers     } else {
160*60869b22SIan Rogers         return DemangleInvalid;
161*60869b22SIan Rogers     }
162*60869b22SIan Rogers 
163*60869b22SIan Rogers     // Paths always start with uppercase characters.
164*60869b22SIan Rogers     if (*inner < 'A' || *inner > 'Z') {
165*60869b22SIan Rogers         return DemangleInvalid;
166*60869b22SIan Rogers     }
167*60869b22SIan Rogers 
168*60869b22SIan Rogers     if (!str_isascii(inner, inner_len)) {
169*60869b22SIan Rogers         return DemangleInvalid;
170*60869b22SIan Rogers     }
171*60869b22SIan Rogers 
172*60869b22SIan Rogers     struct parser parser = { inner, inner_len, 0, 0 };
173*60869b22SIan Rogers 
174*60869b22SIan Rogers     demangle_status status = try_parse_path(&parser);
175*60869b22SIan Rogers     if (status != DemangleOk) return status;
176*60869b22SIan Rogers     char next = parser.sym[parser.next];
177*60869b22SIan Rogers 
178*60869b22SIan Rogers     // Instantiating crate (paths always start with uppercase characters).
179*60869b22SIan Rogers     if (parser.next < parser.sym_len && next >= 'A' && next <= 'Z') {
180*60869b22SIan Rogers         status = try_parse_path(&parser);
181*60869b22SIan Rogers         if (status != DemangleOk) return status;
182*60869b22SIan Rogers     }
183*60869b22SIan Rogers 
184*60869b22SIan Rogers     res->mangled = inner;
185*60869b22SIan Rogers     res->mangled_len = inner_len;
186*60869b22SIan Rogers     if (rest) {
187*60869b22SIan Rogers         *rest = parser.sym + parser.next;
188*60869b22SIan Rogers     }
189*60869b22SIan Rogers 
190*60869b22SIan Rogers     return DemangleOk;
191*60869b22SIan Rogers }
192*60869b22SIan Rogers 
193*60869b22SIan Rogers // This might require `len` to be up to 3 characters bigger than the real output len in case of utf-8
194*60869b22SIan Rogers NODISCARD static overflow_status rust_demangle_v0_display_demangle(struct demangle_v0 res, char *out, size_t len, bool alternate) {
195*60869b22SIan Rogers     struct printer printer = {
196*60869b22SIan Rogers         DemangleOk,
197*60869b22SIan Rogers         {
198*60869b22SIan Rogers             res.mangled,
199*60869b22SIan Rogers             res.mangled_len,
200*60869b22SIan Rogers             0,
201*60869b22SIan Rogers             0
202*60869b22SIan Rogers         },
203*60869b22SIan Rogers         out,
204*60869b22SIan Rogers         len,
205*60869b22SIan Rogers         0,
206*60869b22SIan Rogers         alternate
207*60869b22SIan Rogers     };
208*60869b22SIan Rogers     if (printer_print_path(&printer, true) == OverflowOverflow) {
209*60869b22SIan Rogers         return OverflowOverflow;
210*60869b22SIan Rogers     }
211*60869b22SIan Rogers     if (printer.out_len < OVERFLOW_MARGIN) {
212*60869b22SIan Rogers         return OverflowOverflow;
213*60869b22SIan Rogers     }
214*60869b22SIan Rogers     *printer.out = '\0';
215*60869b22SIan Rogers     return OverflowOk;
216*60869b22SIan Rogers }
217*60869b22SIan Rogers 
218*60869b22SIan Rogers static size_t code_to_utf8(unsigned char *buffer, uint32_t code)
219*60869b22SIan Rogers {
220*60869b22SIan Rogers     if (code <= 0x7F) {
221*60869b22SIan Rogers         buffer[0] = code;
222*60869b22SIan Rogers         return 1;
223*60869b22SIan Rogers     }
224*60869b22SIan Rogers     if (code <= 0x7FF) {
225*60869b22SIan Rogers         buffer[0] = 0xC0 | (code >> 6);            /* 110xxxxx */
226*60869b22SIan Rogers         buffer[1] = 0x80 | (code & 0x3F);          /* 10xxxxxx */
227*60869b22SIan Rogers         return 2;
228*60869b22SIan Rogers     }
229*60869b22SIan Rogers     if (code <= 0xFFFF) {
230*60869b22SIan Rogers         buffer[0] = 0xE0 | (code >> 12);           /* 1110xxxx */
231*60869b22SIan Rogers         buffer[1] = 0x80 | ((code >> 6) & 0x3F);   /* 10xxxxxx */
232*60869b22SIan Rogers         buffer[2] = 0x80 | (code & 0x3F);          /* 10xxxxxx */
233*60869b22SIan Rogers         return 3;
234*60869b22SIan Rogers     }
235*60869b22SIan Rogers     if (code <= 0x10FFFF) {
236*60869b22SIan Rogers         buffer[0] = 0xF0 | (code >> 18);           /* 11110xxx */
237*60869b22SIan Rogers         buffer[1] = 0x80 | ((code >> 12) & 0x3F);  /* 10xxxxxx */
238*60869b22SIan Rogers         buffer[2] = 0x80 | ((code >> 6) & 0x3F);   /* 10xxxxxx */
239*60869b22SIan Rogers         buffer[3] = 0x80 | (code & 0x3F);          /* 10xxxxxx */
240*60869b22SIan Rogers         return 4;
241*60869b22SIan Rogers     }
242*60869b22SIan Rogers     return 0;
243*60869b22SIan Rogers }
244*60869b22SIan Rogers 
245*60869b22SIan Rogers 
246*60869b22SIan Rogers // return length of char at byte, or SIZE_MAX if invalid. buf should have 4 valid characters
247*60869b22SIan Rogers static NODISCARD size_t utf8_next_char(uint8_t *s, uint32_t *ch) {
248*60869b22SIan Rogers     uint8_t byte = *s;
249*60869b22SIan Rogers     // UTF8-1      = %x00-7F
250*60869b22SIan Rogers     // UTF8-2      = %xC2-DF UTF8-tail
251*60869b22SIan Rogers     // UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
252*60869b22SIan Rogers     //               %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
253*60869b22SIan Rogers     // UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
254*60869b22SIan Rogers     //               %xF4 %x80-8F 2( UTF8-tail )
255*60869b22SIan Rogers     if (byte < 0x80) {
256*60869b22SIan Rogers         *ch = byte;
257*60869b22SIan Rogers         return 1;
258*60869b22SIan Rogers     } else if (byte < 0xc2) {
259*60869b22SIan Rogers         return SIZE_MAX;
260*60869b22SIan Rogers     } else if (byte < 0xe0) {
261*60869b22SIan Rogers         if (s[1] >= 0x80 && s[1] < 0xc0) {
262*60869b22SIan Rogers             *ch = ((byte&0x1f)<<6) + (s[1] & 0x3f);
263*60869b22SIan Rogers             return 2;
264*60869b22SIan Rogers         }
265*60869b22SIan Rogers         return SIZE_MAX;
266*60869b22SIan Rogers     } if (byte < 0xf0) {
267*60869b22SIan Rogers         if (!(s[1] >= 0x80 && s[1] < 0xc0) || !(s[2] >= 0x80 && s[2] < 0xc0)) {
268*60869b22SIan Rogers             return SIZE_MAX; // basic validation
269*60869b22SIan Rogers         }
270*60869b22SIan Rogers         if (byte == 0xe0 && s[1] < 0xa0) {
271*60869b22SIan Rogers             return SIZE_MAX; // overshort
272*60869b22SIan Rogers         }
273*60869b22SIan Rogers         if (byte == 0xed && s[1] >= 0xa0) {
274*60869b22SIan Rogers             return SIZE_MAX; // surrogate
275*60869b22SIan Rogers         }
276*60869b22SIan Rogers         *ch = ((byte&0x0f)<<12) + ((s[1] & 0x3f)<<6) + (s[2] & 0x3f);
277*60869b22SIan Rogers         return 3;
278*60869b22SIan Rogers     } else if (byte < 0xf5) {
279*60869b22SIan Rogers         if (!(s[1] >= 0x80 && s[1] < 0xc0) || !(s[2] >= 0x80 && s[2] < 0xc0) || !(s[3] >= 0x80 && s[3] < 0xc0)) {
280*60869b22SIan Rogers             return SIZE_MAX; // basic validation
281*60869b22SIan Rogers         }
282*60869b22SIan Rogers         if (byte == 0xf0 && s[1] < 0x90) {
283*60869b22SIan Rogers             return SIZE_MAX; // overshort
284*60869b22SIan Rogers         }
285*60869b22SIan Rogers         if (byte == 0xf4 && s[1] >= 0x90) {
286*60869b22SIan Rogers             return SIZE_MAX; // over max
287*60869b22SIan Rogers         }
288*60869b22SIan Rogers         *ch = ((byte&0x07)<<18) + ((s[1] & 0x3f)<<12) + ((s[2] & 0x3f)<<6) + (s[3]&0x3f);
289*60869b22SIan Rogers         return 4;
290*60869b22SIan Rogers     } else {
291*60869b22SIan Rogers         return SIZE_MAX;
292*60869b22SIan Rogers     }
293*60869b22SIan Rogers }
294*60869b22SIan Rogers 
295*60869b22SIan Rogers static NODISCARD bool validate_char(uint32_t n) {
296*60869b22SIan Rogers     return ((n ^ 0xd800) - 0x800) < 0x110000 - 0x800;
297*60869b22SIan Rogers }
298*60869b22SIan Rogers 
299*60869b22SIan Rogers #define SMALL_PUNYCODE_LEN 128
300*60869b22SIan Rogers 
301*60869b22SIan Rogers static NODISCARD punycode_status punycode_decode(const char *start, size_t ascii_len, const char *punycode_start, size_t punycode_len, uint32_t (*out_)[SMALL_PUNYCODE_LEN], size_t *out_len) {
302*60869b22SIan Rogers     uint32_t *out = *out_;
303*60869b22SIan Rogers 
304*60869b22SIan Rogers     if (punycode_len == 0) {
305*60869b22SIan Rogers         return PunycodeError;
306*60869b22SIan Rogers     }
307*60869b22SIan Rogers 
308*60869b22SIan Rogers     if (ascii_len > SMALL_PUNYCODE_LEN) {
309*60869b22SIan Rogers         return PunycodeError;
310*60869b22SIan Rogers     }
311*60869b22SIan Rogers     for (size_t i = 0; i < ascii_len; i++) {
312*60869b22SIan Rogers         out[i] = start[i];
313*60869b22SIan Rogers     }
314*60869b22SIan Rogers     size_t len = ascii_len;
315*60869b22SIan Rogers 
316*60869b22SIan Rogers     size_t base = 36, t_min = 1, t_max = 26, skew = 38, damp = 700, bias = 72, i = 0, n = 0x80;
317*60869b22SIan Rogers     for (;;) {
318*60869b22SIan Rogers         size_t delta = 0, w = 1, k = 0;
319*60869b22SIan Rogers         for (;;) {
320*60869b22SIan Rogers             k += base;
321*60869b22SIan Rogers             size_t biased = k < bias ? 0 : k - bias;
322*60869b22SIan Rogers             size_t t = MIN(MAX(biased, t_min), t_max);
323*60869b22SIan Rogers             size_t d;
324*60869b22SIan Rogers             if (punycode_len == 0) {
325*60869b22SIan Rogers                 return PunycodeError;
326*60869b22SIan Rogers             }
327*60869b22SIan Rogers             char nx = *punycode_start++;
328*60869b22SIan Rogers             punycode_len--;
329*60869b22SIan Rogers             if ('a' <= nx && nx <= 'z') {
330*60869b22SIan Rogers                 d = nx - 'a';
331*60869b22SIan Rogers             } else if ('0' <= nx && nx <= '9') {
332*60869b22SIan Rogers                 d = 26 + (nx - '0');
333*60869b22SIan Rogers             } else {
334*60869b22SIan Rogers                 return PunycodeError;
335*60869b22SIan Rogers             }
336*60869b22SIan Rogers             if (w == 0 || d > SIZE_MAX / w || d*w > SIZE_MAX - delta) {
337*60869b22SIan Rogers                 return PunycodeError;
338*60869b22SIan Rogers             }
339*60869b22SIan Rogers             delta += d * w;
340*60869b22SIan Rogers             if (d < t) {
341*60869b22SIan Rogers                 break;
342*60869b22SIan Rogers             }
343*60869b22SIan Rogers             if (base < t || w == 0 || (base - t) > SIZE_MAX / w) {
344*60869b22SIan Rogers                 return PunycodeError;
345*60869b22SIan Rogers             }
346*60869b22SIan Rogers             w *= (base - t);
347*60869b22SIan Rogers         }
348*60869b22SIan Rogers 
349*60869b22SIan Rogers         len += 1;
350*60869b22SIan Rogers         if (i > SIZE_MAX - delta) {
351*60869b22SIan Rogers             return PunycodeError;
352*60869b22SIan Rogers         }
353*60869b22SIan Rogers         i += delta;
354*60869b22SIan Rogers         if (n > SIZE_MAX - i / len) {
355*60869b22SIan Rogers             return PunycodeError;
356*60869b22SIan Rogers         }
357*60869b22SIan Rogers         n += i / len;
358*60869b22SIan Rogers         i %= len;
359*60869b22SIan Rogers 
360*60869b22SIan Rogers         // char validation
361*60869b22SIan Rogers         if (n > UINT32_MAX || !validate_char((uint32_t)n)) {
362*60869b22SIan Rogers             return PunycodeError;
363*60869b22SIan Rogers         }
364*60869b22SIan Rogers 
365*60869b22SIan Rogers         // insert new character
366*60869b22SIan Rogers         if (len > SMALL_PUNYCODE_LEN) {
367*60869b22SIan Rogers             return PunycodeError;
368*60869b22SIan Rogers         }
369*60869b22SIan Rogers         memmove(out + i + 1, out + i, (len - i - 1) * sizeof(uint32_t));
370*60869b22SIan Rogers         out[i] = (uint32_t)n;
371*60869b22SIan Rogers 
372*60869b22SIan Rogers         // start i index at incremented position
373*60869b22SIan Rogers         i++;
374*60869b22SIan Rogers 
375*60869b22SIan Rogers         // If there are no more deltas, decoding is complete.
376*60869b22SIan Rogers         if (punycode_len == 0) {
377*60869b22SIan Rogers             *out_len = len;
378*60869b22SIan Rogers             return PunycodeOk;
379*60869b22SIan Rogers         }
380*60869b22SIan Rogers 
381*60869b22SIan Rogers         // Perform bias adaptation.
382*60869b22SIan Rogers         delta /= damp;
383*60869b22SIan Rogers         damp = 2;
384*60869b22SIan Rogers 
385*60869b22SIan Rogers         delta += delta / len;
386*60869b22SIan Rogers         k = 0;
387*60869b22SIan Rogers         while (delta > ((base - t_min) * t_max) / 2) {
388*60869b22SIan Rogers             delta /= base - t_min;
389*60869b22SIan Rogers             k += base;
390*60869b22SIan Rogers         }
391*60869b22SIan Rogers         bias = k + ((base - t_min + 1) * delta) / (delta + skew);
392*60869b22SIan Rogers     }
393*60869b22SIan Rogers }
394*60869b22SIan Rogers 
395*60869b22SIan Rogers struct ident {
396*60869b22SIan Rogers     const char *ascii_start;
397*60869b22SIan Rogers     size_t ascii_len;
398*60869b22SIan Rogers     const char *punycode_start;
399*60869b22SIan Rogers     size_t punycode_len;
400*60869b22SIan Rogers };
401*60869b22SIan Rogers 
402*60869b22SIan Rogers static NODISCARD overflow_status display_ident(const char *ascii_start, size_t ascii_len, const char *punycode_start, size_t punycode_len, uint8_t *out, size_t *out_len) {
403*60869b22SIan Rogers     uint32_t outbuf[SMALL_PUNYCODE_LEN];
404*60869b22SIan Rogers 
405*60869b22SIan Rogers     size_t wide_len;
406*60869b22SIan Rogers     size_t out_buflen = *out_len;
407*60869b22SIan Rogers 
408*60869b22SIan Rogers     if (punycode_len == 0) {
409*60869b22SIan Rogers         if (ascii_len > out_buflen) {
410*60869b22SIan Rogers             return OverflowOverflow;
411*60869b22SIan Rogers         }
412*60869b22SIan Rogers         memcpy(out, ascii_start, ascii_len);
413*60869b22SIan Rogers         *out_len = ascii_len;
414*60869b22SIan Rogers     } else if (punycode_decode(ascii_start, ascii_len, punycode_start, punycode_len, &outbuf, &wide_len) == PunycodeOk) {
415*60869b22SIan Rogers         size_t narrow_len = 0;
416*60869b22SIan Rogers         for (size_t i = 0; i < wide_len; i++) {
417*60869b22SIan Rogers             if (out_buflen - narrow_len < 4) {
418*60869b22SIan Rogers                 return OverflowOverflow;
419*60869b22SIan Rogers             }
420*60869b22SIan Rogers             unsigned char *pos = &out[narrow_len];
421*60869b22SIan Rogers             narrow_len += code_to_utf8(pos, outbuf[i]);
422*60869b22SIan Rogers         }
423*60869b22SIan Rogers         *out_len = narrow_len;
424*60869b22SIan Rogers     } else {
425*60869b22SIan Rogers         size_t narrow_len = 0;
426*60869b22SIan Rogers         if (out_buflen < strlen("punycode{")) {
427*60869b22SIan Rogers             return OverflowOverflow;
428*60869b22SIan Rogers         }
429*60869b22SIan Rogers         memcpy(out, "punycode{", strlen("punycode{"));
430*60869b22SIan Rogers         narrow_len = strlen("punycode{");
431*60869b22SIan Rogers         if (ascii_len > 0) {
432*60869b22SIan Rogers             if (out_buflen - narrow_len < ascii_len || out_buflen - narrow_len - ascii_len < 1) {
433*60869b22SIan Rogers                 return OverflowOverflow;
434*60869b22SIan Rogers             }
435*60869b22SIan Rogers             memcpy(out + narrow_len, ascii_start, ascii_len);
436*60869b22SIan Rogers             narrow_len += ascii_len;
437*60869b22SIan Rogers             out[narrow_len] = '-';
438*60869b22SIan Rogers             narrow_len++;
439*60869b22SIan Rogers         }
440*60869b22SIan Rogers         if (out_buflen - narrow_len < punycode_len || out_buflen - narrow_len - punycode_len < 1) {
441*60869b22SIan Rogers             return OverflowOverflow;
442*60869b22SIan Rogers         }
443*60869b22SIan Rogers         memcpy(out + narrow_len, punycode_start, punycode_len);
444*60869b22SIan Rogers         narrow_len += punycode_len;
445*60869b22SIan Rogers         out[narrow_len] = '}';
446*60869b22SIan Rogers         narrow_len++;
447*60869b22SIan Rogers         *out_len = narrow_len;
448*60869b22SIan Rogers     }
449*60869b22SIan Rogers 
450*60869b22SIan Rogers     return OverflowOk;
451*60869b22SIan Rogers }
452*60869b22SIan Rogers 
453*60869b22SIan Rogers static NODISCARD bool try_parse_uint(const char *buf, size_t len, uint64_t *result) {
454*60869b22SIan Rogers     size_t cur = 0;
455*60869b22SIan Rogers     for(;cur < len && buf[cur] == '0';cur++);
456*60869b22SIan Rogers     uint64_t result_val = 0;
457*60869b22SIan Rogers     if (len - cur > 16) return false;
458*60869b22SIan Rogers     for(;cur < len;cur++) {
459*60869b22SIan Rogers         char c = buf[cur];
460*60869b22SIan Rogers         result_val <<= 4;
461*60869b22SIan Rogers         if ('0' <= c && c <= '9') {
462*60869b22SIan Rogers             result_val += c - '0';
463*60869b22SIan Rogers         } else if ('a' <= c && c <= 'f') {
464*60869b22SIan Rogers             result_val += 10 + (c - 'a');
465*60869b22SIan Rogers         } else {
466*60869b22SIan Rogers             return false;
467*60869b22SIan Rogers         }
468*60869b22SIan Rogers     }
469*60869b22SIan Rogers     *result = result_val;
470*60869b22SIan Rogers     return true;
471*60869b22SIan Rogers }
472*60869b22SIan Rogers 
473*60869b22SIan Rogers static NODISCARD bool dinibble2int(const char *buf, uint8_t *result) {
474*60869b22SIan Rogers     uint8_t result_val = 0;
475*60869b22SIan Rogers     for (int i = 0; i < 2; i++) {
476*60869b22SIan Rogers         char c = buf[i];
477*60869b22SIan Rogers         result_val <<= 4;
478*60869b22SIan Rogers         if ('0' <= c && c <= '9') {
479*60869b22SIan Rogers             result_val += c - '0';
480*60869b22SIan Rogers         } else if ('a' <= c && c <= 'f') {
481*60869b22SIan Rogers             result_val += 10 + (c - 'a');
482*60869b22SIan Rogers         } else {
483*60869b22SIan Rogers             return false;
484*60869b22SIan Rogers         }
485*60869b22SIan Rogers     }
486*60869b22SIan Rogers     *result = result_val;
487*60869b22SIan Rogers     return true;
488*60869b22SIan Rogers }
489*60869b22SIan Rogers 
490*60869b22SIan Rogers 
491*60869b22SIan Rogers typedef enum {
492*60869b22SIan Rogers     NtsOk = 0,
493*60869b22SIan Rogers     NtsOverflow = 1,
494*60869b22SIan Rogers     NtsInvalid = 2
495*60869b22SIan Rogers } nibbles_to_string_status;
496*60869b22SIan Rogers 
497*60869b22SIan Rogers // '\u{10ffff}', +margin
498*60869b22SIan Rogers #define ESCAPED_SIZE 12
499*60869b22SIan Rogers 
500*60869b22SIan Rogers static NODISCARD size_t char_to_string(uint32_t ch, uint8_t quote, bool first, char (*buf)[ESCAPED_SIZE]) {
501*60869b22SIan Rogers     // encode the character
502*60869b22SIan Rogers     char *escaped_buf = *buf;
503*60869b22SIan Rogers     escaped_buf[0] = '\\';
504*60869b22SIan Rogers     size_t escaped_len = 2;
505*60869b22SIan Rogers     switch (ch) {
506*60869b22SIan Rogers         case '\0':
507*60869b22SIan Rogers         escaped_buf[1] = '0';
508*60869b22SIan Rogers         break;
509*60869b22SIan Rogers         case '\t':
510*60869b22SIan Rogers         escaped_buf[1] = 't';
511*60869b22SIan Rogers         break;
512*60869b22SIan Rogers         case '\r':
513*60869b22SIan Rogers         escaped_buf[1] = 'r';
514*60869b22SIan Rogers         break;
515*60869b22SIan Rogers         case '\n':
516*60869b22SIan Rogers         escaped_buf[1] = 'n';
517*60869b22SIan Rogers         break;
518*60869b22SIan Rogers         case '\\':
519*60869b22SIan Rogers         escaped_buf[1] = '\\';
520*60869b22SIan Rogers         break;
521*60869b22SIan Rogers         default:
522*60869b22SIan Rogers         if (ch == quote) {
523*60869b22SIan Rogers             escaped_buf[1] = ch;
524*60869b22SIan Rogers         } else if (!unicode_isprint(ch) || (first && unicode_isgraphemextend(ch))) {
525*60869b22SIan Rogers             int hexlen = snprintf(escaped_buf, ESCAPED_SIZE, "\\u{%x}", (unsigned int)ch);
526*60869b22SIan Rogers             if (hexlen < 0) {
527*60869b22SIan Rogers                 return 0; // (snprintf shouldn't fail!)
528*60869b22SIan Rogers             }
529*60869b22SIan Rogers             escaped_len = hexlen;
530*60869b22SIan Rogers         } else {
531*60869b22SIan Rogers             // printable character
532*60869b22SIan Rogers             escaped_buf[0] = ch;
533*60869b22SIan Rogers             escaped_len = 1;
534*60869b22SIan Rogers         }
535*60869b22SIan Rogers         break;
536*60869b22SIan Rogers     }
537*60869b22SIan Rogers 
538*60869b22SIan Rogers     return escaped_len;
539*60869b22SIan Rogers }
540*60869b22SIan Rogers 
541*60869b22SIan Rogers // convert nibbles to a single/double-quoted string
542*60869b22SIan Rogers static NODISCARD nibbles_to_string_status nibbles_to_string(const char *buf, size_t len, uint8_t *out, size_t *out_len) {
543*60869b22SIan Rogers     uint8_t quote = '"';
544*60869b22SIan Rogers     bool first = true;
545*60869b22SIan Rogers 
546*60869b22SIan Rogers     if ((len % 2) != 0) {
547*60869b22SIan Rogers         return NtsInvalid; // odd number of nibbles
548*60869b22SIan Rogers     }
549*60869b22SIan Rogers 
550*60869b22SIan Rogers     size_t cur_out_len = 0;
551*60869b22SIan Rogers 
552*60869b22SIan Rogers     // write starting quote
553*60869b22SIan Rogers     if (out != NULL) {
554*60869b22SIan Rogers         cur_out_len = *out_len;
555*60869b22SIan Rogers         if (cur_out_len == 0) {
556*60869b22SIan Rogers             return NtsOverflow;
557*60869b22SIan Rogers         }
558*60869b22SIan Rogers         *out++ = quote;
559*60869b22SIan Rogers         cur_out_len--;
560*60869b22SIan Rogers     }
561*60869b22SIan Rogers 
562*60869b22SIan Rogers     uint8_t conv_buf[4] = {0};
563*60869b22SIan Rogers     size_t conv_buf_len = 0;
564*60869b22SIan Rogers     while (len > 1 || conv_buf_len > 0) {
565*60869b22SIan Rogers         while (len > 1 && conv_buf_len < sizeof(conv_buf)) {
566*60869b22SIan Rogers             if (!dinibble2int(buf, &conv_buf[conv_buf_len])) {
567*60869b22SIan Rogers                 return NtsInvalid;
568*60869b22SIan Rogers             }
569*60869b22SIan Rogers             conv_buf_len++;
570*60869b22SIan Rogers             buf += 2;
571*60869b22SIan Rogers             len -= 2;
572*60869b22SIan Rogers         }
573*60869b22SIan Rogers 
574*60869b22SIan Rogers         // conv_buf is full here if possible, process 1 UTF-8 character
575*60869b22SIan Rogers         uint32_t ch = 0;
576*60869b22SIan Rogers         size_t consumed = utf8_next_char(conv_buf, &ch);
577*60869b22SIan Rogers         if (consumed > conv_buf_len) {
578*60869b22SIan Rogers             // either SIZE_MAX (invalid UTF-8) or finished input buffer and
579*60869b22SIan Rogers             // there are still bytes remaining, in both cases invalid
580*60869b22SIan Rogers             return NtsInvalid;
581*60869b22SIan Rogers         }
582*60869b22SIan Rogers 
583*60869b22SIan Rogers         // "consume" the character
584*60869b22SIan Rogers         memmove(conv_buf, conv_buf+consumed, conv_buf_len-consumed);
585*60869b22SIan Rogers         conv_buf_len -= consumed;
586*60869b22SIan Rogers 
587*60869b22SIan Rogers         char escaped_buf[ESCAPED_SIZE];
588*60869b22SIan Rogers         size_t escaped_len = char_to_string(ch, '"', first, &escaped_buf);
589*60869b22SIan Rogers         if (out != NULL) {
590*60869b22SIan Rogers             if (cur_out_len < escaped_len) {
591*60869b22SIan Rogers                 return NtsOverflow;
592*60869b22SIan Rogers             }
593*60869b22SIan Rogers             memcpy(out, escaped_buf, escaped_len);
594*60869b22SIan Rogers             out += escaped_len;
595*60869b22SIan Rogers             cur_out_len -= escaped_len;
596*60869b22SIan Rogers         }
597*60869b22SIan Rogers         first = false;
598*60869b22SIan Rogers     }
599*60869b22SIan Rogers 
600*60869b22SIan Rogers     // write ending quote
601*60869b22SIan Rogers     if (out != NULL) {
602*60869b22SIan Rogers         if (cur_out_len == 0) {
603*60869b22SIan Rogers             return NtsOverflow;
604*60869b22SIan Rogers         }
605*60869b22SIan Rogers         *out++ = quote;
606*60869b22SIan Rogers         cur_out_len--;
607*60869b22SIan Rogers         *out_len -= cur_out_len; // subtract remaining space to get used space
608*60869b22SIan Rogers     }
609*60869b22SIan Rogers 
610*60869b22SIan Rogers     return NtsOk;
611*60869b22SIan Rogers }
612*60869b22SIan Rogers 
613*60869b22SIan Rogers static const char* basic_type(uint8_t tag) {
614*60869b22SIan Rogers     switch(tag) {
615*60869b22SIan Rogers         case 'b':
616*60869b22SIan Rogers         return "bool";
617*60869b22SIan Rogers         case 'c':
618*60869b22SIan Rogers         return "char";
619*60869b22SIan Rogers         case 'e':
620*60869b22SIan Rogers         return "str";
621*60869b22SIan Rogers         case 'u':
622*60869b22SIan Rogers         return "()";
623*60869b22SIan Rogers         case 'a':
624*60869b22SIan Rogers         return "i8";
625*60869b22SIan Rogers         case 's':
626*60869b22SIan Rogers         return "i16";
627*60869b22SIan Rogers         case 'l':
628*60869b22SIan Rogers         return "i32";
629*60869b22SIan Rogers         case 'x':
630*60869b22SIan Rogers         return "i64";
631*60869b22SIan Rogers         case 'n':
632*60869b22SIan Rogers         return "i128";
633*60869b22SIan Rogers         case 'i':
634*60869b22SIan Rogers         return "isize";
635*60869b22SIan Rogers         case 'h':
636*60869b22SIan Rogers         return "u8";
637*60869b22SIan Rogers         case 't':
638*60869b22SIan Rogers         return "u16";
639*60869b22SIan Rogers         case 'm':
640*60869b22SIan Rogers         return "u32";
641*60869b22SIan Rogers         case 'y':
642*60869b22SIan Rogers         return "u64";
643*60869b22SIan Rogers         case 'o':
644*60869b22SIan Rogers         return "u128";
645*60869b22SIan Rogers         case 'j':
646*60869b22SIan Rogers         return "usize";
647*60869b22SIan Rogers         case 'f':
648*60869b22SIan Rogers         return "f32";
649*60869b22SIan Rogers         case 'd':
650*60869b22SIan Rogers         return "f64";
651*60869b22SIan Rogers         case 'z':
652*60869b22SIan Rogers         return "!";
653*60869b22SIan Rogers         case 'p':
654*60869b22SIan Rogers         return "_";
655*60869b22SIan Rogers         case 'v':
656*60869b22SIan Rogers         return "...";
657*60869b22SIan Rogers         default:
658*60869b22SIan Rogers         return NULL;
659*60869b22SIan Rogers     }
660*60869b22SIan Rogers }
661*60869b22SIan Rogers 
662*60869b22SIan Rogers static NODISCARD demangle_status parser_push_depth(struct parser *parser) {
663*60869b22SIan Rogers     parser->depth++;
664*60869b22SIan Rogers     if (parser->depth > MAX_DEPTH) {
665*60869b22SIan Rogers         return DemangleRecursed;
666*60869b22SIan Rogers     } else {
667*60869b22SIan Rogers         return DemangleOk;
668*60869b22SIan Rogers     }
669*60869b22SIan Rogers }
670*60869b22SIan Rogers 
671*60869b22SIan Rogers static demangle_status parser_pop_depth(struct parser *parser) {
672*60869b22SIan Rogers     parser->depth--;
673*60869b22SIan Rogers     return DemangleOk;
674*60869b22SIan Rogers }
675*60869b22SIan Rogers 
676*60869b22SIan Rogers static uint8_t parser_peek(struct parser const *parser) {
677*60869b22SIan Rogers     if (parser->next == parser->sym_len) {
678*60869b22SIan Rogers         return 0; // add a "pseudo nul terminator" to avoid peeking past the end of a symbol
679*60869b22SIan Rogers     } else {
680*60869b22SIan Rogers         return parser->sym[parser->next];
681*60869b22SIan Rogers     }
682*60869b22SIan Rogers }
683*60869b22SIan Rogers 
684*60869b22SIan Rogers static bool parser_eat(struct parser *parser, uint8_t ch) {
685*60869b22SIan Rogers     if (parser_peek(parser) == ch) {
686*60869b22SIan Rogers         if (ch != 0) { // safety: make sure we don't skip past the NUL terminator
687*60869b22SIan Rogers             parser->next++;
688*60869b22SIan Rogers         }
689*60869b22SIan Rogers         return true;
690*60869b22SIan Rogers     } else {
691*60869b22SIan Rogers         return false;
692*60869b22SIan Rogers     }
693*60869b22SIan Rogers }
694*60869b22SIan Rogers 
695*60869b22SIan Rogers static uint8_t parser_next(struct parser *parser) {
696*60869b22SIan Rogers     // don't advance after end of input, and return an imaginary NUL terminator
697*60869b22SIan Rogers     if (parser->next == parser->sym_len) {
698*60869b22SIan Rogers         return 0;
699*60869b22SIan Rogers     } else {
700*60869b22SIan Rogers         return parser->sym[parser->next++];
701*60869b22SIan Rogers     }
702*60869b22SIan Rogers }
703*60869b22SIan Rogers 
704*60869b22SIan Rogers static NODISCARD demangle_status parser_ch(struct parser *parser, uint8_t *next) {
705*60869b22SIan Rogers     // don't advance after end of input
706*60869b22SIan Rogers     if (parser->next == parser->sym_len) {
707*60869b22SIan Rogers         return DemangleInvalid;
708*60869b22SIan Rogers     } else {
709*60869b22SIan Rogers         *next = parser->sym[parser->next++];
710*60869b22SIan Rogers         return DemangleOk;
711*60869b22SIan Rogers     }
712*60869b22SIan Rogers }
713*60869b22SIan Rogers 
714*60869b22SIan Rogers struct buf {
715*60869b22SIan Rogers     const char *start;
716*60869b22SIan Rogers     size_t len;
717*60869b22SIan Rogers };
718*60869b22SIan Rogers 
719*60869b22SIan Rogers static NODISCARD demangle_status parser_hex_nibbles(struct parser *parser, struct buf *buf) {
720*60869b22SIan Rogers     size_t start = parser->next;
721*60869b22SIan Rogers     for (;;) {
722*60869b22SIan Rogers         uint8_t ch = parser_next(parser);
723*60869b22SIan Rogers         if (ch == '_') {
724*60869b22SIan Rogers             break;
725*60869b22SIan Rogers         }
726*60869b22SIan Rogers         if (!(('0' <= ch && ch <= '9') || ('a' <= ch && ch <= 'f'))) {
727*60869b22SIan Rogers             return DemangleInvalid;
728*60869b22SIan Rogers         }
729*60869b22SIan Rogers     }
730*60869b22SIan Rogers     buf->start = parser->sym + start;
731*60869b22SIan Rogers     buf->len = parser->next - start - 1; // skip final _
732*60869b22SIan Rogers     return DemangleOk;
733*60869b22SIan Rogers }
734*60869b22SIan Rogers 
735*60869b22SIan Rogers static NODISCARD demangle_status parser_digit_10(struct parser *parser, uint8_t *out) {
736*60869b22SIan Rogers     uint8_t ch = parser_peek(parser);
737*60869b22SIan Rogers     if ('0' <= ch && ch <= '9') {
738*60869b22SIan Rogers         *out = ch - '0';
739*60869b22SIan Rogers         parser->next++;
740*60869b22SIan Rogers         return DemangleOk;
741*60869b22SIan Rogers     } else {
742*60869b22SIan Rogers         return DemangleInvalid;
743*60869b22SIan Rogers     }
744*60869b22SIan Rogers }
745*60869b22SIan Rogers 
746*60869b22SIan Rogers static NODISCARD demangle_status parser_digit_62(struct parser *parser, uint64_t *out) {
747*60869b22SIan Rogers     uint8_t ch = parser_peek(parser);
748*60869b22SIan Rogers     if ('0' <= ch && ch <= '9') {
749*60869b22SIan Rogers         *out = ch - '0';
750*60869b22SIan Rogers         parser->next++;
751*60869b22SIan Rogers         return DemangleOk;
752*60869b22SIan Rogers     } else if ('a' <= ch && ch <= 'z') {
753*60869b22SIan Rogers         *out = 10 + (ch - 'a');
754*60869b22SIan Rogers         parser->next++;
755*60869b22SIan Rogers         return DemangleOk;
756*60869b22SIan Rogers     } else if ('A' <= ch && ch <= 'Z') {
757*60869b22SIan Rogers         *out = 10 + 26 + (ch - 'A');
758*60869b22SIan Rogers         parser->next++;
759*60869b22SIan Rogers         return DemangleOk;
760*60869b22SIan Rogers     } else {
761*60869b22SIan Rogers         return DemangleInvalid;
762*60869b22SIan Rogers     }
763*60869b22SIan Rogers }
764*60869b22SIan Rogers 
765*60869b22SIan Rogers static NODISCARD demangle_status parser_integer_62(struct parser *parser, uint64_t *out) {
766*60869b22SIan Rogers     if (parser_eat(parser, '_')) {
767*60869b22SIan Rogers         *out = 0;
768*60869b22SIan Rogers         return DemangleOk;
769*60869b22SIan Rogers     }
770*60869b22SIan Rogers 
771*60869b22SIan Rogers     uint64_t x = 0;
772*60869b22SIan Rogers     demangle_status status;
773*60869b22SIan Rogers     while (!parser_eat(parser, '_')) {
774*60869b22SIan Rogers         uint64_t d;
775*60869b22SIan Rogers         if ((status = parser_digit_62(parser, &d)) != DemangleOk) {
776*60869b22SIan Rogers             return status;
777*60869b22SIan Rogers         }
778*60869b22SIan Rogers         if (x > UINT64_MAX / 62) {
779*60869b22SIan Rogers             return DemangleInvalid;
780*60869b22SIan Rogers         }
781*60869b22SIan Rogers         x *= 62;
782*60869b22SIan Rogers         if (x > UINT64_MAX - d) {
783*60869b22SIan Rogers             return DemangleInvalid;
784*60869b22SIan Rogers         }
785*60869b22SIan Rogers         x += d;
786*60869b22SIan Rogers     }
787*60869b22SIan Rogers     if (x == UINT64_MAX) {
788*60869b22SIan Rogers         return DemangleInvalid;
789*60869b22SIan Rogers     }
790*60869b22SIan Rogers     *out = x + 1;
791*60869b22SIan Rogers     return DemangleOk;
792*60869b22SIan Rogers }
793*60869b22SIan Rogers 
794*60869b22SIan Rogers static NODISCARD demangle_status parser_opt_integer_62(struct parser *parser, uint8_t tag, uint64_t *out) {
795*60869b22SIan Rogers     if (!parser_eat(parser, tag)) {
796*60869b22SIan Rogers         *out = 0;
797*60869b22SIan Rogers         return DemangleOk;
798*60869b22SIan Rogers     }
799*60869b22SIan Rogers 
800*60869b22SIan Rogers     demangle_status status;
801*60869b22SIan Rogers     if ((status = parser_integer_62(parser, out)) != DemangleOk) {
802*60869b22SIan Rogers         return status;
803*60869b22SIan Rogers     }
804*60869b22SIan Rogers     if (*out == UINT64_MAX) {
805*60869b22SIan Rogers         return DemangleInvalid;
806*60869b22SIan Rogers     }
807*60869b22SIan Rogers     *out = *out + 1;
808*60869b22SIan Rogers     return DemangleOk;
809*60869b22SIan Rogers }
810*60869b22SIan Rogers 
811*60869b22SIan Rogers static NODISCARD demangle_status parser_disambiguator(struct parser *parser, uint64_t *out) {
812*60869b22SIan Rogers     return parser_opt_integer_62(parser, 's', out);
813*60869b22SIan Rogers }
814*60869b22SIan Rogers 
815*60869b22SIan Rogers typedef uint8_t parser_namespace_type;
816*60869b22SIan Rogers 
817*60869b22SIan Rogers static NODISCARD demangle_status parser_namespace(struct parser *parser, parser_namespace_type *out) {
818*60869b22SIan Rogers     uint8_t next = parser_next(parser);
819*60869b22SIan Rogers     if ('A' <= next && next <= 'Z') {
820*60869b22SIan Rogers         *out = next;
821*60869b22SIan Rogers         return DemangleOk;
822*60869b22SIan Rogers     } else if ('a' <= next && next <= 'z') {
823*60869b22SIan Rogers         *out = 0;
824*60869b22SIan Rogers         return DemangleOk;
825*60869b22SIan Rogers     } else {
826*60869b22SIan Rogers         return DemangleInvalid;
827*60869b22SIan Rogers     }
828*60869b22SIan Rogers }
829*60869b22SIan Rogers 
830*60869b22SIan Rogers static NODISCARD demangle_status parser_backref(struct parser *parser, struct parser *out) {
831*60869b22SIan Rogers     size_t start = parser->next;
832*60869b22SIan Rogers     if (start == 0) {
833*60869b22SIan Rogers         return DemangleBug;
834*60869b22SIan Rogers     }
835*60869b22SIan Rogers     size_t s_start = start - 1;
836*60869b22SIan Rogers     uint64_t i;
837*60869b22SIan Rogers     demangle_status status = parser_integer_62(parser, &i);
838*60869b22SIan Rogers     if (status != DemangleOk) {
839*60869b22SIan Rogers         return status;
840*60869b22SIan Rogers     }
841*60869b22SIan Rogers     if (i >= s_start) {
842*60869b22SIan Rogers         return DemangleInvalid;
843*60869b22SIan Rogers     }
844*60869b22SIan Rogers     struct parser res = {
845*60869b22SIan Rogers         .sym = parser->sym,
846*60869b22SIan Rogers         .sym_len = parser->sym_len,
847*60869b22SIan Rogers         .next = (size_t)i,
848*60869b22SIan Rogers         .depth = parser->depth
849*60869b22SIan Rogers     };
850*60869b22SIan Rogers     status = parser_push_depth(&res);
851*60869b22SIan Rogers     if (status != DemangleOk) {
852*60869b22SIan Rogers         return status;
853*60869b22SIan Rogers     }
854*60869b22SIan Rogers     *out = res;
855*60869b22SIan Rogers     return DemangleOk;
856*60869b22SIan Rogers }
857*60869b22SIan Rogers 
858*60869b22SIan Rogers static NODISCARD demangle_status parser_ident(struct parser *parser, struct ident *out) {
859*60869b22SIan Rogers     bool is_punycode = parser_eat(parser, 'u');
860*60869b22SIan Rogers     size_t len;
861*60869b22SIan Rogers     uint8_t d;
862*60869b22SIan Rogers     demangle_status status = parser_digit_10(parser, &d);
863*60869b22SIan Rogers     len = d;
864*60869b22SIan Rogers     if (status != DemangleOk) {
865*60869b22SIan Rogers         return status;
866*60869b22SIan Rogers     }
867*60869b22SIan Rogers     if (len) {
868*60869b22SIan Rogers         for (;;) {
869*60869b22SIan Rogers             status = parser_digit_10(parser, &d);
870*60869b22SIan Rogers             if (status != DemangleOk) {
871*60869b22SIan Rogers                 break;
872*60869b22SIan Rogers             }
873*60869b22SIan Rogers             if (len > SIZE_MAX / 10) {
874*60869b22SIan Rogers                 return DemangleInvalid;
875*60869b22SIan Rogers             }
876*60869b22SIan Rogers             len *= 10;
877*60869b22SIan Rogers             if (len > SIZE_MAX - d) {
878*60869b22SIan Rogers                 return DemangleInvalid;
879*60869b22SIan Rogers             }
880*60869b22SIan Rogers             len += d;
881*60869b22SIan Rogers         }
882*60869b22SIan Rogers     }
883*60869b22SIan Rogers 
884*60869b22SIan Rogers     // Skip past the optional `_` separator.
885*60869b22SIan Rogers     parser_eat(parser, '_');
886*60869b22SIan Rogers 
887*60869b22SIan Rogers     size_t start = parser->next;
888*60869b22SIan Rogers     if (parser->sym_len - parser->next < len) {
889*60869b22SIan Rogers         return DemangleInvalid;
890*60869b22SIan Rogers     }
891*60869b22SIan Rogers     parser->next += len;
892*60869b22SIan Rogers 
893*60869b22SIan Rogers     const char *ident = &parser->sym[start];
894*60869b22SIan Rogers 
895*60869b22SIan Rogers     if (is_punycode) {
896*60869b22SIan Rogers         const char *underscore = demangle_memrchr(ident, '_', (size_t)len);
897*60869b22SIan Rogers         if (underscore == NULL) {
898*60869b22SIan Rogers             *out = (struct ident){
899*60869b22SIan Rogers                 .ascii_start="",
900*60869b22SIan Rogers                 .ascii_len=0,
901*60869b22SIan Rogers                 .punycode_start=ident,
902*60869b22SIan Rogers                 .punycode_len=len
903*60869b22SIan Rogers             };
904*60869b22SIan Rogers         } else {
905*60869b22SIan Rogers             size_t ascii_len = underscore - ident;
906*60869b22SIan Rogers             // ascii_len <= len - 1 since `_` is in the first len bytes
907*60869b22SIan Rogers             size_t punycode_len = len - 1 - ascii_len;
908*60869b22SIan Rogers             *out = (struct ident){
909*60869b22SIan Rogers                 .ascii_start=ident,
910*60869b22SIan Rogers                 .ascii_len=ascii_len,
911*60869b22SIan Rogers                 .punycode_start=underscore + 1,
912*60869b22SIan Rogers                 .punycode_len=punycode_len
913*60869b22SIan Rogers             };
914*60869b22SIan Rogers         }
915*60869b22SIan Rogers         if (out->punycode_len == 0) {
916*60869b22SIan Rogers             return DemangleInvalid;
917*60869b22SIan Rogers         }
918*60869b22SIan Rogers         return DemangleOk;
919*60869b22SIan Rogers     } else {
920*60869b22SIan Rogers         *out = (struct ident) {
921*60869b22SIan Rogers             .ascii_start=ident,
922*60869b22SIan Rogers             .ascii_len=(size_t)len,
923*60869b22SIan Rogers             .punycode_start="",
924*60869b22SIan Rogers             .punycode_len=0,
925*60869b22SIan Rogers         };
926*60869b22SIan Rogers         return DemangleOk;
927*60869b22SIan Rogers     }
928*60869b22SIan Rogers }
929*60869b22SIan Rogers 
930*60869b22SIan Rogers #define INVALID_SYNTAX "{invalid syntax}"
931*60869b22SIan Rogers 
932*60869b22SIan Rogers static const char *demangle_error_message(demangle_status status) {
933*60869b22SIan Rogers     switch (status) {
934*60869b22SIan Rogers         case DemangleInvalid:
935*60869b22SIan Rogers         return INVALID_SYNTAX;
936*60869b22SIan Rogers         case DemangleBug:
937*60869b22SIan Rogers         return "{bug}";
938*60869b22SIan Rogers         case DemangleRecursed:
939*60869b22SIan Rogers         return "{recursion limit reached}";
940*60869b22SIan Rogers         default:
941*60869b22SIan Rogers         return "{unknown error}";
942*60869b22SIan Rogers     }
943*60869b22SIan Rogers }
944*60869b22SIan Rogers 
945*60869b22SIan Rogers #define PRINT(print_fn) \
946*60869b22SIan Rogers  do { \
947*60869b22SIan Rogers    if ((print_fn) == OverflowOverflow) { \
948*60869b22SIan Rogers     return OverflowOverflow; \
949*60869b22SIan Rogers    } \
950*60869b22SIan Rogers  } while(0)
951*60869b22SIan Rogers 
952*60869b22SIan Rogers #define PRINT_CH(printer, s) PRINT(printer_print_ch((printer), (s)))
953*60869b22SIan Rogers #define PRINT_STR(printer, s) PRINT(printer_print_str((printer), (s)))
954*60869b22SIan Rogers #define PRINT_U64(printer, s) PRINT(printer_print_u64((printer), (s)))
955*60869b22SIan Rogers #define PRINT_IDENT(printer, s) PRINT(printer_print_ident((printer), (s)))
956*60869b22SIan Rogers 
957*60869b22SIan Rogers #define INVALID(printer) \
958*60869b22SIan Rogers   do { \
959*60869b22SIan Rogers     PRINT_STR((printer), INVALID_SYNTAX); \
960*60869b22SIan Rogers     (printer)->status = DemangleInvalid; \
961*60869b22SIan Rogers     return OverflowOk; \
962*60869b22SIan Rogers   } while(0)
963*60869b22SIan Rogers 
964*60869b22SIan Rogers #define PARSE(printer, method, ...) \
965*60869b22SIan Rogers   do { \
966*60869b22SIan Rogers     if ((printer)->status != DemangleOk) { \
967*60869b22SIan Rogers       PRINT_STR((printer), "?"); \
968*60869b22SIan Rogers       return OverflowOk; \
969*60869b22SIan Rogers     } else { \
970*60869b22SIan Rogers       demangle_status _parse_status = method(&(printer)->parser, ## __VA_ARGS__); \
971*60869b22SIan Rogers       if (_parse_status != DemangleOk) { \
972*60869b22SIan Rogers         PRINT_STR((printer), demangle_error_message(_parse_status)); \
973*60869b22SIan Rogers         (printer)->status = _parse_status; \
974*60869b22SIan Rogers         return OverflowOk; \
975*60869b22SIan Rogers       } \
976*60869b22SIan Rogers     } \
977*60869b22SIan Rogers   } while(0)
978*60869b22SIan Rogers 
979*60869b22SIan Rogers #define PRINT_SEP_LIST(printer, body, sep) \
980*60869b22SIan Rogers   do { \
981*60869b22SIan Rogers     size_t _sep_list_i; \
982*60869b22SIan Rogers     PRINT_SEP_LIST_COUNT(printer, _sep_list_i, body, sep); \
983*60869b22SIan Rogers   } while(0)
984*60869b22SIan Rogers 
985*60869b22SIan Rogers #define PRINT_SEP_LIST_COUNT(printer, count, body, sep) \
986*60869b22SIan Rogers   do { \
987*60869b22SIan Rogers     count = 0; \
988*60869b22SIan Rogers     while ((printer)->status == DemangleOk && !printer_eat((printer), 'E')) { \
989*60869b22SIan Rogers       if (count > 0) { PRINT_STR(printer, sep); } \
990*60869b22SIan Rogers       body; \
991*60869b22SIan Rogers       count++; \
992*60869b22SIan Rogers     } \
993*60869b22SIan Rogers   } while(0)
994*60869b22SIan Rogers 
995*60869b22SIan Rogers static bool printer_eat(struct printer *printer, uint8_t b) {
996*60869b22SIan Rogers     if (printer->status != DemangleOk) {
997*60869b22SIan Rogers         return false;
998*60869b22SIan Rogers     }
999*60869b22SIan Rogers 
1000*60869b22SIan Rogers     return parser_eat(&printer->parser, b);
1001*60869b22SIan Rogers }
1002*60869b22SIan Rogers 
1003*60869b22SIan Rogers static void printer_pop_depth(struct printer *printer) {
1004*60869b22SIan Rogers     if (printer->status == DemangleOk) {
1005*60869b22SIan Rogers         parser_pop_depth(&printer->parser);
1006*60869b22SIan Rogers     }
1007*60869b22SIan Rogers }
1008*60869b22SIan Rogers 
1009*60869b22SIan Rogers static NODISCARD overflow_status printer_print_buf(struct printer *printer, const char *start, size_t len) {
1010*60869b22SIan Rogers     if (printer->out == NULL) {
1011*60869b22SIan Rogers         return OverflowOk;
1012*60869b22SIan Rogers     }
1013*60869b22SIan Rogers     if (printer->out_len < len) {
1014*60869b22SIan Rogers         return OverflowOverflow;
1015*60869b22SIan Rogers     }
1016*60869b22SIan Rogers 
1017*60869b22SIan Rogers     memcpy(printer->out, start, len);
1018*60869b22SIan Rogers     printer->out += len;
1019*60869b22SIan Rogers     printer->out_len -= len;
1020*60869b22SIan Rogers     return OverflowOk;
1021*60869b22SIan Rogers }
1022*60869b22SIan Rogers 
1023*60869b22SIan Rogers static NODISCARD overflow_status printer_print_str(struct printer *printer, const char *buf) {
1024*60869b22SIan Rogers     return printer_print_buf(printer, buf, strlen(buf));
1025*60869b22SIan Rogers }
1026*60869b22SIan Rogers 
1027*60869b22SIan Rogers static NODISCARD overflow_status printer_print_ch(struct printer *printer, char ch) {
1028*60869b22SIan Rogers     return printer_print_buf(printer, &ch, 1);
1029*60869b22SIan Rogers }
1030*60869b22SIan Rogers 
1031*60869b22SIan Rogers static NODISCARD overflow_status printer_print_u64(struct printer *printer, uint64_t n) {
1032*60869b22SIan Rogers     char buf[32] = {0};
1033*60869b22SIan Rogers     sprintf(buf, "%llu", (unsigned long long)n); // printing uint64 uses 21 < 32 chars
1034*60869b22SIan Rogers     return printer_print_str(printer, buf);
1035*60869b22SIan Rogers }
1036*60869b22SIan Rogers 
1037*60869b22SIan Rogers static NODISCARD overflow_status printer_print_ident(struct printer *printer, struct ident *ident) {
1038*60869b22SIan Rogers     if (printer->out == NULL) {
1039*60869b22SIan Rogers         return OverflowOk;
1040*60869b22SIan Rogers     }
1041*60869b22SIan Rogers 
1042*60869b22SIan Rogers     size_t out_len = printer->out_len;
1043*60869b22SIan Rogers     overflow_status status;
1044*60869b22SIan Rogers     if ((status = display_ident(ident->ascii_start, ident->ascii_len, ident->punycode_start, ident->punycode_len, (uint8_t*)printer->out, &out_len)) != OverflowOk) {
1045*60869b22SIan Rogers         return status;
1046*60869b22SIan Rogers     }
1047*60869b22SIan Rogers     printer->out += out_len;
1048*60869b22SIan Rogers     printer->out_len -= out_len;
1049*60869b22SIan Rogers     return OverflowOk;
1050*60869b22SIan Rogers }
1051*60869b22SIan Rogers 
1052*60869b22SIan Rogers typedef overflow_status (*printer_fn)(struct printer *printer);
1053*60869b22SIan Rogers typedef overflow_status (*backref_fn)(struct printer *printer, bool *arg);
1054*60869b22SIan Rogers 
1055*60869b22SIan Rogers static NODISCARD overflow_status printer_print_backref(struct printer *printer, backref_fn func, bool *arg) {
1056*60869b22SIan Rogers     struct parser backref;
1057*60869b22SIan Rogers     PARSE(printer, parser_backref, &backref);
1058*60869b22SIan Rogers 
1059*60869b22SIan Rogers     if (printer->out == NULL) {
1060*60869b22SIan Rogers         return OverflowOk;
1061*60869b22SIan Rogers     }
1062*60869b22SIan Rogers 
1063*60869b22SIan Rogers     struct parser orig_parser = printer->parser;
1064*60869b22SIan Rogers     demangle_status orig_status = printer->status; // fixme not sure this is needed match for Ok on the Rust side
1065*60869b22SIan Rogers     printer->parser = backref;
1066*60869b22SIan Rogers     printer->status = DemangleOk;
1067*60869b22SIan Rogers     overflow_status status = func(printer, arg);
1068*60869b22SIan Rogers     printer->parser = orig_parser;
1069*60869b22SIan Rogers     printer->status = orig_status;
1070*60869b22SIan Rogers 
1071*60869b22SIan Rogers     return status;
1072*60869b22SIan Rogers }
1073*60869b22SIan Rogers 
1074*60869b22SIan Rogers static NODISCARD overflow_status printer_print_lifetime_from_index(struct printer *printer, uint64_t lt) {
1075*60869b22SIan Rogers     // Bound lifetimes aren't tracked when skipping printing.
1076*60869b22SIan Rogers     if (printer->out == NULL) {
1077*60869b22SIan Rogers         return OverflowOk;
1078*60869b22SIan Rogers     }
1079*60869b22SIan Rogers 
1080*60869b22SIan Rogers     PRINT_STR(printer, "'");
1081*60869b22SIan Rogers     if (lt == 0) {
1082*60869b22SIan Rogers         PRINT_STR(printer, "_");
1083*60869b22SIan Rogers         return OverflowOk;
1084*60869b22SIan Rogers     }
1085*60869b22SIan Rogers 
1086*60869b22SIan Rogers     if (printer->bound_lifetime_depth < lt) {
1087*60869b22SIan Rogers         INVALID(printer);
1088*60869b22SIan Rogers     } else {
1089*60869b22SIan Rogers         uint64_t depth = printer->bound_lifetime_depth - lt;
1090*60869b22SIan Rogers         if (depth < 26) {
1091*60869b22SIan Rogers             PRINT_CH(printer, 'a' + depth);
1092*60869b22SIan Rogers         } else {
1093*60869b22SIan Rogers             PRINT_STR(printer, "_");
1094*60869b22SIan Rogers             PRINT_U64(printer, depth);
1095*60869b22SIan Rogers         }
1096*60869b22SIan Rogers 
1097*60869b22SIan Rogers         return OverflowOk;
1098*60869b22SIan Rogers     }
1099*60869b22SIan Rogers }
1100*60869b22SIan Rogers 
1101*60869b22SIan Rogers static NODISCARD overflow_status printer_in_binder(struct printer *printer, printer_fn func) {
1102*60869b22SIan Rogers     uint64_t bound_lifetimes;
1103*60869b22SIan Rogers     PARSE(printer, parser_opt_integer_62, 'G', &bound_lifetimes);
1104*60869b22SIan Rogers 
1105*60869b22SIan Rogers     // Don't track bound lifetimes when skipping printing.
1106*60869b22SIan Rogers     if (printer->out == NULL) {
1107*60869b22SIan Rogers         return func(printer);
1108*60869b22SIan Rogers     }
1109*60869b22SIan Rogers 
1110*60869b22SIan Rogers     if (bound_lifetimes > 0) {
1111*60869b22SIan Rogers         PRINT_STR(printer, "for<");
1112*60869b22SIan Rogers         for (uint64_t i = 0; i < bound_lifetimes; i++) {
1113*60869b22SIan Rogers             if (i > 0) {
1114*60869b22SIan Rogers                 PRINT_STR(printer, ", ");
1115*60869b22SIan Rogers             }
1116*60869b22SIan Rogers             printer->bound_lifetime_depth++;
1117*60869b22SIan Rogers             PRINT(printer_print_lifetime_from_index(printer, 1));
1118*60869b22SIan Rogers         }
1119*60869b22SIan Rogers         PRINT_STR(printer, "> ");
1120*60869b22SIan Rogers     }
1121*60869b22SIan Rogers 
1122*60869b22SIan Rogers     overflow_status r = func(printer);
1123*60869b22SIan Rogers     printer->bound_lifetime_depth -= bound_lifetimes;
1124*60869b22SIan Rogers 
1125*60869b22SIan Rogers     return r;
1126*60869b22SIan Rogers }
1127*60869b22SIan Rogers 
1128*60869b22SIan Rogers static NODISCARD overflow_status printer_print_generic_arg(struct printer *printer) {
1129*60869b22SIan Rogers     if (printer_eat(printer, 'L')) {
1130*60869b22SIan Rogers         uint64_t lt;
1131*60869b22SIan Rogers         PARSE(printer, parser_integer_62, &lt);
1132*60869b22SIan Rogers         return printer_print_lifetime_from_index(printer, lt);
1133*60869b22SIan Rogers     } else if (printer_eat(printer, 'K')) {
1134*60869b22SIan Rogers         return printer_print_const(printer, false);
1135*60869b22SIan Rogers     } else {
1136*60869b22SIan Rogers         return printer_print_type(printer);
1137*60869b22SIan Rogers     }
1138*60869b22SIan Rogers }
1139*60869b22SIan Rogers 
1140*60869b22SIan Rogers static NODISCARD overflow_status printer_print_generic_args(struct printer *printer) {
1141*60869b22SIan Rogers     PRINT_STR(printer, "<");
1142*60869b22SIan Rogers     PRINT_SEP_LIST(printer, PRINT(printer_print_generic_arg(printer)), ", ");
1143*60869b22SIan Rogers     PRINT_STR(printer, ">");
1144*60869b22SIan Rogers     return OverflowOk;
1145*60869b22SIan Rogers }
1146*60869b22SIan Rogers 
1147*60869b22SIan Rogers static NODISCARD overflow_status printer_print_path_out_of_value(struct printer *printer, bool *_arg) {
1148*60869b22SIan Rogers     (void)_arg;
1149*60869b22SIan Rogers     return printer_print_path(printer, false);
1150*60869b22SIan Rogers }
1151*60869b22SIan Rogers 
1152*60869b22SIan Rogers static NODISCARD overflow_status printer_print_path_in_value(struct printer *printer, bool *_arg) {
1153*60869b22SIan Rogers     (void)_arg;
1154*60869b22SIan Rogers     return printer_print_path(printer, true);
1155*60869b22SIan Rogers }
1156*60869b22SIan Rogers 
1157*60869b22SIan Rogers static NODISCARD overflow_status printer_print_path(struct printer *printer, bool in_value) {
1158*60869b22SIan Rogers     PARSE(printer, parser_push_depth);
1159*60869b22SIan Rogers     uint8_t tag;
1160*60869b22SIan Rogers     PARSE(printer, parser_ch, &tag);
1161*60869b22SIan Rogers 
1162*60869b22SIan Rogers     overflow_status st;
1163*60869b22SIan Rogers     uint64_t dis;
1164*60869b22SIan Rogers     struct ident name;
1165*60869b22SIan Rogers     parser_namespace_type ns;
1166*60869b22SIan Rogers     char *orig_out;
1167*60869b22SIan Rogers 
1168*60869b22SIan Rogers     switch(tag) {
1169*60869b22SIan Rogers     case 'C':
1170*60869b22SIan Rogers         PARSE(printer, parser_disambiguator, &dis);
1171*60869b22SIan Rogers         PARSE(printer, parser_ident, &name);
1172*60869b22SIan Rogers 
1173*60869b22SIan Rogers         PRINT_IDENT(printer, &name);
1174*60869b22SIan Rogers 
1175*60869b22SIan Rogers         if (printer->out != NULL && !printer->alternate && dis != 0) {
1176*60869b22SIan Rogers             PRINT_STR(printer, "[");
1177*60869b22SIan Rogers             char buf[24] = {0};
1178*60869b22SIan Rogers             sprintf(buf, "%llx", (unsigned long long)dis);
1179*60869b22SIan Rogers             PRINT_STR(printer, buf);
1180*60869b22SIan Rogers             PRINT_STR(printer, "]");
1181*60869b22SIan Rogers         }
1182*60869b22SIan Rogers         break;
1183*60869b22SIan Rogers     case 'N':
1184*60869b22SIan Rogers         PARSE(printer, parser_namespace, &ns);
1185*60869b22SIan Rogers         if ((st = printer_print_path(printer, in_value)) != OverflowOk) {
1186*60869b22SIan Rogers             return st;
1187*60869b22SIan Rogers         }
1188*60869b22SIan Rogers 
1189*60869b22SIan Rogers         // HACK(eddyb) if the parser is already marked as having errored,
1190*60869b22SIan Rogers         // `parse!` below will print a `?` without its preceding `::`
1191*60869b22SIan Rogers         // (because printing the `::` is skipped in certain conditions,
1192*60869b22SIan Rogers         // i.e. a lowercase namespace with an empty identifier),
1193*60869b22SIan Rogers         // so in order to get `::?`, the `::` has to be printed here.
1194*60869b22SIan Rogers         if (printer->status != DemangleOk) {
1195*60869b22SIan Rogers             PRINT_STR(printer, "::");
1196*60869b22SIan Rogers         }
1197*60869b22SIan Rogers 
1198*60869b22SIan Rogers         PARSE(printer, parser_disambiguator, &dis);
1199*60869b22SIan Rogers         PARSE(printer, parser_ident, &name);
1200*60869b22SIan Rogers         // Special namespace, like closures and shims
1201*60869b22SIan Rogers         if (ns) {
1202*60869b22SIan Rogers             PRINT_STR(printer, "::{");
1203*60869b22SIan Rogers             if (ns == 'C') {
1204*60869b22SIan Rogers                 PRINT_STR(printer, "closure");
1205*60869b22SIan Rogers             } else if (ns == 'S') {
1206*60869b22SIan Rogers                 PRINT_STR(printer, "shim");
1207*60869b22SIan Rogers             } else {
1208*60869b22SIan Rogers                 PRINT_CH(printer, ns);
1209*60869b22SIan Rogers             }
1210*60869b22SIan Rogers             if (name.ascii_len != 0 || name.punycode_len != 0) {
1211*60869b22SIan Rogers                 PRINT_STR(printer, ":");
1212*60869b22SIan Rogers                 PRINT_IDENT(printer, &name);
1213*60869b22SIan Rogers             }
1214*60869b22SIan Rogers             PRINT_STR(printer, "#");
1215*60869b22SIan Rogers             PRINT_U64(printer, dis);
1216*60869b22SIan Rogers             PRINT_STR(printer, "}");
1217*60869b22SIan Rogers         } else {
1218*60869b22SIan Rogers             // Implementation-specific/unspecified namespaces
1219*60869b22SIan Rogers             if (name.ascii_len != 0 || name.punycode_len != 0) {
1220*60869b22SIan Rogers                 PRINT_STR(printer, "::");
1221*60869b22SIan Rogers                 PRINT_IDENT(printer, &name);
1222*60869b22SIan Rogers             }
1223*60869b22SIan Rogers         }
1224*60869b22SIan Rogers         break;
1225*60869b22SIan Rogers     case 'M':
1226*60869b22SIan Rogers     case 'X':
1227*60869b22SIan Rogers     // for impls, ignore the impls own path
1228*60869b22SIan Rogers     PARSE(printer, parser_disambiguator, &dis);
1229*60869b22SIan Rogers     orig_out = printer->out;
1230*60869b22SIan Rogers     printer->out = NULL;
1231*60869b22SIan Rogers     PRINT(printer_print_path(printer, false));
1232*60869b22SIan Rogers     printer->out = orig_out;
1233*60869b22SIan Rogers 
1234*60869b22SIan Rogers     // fallthru
1235*60869b22SIan Rogers     case 'Y':
1236*60869b22SIan Rogers     PRINT_STR(printer, "<");
1237*60869b22SIan Rogers     PRINT(printer_print_type(printer));
1238*60869b22SIan Rogers     if (tag != 'M') {
1239*60869b22SIan Rogers         PRINT_STR(printer, " as ");
1240*60869b22SIan Rogers         PRINT(printer_print_path(printer, false));
1241*60869b22SIan Rogers     }
1242*60869b22SIan Rogers     PRINT_STR(printer, ">");
1243*60869b22SIan Rogers     break;
1244*60869b22SIan Rogers     case 'I':
1245*60869b22SIan Rogers     PRINT(printer_print_path(printer, in_value));
1246*60869b22SIan Rogers     if (in_value) {
1247*60869b22SIan Rogers         PRINT_STR(printer, "::");
1248*60869b22SIan Rogers     }
1249*60869b22SIan Rogers     PRINT(printer_print_generic_args(printer));
1250*60869b22SIan Rogers     break;
1251*60869b22SIan Rogers     case 'B':
1252*60869b22SIan Rogers     PRINT(printer_print_backref(printer, in_value ? printer_print_path_in_value : printer_print_path_out_of_value, NULL));
1253*60869b22SIan Rogers     break;
1254*60869b22SIan Rogers     default:
1255*60869b22SIan Rogers     INVALID(printer);
1256*60869b22SIan Rogers     break;
1257*60869b22SIan Rogers     }
1258*60869b22SIan Rogers 
1259*60869b22SIan Rogers     printer_pop_depth(printer);
1260*60869b22SIan Rogers     return OverflowOk;
1261*60869b22SIan Rogers }
1262*60869b22SIan Rogers 
1263*60869b22SIan Rogers static NODISCARD overflow_status printer_print_const_uint(struct printer *printer, uint8_t tag) {
1264*60869b22SIan Rogers     struct buf hex;
1265*60869b22SIan Rogers     PARSE(printer, parser_hex_nibbles, &hex);
1266*60869b22SIan Rogers 
1267*60869b22SIan Rogers     uint64_t val;
1268*60869b22SIan Rogers     if (try_parse_uint(hex.start, hex.len, &val)) {
1269*60869b22SIan Rogers         PRINT_U64(printer, val);
1270*60869b22SIan Rogers     } else {
1271*60869b22SIan Rogers         PRINT_STR(printer, "0x");
1272*60869b22SIan Rogers         PRINT(printer_print_buf(printer, hex.start, hex.len));
1273*60869b22SIan Rogers     }
1274*60869b22SIan Rogers 
1275*60869b22SIan Rogers     if (printer->out != NULL && !printer->alternate) {
1276*60869b22SIan Rogers         const char *ty = basic_type(tag);
1277*60869b22SIan Rogers         if (/* safety */ ty != NULL) {
1278*60869b22SIan Rogers             PRINT_STR(printer, ty);
1279*60869b22SIan Rogers         }
1280*60869b22SIan Rogers     }
1281*60869b22SIan Rogers 
1282*60869b22SIan Rogers     return OverflowOk;
1283*60869b22SIan Rogers }
1284*60869b22SIan Rogers 
1285*60869b22SIan Rogers static NODISCARD overflow_status printer_print_const_str_literal(struct printer *printer) {
1286*60869b22SIan Rogers     struct buf hex;
1287*60869b22SIan Rogers     PARSE(printer, parser_hex_nibbles, &hex);
1288*60869b22SIan Rogers 
1289*60869b22SIan Rogers     size_t out_len = SIZE_MAX;
1290*60869b22SIan Rogers     nibbles_to_string_status nts_status = nibbles_to_string(hex.start, hex.len, NULL, &out_len);
1291*60869b22SIan Rogers     switch (nts_status) {
1292*60869b22SIan Rogers     case NtsOk:
1293*60869b22SIan Rogers         if (printer->out != NULL) {
1294*60869b22SIan Rogers             out_len = printer->out_len;
1295*60869b22SIan Rogers             nts_status = nibbles_to_string(hex.start, hex.len, (uint8_t*)printer->out, &out_len);
1296*60869b22SIan Rogers             if (nts_status != NtsOk) {
1297*60869b22SIan Rogers                 return OverflowOverflow;
1298*60869b22SIan Rogers             }
1299*60869b22SIan Rogers             printer->out += out_len;
1300*60869b22SIan Rogers             printer->out_len -= out_len;
1301*60869b22SIan Rogers         }
1302*60869b22SIan Rogers         return OverflowOk;
1303*60869b22SIan Rogers     case NtsOverflow:
1304*60869b22SIan Rogers         // technically if there is a string of size `SIZE_MAX/6` whose escaped version overflows
1305*60869b22SIan Rogers         // SIZE_MAX but has an invalid char, this will be a "fake" overflow. In practice,
1306*60869b22SIan Rogers         // that is not going to happen and a fuzzer will not generate strings of this length.
1307*60869b22SIan Rogers         return OverflowOverflow;
1308*60869b22SIan Rogers     case NtsInvalid:
1309*60869b22SIan Rogers     default:
1310*60869b22SIan Rogers         INVALID(printer);
1311*60869b22SIan Rogers     }
1312*60869b22SIan Rogers }
1313*60869b22SIan Rogers 
1314*60869b22SIan Rogers static NODISCARD overflow_status printer_print_const_struct(struct printer *printer) {
1315*60869b22SIan Rogers     uint64_t dis;
1316*60869b22SIan Rogers     struct ident name;
1317*60869b22SIan Rogers     PARSE(printer, parser_disambiguator, &dis);
1318*60869b22SIan Rogers     PARSE(printer, parser_ident, &name);
1319*60869b22SIan Rogers     PRINT_IDENT(printer, &name);
1320*60869b22SIan Rogers     PRINT_STR(printer, ": ");
1321*60869b22SIan Rogers     return printer_print_const(printer, true);
1322*60869b22SIan Rogers }
1323*60869b22SIan Rogers 
1324*60869b22SIan Rogers static NODISCARD overflow_status printer_print_const_out_of_value(struct printer *printer, bool *_arg) {
1325*60869b22SIan Rogers     (void)_arg;
1326*60869b22SIan Rogers     return printer_print_const(printer, false);
1327*60869b22SIan Rogers }
1328*60869b22SIan Rogers 
1329*60869b22SIan Rogers static NODISCARD overflow_status printer_print_const_in_value(struct printer *printer, bool *_arg) {
1330*60869b22SIan Rogers     (void)_arg;
1331*60869b22SIan Rogers     return printer_print_const(printer, true);
1332*60869b22SIan Rogers }
1333*60869b22SIan Rogers 
1334*60869b22SIan Rogers static NODISCARD overflow_status printer_print_const(struct printer *printer, bool in_value) {
1335*60869b22SIan Rogers     uint8_t tag;
1336*60869b22SIan Rogers 
1337*60869b22SIan Rogers     PARSE(printer, parser_ch, &tag);
1338*60869b22SIan Rogers     PARSE(printer, parser_push_depth);
1339*60869b22SIan Rogers 
1340*60869b22SIan Rogers     struct buf hex;
1341*60869b22SIan Rogers     uint64_t val;
1342*60869b22SIan Rogers     size_t count;
1343*60869b22SIan Rogers 
1344*60869b22SIan Rogers     bool opened_brace = false;
1345*60869b22SIan Rogers #define OPEN_BRACE_IF_OUTSIDE_EXPR \
1346*60869b22SIan Rogers         do { if (!in_value) { \
1347*60869b22SIan Rogers             opened_brace = true; \
1348*60869b22SIan Rogers             PRINT_STR(printer, "{"); \
1349*60869b22SIan Rogers         } } while(0)
1350*60869b22SIan Rogers 
1351*60869b22SIan Rogers     switch(tag) {
1352*60869b22SIan Rogers     case 'p':
1353*60869b22SIan Rogers         PRINT_STR(printer, "_");
1354*60869b22SIan Rogers         break;
1355*60869b22SIan Rogers     // Primitive leaves with hex-encoded values (see `basic_type`).
1356*60869b22SIan Rogers     case 'a':
1357*60869b22SIan Rogers     case 's':
1358*60869b22SIan Rogers     case 'l':
1359*60869b22SIan Rogers     case 'x':
1360*60869b22SIan Rogers     case 'n':
1361*60869b22SIan Rogers     case 'i':
1362*60869b22SIan Rogers         if (printer_eat(printer, 'n')) {
1363*60869b22SIan Rogers             PRINT_STR(printer, "-");
1364*60869b22SIan Rogers         }
1365*60869b22SIan Rogers         /* fallthrough */
1366*60869b22SIan Rogers     case 'h':
1367*60869b22SIan Rogers     case 't':
1368*60869b22SIan Rogers     case 'm':
1369*60869b22SIan Rogers     case 'y':
1370*60869b22SIan Rogers     case 'o':
1371*60869b22SIan Rogers     case 'j':
1372*60869b22SIan Rogers         PRINT(printer_print_const_uint(printer, tag));
1373*60869b22SIan Rogers         break;
1374*60869b22SIan Rogers     case 'b':
1375*60869b22SIan Rogers         PARSE(printer, parser_hex_nibbles, &hex);
1376*60869b22SIan Rogers         if (try_parse_uint(hex.start, hex.len, &val)) {
1377*60869b22SIan Rogers             if (val == 0) {
1378*60869b22SIan Rogers                 PRINT_STR(printer, "false");
1379*60869b22SIan Rogers             } else if (val == 1) {
1380*60869b22SIan Rogers                 PRINT_STR(printer, "true");
1381*60869b22SIan Rogers             } else {
1382*60869b22SIan Rogers                 INVALID(printer);
1383*60869b22SIan Rogers             }
1384*60869b22SIan Rogers         } else {
1385*60869b22SIan Rogers             INVALID(printer);
1386*60869b22SIan Rogers         }
1387*60869b22SIan Rogers         break;
1388*60869b22SIan Rogers     case 'c':
1389*60869b22SIan Rogers         PARSE(printer, parser_hex_nibbles, &hex);
1390*60869b22SIan Rogers         if (try_parse_uint(hex.start, hex.len, &val)
1391*60869b22SIan Rogers             && val < UINT32_MAX
1392*60869b22SIan Rogers             && validate_char((uint32_t)val))
1393*60869b22SIan Rogers         {
1394*60869b22SIan Rogers             char escaped_buf[ESCAPED_SIZE];
1395*60869b22SIan Rogers             size_t escaped_size = char_to_string((uint32_t)val, '\'', true, &escaped_buf);
1396*60869b22SIan Rogers 
1397*60869b22SIan Rogers             PRINT_STR(printer, "'");
1398*60869b22SIan Rogers             PRINT(printer_print_buf(printer, escaped_buf, escaped_size));
1399*60869b22SIan Rogers             PRINT_STR(printer, "'");
1400*60869b22SIan Rogers         } else {
1401*60869b22SIan Rogers             INVALID(printer);
1402*60869b22SIan Rogers         }
1403*60869b22SIan Rogers         break;
1404*60869b22SIan Rogers     case 'e':
1405*60869b22SIan Rogers         OPEN_BRACE_IF_OUTSIDE_EXPR;
1406*60869b22SIan Rogers         PRINT_STR(printer, "*");
1407*60869b22SIan Rogers         PRINT(printer_print_const_str_literal(printer));
1408*60869b22SIan Rogers         break;
1409*60869b22SIan Rogers     case 'R':
1410*60869b22SIan Rogers     case 'Q':
1411*60869b22SIan Rogers         if (tag == 'R' && printer_eat(printer, 'e')) {
1412*60869b22SIan Rogers             PRINT(printer_print_const_str_literal(printer));
1413*60869b22SIan Rogers         } else {
1414*60869b22SIan Rogers             OPEN_BRACE_IF_OUTSIDE_EXPR;
1415*60869b22SIan Rogers             PRINT_STR(printer, "&");
1416*60869b22SIan Rogers             if (tag != 'R') {
1417*60869b22SIan Rogers                 PRINT_STR(printer, "mut ");
1418*60869b22SIan Rogers             }
1419*60869b22SIan Rogers             PRINT(printer_print_const(printer, true));
1420*60869b22SIan Rogers         }
1421*60869b22SIan Rogers         break;
1422*60869b22SIan Rogers     case 'A':
1423*60869b22SIan Rogers         OPEN_BRACE_IF_OUTSIDE_EXPR;
1424*60869b22SIan Rogers         PRINT_STR(printer, "[");
1425*60869b22SIan Rogers         PRINT_SEP_LIST(printer, PRINT(printer_print_const(printer, true)), ", ");
1426*60869b22SIan Rogers         PRINT_STR(printer, "]");
1427*60869b22SIan Rogers         break;
1428*60869b22SIan Rogers     case 'T':
1429*60869b22SIan Rogers         OPEN_BRACE_IF_OUTSIDE_EXPR;
1430*60869b22SIan Rogers         PRINT_STR(printer, "(");
1431*60869b22SIan Rogers         PRINT_SEP_LIST_COUNT(printer, count, PRINT(printer_print_const(printer, true)), ", ");
1432*60869b22SIan Rogers         if (count == 1) {
1433*60869b22SIan Rogers             PRINT_STR(printer, ",");
1434*60869b22SIan Rogers         }
1435*60869b22SIan Rogers         PRINT_STR(printer, ")");
1436*60869b22SIan Rogers         break;
1437*60869b22SIan Rogers     case 'V':
1438*60869b22SIan Rogers         OPEN_BRACE_IF_OUTSIDE_EXPR;
1439*60869b22SIan Rogers         PRINT(printer_print_path(printer, true));
1440*60869b22SIan Rogers         PARSE(printer, parser_ch, &tag);
1441*60869b22SIan Rogers         switch(tag) {
1442*60869b22SIan Rogers         case 'U':
1443*60869b22SIan Rogers         break;
1444*60869b22SIan Rogers         case 'T':
1445*60869b22SIan Rogers         PRINT_STR(printer, "(");
1446*60869b22SIan Rogers         PRINT_SEP_LIST(printer, PRINT(printer_print_const(printer, true)), ", ");
1447*60869b22SIan Rogers         PRINT_STR(printer, ")");
1448*60869b22SIan Rogers         break;
1449*60869b22SIan Rogers         case 'S':
1450*60869b22SIan Rogers         PRINT_STR(printer, " { ");
1451*60869b22SIan Rogers         PRINT_SEP_LIST(printer, PRINT(printer_print_const_struct(printer)), ", ");
1452*60869b22SIan Rogers         PRINT_STR(printer, " }");
1453*60869b22SIan Rogers         break;
1454*60869b22SIan Rogers         default:
1455*60869b22SIan Rogers         INVALID(printer);
1456*60869b22SIan Rogers         }
1457*60869b22SIan Rogers         break;
1458*60869b22SIan Rogers     case 'B':
1459*60869b22SIan Rogers         PRINT(printer_print_backref(printer, in_value ? printer_print_const_in_value : printer_print_const_out_of_value, NULL));
1460*60869b22SIan Rogers         break;
1461*60869b22SIan Rogers     default:
1462*60869b22SIan Rogers         INVALID(printer);
1463*60869b22SIan Rogers     }
1464*60869b22SIan Rogers #undef OPEN_BRACE_IF_OUTSIDE_EXPR
1465*60869b22SIan Rogers 
1466*60869b22SIan Rogers     if (opened_brace) {
1467*60869b22SIan Rogers         PRINT_STR(printer, "}");
1468*60869b22SIan Rogers     }
1469*60869b22SIan Rogers     printer_pop_depth(printer);
1470*60869b22SIan Rogers 
1471*60869b22SIan Rogers     return OverflowOk;
1472*60869b22SIan Rogers }
1473*60869b22SIan Rogers 
1474*60869b22SIan Rogers /// A trait in a trait object may have some "existential projections"
1475*60869b22SIan Rogers /// (i.e. associated type bindings) after it, which should be printed
1476*60869b22SIan Rogers /// in the `<...>` of the trait, e.g. `dyn Trait<T, U, Assoc=X>`.
1477*60869b22SIan Rogers /// To this end, this method will keep the `<...>` of an 'I' path
1478*60869b22SIan Rogers /// open, by omitting the `>`, and return `Ok(true)` in that case.
1479*60869b22SIan Rogers static NODISCARD overflow_status printer_print_maybe_open_generics(struct printer *printer, bool *open) {
1480*60869b22SIan Rogers     if (printer_eat(printer, 'B')) {
1481*60869b22SIan Rogers         // NOTE(eddyb) the closure may not run if printing is being skipped,
1482*60869b22SIan Rogers         // but in that case the returned boolean doesn't matter.
1483*60869b22SIan Rogers         *open = false;
1484*60869b22SIan Rogers         return printer_print_backref(printer, printer_print_maybe_open_generics, open);
1485*60869b22SIan Rogers     } else if(printer_eat(printer, 'I')) {
1486*60869b22SIan Rogers         PRINT(printer_print_path(printer, false));
1487*60869b22SIan Rogers         PRINT_STR(printer, "<");
1488*60869b22SIan Rogers         PRINT_SEP_LIST(printer, PRINT(printer_print_generic_arg(printer)), ", ");
1489*60869b22SIan Rogers         *open = true;
1490*60869b22SIan Rogers         return OverflowOk;
1491*60869b22SIan Rogers     } else {
1492*60869b22SIan Rogers         PRINT(printer_print_path(printer, false));
1493*60869b22SIan Rogers         *open = false;
1494*60869b22SIan Rogers         return OverflowOk;
1495*60869b22SIan Rogers     }
1496*60869b22SIan Rogers }
1497*60869b22SIan Rogers 
1498*60869b22SIan Rogers static NODISCARD overflow_status printer_print_dyn_trait(struct printer *printer) {
1499*60869b22SIan Rogers     bool open;
1500*60869b22SIan Rogers     PRINT(printer_print_maybe_open_generics(printer, &open));
1501*60869b22SIan Rogers 
1502*60869b22SIan Rogers     while (printer_eat(printer, 'p')) {
1503*60869b22SIan Rogers         if (!open) {
1504*60869b22SIan Rogers             PRINT_STR(printer, "<");
1505*60869b22SIan Rogers             open = true;
1506*60869b22SIan Rogers         } else {
1507*60869b22SIan Rogers             PRINT_STR(printer, ", ");
1508*60869b22SIan Rogers         }
1509*60869b22SIan Rogers 
1510*60869b22SIan Rogers         struct ident name;
1511*60869b22SIan Rogers         PARSE(printer, parser_ident, &name);
1512*60869b22SIan Rogers 
1513*60869b22SIan Rogers         PRINT_IDENT(printer, &name);
1514*60869b22SIan Rogers         PRINT_STR(printer, " = ");
1515*60869b22SIan Rogers         PRINT(printer_print_type(printer));
1516*60869b22SIan Rogers     }
1517*60869b22SIan Rogers 
1518*60869b22SIan Rogers     if (open) {
1519*60869b22SIan Rogers         PRINT_STR(printer, ">");
1520*60869b22SIan Rogers     }
1521*60869b22SIan Rogers 
1522*60869b22SIan Rogers     return OverflowOk;
1523*60869b22SIan Rogers }
1524*60869b22SIan Rogers 
1525*60869b22SIan Rogers static NODISCARD overflow_status printer_print_object_bounds(struct printer *printer) {
1526*60869b22SIan Rogers     PRINT_SEP_LIST(printer, PRINT(printer_print_dyn_trait(printer)), " + ");
1527*60869b22SIan Rogers     return OverflowOk;
1528*60869b22SIan Rogers }
1529*60869b22SIan Rogers 
1530*60869b22SIan Rogers static NODISCARD overflow_status printer_print_function_type(struct printer *printer) {
1531*60869b22SIan Rogers     bool is_unsafe = printer_eat(printer, 'U');
1532*60869b22SIan Rogers     const char *abi;
1533*60869b22SIan Rogers     size_t abi_len;
1534*60869b22SIan Rogers     if (printer_eat(printer, 'K')) {
1535*60869b22SIan Rogers         if (printer_eat(printer, 'C')) {
1536*60869b22SIan Rogers             abi = "C";
1537*60869b22SIan Rogers             abi_len = 1;
1538*60869b22SIan Rogers         } else {
1539*60869b22SIan Rogers             struct ident abi_ident;
1540*60869b22SIan Rogers             PARSE(printer, parser_ident, &abi_ident);
1541*60869b22SIan Rogers             if (abi_ident.ascii_len == 0 || abi_ident.punycode_len != 0) {
1542*60869b22SIan Rogers                 INVALID(printer);
1543*60869b22SIan Rogers             }
1544*60869b22SIan Rogers             abi = abi_ident.ascii_start;
1545*60869b22SIan Rogers             abi_len = abi_ident.ascii_len;
1546*60869b22SIan Rogers         }
1547*60869b22SIan Rogers     } else {
1548*60869b22SIan Rogers         abi = NULL;
1549*60869b22SIan Rogers         abi_len = 0;
1550*60869b22SIan Rogers     }
1551*60869b22SIan Rogers 
1552*60869b22SIan Rogers     if (is_unsafe) {
1553*60869b22SIan Rogers         PRINT_STR(printer, "unsafe ");
1554*60869b22SIan Rogers     }
1555*60869b22SIan Rogers 
1556*60869b22SIan Rogers     if (abi != NULL) {
1557*60869b22SIan Rogers         PRINT_STR(printer, "extern \"");
1558*60869b22SIan Rogers 
1559*60869b22SIan Rogers         // replace _ with -
1560*60869b22SIan Rogers         while (abi_len > 0) {
1561*60869b22SIan Rogers             const char *minus = memchr(abi, '_', abi_len);
1562*60869b22SIan Rogers             if (minus == NULL) {
1563*60869b22SIan Rogers                 PRINT(printer_print_buf(printer, (const char*)abi, abi_len));
1564*60869b22SIan Rogers                 break;
1565*60869b22SIan Rogers             } else {
1566*60869b22SIan Rogers                 size_t space_to_minus = minus - abi;
1567*60869b22SIan Rogers                 PRINT(printer_print_buf(printer, (const char*)abi, space_to_minus));
1568*60869b22SIan Rogers                 PRINT_STR(printer, "-");
1569*60869b22SIan Rogers                 abi = minus + 1;
1570*60869b22SIan Rogers                 abi_len -= (space_to_minus + 1);
1571*60869b22SIan Rogers             }
1572*60869b22SIan Rogers         }
1573*60869b22SIan Rogers 
1574*60869b22SIan Rogers         PRINT_STR(printer, "\" ");
1575*60869b22SIan Rogers     }
1576*60869b22SIan Rogers 
1577*60869b22SIan Rogers     PRINT_STR(printer, "fn(");
1578*60869b22SIan Rogers     PRINT_SEP_LIST(printer, PRINT(printer_print_type(printer)), ", ");
1579*60869b22SIan Rogers     PRINT_STR(printer, ")");
1580*60869b22SIan Rogers 
1581*60869b22SIan Rogers     if (printer_eat(printer, 'u')) {
1582*60869b22SIan Rogers         // Skip printing the return type if it's 'u', i.e. `()`.
1583*60869b22SIan Rogers     } else {
1584*60869b22SIan Rogers         PRINT_STR(printer, " -> ");
1585*60869b22SIan Rogers         PRINT(printer_print_type(printer));
1586*60869b22SIan Rogers     }
1587*60869b22SIan Rogers 
1588*60869b22SIan Rogers     return OverflowOk;
1589*60869b22SIan Rogers }
1590*60869b22SIan Rogers 
1591*60869b22SIan Rogers static NODISCARD overflow_status printer_print_type_backref(struct printer *printer, bool *_arg) {
1592*60869b22SIan Rogers     (void)_arg;
1593*60869b22SIan Rogers     return printer_print_type(printer);
1594*60869b22SIan Rogers }
1595*60869b22SIan Rogers 
1596*60869b22SIan Rogers static NODISCARD overflow_status printer_print_type(struct printer *printer) {
1597*60869b22SIan Rogers     uint8_t tag;
1598*60869b22SIan Rogers     PARSE(printer, parser_ch, &tag);
1599*60869b22SIan Rogers 
1600*60869b22SIan Rogers     const char *basic_ty = basic_type(tag);
1601*60869b22SIan Rogers     if (basic_ty) {
1602*60869b22SIan Rogers         return printer_print_str(printer, basic_ty);
1603*60869b22SIan Rogers     }
1604*60869b22SIan Rogers 
1605*60869b22SIan Rogers     uint64_t count;
1606*60869b22SIan Rogers     uint64_t lt;
1607*60869b22SIan Rogers 
1608*60869b22SIan Rogers     PARSE(printer, parser_push_depth);
1609*60869b22SIan Rogers     switch (tag) {
1610*60869b22SIan Rogers     case 'R':
1611*60869b22SIan Rogers     case 'Q':
1612*60869b22SIan Rogers         PRINT_STR(printer, "&");
1613*60869b22SIan Rogers         if (printer_eat(printer, 'L')) {
1614*60869b22SIan Rogers             PARSE(printer, parser_integer_62, &lt);
1615*60869b22SIan Rogers             if (lt != 0) {
1616*60869b22SIan Rogers                 PRINT(printer_print_lifetime_from_index(printer, lt));
1617*60869b22SIan Rogers                 PRINT_STR(printer, " ");
1618*60869b22SIan Rogers             }
1619*60869b22SIan Rogers         }
1620*60869b22SIan Rogers         if (tag != 'R') {
1621*60869b22SIan Rogers             PRINT_STR(printer, "mut ");
1622*60869b22SIan Rogers         }
1623*60869b22SIan Rogers         PRINT(printer_print_type(printer));
1624*60869b22SIan Rogers         break;
1625*60869b22SIan Rogers     case 'P':
1626*60869b22SIan Rogers     case 'O':
1627*60869b22SIan Rogers         PRINT_STR(printer, "*");
1628*60869b22SIan Rogers         if (tag != 'P') {
1629*60869b22SIan Rogers             PRINT_STR(printer, "mut ");
1630*60869b22SIan Rogers         } else {
1631*60869b22SIan Rogers             PRINT_STR(printer, "const ");
1632*60869b22SIan Rogers         }
1633*60869b22SIan Rogers         PRINT(printer_print_type(printer));
1634*60869b22SIan Rogers         break;
1635*60869b22SIan Rogers     case 'A':
1636*60869b22SIan Rogers     case 'S':
1637*60869b22SIan Rogers         PRINT_STR(printer, "[");
1638*60869b22SIan Rogers         PRINT(printer_print_type(printer));
1639*60869b22SIan Rogers         if (tag == 'A') {
1640*60869b22SIan Rogers             PRINT_STR(printer, "; ");
1641*60869b22SIan Rogers             PRINT(printer_print_const(printer, true));
1642*60869b22SIan Rogers         }
1643*60869b22SIan Rogers         PRINT_STR(printer, "]");
1644*60869b22SIan Rogers         break;
1645*60869b22SIan Rogers     case 'T':
1646*60869b22SIan Rogers         PRINT_STR(printer, "(");
1647*60869b22SIan Rogers         PRINT_SEP_LIST_COUNT(printer, count, PRINT(printer_print_type(printer)), ", ");
1648*60869b22SIan Rogers         if (count == 1) {
1649*60869b22SIan Rogers             PRINT_STR(printer, ",");
1650*60869b22SIan Rogers         }
1651*60869b22SIan Rogers         PRINT_STR(printer, ")");
1652*60869b22SIan Rogers         break;
1653*60869b22SIan Rogers     case 'F':
1654*60869b22SIan Rogers         PRINT(printer_in_binder(printer, printer_print_function_type));
1655*60869b22SIan Rogers         break;
1656*60869b22SIan Rogers     case 'D':
1657*60869b22SIan Rogers         PRINT_STR(printer, "dyn ");
1658*60869b22SIan Rogers         PRINT(printer_in_binder(printer, printer_print_object_bounds));
1659*60869b22SIan Rogers 
1660*60869b22SIan Rogers         if (!printer_eat(printer, 'L')) {
1661*60869b22SIan Rogers             INVALID(printer);
1662*60869b22SIan Rogers         }
1663*60869b22SIan Rogers         PARSE(printer, parser_integer_62, &lt);
1664*60869b22SIan Rogers 
1665*60869b22SIan Rogers         if (lt != 0) {
1666*60869b22SIan Rogers             PRINT_STR(printer, " + ");
1667*60869b22SIan Rogers             PRINT(printer_print_lifetime_from_index(printer, lt));
1668*60869b22SIan Rogers         }
1669*60869b22SIan Rogers         break;
1670*60869b22SIan Rogers     case 'B':
1671*60869b22SIan Rogers         PRINT(printer_print_backref(printer, printer_print_type_backref, NULL));
1672*60869b22SIan Rogers         break;
1673*60869b22SIan Rogers     default:
1674*60869b22SIan Rogers         // Go back to the tag, so `print_path` also sees it.
1675*60869b22SIan Rogers         if (printer->status == DemangleOk && /* safety */ printer->parser.next > 0) {
1676*60869b22SIan Rogers             printer->parser.next--;
1677*60869b22SIan Rogers         }
1678*60869b22SIan Rogers         PRINT(printer_print_path(printer, false));
1679*60869b22SIan Rogers     }
1680*60869b22SIan Rogers 
1681*60869b22SIan Rogers     printer_pop_depth(printer);
1682*60869b22SIan Rogers     return OverflowOk;
1683*60869b22SIan Rogers }
1684*60869b22SIan Rogers 
1685*60869b22SIan Rogers NODISCARD static demangle_status rust_demangle_legacy_demangle(const char *s, size_t s_len, struct demangle_legacy *res, const char **rest)
1686*60869b22SIan Rogers {
1687*60869b22SIan Rogers     if (s_len > strlen(s)) {
1688*60869b22SIan Rogers         // s_len only exists to shorten the string, this is not a buffer API
1689*60869b22SIan Rogers         return DemangleInvalid;
1690*60869b22SIan Rogers     }
1691*60869b22SIan Rogers 
1692*60869b22SIan Rogers     const char *inner;
1693*60869b22SIan Rogers     size_t inner_len;
1694*60869b22SIan Rogers     if (s_len >= 3 && !strncmp(s, "_ZN", 3)) {
1695*60869b22SIan Rogers         inner = s + 3;
1696*60869b22SIan Rogers         inner_len = s_len - 3;
1697*60869b22SIan Rogers     } else if (s_len >= 2 && !strncmp(s, "ZN", 2)) {
1698*60869b22SIan Rogers         // On Windows, dbghelp strips leading underscores, so we accept "ZN...E"
1699*60869b22SIan Rogers         // form too.
1700*60869b22SIan Rogers         inner = s + 2;
1701*60869b22SIan Rogers         inner_len = s_len - 2;
1702*60869b22SIan Rogers     } else if (s_len >= 4 && !strncmp(s, "__ZN", 4)) {
1703*60869b22SIan Rogers         // On OSX, symbols are prefixed with an extra _
1704*60869b22SIan Rogers         inner = s + 4;
1705*60869b22SIan Rogers         inner_len = s_len - 4;
1706*60869b22SIan Rogers     } else {
1707*60869b22SIan Rogers         return DemangleInvalid;
1708*60869b22SIan Rogers     }
1709*60869b22SIan Rogers 
1710*60869b22SIan Rogers     if (!str_isascii(inner, inner_len)) {
1711*60869b22SIan Rogers         return DemangleInvalid;
1712*60869b22SIan Rogers     }
1713*60869b22SIan Rogers 
1714*60869b22SIan Rogers     size_t elements = 0;
1715*60869b22SIan Rogers     const char *chars = inner;
1716*60869b22SIan Rogers     size_t chars_len = inner_len;
1717*60869b22SIan Rogers     if (chars_len == 0) {
1718*60869b22SIan Rogers         return DemangleInvalid;
1719*60869b22SIan Rogers     }
1720*60869b22SIan Rogers     char c;
1721*60869b22SIan Rogers     while ((c = *chars) != 'E') {
1722*60869b22SIan Rogers         // Decode an identifier element's length
1723*60869b22SIan Rogers         if (c < '0' || c > '9') {
1724*60869b22SIan Rogers             return DemangleInvalid;
1725*60869b22SIan Rogers         }
1726*60869b22SIan Rogers         size_t len = 0;
1727*60869b22SIan Rogers         while (c >= '0' && c <= '9') {
1728*60869b22SIan Rogers             size_t d = c - '0';
1729*60869b22SIan Rogers             if (len > SIZE_MAX / 10) {
1730*60869b22SIan Rogers                 return DemangleInvalid;
1731*60869b22SIan Rogers             }
1732*60869b22SIan Rogers             len *= 10;
1733*60869b22SIan Rogers             if (len > SIZE_MAX - d) {
1734*60869b22SIan Rogers                 return DemangleInvalid;
1735*60869b22SIan Rogers             }
1736*60869b22SIan Rogers             len += d;
1737*60869b22SIan Rogers 
1738*60869b22SIan Rogers             chars++;
1739*60869b22SIan Rogers             chars_len--;
1740*60869b22SIan Rogers             if (chars_len == 0) {
1741*60869b22SIan Rogers                 return DemangleInvalid;
1742*60869b22SIan Rogers             }
1743*60869b22SIan Rogers             c = *chars;
1744*60869b22SIan Rogers         }
1745*60869b22SIan Rogers 
1746*60869b22SIan Rogers         // Advance by the length
1747*60869b22SIan Rogers         if (chars_len <= len) {
1748*60869b22SIan Rogers             return DemangleInvalid;
1749*60869b22SIan Rogers         }
1750*60869b22SIan Rogers         chars += len;
1751*60869b22SIan Rogers         chars_len -= len;
1752*60869b22SIan Rogers         elements++;
1753*60869b22SIan Rogers     }
1754*60869b22SIan Rogers     *res = (struct demangle_legacy) { inner, inner_len, elements };
1755*60869b22SIan Rogers     *rest = chars + 1;
1756*60869b22SIan Rogers     return DemangleOk;
1757*60869b22SIan Rogers }
1758*60869b22SIan Rogers 
1759*60869b22SIan Rogers static bool is_rust_hash(const char *s, size_t len) {
1760*60869b22SIan Rogers     if (len == 0 || s[0] != 'h') {
1761*60869b22SIan Rogers         return false;
1762*60869b22SIan Rogers     }
1763*60869b22SIan Rogers 
1764*60869b22SIan Rogers     for (size_t i = 1; i < len; i++) {
1765*60869b22SIan Rogers         if (!((s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'f') || (s[i] >= 'A' && s[i] <= 'F'))) {
1766*60869b22SIan Rogers             return false;
1767*60869b22SIan Rogers         }
1768*60869b22SIan Rogers     }
1769*60869b22SIan Rogers 
1770*60869b22SIan Rogers     return true;
1771*60869b22SIan Rogers }
1772*60869b22SIan Rogers 
1773*60869b22SIan Rogers NODISCARD static overflow_status rust_demangle_legacy_display_demangle(struct demangle_legacy res, char *out, size_t len, bool alternate)
1774*60869b22SIan Rogers {
1775*60869b22SIan Rogers     struct printer printer = {
1776*60869b22SIan Rogers         // not actually using the parser part of the printer, just keeping it to share the format functions
1777*60869b22SIan Rogers         DemangleOk,
1778*60869b22SIan Rogers         { NULL },
1779*60869b22SIan Rogers         out,
1780*60869b22SIan Rogers         len,
1781*60869b22SIan Rogers         0,
1782*60869b22SIan Rogers         alternate
1783*60869b22SIan Rogers     };
1784*60869b22SIan Rogers     const char *inner = res.mangled;
1785*60869b22SIan Rogers     for (size_t element = 0; element < res.elements; element++) {
1786*60869b22SIan Rogers         size_t i = 0;
1787*60869b22SIan Rogers         const char *rest;
1788*60869b22SIan Rogers         for (rest = inner; rest < res.mangled + res.mangled_len && *rest >= '0' && *rest <= '9'; rest++) {
1789*60869b22SIan Rogers             i *= 10;
1790*60869b22SIan Rogers             i += *rest - '0';
1791*60869b22SIan Rogers         }
1792*60869b22SIan Rogers         if ((size_t)(res.mangled + res.mangled_len - rest) < i) {
1793*60869b22SIan Rogers             // safety: shouldn't reach this place if the input string is validated. bail out.
1794*60869b22SIan Rogers             // safety: we knwo rest <= res.mangled + res.mangled_len from the for-loop above
1795*60869b22SIan Rogers             break;
1796*60869b22SIan Rogers         }
1797*60869b22SIan Rogers 
1798*60869b22SIan Rogers         size_t len = i;
1799*60869b22SIan Rogers         inner = rest + len;
1800*60869b22SIan Rogers 
1801*60869b22SIan Rogers         // From here on, inner contains a pointer to the next element, rest[:len] to the current one
1802*60869b22SIan Rogers         if (alternate && element + 1 == res.elements && is_rust_hash(rest, i)) {
1803*60869b22SIan Rogers             break;
1804*60869b22SIan Rogers         }
1805*60869b22SIan Rogers         if (element != 0) {
1806*60869b22SIan Rogers             PRINT_STR(&printer, "::");
1807*60869b22SIan Rogers         }
1808*60869b22SIan Rogers 
1809*60869b22SIan Rogers         if (len >= 2 && !strncmp(rest, "_$", 2)) {
1810*60869b22SIan Rogers             rest++;
1811*60869b22SIan Rogers             len--;
1812*60869b22SIan Rogers         }
1813*60869b22SIan Rogers 
1814*60869b22SIan Rogers         while (len > 0) {
1815*60869b22SIan Rogers             if (rest[0] == '.') {
1816*60869b22SIan Rogers                 if (len >= 2 && rest[1] == '.') {
1817*60869b22SIan Rogers                     PRINT_STR(&printer, "::");
1818*60869b22SIan Rogers                     rest += 2;
1819*60869b22SIan Rogers                     len -= 2;
1820*60869b22SIan Rogers                 } else {
1821*60869b22SIan Rogers                     PRINT_STR(&printer, ".");
1822*60869b22SIan Rogers                     rest += 1;
1823*60869b22SIan Rogers                     len -= 1;
1824*60869b22SIan Rogers                 }
1825*60869b22SIan Rogers             } else if (rest[0] == '$') {
1826*60869b22SIan Rogers                 const char *escape = memchr(rest + 1, '$', len - 1);
1827*60869b22SIan Rogers                 if (escape == NULL) {
1828*60869b22SIan Rogers                     break;
1829*60869b22SIan Rogers                 }
1830*60869b22SIan Rogers                 const char *escape_start = rest + 1;
1831*60869b22SIan Rogers                 size_t escape_len = escape - (rest + 1);
1832*60869b22SIan Rogers 
1833*60869b22SIan Rogers                 size_t next_len = len - (escape + 1 - rest);
1834*60869b22SIan Rogers                 const char *next_rest = escape + 1;
1835*60869b22SIan Rogers 
1836*60869b22SIan Rogers                 char ch;
1837*60869b22SIan Rogers                 if ((escape_len == 2 && escape_start[0] == 'S' && escape_start[1] == 'P')) {
1838*60869b22SIan Rogers                     ch = '@';
1839*60869b22SIan Rogers                 } else if ((escape_len == 2 && escape_start[0] == 'B' && escape_start[1] == 'P')) {
1840*60869b22SIan Rogers                     ch = '*';
1841*60869b22SIan Rogers                 } else if ((escape_len == 2 && escape_start[0] == 'R' && escape_start[1] == 'F')) {
1842*60869b22SIan Rogers                     ch = '&';
1843*60869b22SIan Rogers                 } else if ((escape_len == 2 && escape_start[0] == 'L' && escape_start[1] == 'T')) {
1844*60869b22SIan Rogers                     ch = '<';
1845*60869b22SIan Rogers                 } else if ((escape_len == 2 && escape_start[0] == 'G' && escape_start[1] == 'T')) {
1846*60869b22SIan Rogers                     ch = '>';
1847*60869b22SIan Rogers                 } else if ((escape_len == 2 && escape_start[0] == 'L' && escape_start[1] == 'P')) {
1848*60869b22SIan Rogers                     ch = '(';
1849*60869b22SIan Rogers                 } else if ((escape_len == 2 && escape_start[0] == 'R' && escape_start[1] == 'P')) {
1850*60869b22SIan Rogers                     ch = ')';
1851*60869b22SIan Rogers                 } else if ((escape_len == 1 && escape_start[0] == 'C')) {
1852*60869b22SIan Rogers                     ch = ',';
1853*60869b22SIan Rogers                 } else {
1854*60869b22SIan Rogers                     if (escape_len > 1 && escape_start[0] == 'u') {
1855*60869b22SIan Rogers                         escape_start++;
1856*60869b22SIan Rogers                         escape_len--;
1857*60869b22SIan Rogers                         uint64_t val;
1858*60869b22SIan Rogers                         if (try_parse_uint(escape_start, escape_len, &val)
1859*60869b22SIan Rogers                             && val < UINT32_MAX
1860*60869b22SIan Rogers                             && validate_char((uint32_t)val))
1861*60869b22SIan Rogers                         {
1862*60869b22SIan Rogers                             if (!unicode_iscontrol(val)) {
1863*60869b22SIan Rogers                                 uint8_t wchr[4];
1864*60869b22SIan Rogers                                 size_t wchr_len = code_to_utf8(wchr, (uint32_t)val);
1865*60869b22SIan Rogers                                 PRINT(printer_print_buf(&printer, (const char*)wchr, wchr_len));
1866*60869b22SIan Rogers                                 len = next_len;
1867*60869b22SIan Rogers                                 rest = next_rest;
1868*60869b22SIan Rogers                                 continue;
1869*60869b22SIan Rogers                             }
1870*60869b22SIan Rogers                         }
1871*60869b22SIan Rogers                     }
1872*60869b22SIan Rogers                     break; // print the rest of this element raw
1873*60869b22SIan Rogers                 }
1874*60869b22SIan Rogers                 PRINT_CH(&printer, ch);
1875*60869b22SIan Rogers                 len = next_len;
1876*60869b22SIan Rogers                 rest = next_rest;
1877*60869b22SIan Rogers             } else {
1878*60869b22SIan Rogers                 size_t j = 0;
1879*60869b22SIan Rogers                 for (;j < len && rest[j] != '$' && rest[j] != '.';j++);
1880*60869b22SIan Rogers                 if (j == len) {
1881*60869b22SIan Rogers                     break;
1882*60869b22SIan Rogers                 }
1883*60869b22SIan Rogers                 PRINT(printer_print_buf(&printer, rest, j));
1884*60869b22SIan Rogers                 rest += j;
1885*60869b22SIan Rogers                 len -= j;
1886*60869b22SIan Rogers             }
1887*60869b22SIan Rogers         }
1888*60869b22SIan Rogers         PRINT(printer_print_buf(&printer, rest, len));
1889*60869b22SIan Rogers     }
1890*60869b22SIan Rogers 
1891*60869b22SIan Rogers     if (printer.out_len < OVERFLOW_MARGIN) {
1892*60869b22SIan Rogers         return OverflowOverflow;
1893*60869b22SIan Rogers     }
1894*60869b22SIan Rogers     *printer.out = '\0';
1895*60869b22SIan Rogers     return OverflowOk;
1896*60869b22SIan Rogers }
1897*60869b22SIan Rogers 
1898*60869b22SIan Rogers static bool is_symbol_like(const char *s, size_t len) {
1899*60869b22SIan Rogers     // rust-demangle definition of symbol like: control characters and space are not symbol-like, all else is
1900*60869b22SIan Rogers     for (size_t i = 0; i < len; i++) {
1901*60869b22SIan Rogers         char ch = s[i];
1902*60869b22SIan Rogers         if (!(ch >= 0x21 && ch <= 0x7e)) {
1903*60869b22SIan Rogers             return false;
1904*60869b22SIan Rogers         }
1905*60869b22SIan Rogers     }
1906*60869b22SIan Rogers     return true;
1907*60869b22SIan Rogers }
1908*60869b22SIan Rogers 
1909*60869b22SIan Rogers void rust_demangle_demangle(const char *s, struct demangle *res)
1910*60869b22SIan Rogers {
1911*60869b22SIan Rogers     // During ThinLTO LLVM may import and rename internal symbols, so strip out
1912*60869b22SIan Rogers     // those endings first as they're one of the last manglings applied to symbol
1913*60869b22SIan Rogers     // names.
1914*60869b22SIan Rogers     const char *llvm = ".llvm.";
1915*60869b22SIan Rogers     const char *found_llvm = strstr(s, llvm);
1916*60869b22SIan Rogers     size_t s_len = strlen(s);
1917*60869b22SIan Rogers     if (found_llvm) {
1918*60869b22SIan Rogers         const char *all_hex_ptr = found_llvm + strlen(".llvm.");
1919*60869b22SIan Rogers         bool all_hex = true;
1920*60869b22SIan Rogers         for (;*all_hex_ptr;all_hex_ptr++) {
1921*60869b22SIan Rogers             if (!(('0' <= *all_hex_ptr && *all_hex_ptr <= '9') ||
1922*60869b22SIan Rogers                   ('A' <= *all_hex_ptr && *all_hex_ptr <= 'F') ||
1923*60869b22SIan Rogers                   *all_hex_ptr == '@')) {
1924*60869b22SIan Rogers                 all_hex = false;
1925*60869b22SIan Rogers                 break;
1926*60869b22SIan Rogers             }
1927*60869b22SIan Rogers         }
1928*60869b22SIan Rogers 
1929*60869b22SIan Rogers         if (all_hex) {
1930*60869b22SIan Rogers             s_len = found_llvm - s;
1931*60869b22SIan Rogers         }
1932*60869b22SIan Rogers     }
1933*60869b22SIan Rogers 
1934*60869b22SIan Rogers     const char *suffix;
1935*60869b22SIan Rogers     struct demangle_legacy legacy;
1936*60869b22SIan Rogers     demangle_status st = rust_demangle_legacy_demangle(s, s_len, &legacy, &suffix);
1937*60869b22SIan Rogers     if (st == DemangleOk) {
1938*60869b22SIan Rogers         *res = (struct demangle) {
1939*60869b22SIan Rogers             .style=DemangleStyleLegacy,
1940*60869b22SIan Rogers             .mangled=legacy.mangled,
1941*60869b22SIan Rogers             .mangled_len=legacy.mangled_len,
1942*60869b22SIan Rogers             .elements=legacy.elements,
1943*60869b22SIan Rogers             .original=s,
1944*60869b22SIan Rogers             .original_len=s_len,
1945*60869b22SIan Rogers             .suffix=suffix,
1946*60869b22SIan Rogers             .suffix_len=s_len - (suffix - s),
1947*60869b22SIan Rogers         };
1948*60869b22SIan Rogers     } else {
1949*60869b22SIan Rogers         struct demangle_v0 v0;
1950*60869b22SIan Rogers         st = rust_demangle_v0_demangle(s, s_len, &v0, &suffix);
1951*60869b22SIan Rogers         if (st == DemangleOk) {
1952*60869b22SIan Rogers             *res = (struct demangle) {
1953*60869b22SIan Rogers                 .style=DemangleStyleV0,
1954*60869b22SIan Rogers                 .mangled=v0.mangled,
1955*60869b22SIan Rogers                 .mangled_len=v0.mangled_len,
1956*60869b22SIan Rogers                 .elements=0,
1957*60869b22SIan Rogers                 .original=s,
1958*60869b22SIan Rogers                 .original_len=s_len,
1959*60869b22SIan Rogers                 .suffix=suffix,
1960*60869b22SIan Rogers                 .suffix_len=s_len - (suffix - s),
1961*60869b22SIan Rogers             };
1962*60869b22SIan Rogers         } else {
1963*60869b22SIan Rogers             *res = (struct demangle) {
1964*60869b22SIan Rogers                 .style=DemangleStyleUnknown,
1965*60869b22SIan Rogers                 .mangled=NULL,
1966*60869b22SIan Rogers                 .mangled_len=0,
1967*60869b22SIan Rogers                 .elements=0,
1968*60869b22SIan Rogers                 .original=s,
1969*60869b22SIan Rogers                 .original_len=s_len,
1970*60869b22SIan Rogers                 .suffix=s,
1971*60869b22SIan Rogers                 .suffix_len=0,
1972*60869b22SIan Rogers             };
1973*60869b22SIan Rogers         }
1974*60869b22SIan Rogers     }
1975*60869b22SIan Rogers 
1976*60869b22SIan Rogers     // Output like LLVM IR adds extra period-delimited words. See if
1977*60869b22SIan Rogers     // we are in that case and save the trailing words if so.
1978*60869b22SIan Rogers     if (res->suffix_len) {
1979*60869b22SIan Rogers         if (res->suffix[0] == '.' && is_symbol_like(res->suffix, res->suffix_len)) {
1980*60869b22SIan Rogers             // Keep the suffix
1981*60869b22SIan Rogers         } else {
1982*60869b22SIan Rogers             // Reset the suffix and invalidate the demangling
1983*60869b22SIan Rogers             res->style = DemangleStyleUnknown;
1984*60869b22SIan Rogers             res->suffix_len = 0;
1985*60869b22SIan Rogers         }
1986*60869b22SIan Rogers     }
1987*60869b22SIan Rogers }
1988*60869b22SIan Rogers 
1989*60869b22SIan Rogers bool rust_demangle_is_known(struct demangle *res) {
1990*60869b22SIan Rogers     return res->style != DemangleStyleUnknown;
1991*60869b22SIan Rogers }
1992*60869b22SIan Rogers 
1993*60869b22SIan Rogers overflow_status rust_demangle_display_demangle(struct demangle const *res, char *out, size_t len, bool alternate) {
1994*60869b22SIan Rogers     size_t original_len = res->original_len;
1995*60869b22SIan Rogers     size_t out_len;
1996*60869b22SIan Rogers     switch (res->style) {
1997*60869b22SIan Rogers     case DemangleStyleUnknown:
1998*60869b22SIan Rogers     if (len < original_len) {
1999*60869b22SIan Rogers         return OverflowOverflow;
2000*60869b22SIan Rogers     } else {
2001*60869b22SIan Rogers         memcpy(out, res->original, original_len);
2002*60869b22SIan Rogers         out += original_len;
2003*60869b22SIan Rogers         len -= original_len;
2004*60869b22SIan Rogers         break;
2005*60869b22SIan Rogers     }
2006*60869b22SIan Rogers     break;
2007*60869b22SIan Rogers     case DemangleStyleLegacy: {
2008*60869b22SIan Rogers         struct demangle_legacy legacy = {
2009*60869b22SIan Rogers             res->mangled,
2010*60869b22SIan Rogers             res->mangled_len,
2011*60869b22SIan Rogers             res->elements
2012*60869b22SIan Rogers         };
2013*60869b22SIan Rogers         if (rust_demangle_legacy_display_demangle(legacy, out, len, alternate) == OverflowOverflow) {
2014*60869b22SIan Rogers             return OverflowOverflow;
2015*60869b22SIan Rogers         }
2016*60869b22SIan Rogers         out_len = strlen(out);
2017*60869b22SIan Rogers         out += out_len;
2018*60869b22SIan Rogers         len -= out_len;
2019*60869b22SIan Rogers         break;
2020*60869b22SIan Rogers     }
2021*60869b22SIan Rogers     case DemangleStyleV0: {
2022*60869b22SIan Rogers         struct demangle_v0 v0 = {
2023*60869b22SIan Rogers             res->mangled,
2024*60869b22SIan Rogers             res->mangled_len
2025*60869b22SIan Rogers         };
2026*60869b22SIan Rogers         if (rust_demangle_v0_display_demangle(v0, out, len, alternate) == OverflowOverflow) {
2027*60869b22SIan Rogers             return OverflowOverflow;
2028*60869b22SIan Rogers         }
2029*60869b22SIan Rogers         out_len = strlen(out);
2030*60869b22SIan Rogers         out += out_len;
2031*60869b22SIan Rogers         len -= out_len;
2032*60869b22SIan Rogers         break;
2033*60869b22SIan Rogers     }
2034*60869b22SIan Rogers     }
2035*60869b22SIan Rogers     size_t suffix_len = res->suffix_len;
2036*60869b22SIan Rogers     if (len < suffix_len || len - suffix_len < OVERFLOW_MARGIN) {
2037*60869b22SIan Rogers         return OverflowOverflow;
2038*60869b22SIan Rogers     }
2039*60869b22SIan Rogers     memcpy(out, res->suffix, suffix_len);
2040*60869b22SIan Rogers     out[suffix_len] = 0;
2041*60869b22SIan Rogers     return OverflowOk;
2042*60869b22SIan Rogers }
2043