Home | History | Annotate | Line # | Download | only in libiberty
      1      1.1  christos /* Demangler for the Rust programming language
      2  1.1.1.6  christos    Copyright (C) 2016-2025 Free Software Foundation, Inc.
      3      1.1  christos    Written by David Tolnay (dtolnay (at) gmail.com).
      4  1.1.1.4  christos    Rewritten by Eduard-Mihai Burtescu (eddyb (at) lyken.rs) for v0 support.
      5      1.1  christos 
      6      1.1  christos This file is part of the libiberty library.
      7      1.1  christos Libiberty is free software; you can redistribute it and/or
      8      1.1  christos modify it under the terms of the GNU Library General Public
      9      1.1  christos License as published by the Free Software Foundation; either
     10      1.1  christos version 2 of the License, or (at your option) any later version.
     11      1.1  christos 
     12      1.1  christos In addition to the permissions in the GNU Library General Public
     13      1.1  christos License, the Free Software Foundation gives you unlimited permission
     14      1.1  christos to link the compiled version of this file into combinations with other
     15      1.1  christos programs, and to distribute those combinations without any restriction
     16      1.1  christos coming from the use of this file.  (The Library Public License
     17      1.1  christos restrictions do apply in other respects; for example, they cover
     18      1.1  christos modification of the file, and distribution when not linked into a
     19      1.1  christos combined executable.)
     20      1.1  christos 
     21      1.1  christos Libiberty is distributed in the hope that it will be useful,
     22      1.1  christos but WITHOUT ANY WARRANTY; without even the implied warranty of
     23      1.1  christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     24      1.1  christos Library General Public License for more details.
     25      1.1  christos 
     26      1.1  christos You should have received a copy of the GNU Library General Public
     27      1.1  christos License along with libiberty; see the file COPYING.LIB.
     28      1.1  christos If not, see <http://www.gnu.org/licenses/>.  */
     29      1.1  christos 
     30      1.1  christos 
     31      1.1  christos #ifdef HAVE_CONFIG_H
     32      1.1  christos #include "config.h"
     33      1.1  christos #endif
     34      1.1  christos 
     35      1.1  christos #include "safe-ctype.h"
     36      1.1  christos 
     37  1.1.1.3  christos #include <inttypes.h>
     38      1.1  christos #include <sys/types.h>
     39      1.1  christos #include <string.h>
     40      1.1  christos #include <stdio.h>
     41  1.1.1.3  christos #include <stdlib.h>
     42      1.1  christos 
     43      1.1  christos #ifdef HAVE_STRING_H
     44      1.1  christos #include <string.h>
     45      1.1  christos #else
     46      1.1  christos extern size_t strlen(const char *s);
     47      1.1  christos extern int strncmp(const char *s1, const char *s2, size_t n);
     48      1.1  christos extern void *memset(void *s, int c, size_t n);
     49      1.1  christos #endif
     50      1.1  christos 
     51      1.1  christos #include <demangle.h>
     52      1.1  christos #include "libiberty.h"
     53      1.1  christos 
     54  1.1.1.3  christos struct rust_demangler
     55  1.1.1.3  christos {
     56  1.1.1.3  christos   const char *sym;
     57  1.1.1.3  christos   size_t sym_len;
     58      1.1  christos 
     59  1.1.1.3  christos   void *callback_opaque;
     60  1.1.1.3  christos   demangle_callbackref callback;
     61      1.1  christos 
     62  1.1.1.3  christos   /* Position of the next character to read from the symbol. */
     63  1.1.1.3  christos   size_t next;
     64      1.1  christos 
     65  1.1.1.3  christos   /* Non-zero if any error occurred. */
     66  1.1.1.3  christos   int errored;
     67      1.1  christos 
     68  1.1.1.4  christos   /* Non-zero if nothing should be printed. */
     69  1.1.1.4  christos   int skipping_printing;
     70  1.1.1.4  christos 
     71  1.1.1.3  christos   /* Non-zero if printing should be verbose (e.g. include hashes). */
     72  1.1.1.3  christos   int verbose;
     73      1.1  christos 
     74  1.1.1.3  christos   /* Rust mangling version, with legacy mangling being -1. */
     75  1.1.1.3  christos   int version;
     76  1.1.1.4  christos 
     77  1.1.1.4  christos   /* Recursion depth.  */
     78  1.1.1.4  christos   unsigned int recursion;
     79  1.1.1.4  christos   /* Maximum number of times demangle_path may be called recursively.  */
     80  1.1.1.4  christos #define RUST_MAX_RECURSION_COUNT  1024
     81  1.1.1.4  christos #define RUST_NO_RECURSION_LIMIT   ((unsigned int) -1)
     82  1.1.1.4  christos 
     83  1.1.1.4  christos   uint64_t bound_lifetime_depth;
     84  1.1.1.3  christos };
     85      1.1  christos 
     86  1.1.1.3  christos /* Parsing functions. */
     87      1.1  christos 
     88  1.1.1.3  christos static char
     89  1.1.1.3  christos peek (const struct rust_demangler *rdm)
     90      1.1  christos {
     91  1.1.1.3  christos   if (rdm->next < rdm->sym_len)
     92  1.1.1.3  christos     return rdm->sym[rdm->next];
     93  1.1.1.3  christos   return 0;
     94  1.1.1.3  christos }
     95      1.1  christos 
     96  1.1.1.4  christos static int
     97  1.1.1.4  christos eat (struct rust_demangler *rdm, char c)
     98  1.1.1.4  christos {
     99  1.1.1.4  christos   if (peek (rdm) == c)
    100  1.1.1.4  christos     {
    101  1.1.1.4  christos       rdm->next++;
    102  1.1.1.4  christos       return 1;
    103  1.1.1.4  christos     }
    104  1.1.1.4  christos   else
    105  1.1.1.4  christos     return 0;
    106  1.1.1.4  christos }
    107  1.1.1.4  christos 
    108  1.1.1.3  christos static char
    109  1.1.1.3  christos next (struct rust_demangler *rdm)
    110  1.1.1.3  christos {
    111  1.1.1.3  christos   char c = peek (rdm);
    112  1.1.1.3  christos   if (!c)
    113  1.1.1.3  christos     rdm->errored = 1;
    114  1.1.1.3  christos   else
    115  1.1.1.3  christos     rdm->next++;
    116  1.1.1.3  christos   return c;
    117  1.1.1.3  christos }
    118  1.1.1.3  christos 
    119  1.1.1.4  christos static uint64_t
    120  1.1.1.4  christos parse_integer_62 (struct rust_demangler *rdm)
    121  1.1.1.4  christos {
    122  1.1.1.4  christos   char c;
    123  1.1.1.4  christos   uint64_t x;
    124  1.1.1.4  christos 
    125  1.1.1.4  christos   if (eat (rdm, '_'))
    126  1.1.1.4  christos     return 0;
    127  1.1.1.4  christos 
    128  1.1.1.4  christos   x = 0;
    129  1.1.1.4  christos   while (!eat (rdm, '_') && !rdm->errored)
    130  1.1.1.4  christos     {
    131  1.1.1.4  christos       c = next (rdm);
    132  1.1.1.4  christos       x *= 62;
    133  1.1.1.4  christos       if (ISDIGIT (c))
    134  1.1.1.4  christos         x += c - '0';
    135  1.1.1.4  christos       else if (ISLOWER (c))
    136  1.1.1.4  christos         x += 10 + (c - 'a');
    137  1.1.1.4  christos       else if (ISUPPER (c))
    138  1.1.1.4  christos         x += 10 + 26 + (c - 'A');
    139  1.1.1.4  christos       else
    140  1.1.1.4  christos         {
    141  1.1.1.4  christos           rdm->errored = 1;
    142  1.1.1.4  christos           return 0;
    143  1.1.1.4  christos         }
    144  1.1.1.4  christos     }
    145  1.1.1.4  christos   return x + 1;
    146  1.1.1.4  christos }
    147  1.1.1.4  christos 
    148  1.1.1.4  christos static uint64_t
    149  1.1.1.4  christos parse_opt_integer_62 (struct rust_demangler *rdm, char tag)
    150  1.1.1.4  christos {
    151  1.1.1.4  christos   if (!eat (rdm, tag))
    152  1.1.1.4  christos     return 0;
    153  1.1.1.4  christos   return 1 + parse_integer_62 (rdm);
    154  1.1.1.4  christos }
    155  1.1.1.4  christos 
    156  1.1.1.4  christos static uint64_t
    157  1.1.1.4  christos parse_disambiguator (struct rust_demangler *rdm)
    158  1.1.1.4  christos {
    159  1.1.1.4  christos   return parse_opt_integer_62 (rdm, 's');
    160  1.1.1.4  christos }
    161  1.1.1.4  christos 
    162  1.1.1.4  christos static size_t
    163  1.1.1.4  christos parse_hex_nibbles (struct rust_demangler *rdm, uint64_t *value)
    164  1.1.1.4  christos {
    165  1.1.1.4  christos   char c;
    166  1.1.1.4  christos   size_t hex_len;
    167  1.1.1.4  christos 
    168  1.1.1.4  christos   hex_len = 0;
    169  1.1.1.4  christos   *value = 0;
    170  1.1.1.4  christos 
    171  1.1.1.4  christos   while (!eat (rdm, '_'))
    172  1.1.1.4  christos     {
    173  1.1.1.4  christos       *value <<= 4;
    174  1.1.1.4  christos 
    175  1.1.1.4  christos       c = next (rdm);
    176  1.1.1.4  christos       if (ISDIGIT (c))
    177  1.1.1.4  christos         *value |= c - '0';
    178  1.1.1.4  christos       else if (c >= 'a' && c <= 'f')
    179  1.1.1.4  christos         *value |= 10 + (c - 'a');
    180  1.1.1.4  christos       else
    181  1.1.1.4  christos         {
    182  1.1.1.4  christos           rdm->errored = 1;
    183  1.1.1.4  christos           return 0;
    184  1.1.1.4  christos         }
    185  1.1.1.4  christos       hex_len++;
    186  1.1.1.4  christos     }
    187  1.1.1.4  christos 
    188  1.1.1.4  christos   return hex_len;
    189  1.1.1.4  christos }
    190  1.1.1.4  christos 
    191  1.1.1.3  christos struct rust_mangled_ident
    192  1.1.1.3  christos {
    193  1.1.1.3  christos   /* ASCII part of the identifier. */
    194  1.1.1.3  christos   const char *ascii;
    195  1.1.1.3  christos   size_t ascii_len;
    196  1.1.1.4  christos 
    197  1.1.1.4  christos   /* Punycode insertion codes for Unicode codepoints, if any. */
    198  1.1.1.4  christos   const char *punycode;
    199  1.1.1.4  christos   size_t punycode_len;
    200  1.1.1.3  christos };
    201  1.1.1.3  christos 
    202  1.1.1.3  christos static struct rust_mangled_ident
    203  1.1.1.3  christos parse_ident (struct rust_demangler *rdm)
    204  1.1.1.3  christos {
    205  1.1.1.3  christos   char c;
    206  1.1.1.3  christos   size_t start, len;
    207  1.1.1.4  christos   int is_punycode = 0;
    208  1.1.1.3  christos   struct rust_mangled_ident ident;
    209  1.1.1.3  christos 
    210  1.1.1.3  christos   ident.ascii = NULL;
    211  1.1.1.3  christos   ident.ascii_len = 0;
    212  1.1.1.4  christos   ident.punycode = NULL;
    213  1.1.1.4  christos   ident.punycode_len = 0;
    214  1.1.1.4  christos 
    215  1.1.1.4  christos   if (rdm->version != -1)
    216  1.1.1.4  christos     is_punycode = eat (rdm, 'u');
    217  1.1.1.3  christos 
    218  1.1.1.3  christos   c = next (rdm);
    219  1.1.1.3  christos   if (!ISDIGIT (c))
    220  1.1.1.3  christos     {
    221  1.1.1.3  christos       rdm->errored = 1;
    222  1.1.1.3  christos       return ident;
    223  1.1.1.3  christos     }
    224  1.1.1.3  christos   len = c - '0';
    225  1.1.1.3  christos 
    226  1.1.1.3  christos   if (c != '0')
    227  1.1.1.3  christos     while (ISDIGIT (peek (rdm)))
    228  1.1.1.3  christos       len = len * 10 + (next (rdm) - '0');
    229  1.1.1.3  christos 
    230  1.1.1.4  christos   /* Skip past the optional `_` separator (v0). */
    231  1.1.1.4  christos   if (rdm->version != -1)
    232  1.1.1.4  christos     eat (rdm, '_');
    233  1.1.1.4  christos 
    234  1.1.1.3  christos   start = rdm->next;
    235  1.1.1.3  christos   rdm->next += len;
    236  1.1.1.3  christos   /* Check for overflows. */
    237  1.1.1.3  christos   if ((start > rdm->next) || (rdm->next > rdm->sym_len))
    238  1.1.1.3  christos     {
    239  1.1.1.3  christos       rdm->errored = 1;
    240  1.1.1.3  christos       return ident;
    241  1.1.1.3  christos     }
    242  1.1.1.3  christos 
    243  1.1.1.3  christos   ident.ascii = rdm->sym + start;
    244  1.1.1.3  christos   ident.ascii_len = len;
    245  1.1.1.3  christos 
    246  1.1.1.4  christos   if (is_punycode)
    247  1.1.1.4  christos     {
    248  1.1.1.4  christos       ident.punycode_len = 0;
    249  1.1.1.4  christos       while (ident.ascii_len > 0)
    250  1.1.1.4  christos         {
    251  1.1.1.4  christos           ident.ascii_len--;
    252  1.1.1.4  christos 
    253  1.1.1.4  christos           /* The last '_' is a separator between ascii & punycode. */
    254  1.1.1.4  christos           if (ident.ascii[ident.ascii_len] == '_')
    255  1.1.1.4  christos             break;
    256  1.1.1.4  christos 
    257  1.1.1.4  christos           ident.punycode_len++;
    258  1.1.1.4  christos         }
    259  1.1.1.4  christos       if (!ident.punycode_len)
    260  1.1.1.4  christos         {
    261  1.1.1.4  christos           rdm->errored = 1;
    262  1.1.1.4  christos           return ident;
    263  1.1.1.4  christos         }
    264  1.1.1.4  christos       ident.punycode = ident.ascii + (len - ident.punycode_len);
    265  1.1.1.4  christos     }
    266  1.1.1.4  christos 
    267  1.1.1.3  christos   if (ident.ascii_len == 0)
    268  1.1.1.3  christos     ident.ascii = NULL;
    269  1.1.1.3  christos 
    270  1.1.1.3  christos   return ident;
    271  1.1.1.3  christos }
    272  1.1.1.3  christos 
    273  1.1.1.3  christos /* Printing functions. */
    274  1.1.1.3  christos 
    275  1.1.1.3  christos static void
    276  1.1.1.3  christos print_str (struct rust_demangler *rdm, const char *data, size_t len)
    277  1.1.1.3  christos {
    278  1.1.1.4  christos   if (!rdm->errored && !rdm->skipping_printing)
    279  1.1.1.3  christos     rdm->callback (data, len, rdm->callback_opaque);
    280  1.1.1.3  christos }
    281  1.1.1.3  christos 
    282  1.1.1.3  christos #define PRINT(s) print_str (rdm, s, strlen (s))
    283  1.1.1.3  christos 
    284  1.1.1.4  christos static void
    285  1.1.1.4  christos print_uint64 (struct rust_demangler *rdm, uint64_t x)
    286  1.1.1.4  christos {
    287  1.1.1.4  christos   char s[21];
    288  1.1.1.4  christos   snprintf (s, 21, "%" PRIu64, x);
    289  1.1.1.4  christos   PRINT (s);
    290  1.1.1.4  christos }
    291  1.1.1.4  christos 
    292  1.1.1.4  christos static void
    293  1.1.1.4  christos print_uint64_hex (struct rust_demangler *rdm, uint64_t x)
    294  1.1.1.4  christos {
    295  1.1.1.4  christos   char s[17];
    296  1.1.1.4  christos   snprintf (s, 17, "%" PRIx64, x);
    297  1.1.1.4  christos   PRINT (s);
    298  1.1.1.4  christos }
    299  1.1.1.4  christos 
    300  1.1.1.3  christos /* Return a 0x0-0xf value if the char is 0-9a-f, and -1 otherwise. */
    301  1.1.1.3  christos static int
    302  1.1.1.3  christos decode_lower_hex_nibble (char nibble)
    303  1.1.1.3  christos {
    304  1.1.1.3  christos   if ('0' <= nibble && nibble <= '9')
    305  1.1.1.3  christos     return nibble - '0';
    306  1.1.1.3  christos   if ('a' <= nibble && nibble <= 'f')
    307  1.1.1.3  christos     return 0xa + (nibble - 'a');
    308  1.1.1.3  christos   return -1;
    309  1.1.1.3  christos }
    310  1.1.1.3  christos 
    311  1.1.1.3  christos /* Return the unescaped character for a "$...$" escape, or 0 if invalid. */
    312  1.1.1.3  christos static char
    313  1.1.1.3  christos decode_legacy_escape (const char *e, size_t len, size_t *out_len)
    314  1.1.1.3  christos {
    315  1.1.1.3  christos   char c = 0;
    316  1.1.1.3  christos   size_t escape_len = 0;
    317  1.1.1.3  christos   int lo_nibble = -1, hi_nibble = -1;
    318      1.1  christos 
    319  1.1.1.3  christos   if (len < 3 || e[0] != '$')
    320      1.1  christos     return 0;
    321      1.1  christos 
    322  1.1.1.3  christos   e++;
    323  1.1.1.3  christos   len--;
    324  1.1.1.3  christos 
    325  1.1.1.3  christos   if (e[0] == 'C')
    326  1.1.1.3  christos     {
    327  1.1.1.3  christos       escape_len = 1;
    328  1.1.1.3  christos 
    329  1.1.1.3  christos       c = ',';
    330  1.1.1.3  christos     }
    331  1.1.1.3  christos   else if (len > 2)
    332  1.1.1.3  christos     {
    333  1.1.1.3  christos       escape_len = 2;
    334  1.1.1.3  christos 
    335  1.1.1.3  christos       if (e[0] == 'S' && e[1] == 'P')
    336  1.1.1.3  christos         c = '@';
    337  1.1.1.3  christos       else if (e[0] == 'B' && e[1] == 'P')
    338  1.1.1.3  christos         c = '*';
    339  1.1.1.3  christos       else if (e[0] == 'R' && e[1] == 'F')
    340  1.1.1.3  christos         c = '&';
    341  1.1.1.3  christos       else if (e[0] == 'L' && e[1] == 'T')
    342  1.1.1.3  christos         c = '<';
    343  1.1.1.3  christos       else if (e[0] == 'G' && e[1] == 'T')
    344  1.1.1.3  christos         c = '>';
    345  1.1.1.3  christos       else if (e[0] == 'L' && e[1] == 'P')
    346  1.1.1.3  christos         c = '(';
    347  1.1.1.3  christos       else if (e[0] == 'R' && e[1] == 'P')
    348  1.1.1.3  christos         c = ')';
    349  1.1.1.3  christos       else if (e[0] == 'u' && len > 3)
    350  1.1.1.3  christos         {
    351  1.1.1.3  christos           escape_len = 3;
    352  1.1.1.3  christos 
    353  1.1.1.3  christos           hi_nibble = decode_lower_hex_nibble (e[1]);
    354  1.1.1.3  christos           if (hi_nibble < 0)
    355  1.1.1.3  christos             return 0;
    356  1.1.1.3  christos           lo_nibble = decode_lower_hex_nibble (e[2]);
    357  1.1.1.3  christos           if (lo_nibble < 0)
    358  1.1.1.3  christos             return 0;
    359  1.1.1.3  christos 
    360  1.1.1.3  christos           /* Only allow non-control ASCII characters. */
    361  1.1.1.3  christos           if (hi_nibble > 7)
    362  1.1.1.3  christos             return 0;
    363  1.1.1.3  christos           c = (hi_nibble << 4) | lo_nibble;
    364  1.1.1.3  christos           if (c < 0x20)
    365  1.1.1.3  christos             return 0;
    366  1.1.1.3  christos         }
    367  1.1.1.3  christos     }
    368  1.1.1.3  christos 
    369  1.1.1.3  christos   if (!c || len <= escape_len || e[escape_len] != '$')
    370      1.1  christos     return 0;
    371      1.1  christos 
    372  1.1.1.3  christos   *out_len = 2 + escape_len;
    373  1.1.1.3  christos   return c;
    374      1.1  christos }
    375      1.1  christos 
    376  1.1.1.3  christos static void
    377  1.1.1.3  christos print_ident (struct rust_demangler *rdm, struct rust_mangled_ident ident)
    378  1.1.1.3  christos {
    379  1.1.1.3  christos   char unescaped;
    380  1.1.1.4  christos   uint8_t *out, *p, d;
    381  1.1.1.4  christos   size_t len, cap, punycode_pos, j;
    382  1.1.1.4  christos   /* Punycode parameters and state. */
    383  1.1.1.4  christos   uint32_t c;
    384  1.1.1.4  christos   size_t base, t_min, t_max, skew, damp, bias, i;
    385  1.1.1.4  christos   size_t delta, w, k, t;
    386  1.1.1.3  christos 
    387  1.1.1.4  christos   if (rdm->errored || rdm->skipping_printing)
    388  1.1.1.3  christos     return;
    389  1.1.1.3  christos 
    390  1.1.1.3  christos   if (rdm->version == -1)
    391  1.1.1.3  christos     {
    392  1.1.1.3  christos       /* Ignore leading underscores preceding escape sequences.
    393  1.1.1.3  christos          The mangler inserts an underscore to make sure the
    394  1.1.1.3  christos          identifier begins with a XID_Start character. */
    395  1.1.1.3  christos       if (ident.ascii_len >= 2 && ident.ascii[0] == '_'
    396  1.1.1.3  christos           && ident.ascii[1] == '$')
    397  1.1.1.3  christos         {
    398  1.1.1.3  christos           ident.ascii++;
    399  1.1.1.3  christos           ident.ascii_len--;
    400  1.1.1.3  christos         }
    401  1.1.1.3  christos 
    402  1.1.1.3  christos       while (ident.ascii_len > 0)
    403  1.1.1.3  christos         {
    404  1.1.1.3  christos           /* Handle legacy escape sequences ("$...$", ".." or "."). */
    405  1.1.1.3  christos           if (ident.ascii[0] == '$')
    406  1.1.1.3  christos             {
    407  1.1.1.3  christos               unescaped
    408  1.1.1.3  christos                   = decode_legacy_escape (ident.ascii, ident.ascii_len, &len);
    409  1.1.1.3  christos               if (unescaped)
    410  1.1.1.3  christos                 print_str (rdm, &unescaped, 1);
    411  1.1.1.3  christos               else
    412  1.1.1.3  christos                 {
    413  1.1.1.3  christos                   /* Unexpected escape sequence, print the rest verbatim. */
    414  1.1.1.3  christos                   print_str (rdm, ident.ascii, ident.ascii_len);
    415  1.1.1.3  christos                   return;
    416  1.1.1.3  christos                 }
    417  1.1.1.3  christos             }
    418  1.1.1.3  christos           else if (ident.ascii[0] == '.')
    419  1.1.1.3  christos             {
    420  1.1.1.3  christos               if (ident.ascii_len >= 2 && ident.ascii[1] == '.')
    421  1.1.1.3  christos                 {
    422  1.1.1.3  christos                   /* ".." becomes "::" */
    423  1.1.1.3  christos                   PRINT ("::");
    424  1.1.1.3  christos                   len = 2;
    425  1.1.1.3  christos                 }
    426  1.1.1.3  christos               else
    427  1.1.1.3  christos                 {
    428  1.1.1.4  christos                   PRINT (".");
    429  1.1.1.3  christos                   len = 1;
    430  1.1.1.3  christos                 }
    431  1.1.1.3  christos             }
    432  1.1.1.3  christos           else
    433  1.1.1.3  christos             {
    434  1.1.1.3  christos               /* Print everything before the next escape sequence, at once. */
    435  1.1.1.3  christos               for (len = 0; len < ident.ascii_len; len++)
    436  1.1.1.3  christos                 if (ident.ascii[len] == '$' || ident.ascii[len] == '.')
    437  1.1.1.3  christos                   break;
    438  1.1.1.3  christos 
    439  1.1.1.3  christos               print_str (rdm, ident.ascii, len);
    440  1.1.1.3  christos             }
    441  1.1.1.3  christos 
    442  1.1.1.3  christos           ident.ascii += len;
    443  1.1.1.3  christos           ident.ascii_len -= len;
    444  1.1.1.3  christos         }
    445      1.1  christos 
    446  1.1.1.3  christos       return;
    447  1.1.1.3  christos     }
    448  1.1.1.4  christos 
    449  1.1.1.4  christos   if (!ident.punycode)
    450  1.1.1.4  christos     {
    451  1.1.1.4  christos       print_str (rdm, ident.ascii, ident.ascii_len);
    452  1.1.1.4  christos       return;
    453  1.1.1.4  christos     }
    454  1.1.1.4  christos 
    455  1.1.1.4  christos   len = 0;
    456  1.1.1.4  christos   cap = 4;
    457  1.1.1.4  christos   while (cap < ident.ascii_len)
    458  1.1.1.4  christos     {
    459  1.1.1.4  christos       cap *= 2;
    460  1.1.1.4  christos       /* Check for overflows. */
    461  1.1.1.4  christos       if ((cap * 4) / 4 != cap)
    462  1.1.1.4  christos         {
    463  1.1.1.4  christos           rdm->errored = 1;
    464  1.1.1.4  christos           return;
    465  1.1.1.4  christos         }
    466  1.1.1.4  christos     }
    467  1.1.1.4  christos 
    468  1.1.1.4  christos   /* Store the output codepoints as groups of 4 UTF-8 bytes. */
    469  1.1.1.4  christos   out = (uint8_t *)malloc (cap * 4);
    470  1.1.1.4  christos   if (!out)
    471  1.1.1.4  christos     {
    472  1.1.1.4  christos       rdm->errored = 1;
    473  1.1.1.4  christos       return;
    474  1.1.1.4  christos     }
    475  1.1.1.4  christos 
    476  1.1.1.4  christos   /* Populate initial output from ASCII fragment. */
    477  1.1.1.4  christos   for (len = 0; len < ident.ascii_len; len++)
    478  1.1.1.4  christos     {
    479  1.1.1.4  christos       p = out + 4 * len;
    480  1.1.1.4  christos       p[0] = 0;
    481  1.1.1.4  christos       p[1] = 0;
    482  1.1.1.4  christos       p[2] = 0;
    483  1.1.1.4  christos       p[3] = ident.ascii[len];
    484  1.1.1.4  christos     }
    485  1.1.1.4  christos 
    486  1.1.1.4  christos   /* Punycode parameters and initial state. */
    487  1.1.1.4  christos   base = 36;
    488  1.1.1.4  christos   t_min = 1;
    489  1.1.1.4  christos   t_max = 26;
    490  1.1.1.4  christos   skew = 38;
    491  1.1.1.4  christos   damp = 700;
    492  1.1.1.4  christos   bias = 72;
    493  1.1.1.4  christos   i = 0;
    494  1.1.1.4  christos   c = 0x80;
    495  1.1.1.4  christos 
    496  1.1.1.4  christos   punycode_pos = 0;
    497  1.1.1.4  christos   while (punycode_pos < ident.punycode_len)
    498  1.1.1.4  christos     {
    499  1.1.1.4  christos       /* Read one delta value. */
    500  1.1.1.4  christos       delta = 0;
    501  1.1.1.4  christos       w = 1;
    502  1.1.1.4  christos       k = 0;
    503  1.1.1.4  christos       do
    504  1.1.1.4  christos         {
    505  1.1.1.4  christos           k += base;
    506  1.1.1.4  christos           t = k < bias ? 0 : (k - bias);
    507  1.1.1.4  christos           if (t < t_min)
    508  1.1.1.4  christos             t = t_min;
    509  1.1.1.4  christos           if (t > t_max)
    510  1.1.1.4  christos             t = t_max;
    511  1.1.1.4  christos 
    512  1.1.1.4  christos           if (punycode_pos >= ident.punycode_len)
    513  1.1.1.4  christos             goto cleanup;
    514  1.1.1.4  christos           d = ident.punycode[punycode_pos++];
    515  1.1.1.4  christos 
    516  1.1.1.4  christos           if (ISLOWER (d))
    517  1.1.1.4  christos             d = d - 'a';
    518  1.1.1.4  christos           else if (ISDIGIT (d))
    519  1.1.1.4  christos             d = 26 + (d - '0');
    520  1.1.1.4  christos           else
    521  1.1.1.4  christos             {
    522  1.1.1.4  christos               rdm->errored = 1;
    523  1.1.1.4  christos               goto cleanup;
    524  1.1.1.4  christos             }
    525  1.1.1.4  christos 
    526  1.1.1.4  christos           delta += d * w;
    527  1.1.1.4  christos           w *= base - t;
    528  1.1.1.4  christos         }
    529  1.1.1.4  christos       while (d >= t);
    530  1.1.1.4  christos 
    531  1.1.1.4  christos       /* Compute the new insert position and character. */
    532  1.1.1.4  christos       len++;
    533  1.1.1.4  christos       i += delta;
    534  1.1.1.4  christos       c += i / len;
    535  1.1.1.4  christos       i %= len;
    536  1.1.1.4  christos 
    537  1.1.1.4  christos       /* Ensure enough space is available. */
    538  1.1.1.4  christos       if (cap < len)
    539  1.1.1.4  christos         {
    540  1.1.1.4  christos           cap *= 2;
    541  1.1.1.4  christos           /* Check for overflows. */
    542  1.1.1.4  christos           if ((cap * 4) / 4 != cap || cap < len)
    543  1.1.1.4  christos             {
    544  1.1.1.4  christos               rdm->errored = 1;
    545  1.1.1.4  christos               goto cleanup;
    546  1.1.1.4  christos             }
    547  1.1.1.4  christos         }
    548  1.1.1.4  christos       p = (uint8_t *)realloc (out, cap * 4);
    549  1.1.1.4  christos       if (!p)
    550  1.1.1.4  christos         {
    551  1.1.1.4  christos           rdm->errored = 1;
    552  1.1.1.4  christos           goto cleanup;
    553  1.1.1.4  christos         }
    554  1.1.1.4  christos       out = p;
    555  1.1.1.4  christos 
    556  1.1.1.4  christos       /* Move the characters after the insert position. */
    557  1.1.1.4  christos       p = out + i * 4;
    558  1.1.1.4  christos       memmove (p + 4, p, (len - i - 1) * 4);
    559  1.1.1.4  christos 
    560  1.1.1.4  christos       /* Insert the new character, as UTF-8 bytes. */
    561  1.1.1.4  christos       p[0] = c >= 0x10000 ? 0xf0 | (c >> 18) : 0;
    562  1.1.1.4  christos       p[1] = c >= 0x800 ? (c < 0x10000 ? 0xe0 : 0x80) | ((c >> 12) & 0x3f) : 0;
    563  1.1.1.4  christos       p[2] = (c < 0x800 ? 0xc0 : 0x80) | ((c >> 6) & 0x3f);
    564  1.1.1.4  christos       p[3] = 0x80 | (c & 0x3f);
    565  1.1.1.4  christos 
    566  1.1.1.4  christos       /* If there are no more deltas, decoding is complete. */
    567  1.1.1.4  christos       if (punycode_pos == ident.punycode_len)
    568  1.1.1.4  christos         break;
    569  1.1.1.4  christos 
    570  1.1.1.4  christos       i++;
    571  1.1.1.4  christos 
    572  1.1.1.4  christos       /* Perform bias adaptation. */
    573  1.1.1.4  christos       delta /= damp;
    574  1.1.1.4  christos       damp = 2;
    575  1.1.1.4  christos 
    576  1.1.1.4  christos       delta += delta / len;
    577  1.1.1.4  christos       k = 0;
    578  1.1.1.4  christos       while (delta > ((base - t_min) * t_max) / 2)
    579  1.1.1.4  christos         {
    580  1.1.1.4  christos           delta /= base - t_min;
    581  1.1.1.4  christos           k += base;
    582  1.1.1.4  christos         }
    583  1.1.1.4  christos       bias = k + ((base - t_min + 1) * delta) / (delta + skew);
    584  1.1.1.4  christos     }
    585  1.1.1.4  christos 
    586  1.1.1.4  christos   /* Remove all the 0 bytes to leave behind an UTF-8 string. */
    587  1.1.1.4  christos   for (i = 0, j = 0; i < len * 4; i++)
    588  1.1.1.4  christos     if (out[i] != 0)
    589  1.1.1.4  christos       out[j++] = out[i];
    590  1.1.1.4  christos 
    591  1.1.1.4  christos   print_str (rdm, (const char *)out, j);
    592  1.1.1.4  christos 
    593  1.1.1.4  christos cleanup:
    594  1.1.1.4  christos   free (out);
    595  1.1.1.4  christos }
    596  1.1.1.4  christos 
    597  1.1.1.4  christos /* Print the lifetime according to the previously decoded index.
    598  1.1.1.4  christos    An index of `0` always refers to `'_`, but starting with `1`,
    599  1.1.1.4  christos    indices refer to late-bound lifetimes introduced by a binder. */
    600  1.1.1.4  christos static void
    601  1.1.1.4  christos print_lifetime_from_index (struct rust_demangler *rdm, uint64_t lt)
    602  1.1.1.4  christos {
    603  1.1.1.4  christos   char c;
    604  1.1.1.4  christos   uint64_t depth;
    605  1.1.1.4  christos 
    606  1.1.1.4  christos   PRINT ("'");
    607  1.1.1.4  christos   if (lt == 0)
    608  1.1.1.4  christos     {
    609  1.1.1.4  christos       PRINT ("_");
    610  1.1.1.4  christos       return;
    611  1.1.1.4  christos     }
    612  1.1.1.4  christos 
    613  1.1.1.4  christos   depth = rdm->bound_lifetime_depth - lt;
    614  1.1.1.4  christos   /* Try to print lifetimes alphabetically first. */
    615  1.1.1.4  christos   if (depth < 26)
    616  1.1.1.4  christos     {
    617  1.1.1.4  christos       c = 'a' + depth;
    618  1.1.1.4  christos       print_str (rdm, &c, 1);
    619  1.1.1.4  christos     }
    620  1.1.1.4  christos   else
    621  1.1.1.4  christos     {
    622  1.1.1.4  christos       /* Use `'_123` after running out of letters. */
    623  1.1.1.4  christos       PRINT ("_");
    624  1.1.1.4  christos       print_uint64 (rdm, depth);
    625  1.1.1.4  christos     }
    626  1.1.1.4  christos }
    627  1.1.1.4  christos 
    628  1.1.1.4  christos /* Demangling functions. */
    629  1.1.1.4  christos 
    630  1.1.1.4  christos static void demangle_binder (struct rust_demangler *rdm);
    631  1.1.1.4  christos static void demangle_path (struct rust_demangler *rdm, int in_value);
    632  1.1.1.4  christos static void demangle_generic_arg (struct rust_demangler *rdm);
    633  1.1.1.4  christos static void demangle_type (struct rust_demangler *rdm);
    634  1.1.1.4  christos static int demangle_path_maybe_open_generics (struct rust_demangler *rdm);
    635  1.1.1.4  christos static void demangle_dyn_trait (struct rust_demangler *rdm);
    636  1.1.1.4  christos static void demangle_const (struct rust_demangler *rdm);
    637  1.1.1.4  christos static void demangle_const_uint (struct rust_demangler *rdm);
    638  1.1.1.4  christos static void demangle_const_int (struct rust_demangler *rdm);
    639  1.1.1.4  christos static void demangle_const_bool (struct rust_demangler *rdm);
    640  1.1.1.4  christos static void demangle_const_char (struct rust_demangler *rdm);
    641  1.1.1.4  christos 
    642  1.1.1.4  christos /* Optionally enter a binder ('G') for late-bound lifetimes,
    643  1.1.1.4  christos    printing e.g. `for<'a, 'b> `, and make those lifetimes visible
    644  1.1.1.4  christos    to the caller (via depth level, which the caller should reset). */
    645  1.1.1.4  christos static void
    646  1.1.1.4  christos demangle_binder (struct rust_demangler *rdm)
    647  1.1.1.4  christos {
    648  1.1.1.4  christos   uint64_t i, bound_lifetimes;
    649  1.1.1.4  christos 
    650  1.1.1.4  christos   if (rdm->errored)
    651  1.1.1.4  christos     return;
    652  1.1.1.4  christos 
    653  1.1.1.4  christos   bound_lifetimes = parse_opt_integer_62 (rdm, 'G');
    654  1.1.1.4  christos   if (bound_lifetimes > 0)
    655  1.1.1.4  christos     {
    656  1.1.1.4  christos       PRINT ("for<");
    657  1.1.1.4  christos       for (i = 0; i < bound_lifetimes; i++)
    658  1.1.1.4  christos         {
    659  1.1.1.4  christos           if (i > 0)
    660  1.1.1.4  christos             PRINT (", ");
    661  1.1.1.4  christos           rdm->bound_lifetime_depth++;
    662  1.1.1.4  christos           print_lifetime_from_index (rdm, 1);
    663  1.1.1.4  christos         }
    664  1.1.1.4  christos       PRINT ("> ");
    665  1.1.1.4  christos     }
    666  1.1.1.4  christos }
    667  1.1.1.4  christos 
    668  1.1.1.4  christos static void
    669  1.1.1.4  christos demangle_path (struct rust_demangler *rdm, int in_value)
    670  1.1.1.4  christos {
    671  1.1.1.4  christos   char tag, ns;
    672  1.1.1.4  christos   int was_skipping_printing;
    673  1.1.1.4  christos   size_t i, backref, old_next;
    674  1.1.1.4  christos   uint64_t dis;
    675  1.1.1.4  christos   struct rust_mangled_ident name;
    676  1.1.1.4  christos 
    677  1.1.1.4  christos   if (rdm->errored)
    678  1.1.1.4  christos     return;
    679  1.1.1.4  christos 
    680  1.1.1.4  christos   if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
    681  1.1.1.4  christos     {
    682  1.1.1.4  christos       ++ rdm->recursion;
    683  1.1.1.4  christos       if (rdm->recursion > RUST_MAX_RECURSION_COUNT)
    684  1.1.1.4  christos 	/* FIXME: There ought to be a way to report
    685  1.1.1.4  christos 	   that the recursion limit has been reached.  */
    686  1.1.1.4  christos 	goto fail_return;
    687  1.1.1.4  christos     }
    688  1.1.1.4  christos 
    689  1.1.1.4  christos   switch (tag = next (rdm))
    690  1.1.1.4  christos     {
    691  1.1.1.4  christos     case 'C':
    692  1.1.1.4  christos       dis = parse_disambiguator (rdm);
    693  1.1.1.4  christos       name = parse_ident (rdm);
    694  1.1.1.4  christos 
    695  1.1.1.4  christos       print_ident (rdm, name);
    696  1.1.1.4  christos       if (rdm->verbose)
    697  1.1.1.4  christos         {
    698  1.1.1.4  christos           PRINT ("[");
    699  1.1.1.4  christos           print_uint64_hex (rdm, dis);
    700  1.1.1.4  christos           PRINT ("]");
    701  1.1.1.4  christos         }
    702  1.1.1.4  christos       break;
    703  1.1.1.4  christos     case 'N':
    704  1.1.1.4  christos       ns = next (rdm);
    705  1.1.1.4  christos       if (!ISLOWER (ns) && !ISUPPER (ns))
    706  1.1.1.4  christos 	goto fail_return;
    707  1.1.1.4  christos 
    708  1.1.1.4  christos       demangle_path (rdm, in_value);
    709  1.1.1.4  christos 
    710  1.1.1.4  christos       dis = parse_disambiguator (rdm);
    711  1.1.1.4  christos       name = parse_ident (rdm);
    712  1.1.1.4  christos 
    713  1.1.1.4  christos       if (ISUPPER (ns))
    714  1.1.1.4  christos         {
    715  1.1.1.4  christos           /* Special namespaces, like closures and shims. */
    716  1.1.1.4  christos           PRINT ("::{");
    717  1.1.1.4  christos           switch (ns)
    718  1.1.1.4  christos             {
    719  1.1.1.4  christos             case 'C':
    720  1.1.1.4  christos               PRINT ("closure");
    721  1.1.1.4  christos               break;
    722  1.1.1.4  christos             case 'S':
    723  1.1.1.4  christos               PRINT ("shim");
    724  1.1.1.4  christos               break;
    725  1.1.1.4  christos             default:
    726  1.1.1.4  christos               print_str (rdm, &ns, 1);
    727  1.1.1.4  christos             }
    728  1.1.1.4  christos           if (name.ascii || name.punycode)
    729  1.1.1.4  christos             {
    730  1.1.1.4  christos               PRINT (":");
    731  1.1.1.4  christos               print_ident (rdm, name);
    732  1.1.1.4  christos             }
    733  1.1.1.4  christos           PRINT ("#");
    734  1.1.1.4  christos           print_uint64 (rdm, dis);
    735  1.1.1.4  christos           PRINT ("}");
    736  1.1.1.4  christos         }
    737  1.1.1.4  christos       else
    738  1.1.1.4  christos         {
    739  1.1.1.4  christos           /* Implementation-specific/unspecified namespaces. */
    740  1.1.1.4  christos 
    741  1.1.1.4  christos           if (name.ascii || name.punycode)
    742  1.1.1.4  christos             {
    743  1.1.1.4  christos               PRINT ("::");
    744  1.1.1.4  christos               print_ident (rdm, name);
    745  1.1.1.4  christos             }
    746  1.1.1.4  christos         }
    747  1.1.1.4  christos       break;
    748  1.1.1.4  christos     case 'M':
    749  1.1.1.4  christos     case 'X':
    750  1.1.1.4  christos       /* Ignore the `impl`'s own path.*/
    751  1.1.1.4  christos       parse_disambiguator (rdm);
    752  1.1.1.4  christos       was_skipping_printing = rdm->skipping_printing;
    753  1.1.1.4  christos       rdm->skipping_printing = 1;
    754  1.1.1.4  christos       demangle_path (rdm, in_value);
    755  1.1.1.4  christos       rdm->skipping_printing = was_skipping_printing;
    756  1.1.1.4  christos       /* fallthrough */
    757  1.1.1.4  christos     case 'Y':
    758  1.1.1.4  christos       PRINT ("<");
    759  1.1.1.4  christos       demangle_type (rdm);
    760  1.1.1.4  christos       if (tag != 'M')
    761  1.1.1.4  christos         {
    762  1.1.1.4  christos           PRINT (" as ");
    763  1.1.1.4  christos           demangle_path (rdm, 0);
    764  1.1.1.4  christos         }
    765  1.1.1.4  christos       PRINT (">");
    766  1.1.1.4  christos       break;
    767  1.1.1.4  christos     case 'I':
    768  1.1.1.4  christos       demangle_path (rdm, in_value);
    769  1.1.1.4  christos       if (in_value)
    770  1.1.1.4  christos         PRINT ("::");
    771  1.1.1.4  christos       PRINT ("<");
    772  1.1.1.4  christos       for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
    773  1.1.1.4  christos         {
    774  1.1.1.4  christos           if (i > 0)
    775  1.1.1.4  christos             PRINT (", ");
    776  1.1.1.4  christos           demangle_generic_arg (rdm);
    777  1.1.1.4  christos         }
    778  1.1.1.4  christos       PRINT (">");
    779  1.1.1.4  christos       break;
    780  1.1.1.4  christos     case 'B':
    781  1.1.1.4  christos       backref = parse_integer_62 (rdm);
    782  1.1.1.4  christos       if (!rdm->skipping_printing)
    783  1.1.1.4  christos         {
    784  1.1.1.4  christos           old_next = rdm->next;
    785  1.1.1.4  christos           rdm->next = backref;
    786  1.1.1.4  christos           demangle_path (rdm, in_value);
    787  1.1.1.4  christos           rdm->next = old_next;
    788  1.1.1.4  christos         }
    789  1.1.1.4  christos       break;
    790  1.1.1.4  christos     default:
    791  1.1.1.4  christos       goto fail_return;
    792  1.1.1.4  christos     }
    793  1.1.1.4  christos   goto pass_return;
    794  1.1.1.4  christos 
    795  1.1.1.4  christos  fail_return:
    796  1.1.1.4  christos   rdm->errored = 1;
    797  1.1.1.4  christos  pass_return:
    798  1.1.1.4  christos   if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
    799  1.1.1.4  christos     -- rdm->recursion;
    800  1.1.1.4  christos }
    801  1.1.1.4  christos 
    802  1.1.1.4  christos static void
    803  1.1.1.4  christos demangle_generic_arg (struct rust_demangler *rdm)
    804  1.1.1.4  christos {
    805  1.1.1.4  christos   uint64_t lt;
    806  1.1.1.4  christos   if (eat (rdm, 'L'))
    807  1.1.1.4  christos     {
    808  1.1.1.4  christos       lt = parse_integer_62 (rdm);
    809  1.1.1.4  christos       print_lifetime_from_index (rdm, lt);
    810  1.1.1.4  christos     }
    811  1.1.1.4  christos   else if (eat (rdm, 'K'))
    812  1.1.1.4  christos     demangle_const (rdm);
    813  1.1.1.4  christos   else
    814  1.1.1.4  christos     demangle_type (rdm);
    815  1.1.1.4  christos }
    816  1.1.1.4  christos 
    817  1.1.1.4  christos static const char *
    818  1.1.1.4  christos basic_type (char tag)
    819  1.1.1.4  christos {
    820  1.1.1.4  christos   switch (tag)
    821  1.1.1.4  christos     {
    822  1.1.1.4  christos     case 'b':
    823  1.1.1.4  christos       return "bool";
    824  1.1.1.4  christos     case 'c':
    825  1.1.1.4  christos       return "char";
    826  1.1.1.4  christos     case 'e':
    827  1.1.1.4  christos       return "str";
    828  1.1.1.4  christos     case 'u':
    829  1.1.1.4  christos       return "()";
    830  1.1.1.4  christos     case 'a':
    831  1.1.1.4  christos       return "i8";
    832  1.1.1.4  christos     case 's':
    833  1.1.1.4  christos       return "i16";
    834  1.1.1.4  christos     case 'l':
    835  1.1.1.4  christos       return "i32";
    836  1.1.1.4  christos     case 'x':
    837  1.1.1.4  christos       return "i64";
    838  1.1.1.4  christos     case 'n':
    839  1.1.1.4  christos       return "i128";
    840  1.1.1.4  christos     case 'i':
    841  1.1.1.4  christos       return "isize";
    842  1.1.1.4  christos     case 'h':
    843  1.1.1.4  christos       return "u8";
    844  1.1.1.4  christos     case 't':
    845  1.1.1.4  christos       return "u16";
    846  1.1.1.4  christos     case 'm':
    847  1.1.1.4  christos       return "u32";
    848  1.1.1.4  christos     case 'y':
    849  1.1.1.4  christos       return "u64";
    850  1.1.1.4  christos     case 'o':
    851  1.1.1.4  christos       return "u128";
    852  1.1.1.4  christos     case 'j':
    853  1.1.1.4  christos       return "usize";
    854  1.1.1.4  christos     case 'f':
    855  1.1.1.4  christos       return "f32";
    856  1.1.1.4  christos     case 'd':
    857  1.1.1.4  christos       return "f64";
    858  1.1.1.4  christos     case 'z':
    859  1.1.1.4  christos       return "!";
    860  1.1.1.4  christos     case 'p':
    861  1.1.1.4  christos       return "_";
    862  1.1.1.4  christos     case 'v':
    863  1.1.1.4  christos       return "...";
    864  1.1.1.4  christos 
    865  1.1.1.4  christos     default:
    866  1.1.1.4  christos       return NULL;
    867  1.1.1.4  christos     }
    868  1.1.1.4  christos }
    869  1.1.1.4  christos 
    870  1.1.1.4  christos static void
    871  1.1.1.4  christos demangle_type (struct rust_demangler *rdm)
    872  1.1.1.4  christos {
    873  1.1.1.4  christos   char tag;
    874  1.1.1.4  christos   size_t i, old_next, backref;
    875  1.1.1.4  christos   uint64_t lt, old_bound_lifetime_depth;
    876  1.1.1.4  christos   const char *basic;
    877  1.1.1.4  christos   struct rust_mangled_ident abi;
    878  1.1.1.4  christos 
    879  1.1.1.4  christos   if (rdm->errored)
    880  1.1.1.4  christos     return;
    881  1.1.1.4  christos 
    882  1.1.1.4  christos   tag = next (rdm);
    883  1.1.1.4  christos 
    884  1.1.1.4  christos   basic = basic_type (tag);
    885  1.1.1.4  christos   if (basic)
    886  1.1.1.4  christos     {
    887  1.1.1.4  christos       PRINT (basic);
    888  1.1.1.4  christos       return;
    889  1.1.1.4  christos     }
    890  1.1.1.4  christos 
    891  1.1.1.4  christos    if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
    892  1.1.1.4  christos     {
    893  1.1.1.4  christos       ++ rdm->recursion;
    894  1.1.1.4  christos       if (rdm->recursion > RUST_MAX_RECURSION_COUNT)
    895  1.1.1.4  christos 	/* FIXME: There ought to be a way to report
    896  1.1.1.4  christos 	   that the recursion limit has been reached.  */
    897  1.1.1.4  christos 	{
    898  1.1.1.4  christos 	  rdm->errored = 1;
    899  1.1.1.4  christos 	  -- rdm->recursion;
    900  1.1.1.4  christos 	  return;
    901  1.1.1.4  christos 	}
    902  1.1.1.4  christos     }
    903  1.1.1.4  christos 
    904  1.1.1.4  christos   switch (tag)
    905  1.1.1.4  christos     {
    906  1.1.1.4  christos     case 'R':
    907  1.1.1.4  christos     case 'Q':
    908  1.1.1.4  christos       PRINT ("&");
    909  1.1.1.4  christos       if (eat (rdm, 'L'))
    910  1.1.1.4  christos         {
    911  1.1.1.4  christos           lt = parse_integer_62 (rdm);
    912  1.1.1.4  christos           if (lt)
    913  1.1.1.4  christos             {
    914  1.1.1.4  christos               print_lifetime_from_index (rdm, lt);
    915  1.1.1.4  christos               PRINT (" ");
    916  1.1.1.4  christos             }
    917  1.1.1.4  christos         }
    918  1.1.1.4  christos       if (tag != 'R')
    919  1.1.1.4  christos         PRINT ("mut ");
    920  1.1.1.4  christos       demangle_type (rdm);
    921  1.1.1.4  christos       break;
    922  1.1.1.4  christos     case 'P':
    923  1.1.1.4  christos     case 'O':
    924  1.1.1.4  christos       PRINT ("*");
    925  1.1.1.4  christos       if (tag != 'P')
    926  1.1.1.4  christos         PRINT ("mut ");
    927  1.1.1.4  christos       else
    928  1.1.1.4  christos         PRINT ("const ");
    929  1.1.1.4  christos       demangle_type (rdm);
    930  1.1.1.4  christos       break;
    931  1.1.1.4  christos     case 'A':
    932  1.1.1.4  christos     case 'S':
    933  1.1.1.4  christos       PRINT ("[");
    934  1.1.1.4  christos       demangle_type (rdm);
    935  1.1.1.4  christos       if (tag == 'A')
    936  1.1.1.4  christos         {
    937  1.1.1.4  christos           PRINT ("; ");
    938  1.1.1.4  christos           demangle_const (rdm);
    939  1.1.1.4  christos         }
    940  1.1.1.4  christos       PRINT ("]");
    941  1.1.1.4  christos       break;
    942  1.1.1.4  christos     case 'T':
    943  1.1.1.4  christos       PRINT ("(");
    944  1.1.1.4  christos       for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
    945  1.1.1.4  christos         {
    946  1.1.1.4  christos           if (i > 0)
    947  1.1.1.4  christos             PRINT (", ");
    948  1.1.1.4  christos           demangle_type (rdm);
    949  1.1.1.4  christos         }
    950  1.1.1.4  christos       if (i == 1)
    951  1.1.1.4  christos         PRINT (",");
    952  1.1.1.4  christos       PRINT (")");
    953  1.1.1.4  christos       break;
    954  1.1.1.4  christos     case 'F':
    955  1.1.1.4  christos       old_bound_lifetime_depth = rdm->bound_lifetime_depth;
    956  1.1.1.4  christos       demangle_binder (rdm);
    957  1.1.1.4  christos 
    958  1.1.1.4  christos       if (eat (rdm, 'U'))
    959  1.1.1.4  christos         PRINT ("unsafe ");
    960  1.1.1.4  christos 
    961  1.1.1.4  christos       if (eat (rdm, 'K'))
    962  1.1.1.4  christos         {
    963  1.1.1.4  christos           if (eat (rdm, 'C'))
    964  1.1.1.4  christos             {
    965  1.1.1.4  christos               abi.ascii = "C";
    966  1.1.1.4  christos               abi.ascii_len = 1;
    967  1.1.1.4  christos             }
    968  1.1.1.4  christos           else
    969  1.1.1.4  christos             {
    970  1.1.1.4  christos               abi = parse_ident (rdm);
    971  1.1.1.4  christos               if (!abi.ascii || abi.punycode)
    972  1.1.1.4  christos                 {
    973  1.1.1.4  christos                   rdm->errored = 1;
    974  1.1.1.4  christos                   goto restore;
    975  1.1.1.4  christos                 }
    976  1.1.1.4  christos             }
    977  1.1.1.4  christos 
    978  1.1.1.4  christos           PRINT ("extern \"");
    979  1.1.1.4  christos 
    980  1.1.1.4  christos           /* If the ABI had any `-`, they were replaced with `_`,
    981  1.1.1.4  christos              so the parts between `_` have to be re-joined with `-`. */
    982  1.1.1.4  christos           for (i = 0; i < abi.ascii_len; i++)
    983  1.1.1.4  christos             {
    984  1.1.1.4  christos               if (abi.ascii[i] == '_')
    985  1.1.1.4  christos                 {
    986  1.1.1.4  christos                   print_str (rdm, abi.ascii, i);
    987  1.1.1.4  christos                   PRINT ("-");
    988  1.1.1.4  christos                   abi.ascii += i + 1;
    989  1.1.1.4  christos                   abi.ascii_len -= i + 1;
    990  1.1.1.4  christos                   i = 0;
    991  1.1.1.4  christos                 }
    992  1.1.1.4  christos             }
    993  1.1.1.4  christos           print_str (rdm, abi.ascii, abi.ascii_len);
    994  1.1.1.4  christos 
    995  1.1.1.4  christos           PRINT ("\" ");
    996  1.1.1.4  christos         }
    997  1.1.1.4  christos 
    998  1.1.1.4  christos       PRINT ("fn(");
    999  1.1.1.4  christos       for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
   1000  1.1.1.4  christos         {
   1001  1.1.1.4  christos           if (i > 0)
   1002  1.1.1.4  christos             PRINT (", ");
   1003  1.1.1.4  christos           demangle_type (rdm);
   1004  1.1.1.4  christos         }
   1005  1.1.1.4  christos       PRINT (")");
   1006  1.1.1.4  christos 
   1007  1.1.1.4  christos       if (eat (rdm, 'u'))
   1008  1.1.1.4  christos         {
   1009  1.1.1.4  christos           /* Skip printing the return type if it's 'u', i.e. `()`. */
   1010  1.1.1.4  christos         }
   1011  1.1.1.4  christos       else
   1012  1.1.1.4  christos         {
   1013  1.1.1.4  christos           PRINT (" -> ");
   1014  1.1.1.4  christos           demangle_type (rdm);
   1015  1.1.1.4  christos         }
   1016  1.1.1.4  christos 
   1017  1.1.1.4  christos     /* Restore `bound_lifetime_depth` to outside the binder. */
   1018  1.1.1.4  christos     restore:
   1019  1.1.1.4  christos       rdm->bound_lifetime_depth = old_bound_lifetime_depth;
   1020  1.1.1.4  christos       break;
   1021  1.1.1.4  christos     case 'D':
   1022  1.1.1.4  christos       PRINT ("dyn ");
   1023  1.1.1.4  christos 
   1024  1.1.1.4  christos       old_bound_lifetime_depth = rdm->bound_lifetime_depth;
   1025  1.1.1.4  christos       demangle_binder (rdm);
   1026  1.1.1.4  christos 
   1027  1.1.1.4  christos       for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
   1028  1.1.1.4  christos         {
   1029  1.1.1.4  christos           if (i > 0)
   1030  1.1.1.4  christos             PRINT (" + ");
   1031  1.1.1.4  christos           demangle_dyn_trait (rdm);
   1032  1.1.1.4  christos         }
   1033  1.1.1.4  christos 
   1034  1.1.1.4  christos       /* Restore `bound_lifetime_depth` to outside the binder. */
   1035  1.1.1.4  christos       rdm->bound_lifetime_depth = old_bound_lifetime_depth;
   1036  1.1.1.4  christos 
   1037  1.1.1.4  christos       if (!eat (rdm, 'L'))
   1038  1.1.1.4  christos         {
   1039  1.1.1.4  christos           rdm->errored = 1;
   1040  1.1.1.4  christos           return;
   1041  1.1.1.4  christos         }
   1042  1.1.1.4  christos       lt = parse_integer_62 (rdm);
   1043  1.1.1.4  christos       if (lt)
   1044  1.1.1.4  christos         {
   1045  1.1.1.4  christos           PRINT (" + ");
   1046  1.1.1.4  christos           print_lifetime_from_index (rdm, lt);
   1047  1.1.1.4  christos         }
   1048  1.1.1.4  christos       break;
   1049  1.1.1.4  christos     case 'B':
   1050  1.1.1.4  christos       backref = parse_integer_62 (rdm);
   1051  1.1.1.4  christos       if (!rdm->skipping_printing)
   1052  1.1.1.4  christos         {
   1053  1.1.1.4  christos           old_next = rdm->next;
   1054  1.1.1.4  christos           rdm->next = backref;
   1055  1.1.1.4  christos           demangle_type (rdm);
   1056  1.1.1.4  christos           rdm->next = old_next;
   1057  1.1.1.4  christos         }
   1058  1.1.1.4  christos       break;
   1059  1.1.1.4  christos     default:
   1060  1.1.1.4  christos       /* Go back to the tag, so `demangle_path` also sees it. */
   1061  1.1.1.4  christos       rdm->next--;
   1062  1.1.1.4  christos       demangle_path (rdm, 0);
   1063  1.1.1.4  christos     }
   1064  1.1.1.4  christos 
   1065  1.1.1.4  christos   if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
   1066  1.1.1.4  christos     -- rdm->recursion;
   1067  1.1.1.4  christos }
   1068  1.1.1.4  christos 
   1069  1.1.1.4  christos /* A trait in a trait object may have some "existential projections"
   1070  1.1.1.4  christos    (i.e. associated type bindings) after it, which should be printed
   1071  1.1.1.4  christos    in the `<...>` of the trait, e.g. `dyn Trait<T, U, Assoc=X>`.
   1072  1.1.1.4  christos    To this end, this method will keep the `<...>` of an 'I' path
   1073  1.1.1.4  christos    open, by omitting the `>`, and return `Ok(true)` in that case. */
   1074  1.1.1.4  christos static int
   1075  1.1.1.4  christos demangle_path_maybe_open_generics (struct rust_demangler *rdm)
   1076  1.1.1.4  christos {
   1077  1.1.1.4  christos   int open;
   1078  1.1.1.4  christos   size_t i, old_next, backref;
   1079  1.1.1.4  christos 
   1080  1.1.1.4  christos   open = 0;
   1081  1.1.1.4  christos 
   1082  1.1.1.4  christos   if (rdm->errored)
   1083  1.1.1.4  christos     return open;
   1084  1.1.1.4  christos 
   1085  1.1.1.4  christos   if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
   1086  1.1.1.4  christos     {
   1087  1.1.1.4  christos       ++ rdm->recursion;
   1088  1.1.1.4  christos       if (rdm->recursion > RUST_MAX_RECURSION_COUNT)
   1089  1.1.1.4  christos 	{
   1090  1.1.1.4  christos 	  /* FIXME: There ought to be a way to report
   1091  1.1.1.4  christos 	     that the recursion limit has been reached.  */
   1092  1.1.1.4  christos 	  rdm->errored = 1;
   1093  1.1.1.4  christos 	  goto end_of_func;
   1094  1.1.1.4  christos 	}
   1095  1.1.1.4  christos     }
   1096  1.1.1.4  christos 
   1097  1.1.1.4  christos   if (eat (rdm, 'B'))
   1098  1.1.1.4  christos     {
   1099  1.1.1.4  christos       backref = parse_integer_62 (rdm);
   1100  1.1.1.4  christos       if (!rdm->skipping_printing)
   1101  1.1.1.4  christos         {
   1102  1.1.1.4  christos           old_next = rdm->next;
   1103  1.1.1.4  christos           rdm->next = backref;
   1104  1.1.1.4  christos           open = demangle_path_maybe_open_generics (rdm);
   1105  1.1.1.4  christos           rdm->next = old_next;
   1106  1.1.1.4  christos         }
   1107  1.1.1.4  christos     }
   1108  1.1.1.4  christos   else if (eat (rdm, 'I'))
   1109  1.1.1.4  christos     {
   1110  1.1.1.4  christos       demangle_path (rdm, 0);
   1111  1.1.1.4  christos       PRINT ("<");
   1112  1.1.1.4  christos       open = 1;
   1113  1.1.1.4  christos       for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++)
   1114  1.1.1.4  christos         {
   1115  1.1.1.4  christos           if (i > 0)
   1116  1.1.1.4  christos             PRINT (", ");
   1117  1.1.1.4  christos           demangle_generic_arg (rdm);
   1118  1.1.1.4  christos         }
   1119  1.1.1.4  christos     }
   1120  1.1.1.4  christos   else
   1121  1.1.1.4  christos     demangle_path (rdm, 0);
   1122  1.1.1.4  christos 
   1123  1.1.1.4  christos  end_of_func:
   1124  1.1.1.4  christos   if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
   1125  1.1.1.4  christos     -- rdm->recursion;
   1126  1.1.1.4  christos 
   1127  1.1.1.4  christos   return open;
   1128  1.1.1.4  christos }
   1129  1.1.1.4  christos 
   1130  1.1.1.4  christos static void
   1131  1.1.1.4  christos demangle_dyn_trait (struct rust_demangler *rdm)
   1132  1.1.1.4  christos {
   1133  1.1.1.4  christos   int open;
   1134  1.1.1.4  christos   struct rust_mangled_ident name;
   1135  1.1.1.4  christos 
   1136  1.1.1.4  christos   if (rdm->errored)
   1137  1.1.1.4  christos     return;
   1138  1.1.1.4  christos 
   1139  1.1.1.4  christos   open = demangle_path_maybe_open_generics (rdm);
   1140  1.1.1.4  christos 
   1141  1.1.1.4  christos   while (eat (rdm, 'p'))
   1142  1.1.1.4  christos     {
   1143  1.1.1.4  christos       if (!open)
   1144  1.1.1.4  christos         PRINT ("<");
   1145  1.1.1.4  christos       else
   1146  1.1.1.4  christos         PRINT (", ");
   1147  1.1.1.4  christos       open = 1;
   1148  1.1.1.4  christos 
   1149  1.1.1.4  christos       name = parse_ident (rdm);
   1150  1.1.1.4  christos       print_ident (rdm, name);
   1151  1.1.1.4  christos       PRINT (" = ");
   1152  1.1.1.4  christos       demangle_type (rdm);
   1153  1.1.1.4  christos     }
   1154  1.1.1.4  christos 
   1155  1.1.1.4  christos   if (open)
   1156  1.1.1.4  christos     PRINT (">");
   1157  1.1.1.4  christos }
   1158  1.1.1.4  christos 
   1159  1.1.1.4  christos static void
   1160  1.1.1.4  christos demangle_const (struct rust_demangler *rdm)
   1161  1.1.1.4  christos {
   1162  1.1.1.4  christos   char ty_tag;
   1163  1.1.1.4  christos   size_t old_next, backref;
   1164  1.1.1.4  christos 
   1165  1.1.1.4  christos   if (rdm->errored)
   1166  1.1.1.4  christos     return;
   1167  1.1.1.4  christos 
   1168  1.1.1.4  christos   if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
   1169  1.1.1.4  christos     {
   1170  1.1.1.4  christos       ++ rdm->recursion;
   1171  1.1.1.4  christos       if (rdm->recursion > RUST_MAX_RECURSION_COUNT)
   1172  1.1.1.4  christos 	/* FIXME: There ought to be a way to report
   1173  1.1.1.4  christos 	   that the recursion limit has been reached.  */
   1174  1.1.1.4  christos 	goto fail_return;
   1175  1.1.1.4  christos     }
   1176  1.1.1.4  christos 
   1177  1.1.1.4  christos   if (eat (rdm, 'B'))
   1178  1.1.1.4  christos     {
   1179  1.1.1.4  christos       backref = parse_integer_62 (rdm);
   1180  1.1.1.4  christos       if (!rdm->skipping_printing)
   1181  1.1.1.4  christos         {
   1182  1.1.1.4  christos           old_next = rdm->next;
   1183  1.1.1.4  christos           rdm->next = backref;
   1184  1.1.1.4  christos           demangle_const (rdm);
   1185  1.1.1.4  christos           rdm->next = old_next;
   1186  1.1.1.4  christos         }
   1187  1.1.1.4  christos       goto pass_return;
   1188  1.1.1.4  christos     }
   1189  1.1.1.4  christos 
   1190  1.1.1.4  christos   ty_tag = next (rdm);
   1191  1.1.1.4  christos   switch (ty_tag)
   1192  1.1.1.4  christos     {
   1193  1.1.1.4  christos     /* Placeholder. */
   1194  1.1.1.4  christos     case 'p':
   1195  1.1.1.4  christos       PRINT ("_");
   1196  1.1.1.4  christos       goto pass_return;
   1197  1.1.1.4  christos 
   1198  1.1.1.4  christos     /* Unsigned integer types. */
   1199  1.1.1.4  christos     case 'h':
   1200  1.1.1.4  christos     case 't':
   1201  1.1.1.4  christos     case 'm':
   1202  1.1.1.4  christos     case 'y':
   1203  1.1.1.4  christos     case 'o':
   1204  1.1.1.4  christos     case 'j':
   1205  1.1.1.4  christos       demangle_const_uint (rdm);
   1206  1.1.1.4  christos       break;
   1207  1.1.1.4  christos 
   1208  1.1.1.4  christos     /* Signed integer types. */
   1209  1.1.1.4  christos     case 'a':
   1210  1.1.1.4  christos     case 's':
   1211  1.1.1.4  christos     case 'l':
   1212  1.1.1.4  christos     case 'x':
   1213  1.1.1.4  christos     case 'n':
   1214  1.1.1.4  christos     case 'i':
   1215  1.1.1.4  christos       demangle_const_int (rdm);
   1216  1.1.1.4  christos       break;
   1217  1.1.1.4  christos 
   1218  1.1.1.4  christos     /* Boolean. */
   1219  1.1.1.4  christos     case 'b':
   1220  1.1.1.4  christos       demangle_const_bool (rdm);
   1221  1.1.1.4  christos       break;
   1222  1.1.1.4  christos 
   1223  1.1.1.4  christos     /* Character. */
   1224  1.1.1.4  christos     case 'c':
   1225  1.1.1.4  christos       demangle_const_char (rdm);
   1226  1.1.1.4  christos       break;
   1227  1.1.1.4  christos 
   1228  1.1.1.4  christos     default:
   1229  1.1.1.4  christos       goto fail_return;
   1230  1.1.1.4  christos     }
   1231  1.1.1.4  christos 
   1232  1.1.1.4  christos   if (!rdm->errored && rdm->verbose)
   1233  1.1.1.4  christos     {
   1234  1.1.1.4  christos       PRINT (": ");
   1235  1.1.1.4  christos       PRINT (basic_type (ty_tag));
   1236  1.1.1.4  christos     }
   1237  1.1.1.4  christos   goto pass_return;
   1238  1.1.1.4  christos 
   1239  1.1.1.4  christos  fail_return:
   1240  1.1.1.4  christos   rdm->errored = 1;
   1241  1.1.1.4  christos  pass_return:
   1242  1.1.1.4  christos   if (rdm->recursion != RUST_NO_RECURSION_LIMIT)
   1243  1.1.1.4  christos     -- rdm->recursion;
   1244  1.1.1.4  christos }
   1245  1.1.1.4  christos 
   1246  1.1.1.4  christos static void
   1247  1.1.1.4  christos demangle_const_uint (struct rust_demangler *rdm)
   1248  1.1.1.4  christos {
   1249  1.1.1.4  christos   size_t hex_len;
   1250  1.1.1.4  christos   uint64_t value;
   1251  1.1.1.4  christos 
   1252  1.1.1.4  christos   if (rdm->errored)
   1253  1.1.1.4  christos     return;
   1254  1.1.1.4  christos 
   1255  1.1.1.4  christos   hex_len = parse_hex_nibbles (rdm, &value);
   1256  1.1.1.4  christos 
   1257  1.1.1.4  christos   if (hex_len > 16)
   1258  1.1.1.4  christos     {
   1259  1.1.1.4  christos       /* Print anything that doesn't fit in `uint64_t` verbatim. */
   1260  1.1.1.4  christos       PRINT ("0x");
   1261  1.1.1.4  christos       print_str (rdm, rdm->sym + (rdm->next - hex_len), hex_len);
   1262  1.1.1.4  christos     }
   1263  1.1.1.4  christos   else if (hex_len > 0)
   1264  1.1.1.4  christos     print_uint64 (rdm, value);
   1265  1.1.1.4  christos   else
   1266  1.1.1.4  christos     rdm->errored = 1;
   1267  1.1.1.4  christos }
   1268  1.1.1.4  christos 
   1269  1.1.1.4  christos static void
   1270  1.1.1.4  christos demangle_const_int (struct rust_demangler *rdm)
   1271  1.1.1.4  christos {
   1272  1.1.1.4  christos   if (eat (rdm, 'n'))
   1273  1.1.1.4  christos     PRINT ("-");
   1274  1.1.1.4  christos   demangle_const_uint (rdm);
   1275  1.1.1.4  christos }
   1276  1.1.1.4  christos 
   1277  1.1.1.4  christos static void
   1278  1.1.1.4  christos demangle_const_bool (struct rust_demangler *rdm)
   1279  1.1.1.4  christos {
   1280  1.1.1.4  christos   uint64_t value;
   1281  1.1.1.4  christos 
   1282  1.1.1.4  christos   if (parse_hex_nibbles (rdm, &value) != 1)
   1283  1.1.1.4  christos     {
   1284  1.1.1.4  christos       rdm->errored = 1;
   1285  1.1.1.4  christos       return;
   1286  1.1.1.4  christos     }
   1287  1.1.1.4  christos 
   1288  1.1.1.4  christos   if (value == 0)
   1289  1.1.1.4  christos     PRINT ("false");
   1290  1.1.1.4  christos   else if (value == 1)
   1291  1.1.1.4  christos     PRINT ("true");
   1292  1.1.1.4  christos   else
   1293  1.1.1.4  christos     rdm->errored = 1;
   1294  1.1.1.4  christos }
   1295  1.1.1.4  christos 
   1296  1.1.1.4  christos static void
   1297  1.1.1.4  christos demangle_const_char (struct rust_demangler *rdm)
   1298  1.1.1.4  christos {
   1299  1.1.1.4  christos   size_t hex_len;
   1300  1.1.1.4  christos   uint64_t value;
   1301  1.1.1.4  christos 
   1302  1.1.1.4  christos   hex_len = parse_hex_nibbles (rdm, &value);
   1303  1.1.1.4  christos 
   1304  1.1.1.4  christos   if (hex_len == 0 || hex_len > 8)
   1305  1.1.1.4  christos     {
   1306  1.1.1.4  christos       rdm->errored = 1;
   1307  1.1.1.4  christos       return;
   1308  1.1.1.4  christos     }
   1309  1.1.1.4  christos 
   1310  1.1.1.4  christos   /* Match Rust's character "debug" output as best as we can. */
   1311  1.1.1.4  christos   PRINT ("'");
   1312  1.1.1.4  christos   if (value == '\t')
   1313  1.1.1.4  christos     PRINT ("\\t");
   1314  1.1.1.4  christos   else if (value == '\r')
   1315  1.1.1.4  christos     PRINT ("\\r");
   1316  1.1.1.4  christos   else if (value == '\n')
   1317  1.1.1.4  christos     PRINT ("\\n");
   1318  1.1.1.4  christos   else if (value > ' ' && value < '~')
   1319  1.1.1.4  christos     {
   1320  1.1.1.4  christos       /* Rust also considers many non-ASCII codepoints to be printable, but
   1321  1.1.1.4  christos 	 that logic is not easily ported to C. */
   1322  1.1.1.4  christos       char c = value;
   1323  1.1.1.4  christos       print_str (rdm, &c, 1);
   1324  1.1.1.4  christos     }
   1325  1.1.1.4  christos   else
   1326  1.1.1.4  christos     {
   1327  1.1.1.4  christos       PRINT ("\\u{");
   1328  1.1.1.4  christos       print_uint64_hex (rdm, value);
   1329  1.1.1.4  christos       PRINT ("}");
   1330  1.1.1.4  christos     }
   1331  1.1.1.4  christos   PRINT ("'");
   1332  1.1.1.3  christos }
   1333  1.1.1.3  christos 
   1334  1.1.1.3  christos /* A legacy hash is the prefix "h" followed by 16 lowercase hex digits.
   1335  1.1.1.3  christos    The hex digits must contain at least 5 distinct digits. */
   1336      1.1  christos static int
   1337  1.1.1.3  christos is_legacy_prefixed_hash (struct rust_mangled_ident ident)
   1338      1.1  christos {
   1339  1.1.1.3  christos   uint16_t seen;
   1340  1.1.1.3  christos   int nibble;
   1341  1.1.1.3  christos   size_t i, count;
   1342      1.1  christos 
   1343  1.1.1.3  christos   if (ident.ascii_len != 17 || ident.ascii[0] != 'h')
   1344      1.1  christos     return 0;
   1345      1.1  christos 
   1346  1.1.1.3  christos   seen = 0;
   1347  1.1.1.3  christos   for (i = 0; i < 16; i++)
   1348  1.1.1.3  christos     {
   1349  1.1.1.3  christos       nibble = decode_lower_hex_nibble (ident.ascii[1 + i]);
   1350  1.1.1.3  christos       if (nibble < 0)
   1351  1.1.1.3  christos         return 0;
   1352  1.1.1.3  christos       seen |= (uint16_t)1 << nibble;
   1353  1.1.1.3  christos     }
   1354      1.1  christos 
   1355  1.1.1.3  christos   /* Count how many distinct digits were seen. */
   1356      1.1  christos   count = 0;
   1357  1.1.1.3  christos   while (seen)
   1358  1.1.1.3  christos     {
   1359  1.1.1.3  christos       if (seen & 1)
   1360  1.1.1.3  christos         count++;
   1361  1.1.1.3  christos       seen >>= 1;
   1362  1.1.1.3  christos     }
   1363      1.1  christos 
   1364  1.1.1.3  christos   return count >= 5;
   1365      1.1  christos }
   1366      1.1  christos 
   1367  1.1.1.3  christos int
   1368  1.1.1.3  christos rust_demangle_callback (const char *mangled, int options,
   1369  1.1.1.3  christos                         demangle_callbackref callback, void *opaque)
   1370      1.1  christos {
   1371  1.1.1.3  christos   const char *p;
   1372  1.1.1.3  christos   struct rust_demangler rdm;
   1373  1.1.1.3  christos   struct rust_mangled_ident ident;
   1374  1.1.1.3  christos 
   1375  1.1.1.3  christos   rdm.sym = mangled;
   1376  1.1.1.3  christos   rdm.sym_len = 0;
   1377  1.1.1.3  christos 
   1378  1.1.1.3  christos   rdm.callback_opaque = opaque;
   1379  1.1.1.3  christos   rdm.callback = callback;
   1380  1.1.1.3  christos 
   1381  1.1.1.3  christos   rdm.next = 0;
   1382  1.1.1.3  christos   rdm.errored = 0;
   1383  1.1.1.4  christos   rdm.skipping_printing = 0;
   1384  1.1.1.3  christos   rdm.verbose = (options & DMGL_VERBOSE) != 0;
   1385  1.1.1.3  christos   rdm.version = 0;
   1386  1.1.1.4  christos   rdm.recursion = (options & DMGL_NO_RECURSE_LIMIT) ? RUST_NO_RECURSION_LIMIT : 0;
   1387  1.1.1.4  christos   rdm.bound_lifetime_depth = 0;
   1388  1.1.1.3  christos 
   1389  1.1.1.4  christos   /* Rust symbols always start with _R (v0) or _ZN (legacy). */
   1390  1.1.1.4  christos   if (rdm.sym[0] == '_' && rdm.sym[1] == 'R')
   1391  1.1.1.4  christos     rdm.sym += 2;
   1392  1.1.1.4  christos   else if (rdm.sym[0] == '_' && rdm.sym[1] == 'Z' && rdm.sym[2] == 'N')
   1393  1.1.1.3  christos     {
   1394  1.1.1.3  christos       rdm.sym += 3;
   1395  1.1.1.3  christos       rdm.version = -1;
   1396  1.1.1.3  christos     }
   1397  1.1.1.3  christos   else
   1398  1.1.1.3  christos     return 0;
   1399  1.1.1.3  christos 
   1400  1.1.1.4  christos   /* Paths (v0) always start with uppercase characters. */
   1401  1.1.1.4  christos   if (rdm.version != -1 && !ISUPPER (rdm.sym[0]))
   1402  1.1.1.4  christos     return 0;
   1403  1.1.1.4  christos 
   1404  1.1.1.4  christos   /* Rust symbols (v0) use only [_0-9a-zA-Z] characters. */
   1405  1.1.1.3  christos   for (p = rdm.sym; *p; p++)
   1406  1.1.1.3  christos     {
   1407  1.1.1.4  christos       /* Rust v0 symbols can have '.' suffixes, ignore those.  */
   1408  1.1.1.4  christos       if (rdm.version == 0 && *p == '.')
   1409  1.1.1.4  christos         break;
   1410  1.1.1.4  christos 
   1411  1.1.1.3  christos       rdm.sym_len++;
   1412      1.1  christos 
   1413  1.1.1.3  christos       if (*p == '_' || ISALNUM (*p))
   1414  1.1.1.3  christos         continue;
   1415      1.1  christos 
   1416  1.1.1.4  christos       /* Legacy Rust symbols can also contain [.:$] characters.
   1417  1.1.1.4  christos          Or @ in the .suffix (which will be skipped, see below). */
   1418  1.1.1.4  christos       if (rdm.version == -1 && (*p == '$' || *p == '.' || *p == ':'
   1419  1.1.1.4  christos                                 || *p == '@'))
   1420  1.1.1.3  christos         continue;
   1421  1.1.1.3  christos 
   1422  1.1.1.3  christos       return 0;
   1423  1.1.1.3  christos     }
   1424  1.1.1.3  christos 
   1425  1.1.1.3  christos   /* Legacy Rust symbols need to be handled separately. */
   1426  1.1.1.3  christos   if (rdm.version == -1)
   1427  1.1.1.3  christos     {
   1428  1.1.1.4  christos       /* Legacy Rust symbols always end with E.  But can be followed by a
   1429  1.1.1.4  christos          .suffix (which we want to ignore).  */
   1430  1.1.1.4  christos       int dot_suffix = 1;
   1431  1.1.1.4  christos       while (rdm.sym_len > 0 &&
   1432  1.1.1.4  christos              !(dot_suffix && rdm.sym[rdm.sym_len - 1] == 'E'))
   1433  1.1.1.4  christos         {
   1434  1.1.1.4  christos           dot_suffix = rdm.sym[rdm.sym_len - 1] == '.';
   1435  1.1.1.4  christos           rdm.sym_len--;
   1436  1.1.1.4  christos         }
   1437  1.1.1.4  christos 
   1438  1.1.1.3  christos       if (!(rdm.sym_len > 0 && rdm.sym[rdm.sym_len - 1] == 'E'))
   1439  1.1.1.3  christos         return 0;
   1440  1.1.1.3  christos       rdm.sym_len--;
   1441  1.1.1.3  christos 
   1442  1.1.1.3  christos       /* Legacy Rust symbols also always end with a path segment
   1443  1.1.1.3  christos          that encodes a 16 hex digit hash, i.e. '17h[a-f0-9]{16}'.
   1444  1.1.1.3  christos          This early check, before any parse_ident calls, should
   1445  1.1.1.3  christos          quickly filter out most C++ symbols unrelated to Rust. */
   1446  1.1.1.3  christos       if (!(rdm.sym_len > 19
   1447  1.1.1.3  christos             && !memcmp (&rdm.sym[rdm.sym_len - 19], "17h", 3)))
   1448  1.1.1.3  christos         return 0;
   1449  1.1.1.3  christos 
   1450  1.1.1.3  christos       do
   1451  1.1.1.3  christos         {
   1452  1.1.1.3  christos           ident = parse_ident (&rdm);
   1453  1.1.1.3  christos           if (rdm.errored || !ident.ascii)
   1454  1.1.1.3  christos             return 0;
   1455  1.1.1.3  christos         }
   1456  1.1.1.3  christos       while (rdm.next < rdm.sym_len);
   1457  1.1.1.3  christos 
   1458  1.1.1.3  christos       /* The last path segment should be the hash. */
   1459  1.1.1.3  christos       if (!is_legacy_prefixed_hash (ident))
   1460  1.1.1.3  christos         return 0;
   1461  1.1.1.3  christos 
   1462  1.1.1.3  christos       /* Reset the state for a second pass, to print the symbol. */
   1463  1.1.1.3  christos       rdm.next = 0;
   1464  1.1.1.3  christos       if (!rdm.verbose && rdm.sym_len > 19)
   1465  1.1.1.3  christos         {
   1466  1.1.1.3  christos           /* Hide the last segment, containing the hash, if not verbose. */
   1467  1.1.1.3  christos           rdm.sym_len -= 19;
   1468  1.1.1.3  christos         }
   1469  1.1.1.3  christos 
   1470  1.1.1.3  christos       do
   1471  1.1.1.3  christos         {
   1472  1.1.1.3  christos           if (rdm.next > 0)
   1473  1.1.1.3  christos             print_str (&rdm, "::", 2);
   1474  1.1.1.3  christos 
   1475  1.1.1.3  christos           ident = parse_ident (&rdm);
   1476  1.1.1.3  christos           print_ident (&rdm, ident);
   1477  1.1.1.3  christos         }
   1478  1.1.1.3  christos       while (rdm.next < rdm.sym_len);
   1479  1.1.1.3  christos     }
   1480  1.1.1.3  christos   else
   1481  1.1.1.4  christos     {
   1482  1.1.1.4  christos       demangle_path (&rdm, 1);
   1483  1.1.1.4  christos 
   1484  1.1.1.4  christos       /* Skip instantiating crate. */
   1485  1.1.1.4  christos       if (!rdm.errored && rdm.next < rdm.sym_len)
   1486  1.1.1.4  christos         {
   1487  1.1.1.4  christos           rdm.skipping_printing = 1;
   1488  1.1.1.4  christos           demangle_path (&rdm, 0);
   1489  1.1.1.4  christos         }
   1490  1.1.1.4  christos 
   1491  1.1.1.4  christos       /* It's an error to not reach the end. */
   1492  1.1.1.4  christos       rdm.errored |= rdm.next != rdm.sym_len;
   1493  1.1.1.4  christos     }
   1494  1.1.1.3  christos 
   1495  1.1.1.3  christos   return !rdm.errored;
   1496  1.1.1.3  christos }
   1497  1.1.1.3  christos 
   1498  1.1.1.3  christos /* Growable string buffers. */
   1499  1.1.1.3  christos struct str_buf
   1500  1.1.1.3  christos {
   1501  1.1.1.3  christos   char *ptr;
   1502  1.1.1.3  christos   size_t len;
   1503  1.1.1.3  christos   size_t cap;
   1504  1.1.1.3  christos   int errored;
   1505  1.1.1.3  christos };
   1506  1.1.1.3  christos 
   1507  1.1.1.3  christos static void
   1508  1.1.1.3  christos str_buf_reserve (struct str_buf *buf, size_t extra)
   1509  1.1.1.3  christos {
   1510  1.1.1.3  christos   size_t available, min_new_cap, new_cap;
   1511  1.1.1.3  christos   char *new_ptr;
   1512  1.1.1.3  christos 
   1513  1.1.1.3  christos   /* Allocation failed before. */
   1514  1.1.1.3  christos   if (buf->errored)
   1515  1.1.1.3  christos     return;
   1516  1.1.1.3  christos 
   1517  1.1.1.3  christos   available = buf->cap - buf->len;
   1518  1.1.1.3  christos 
   1519  1.1.1.3  christos   if (extra <= available)
   1520      1.1  christos     return;
   1521      1.1  christos 
   1522  1.1.1.3  christos   min_new_cap = buf->cap + (extra - available);
   1523  1.1.1.3  christos 
   1524  1.1.1.3  christos   /* Check for overflows. */
   1525  1.1.1.3  christos   if (min_new_cap < buf->cap)
   1526  1.1.1.3  christos     {
   1527  1.1.1.3  christos       buf->errored = 1;
   1528  1.1.1.3  christos       return;
   1529  1.1.1.3  christos     }
   1530  1.1.1.3  christos 
   1531  1.1.1.3  christos   new_cap = buf->cap;
   1532  1.1.1.3  christos 
   1533  1.1.1.3  christos   if (new_cap == 0)
   1534  1.1.1.3  christos     new_cap = 4;
   1535  1.1.1.3  christos 
   1536  1.1.1.3  christos   /* Double capacity until sufficiently large. */
   1537  1.1.1.3  christos   while (new_cap < min_new_cap)
   1538  1.1.1.3  christos     {
   1539  1.1.1.3  christos       new_cap *= 2;
   1540  1.1.1.3  christos 
   1541  1.1.1.3  christos       /* Check for overflows. */
   1542  1.1.1.3  christos       if (new_cap < buf->cap)
   1543  1.1.1.3  christos         {
   1544  1.1.1.3  christos           buf->errored = 1;
   1545  1.1.1.3  christos           return;
   1546  1.1.1.3  christos         }
   1547  1.1.1.3  christos     }
   1548  1.1.1.3  christos 
   1549  1.1.1.3  christos   new_ptr = (char *)realloc (buf->ptr, new_cap);
   1550  1.1.1.3  christos   if (new_ptr == NULL)
   1551  1.1.1.3  christos     {
   1552  1.1.1.3  christos       free (buf->ptr);
   1553  1.1.1.3  christos       buf->ptr = NULL;
   1554  1.1.1.3  christos       buf->len = 0;
   1555  1.1.1.3  christos       buf->cap = 0;
   1556  1.1.1.3  christos       buf->errored = 1;
   1557  1.1.1.3  christos     }
   1558  1.1.1.3  christos   else
   1559  1.1.1.3  christos     {
   1560  1.1.1.3  christos       buf->ptr = new_ptr;
   1561  1.1.1.3  christos       buf->cap = new_cap;
   1562  1.1.1.3  christos     }
   1563      1.1  christos }
   1564      1.1  christos 
   1565  1.1.1.3  christos static void
   1566  1.1.1.3  christos str_buf_append (struct str_buf *buf, const char *data, size_t len)
   1567      1.1  christos {
   1568  1.1.1.3  christos   str_buf_reserve (buf, len);
   1569  1.1.1.3  christos   if (buf->errored)
   1570  1.1.1.3  christos     return;
   1571      1.1  christos 
   1572  1.1.1.3  christos   memcpy (buf->ptr + buf->len, data, len);
   1573  1.1.1.3  christos   buf->len += len;
   1574  1.1.1.3  christos }
   1575      1.1  christos 
   1576  1.1.1.3  christos static void
   1577  1.1.1.3  christos str_buf_demangle_callback (const char *data, size_t len, void *opaque)
   1578  1.1.1.3  christos {
   1579  1.1.1.3  christos   str_buf_append ((struct str_buf *)opaque, data, len);
   1580  1.1.1.3  christos }
   1581  1.1.1.3  christos 
   1582  1.1.1.3  christos char *
   1583  1.1.1.3  christos rust_demangle (const char *mangled, int options)
   1584  1.1.1.3  christos {
   1585  1.1.1.3  christos   struct str_buf out;
   1586  1.1.1.3  christos   int success;
   1587      1.1  christos 
   1588  1.1.1.3  christos   out.ptr = NULL;
   1589  1.1.1.3  christos   out.len = 0;
   1590  1.1.1.3  christos   out.cap = 0;
   1591  1.1.1.3  christos   out.errored = 0;
   1592  1.1.1.3  christos 
   1593  1.1.1.3  christos   success = rust_demangle_callback (mangled, options,
   1594  1.1.1.3  christos                                     str_buf_demangle_callback, &out);
   1595  1.1.1.3  christos 
   1596  1.1.1.3  christos   if (!success)
   1597  1.1.1.3  christos     {
   1598  1.1.1.3  christos       free (out.ptr);
   1599  1.1.1.3  christos       return NULL;
   1600  1.1.1.3  christos     }
   1601      1.1  christos 
   1602  1.1.1.3  christos   str_buf_append (&out, "\0", 1);
   1603  1.1.1.3  christos   return out.ptr;
   1604      1.1  christos }
   1605