| /* Demangler for the Rust programming language | 
 |    Copyright (C) 2016-2025 Free Software Foundation, Inc. | 
 |    Written by David Tolnay (dtolnay@gmail.com). | 
 |    Rewritten by Eduard-Mihai Burtescu (eddyb@lyken.rs) for v0 support. | 
 |  | 
 | This file is part of the libiberty library. | 
 | Libiberty is free software; you can redistribute it and/or | 
 | modify it under the terms of the GNU Library General Public | 
 | License as published by the Free Software Foundation; either | 
 | version 2 of the License, or (at your option) any later version. | 
 |  | 
 | In addition to the permissions in the GNU Library General Public | 
 | License, the Free Software Foundation gives you unlimited permission | 
 | to link the compiled version of this file into combinations with other | 
 | programs, and to distribute those combinations without any restriction | 
 | coming from the use of this file.  (The Library Public License | 
 | restrictions do apply in other respects; for example, they cover | 
 | modification of the file, and distribution when not linked into a | 
 | combined executable.) | 
 |  | 
 | Libiberty is distributed in the hope that it will be useful, | 
 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
 | Library General Public License for more details. | 
 |  | 
 | You should have received a copy of the GNU Library General Public | 
 | License along with libiberty; see the file COPYING.LIB. | 
 | If not, see <http://www.gnu.org/licenses/>.  */ | 
 |  | 
 |  | 
 | #ifdef HAVE_CONFIG_H | 
 | #include "config.h" | 
 | #endif | 
 |  | 
 | #include "safe-ctype.h" | 
 |  | 
 | #include <inttypes.h> | 
 | #include <sys/types.h> | 
 | #include <string.h> | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 |  | 
 | #ifdef HAVE_STRING_H | 
 | #include <string.h> | 
 | #else | 
 | extern size_t strlen(const char *s); | 
 | extern int strncmp(const char *s1, const char *s2, size_t n); | 
 | extern void *memset(void *s, int c, size_t n); | 
 | #endif | 
 |  | 
 | #include <demangle.h> | 
 | #include "libiberty.h" | 
 |  | 
 | struct rust_demangler | 
 | { | 
 |   const char *sym; | 
 |   size_t sym_len; | 
 |  | 
 |   void *callback_opaque; | 
 |   demangle_callbackref callback; | 
 |  | 
 |   /* Position of the next character to read from the symbol. */ | 
 |   size_t next; | 
 |  | 
 |   /* Non-zero if any error occurred. */ | 
 |   int errored; | 
 |  | 
 |   /* Non-zero if nothing should be printed. */ | 
 |   int skipping_printing; | 
 |  | 
 |   /* Non-zero if printing should be verbose (e.g. include hashes). */ | 
 |   int verbose; | 
 |  | 
 |   /* Rust mangling version, with legacy mangling being -1. */ | 
 |   int version; | 
 |  | 
 |   /* Recursion depth.  */ | 
 |   unsigned int recursion; | 
 |   /* Maximum number of times demangle_path may be called recursively.  */ | 
 | #define RUST_MAX_RECURSION_COUNT  1024 | 
 | #define RUST_NO_RECURSION_LIMIT   ((unsigned int) -1) | 
 |  | 
 |   uint64_t bound_lifetime_depth; | 
 | }; | 
 |  | 
 | /* Parsing functions. */ | 
 |  | 
 | static char | 
 | peek (const struct rust_demangler *rdm) | 
 | { | 
 |   if (rdm->next < rdm->sym_len) | 
 |     return rdm->sym[rdm->next]; | 
 |   return 0; | 
 | } | 
 |  | 
 | static int | 
 | eat (struct rust_demangler *rdm, char c) | 
 | { | 
 |   if (peek (rdm) == c) | 
 |     { | 
 |       rdm->next++; | 
 |       return 1; | 
 |     } | 
 |   else | 
 |     return 0; | 
 | } | 
 |  | 
 | static char | 
 | next (struct rust_demangler *rdm) | 
 | { | 
 |   char c = peek (rdm); | 
 |   if (!c) | 
 |     rdm->errored = 1; | 
 |   else | 
 |     rdm->next++; | 
 |   return c; | 
 | } | 
 |  | 
 | static uint64_t | 
 | parse_integer_62 (struct rust_demangler *rdm) | 
 | { | 
 |   char c; | 
 |   uint64_t x; | 
 |  | 
 |   if (eat (rdm, '_')) | 
 |     return 0; | 
 |  | 
 |   x = 0; | 
 |   while (!eat (rdm, '_') && !rdm->errored) | 
 |     { | 
 |       c = next (rdm); | 
 |       x *= 62; | 
 |       if (ISDIGIT (c)) | 
 |         x += c - '0'; | 
 |       else if (ISLOWER (c)) | 
 |         x += 10 + (c - 'a'); | 
 |       else if (ISUPPER (c)) | 
 |         x += 10 + 26 + (c - 'A'); | 
 |       else | 
 |         { | 
 |           rdm->errored = 1; | 
 |           return 0; | 
 |         } | 
 |     } | 
 |   return x + 1; | 
 | } | 
 |  | 
 | static uint64_t | 
 | parse_opt_integer_62 (struct rust_demangler *rdm, char tag) | 
 | { | 
 |   if (!eat (rdm, tag)) | 
 |     return 0; | 
 |   return 1 + parse_integer_62 (rdm); | 
 | } | 
 |  | 
 | static uint64_t | 
 | parse_disambiguator (struct rust_demangler *rdm) | 
 | { | 
 |   return parse_opt_integer_62 (rdm, 's'); | 
 | } | 
 |  | 
 | static size_t | 
 | parse_hex_nibbles (struct rust_demangler *rdm, uint64_t *value) | 
 | { | 
 |   char c; | 
 |   size_t hex_len; | 
 |  | 
 |   hex_len = 0; | 
 |   *value = 0; | 
 |  | 
 |   while (!eat (rdm, '_')) | 
 |     { | 
 |       *value <<= 4; | 
 |  | 
 |       c = next (rdm); | 
 |       if (ISDIGIT (c)) | 
 |         *value |= c - '0'; | 
 |       else if (c >= 'a' && c <= 'f') | 
 |         *value |= 10 + (c - 'a'); | 
 |       else | 
 |         { | 
 |           rdm->errored = 1; | 
 |           return 0; | 
 |         } | 
 |       hex_len++; | 
 |     } | 
 |  | 
 |   return hex_len; | 
 | } | 
 |  | 
 | struct rust_mangled_ident | 
 | { | 
 |   /* ASCII part of the identifier. */ | 
 |   const char *ascii; | 
 |   size_t ascii_len; | 
 |  | 
 |   /* Punycode insertion codes for Unicode codepoints, if any. */ | 
 |   const char *punycode; | 
 |   size_t punycode_len; | 
 | }; | 
 |  | 
 | static struct rust_mangled_ident | 
 | parse_ident (struct rust_demangler *rdm) | 
 | { | 
 |   char c; | 
 |   size_t start, len; | 
 |   int is_punycode = 0; | 
 |   struct rust_mangled_ident ident; | 
 |  | 
 |   ident.ascii = NULL; | 
 |   ident.ascii_len = 0; | 
 |   ident.punycode = NULL; | 
 |   ident.punycode_len = 0; | 
 |  | 
 |   if (rdm->version != -1) | 
 |     is_punycode = eat (rdm, 'u'); | 
 |  | 
 |   c = next (rdm); | 
 |   if (!ISDIGIT (c)) | 
 |     { | 
 |       rdm->errored = 1; | 
 |       return ident; | 
 |     } | 
 |   len = c - '0'; | 
 |  | 
 |   if (c != '0') | 
 |     while (ISDIGIT (peek (rdm))) | 
 |       len = len * 10 + (next (rdm) - '0'); | 
 |  | 
 |   /* Skip past the optional `_` separator (v0). */ | 
 |   if (rdm->version != -1) | 
 |     eat (rdm, '_'); | 
 |  | 
 |   start = rdm->next; | 
 |   rdm->next += len; | 
 |   /* Check for overflows. */ | 
 |   if ((start > rdm->next) || (rdm->next > rdm->sym_len)) | 
 |     { | 
 |       rdm->errored = 1; | 
 |       return ident; | 
 |     } | 
 |  | 
 |   ident.ascii = rdm->sym + start; | 
 |   ident.ascii_len = len; | 
 |  | 
 |   if (is_punycode) | 
 |     { | 
 |       ident.punycode_len = 0; | 
 |       while (ident.ascii_len > 0) | 
 |         { | 
 |           ident.ascii_len--; | 
 |  | 
 |           /* The last '_' is a separator between ascii & punycode. */ | 
 |           if (ident.ascii[ident.ascii_len] == '_') | 
 |             break; | 
 |  | 
 |           ident.punycode_len++; | 
 |         } | 
 |       if (!ident.punycode_len) | 
 |         { | 
 |           rdm->errored = 1; | 
 |           return ident; | 
 |         } | 
 |       ident.punycode = ident.ascii + (len - ident.punycode_len); | 
 |     } | 
 |  | 
 |   if (ident.ascii_len == 0) | 
 |     ident.ascii = NULL; | 
 |  | 
 |   return ident; | 
 | } | 
 |  | 
 | /* Printing functions. */ | 
 |  | 
 | static void | 
 | print_str (struct rust_demangler *rdm, const char *data, size_t len) | 
 | { | 
 |   if (!rdm->errored && !rdm->skipping_printing) | 
 |     rdm->callback (data, len, rdm->callback_opaque); | 
 | } | 
 |  | 
 | #define PRINT(s) print_str (rdm, s, strlen (s)) | 
 |  | 
 | static void | 
 | print_uint64 (struct rust_demangler *rdm, uint64_t x) | 
 | { | 
 |   char s[21]; | 
 |   snprintf (s, 21, "%" PRIu64, x); | 
 |   PRINT (s); | 
 | } | 
 |  | 
 | static void | 
 | print_uint64_hex (struct rust_demangler *rdm, uint64_t x) | 
 | { | 
 |   char s[17]; | 
 |   snprintf (s, 17, "%" PRIx64, x); | 
 |   PRINT (s); | 
 | } | 
 |  | 
 | /* Return a 0x0-0xf value if the char is 0-9a-f, and -1 otherwise. */ | 
 | static int | 
 | decode_lower_hex_nibble (char nibble) | 
 | { | 
 |   if ('0' <= nibble && nibble <= '9') | 
 |     return nibble - '0'; | 
 |   if ('a' <= nibble && nibble <= 'f') | 
 |     return 0xa + (nibble - 'a'); | 
 |   return -1; | 
 | } | 
 |  | 
 | /* Return the unescaped character for a "$...$" escape, or 0 if invalid. */ | 
 | static char | 
 | decode_legacy_escape (const char *e, size_t len, size_t *out_len) | 
 | { | 
 |   char c = 0; | 
 |   size_t escape_len = 0; | 
 |   int lo_nibble = -1, hi_nibble = -1; | 
 |  | 
 |   if (len < 3 || e[0] != '$') | 
 |     return 0; | 
 |  | 
 |   e++; | 
 |   len--; | 
 |  | 
 |   if (e[0] == 'C') | 
 |     { | 
 |       escape_len = 1; | 
 |  | 
 |       c = ','; | 
 |     } | 
 |   else if (len > 2) | 
 |     { | 
 |       escape_len = 2; | 
 |  | 
 |       if (e[0] == 'S' && e[1] == 'P') | 
 |         c = '@'; | 
 |       else if (e[0] == 'B' && e[1] == 'P') | 
 |         c = '*'; | 
 |       else if (e[0] == 'R' && e[1] == 'F') | 
 |         c = '&'; | 
 |       else if (e[0] == 'L' && e[1] == 'T') | 
 |         c = '<'; | 
 |       else if (e[0] == 'G' && e[1] == 'T') | 
 |         c = '>'; | 
 |       else if (e[0] == 'L' && e[1] == 'P') | 
 |         c = '('; | 
 |       else if (e[0] == 'R' && e[1] == 'P') | 
 |         c = ')'; | 
 |       else if (e[0] == 'u' && len > 3) | 
 |         { | 
 |           escape_len = 3; | 
 |  | 
 |           hi_nibble = decode_lower_hex_nibble (e[1]); | 
 |           if (hi_nibble < 0) | 
 |             return 0; | 
 |           lo_nibble = decode_lower_hex_nibble (e[2]); | 
 |           if (lo_nibble < 0) | 
 |             return 0; | 
 |  | 
 |           /* Only allow non-control ASCII characters. */ | 
 |           if (hi_nibble > 7) | 
 |             return 0; | 
 |           c = (hi_nibble << 4) | lo_nibble; | 
 |           if (c < 0x20) | 
 |             return 0; | 
 |         } | 
 |     } | 
 |  | 
 |   if (!c || len <= escape_len || e[escape_len] != '$') | 
 |     return 0; | 
 |  | 
 |   *out_len = 2 + escape_len; | 
 |   return c; | 
 | } | 
 |  | 
 | static void | 
 | print_ident (struct rust_demangler *rdm, struct rust_mangled_ident ident) | 
 | { | 
 |   char unescaped; | 
 |   uint8_t *out, *p, d; | 
 |   size_t len, cap, punycode_pos, j; | 
 |   /* Punycode parameters and state. */ | 
 |   uint32_t c; | 
 |   size_t base, t_min, t_max, skew, damp, bias, i; | 
 |   size_t delta, w, k, t; | 
 |  | 
 |   if (rdm->errored || rdm->skipping_printing) | 
 |     return; | 
 |  | 
 |   if (rdm->version == -1) | 
 |     { | 
 |       /* Ignore leading underscores preceding escape sequences. | 
 |          The mangler inserts an underscore to make sure the | 
 |          identifier begins with a XID_Start character. */ | 
 |       if (ident.ascii_len >= 2 && ident.ascii[0] == '_' | 
 |           && ident.ascii[1] == '$') | 
 |         { | 
 |           ident.ascii++; | 
 |           ident.ascii_len--; | 
 |         } | 
 |  | 
 |       while (ident.ascii_len > 0) | 
 |         { | 
 |           /* Handle legacy escape sequences ("$...$", ".." or "."). */ | 
 |           if (ident.ascii[0] == '$') | 
 |             { | 
 |               unescaped | 
 |                   = decode_legacy_escape (ident.ascii, ident.ascii_len, &len); | 
 |               if (unescaped) | 
 |                 print_str (rdm, &unescaped, 1); | 
 |               else | 
 |                 { | 
 |                   /* Unexpected escape sequence, print the rest verbatim. */ | 
 |                   print_str (rdm, ident.ascii, ident.ascii_len); | 
 |                   return; | 
 |                 } | 
 |             } | 
 |           else if (ident.ascii[0] == '.') | 
 |             { | 
 |               if (ident.ascii_len >= 2 && ident.ascii[1] == '.') | 
 |                 { | 
 |                   /* ".." becomes "::" */ | 
 |                   PRINT ("::"); | 
 |                   len = 2; | 
 |                 } | 
 |               else | 
 |                 { | 
 |                   PRINT ("."); | 
 |                   len = 1; | 
 |                 } | 
 |             } | 
 |           else | 
 |             { | 
 |               /* Print everything before the next escape sequence, at once. */ | 
 |               for (len = 0; len < ident.ascii_len; len++) | 
 |                 if (ident.ascii[len] == '$' || ident.ascii[len] == '.') | 
 |                   break; | 
 |  | 
 |               print_str (rdm, ident.ascii, len); | 
 |             } | 
 |  | 
 |           ident.ascii += len; | 
 |           ident.ascii_len -= len; | 
 |         } | 
 |  | 
 |       return; | 
 |     } | 
 |  | 
 |   if (!ident.punycode) | 
 |     { | 
 |       print_str (rdm, ident.ascii, ident.ascii_len); | 
 |       return; | 
 |     } | 
 |  | 
 |   len = 0; | 
 |   cap = 4; | 
 |   while (cap < ident.ascii_len) | 
 |     { | 
 |       cap *= 2; | 
 |       /* Check for overflows. */ | 
 |       if ((cap * 4) / 4 != cap) | 
 |         { | 
 |           rdm->errored = 1; | 
 |           return; | 
 |         } | 
 |     } | 
 |  | 
 |   /* Store the output codepoints as groups of 4 UTF-8 bytes. */ | 
 |   out = (uint8_t *)malloc (cap * 4); | 
 |   if (!out) | 
 |     { | 
 |       rdm->errored = 1; | 
 |       return; | 
 |     } | 
 |  | 
 |   /* Populate initial output from ASCII fragment. */ | 
 |   for (len = 0; len < ident.ascii_len; len++) | 
 |     { | 
 |       p = out + 4 * len; | 
 |       p[0] = 0; | 
 |       p[1] = 0; | 
 |       p[2] = 0; | 
 |       p[3] = ident.ascii[len]; | 
 |     } | 
 |  | 
 |   /* Punycode parameters and initial state. */ | 
 |   base = 36; | 
 |   t_min = 1; | 
 |   t_max = 26; | 
 |   skew = 38; | 
 |   damp = 700; | 
 |   bias = 72; | 
 |   i = 0; | 
 |   c = 0x80; | 
 |  | 
 |   punycode_pos = 0; | 
 |   while (punycode_pos < ident.punycode_len) | 
 |     { | 
 |       /* Read one delta value. */ | 
 |       delta = 0; | 
 |       w = 1; | 
 |       k = 0; | 
 |       do | 
 |         { | 
 |           k += base; | 
 |           t = k < bias ? 0 : (k - bias); | 
 |           if (t < t_min) | 
 |             t = t_min; | 
 |           if (t > t_max) | 
 |             t = t_max; | 
 |  | 
 |           if (punycode_pos >= ident.punycode_len) | 
 |             goto cleanup; | 
 |           d = ident.punycode[punycode_pos++]; | 
 |  | 
 |           if (ISLOWER (d)) | 
 |             d = d - 'a'; | 
 |           else if (ISDIGIT (d)) | 
 |             d = 26 + (d - '0'); | 
 |           else | 
 |             { | 
 |               rdm->errored = 1; | 
 |               goto cleanup; | 
 |             } | 
 |  | 
 |           delta += d * w; | 
 |           w *= base - t; | 
 |         } | 
 |       while (d >= t); | 
 |  | 
 |       /* Compute the new insert position and character. */ | 
 |       len++; | 
 |       i += delta; | 
 |       c += i / len; | 
 |       i %= len; | 
 |  | 
 |       /* Ensure enough space is available. */ | 
 |       if (cap < len) | 
 |         { | 
 |           cap *= 2; | 
 |           /* Check for overflows. */ | 
 |           if ((cap * 4) / 4 != cap || cap < len) | 
 |             { | 
 |               rdm->errored = 1; | 
 |               goto cleanup; | 
 |             } | 
 |         } | 
 |       p = (uint8_t *)realloc (out, cap * 4); | 
 |       if (!p) | 
 |         { | 
 |           rdm->errored = 1; | 
 |           goto cleanup; | 
 |         } | 
 |       out = p; | 
 |  | 
 |       /* Move the characters after the insert position. */ | 
 |       p = out + i * 4; | 
 |       memmove (p + 4, p, (len - i - 1) * 4); | 
 |  | 
 |       /* Insert the new character, as UTF-8 bytes. */ | 
 |       p[0] = c >= 0x10000 ? 0xf0 | (c >> 18) : 0; | 
 |       p[1] = c >= 0x800 ? (c < 0x10000 ? 0xe0 : 0x80) | ((c >> 12) & 0x3f) : 0; | 
 |       p[2] = (c < 0x800 ? 0xc0 : 0x80) | ((c >> 6) & 0x3f); | 
 |       p[3] = 0x80 | (c & 0x3f); | 
 |  | 
 |       /* If there are no more deltas, decoding is complete. */ | 
 |       if (punycode_pos == ident.punycode_len) | 
 |         break; | 
 |  | 
 |       i++; | 
 |  | 
 |       /* Perform bias adaptation. */ | 
 |       delta /= damp; | 
 |       damp = 2; | 
 |  | 
 |       delta += delta / len; | 
 |       k = 0; | 
 |       while (delta > ((base - t_min) * t_max) / 2) | 
 |         { | 
 |           delta /= base - t_min; | 
 |           k += base; | 
 |         } | 
 |       bias = k + ((base - t_min + 1) * delta) / (delta + skew); | 
 |     } | 
 |  | 
 |   /* Remove all the 0 bytes to leave behind an UTF-8 string. */ | 
 |   for (i = 0, j = 0; i < len * 4; i++) | 
 |     if (out[i] != 0) | 
 |       out[j++] = out[i]; | 
 |  | 
 |   print_str (rdm, (const char *)out, j); | 
 |  | 
 | cleanup: | 
 |   free (out); | 
 | } | 
 |  | 
 | /* Print the lifetime according to the previously decoded index. | 
 |    An index of `0` always refers to `'_`, but starting with `1`, | 
 |    indices refer to late-bound lifetimes introduced by a binder. */ | 
 | static void | 
 | print_lifetime_from_index (struct rust_demangler *rdm, uint64_t lt) | 
 | { | 
 |   char c; | 
 |   uint64_t depth; | 
 |  | 
 |   PRINT ("'"); | 
 |   if (lt == 0) | 
 |     { | 
 |       PRINT ("_"); | 
 |       return; | 
 |     } | 
 |  | 
 |   depth = rdm->bound_lifetime_depth - lt; | 
 |   /* Try to print lifetimes alphabetically first. */ | 
 |   if (depth < 26) | 
 |     { | 
 |       c = 'a' + depth; | 
 |       print_str (rdm, &c, 1); | 
 |     } | 
 |   else | 
 |     { | 
 |       /* Use `'_123` after running out of letters. */ | 
 |       PRINT ("_"); | 
 |       print_uint64 (rdm, depth); | 
 |     } | 
 | } | 
 |  | 
 | /* Demangling functions. */ | 
 |  | 
 | static void demangle_binder (struct rust_demangler *rdm); | 
 | static void demangle_path (struct rust_demangler *rdm, int in_value); | 
 | static void demangle_generic_arg (struct rust_demangler *rdm); | 
 | static void demangle_type (struct rust_demangler *rdm); | 
 | static int demangle_path_maybe_open_generics (struct rust_demangler *rdm); | 
 | static void demangle_dyn_trait (struct rust_demangler *rdm); | 
 | static void demangle_const (struct rust_demangler *rdm); | 
 | static void demangle_const_uint (struct rust_demangler *rdm); | 
 | static void demangle_const_int (struct rust_demangler *rdm); | 
 | static void demangle_const_bool (struct rust_demangler *rdm); | 
 | static void demangle_const_char (struct rust_demangler *rdm); | 
 |  | 
 | /* Optionally enter a binder ('G') for late-bound lifetimes, | 
 |    printing e.g. `for<'a, 'b> `, and make those lifetimes visible | 
 |    to the caller (via depth level, which the caller should reset). */ | 
 | static void | 
 | demangle_binder (struct rust_demangler *rdm) | 
 | { | 
 |   uint64_t i, bound_lifetimes; | 
 |  | 
 |   if (rdm->errored) | 
 |     return; | 
 |  | 
 |   bound_lifetimes = parse_opt_integer_62 (rdm, 'G'); | 
 |   if (bound_lifetimes > 0) | 
 |     { | 
 |       PRINT ("for<"); | 
 |       for (i = 0; i < bound_lifetimes; i++) | 
 |         { | 
 |           if (i > 0) | 
 |             PRINT (", "); | 
 |           rdm->bound_lifetime_depth++; | 
 |           print_lifetime_from_index (rdm, 1); | 
 |         } | 
 |       PRINT ("> "); | 
 |     } | 
 | } | 
 |  | 
 | static void | 
 | demangle_path (struct rust_demangler *rdm, int in_value) | 
 | { | 
 |   char tag, ns; | 
 |   int was_skipping_printing; | 
 |   size_t i, backref, old_next; | 
 |   uint64_t dis; | 
 |   struct rust_mangled_ident name; | 
 |  | 
 |   if (rdm->errored) | 
 |     return; | 
 |  | 
 |   if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | 
 |     { | 
 |       ++ rdm->recursion; | 
 |       if (rdm->recursion > RUST_MAX_RECURSION_COUNT) | 
 | 	/* FIXME: There ought to be a way to report | 
 | 	   that the recursion limit has been reached.  */ | 
 | 	goto fail_return; | 
 |     } | 
 |  | 
 |   switch (tag = next (rdm)) | 
 |     { | 
 |     case 'C': | 
 |       dis = parse_disambiguator (rdm); | 
 |       name = parse_ident (rdm); | 
 |  | 
 |       print_ident (rdm, name); | 
 |       if (rdm->verbose) | 
 |         { | 
 |           PRINT ("["); | 
 |           print_uint64_hex (rdm, dis); | 
 |           PRINT ("]"); | 
 |         } | 
 |       break; | 
 |     case 'N': | 
 |       ns = next (rdm); | 
 |       if (!ISLOWER (ns) && !ISUPPER (ns)) | 
 | 	goto fail_return; | 
 |  | 
 |       demangle_path (rdm, in_value); | 
 |  | 
 |       dis = parse_disambiguator (rdm); | 
 |       name = parse_ident (rdm); | 
 |  | 
 |       if (ISUPPER (ns)) | 
 |         { | 
 |           /* Special namespaces, like closures and shims. */ | 
 |           PRINT ("::{"); | 
 |           switch (ns) | 
 |             { | 
 |             case 'C': | 
 |               PRINT ("closure"); | 
 |               break; | 
 |             case 'S': | 
 |               PRINT ("shim"); | 
 |               break; | 
 |             default: | 
 |               print_str (rdm, &ns, 1); | 
 |             } | 
 |           if (name.ascii || name.punycode) | 
 |             { | 
 |               PRINT (":"); | 
 |               print_ident (rdm, name); | 
 |             } | 
 |           PRINT ("#"); | 
 |           print_uint64 (rdm, dis); | 
 |           PRINT ("}"); | 
 |         } | 
 |       else | 
 |         { | 
 |           /* Implementation-specific/unspecified namespaces. */ | 
 |  | 
 |           if (name.ascii || name.punycode) | 
 |             { | 
 |               PRINT ("::"); | 
 |               print_ident (rdm, name); | 
 |             } | 
 |         } | 
 |       break; | 
 |     case 'M': | 
 |     case 'X': | 
 |       /* Ignore the `impl`'s own path.*/ | 
 |       parse_disambiguator (rdm); | 
 |       was_skipping_printing = rdm->skipping_printing; | 
 |       rdm->skipping_printing = 1; | 
 |       demangle_path (rdm, in_value); | 
 |       rdm->skipping_printing = was_skipping_printing; | 
 |       /* fallthrough */ | 
 |     case 'Y': | 
 |       PRINT ("<"); | 
 |       demangle_type (rdm); | 
 |       if (tag != 'M') | 
 |         { | 
 |           PRINT (" as "); | 
 |           demangle_path (rdm, 0); | 
 |         } | 
 |       PRINT (">"); | 
 |       break; | 
 |     case 'I': | 
 |       demangle_path (rdm, in_value); | 
 |       if (in_value) | 
 |         PRINT ("::"); | 
 |       PRINT ("<"); | 
 |       for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++) | 
 |         { | 
 |           if (i > 0) | 
 |             PRINT (", "); | 
 |           demangle_generic_arg (rdm); | 
 |         } | 
 |       PRINT (">"); | 
 |       break; | 
 |     case 'B': | 
 |       backref = parse_integer_62 (rdm); | 
 |       if (!rdm->skipping_printing) | 
 |         { | 
 |           old_next = rdm->next; | 
 |           rdm->next = backref; | 
 |           demangle_path (rdm, in_value); | 
 |           rdm->next = old_next; | 
 |         } | 
 |       break; | 
 |     default: | 
 |       goto fail_return; | 
 |     } | 
 |   goto pass_return; | 
 |  | 
 |  fail_return: | 
 |   rdm->errored = 1; | 
 |  pass_return: | 
 |   if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | 
 |     -- rdm->recursion; | 
 | } | 
 |  | 
 | static void | 
 | demangle_generic_arg (struct rust_demangler *rdm) | 
 | { | 
 |   uint64_t lt; | 
 |   if (eat (rdm, 'L')) | 
 |     { | 
 |       lt = parse_integer_62 (rdm); | 
 |       print_lifetime_from_index (rdm, lt); | 
 |     } | 
 |   else if (eat (rdm, 'K')) | 
 |     demangle_const (rdm); | 
 |   else | 
 |     demangle_type (rdm); | 
 | } | 
 |  | 
 | static const char * | 
 | basic_type (char tag) | 
 | { | 
 |   switch (tag) | 
 |     { | 
 |     case 'b': | 
 |       return "bool"; | 
 |     case 'c': | 
 |       return "char"; | 
 |     case 'e': | 
 |       return "str"; | 
 |     case 'u': | 
 |       return "()"; | 
 |     case 'a': | 
 |       return "i8"; | 
 |     case 's': | 
 |       return "i16"; | 
 |     case 'l': | 
 |       return "i32"; | 
 |     case 'x': | 
 |       return "i64"; | 
 |     case 'n': | 
 |       return "i128"; | 
 |     case 'i': | 
 |       return "isize"; | 
 |     case 'h': | 
 |       return "u8"; | 
 |     case 't': | 
 |       return "u16"; | 
 |     case 'm': | 
 |       return "u32"; | 
 |     case 'y': | 
 |       return "u64"; | 
 |     case 'o': | 
 |       return "u128"; | 
 |     case 'j': | 
 |       return "usize"; | 
 |     case 'f': | 
 |       return "f32"; | 
 |     case 'd': | 
 |       return "f64"; | 
 |     case 'z': | 
 |       return "!"; | 
 |     case 'p': | 
 |       return "_"; | 
 |     case 'v': | 
 |       return "..."; | 
 |  | 
 |     default: | 
 |       return NULL; | 
 |     } | 
 | } | 
 |  | 
 | static void | 
 | demangle_type (struct rust_demangler *rdm) | 
 | { | 
 |   char tag; | 
 |   size_t i, old_next, backref; | 
 |   uint64_t lt, old_bound_lifetime_depth; | 
 |   const char *basic; | 
 |   struct rust_mangled_ident abi; | 
 |  | 
 |   if (rdm->errored) | 
 |     return; | 
 |  | 
 |   tag = next (rdm); | 
 |  | 
 |   basic = basic_type (tag); | 
 |   if (basic) | 
 |     { | 
 |       PRINT (basic); | 
 |       return; | 
 |     } | 
 |  | 
 |    if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | 
 |     { | 
 |       ++ rdm->recursion; | 
 |       if (rdm->recursion > RUST_MAX_RECURSION_COUNT) | 
 | 	/* FIXME: There ought to be a way to report | 
 | 	   that the recursion limit has been reached.  */ | 
 | 	{ | 
 | 	  rdm->errored = 1; | 
 | 	  -- rdm->recursion; | 
 | 	  return; | 
 | 	} | 
 |     } | 
 |  | 
 |   switch (tag) | 
 |     { | 
 |     case 'R': | 
 |     case 'Q': | 
 |       PRINT ("&"); | 
 |       if (eat (rdm, 'L')) | 
 |         { | 
 |           lt = parse_integer_62 (rdm); | 
 |           if (lt) | 
 |             { | 
 |               print_lifetime_from_index (rdm, lt); | 
 |               PRINT (" "); | 
 |             } | 
 |         } | 
 |       if (tag != 'R') | 
 |         PRINT ("mut "); | 
 |       demangle_type (rdm); | 
 |       break; | 
 |     case 'P': | 
 |     case 'O': | 
 |       PRINT ("*"); | 
 |       if (tag != 'P') | 
 |         PRINT ("mut "); | 
 |       else | 
 |         PRINT ("const "); | 
 |       demangle_type (rdm); | 
 |       break; | 
 |     case 'A': | 
 |     case 'S': | 
 |       PRINT ("["); | 
 |       demangle_type (rdm); | 
 |       if (tag == 'A') | 
 |         { | 
 |           PRINT ("; "); | 
 |           demangle_const (rdm); | 
 |         } | 
 |       PRINT ("]"); | 
 |       break; | 
 |     case 'T': | 
 |       PRINT ("("); | 
 |       for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++) | 
 |         { | 
 |           if (i > 0) | 
 |             PRINT (", "); | 
 |           demangle_type (rdm); | 
 |         } | 
 |       if (i == 1) | 
 |         PRINT (","); | 
 |       PRINT (")"); | 
 |       break; | 
 |     case 'F': | 
 |       old_bound_lifetime_depth = rdm->bound_lifetime_depth; | 
 |       demangle_binder (rdm); | 
 |  | 
 |       if (eat (rdm, 'U')) | 
 |         PRINT ("unsafe "); | 
 |  | 
 |       if (eat (rdm, 'K')) | 
 |         { | 
 |           if (eat (rdm, 'C')) | 
 |             { | 
 |               abi.ascii = "C"; | 
 |               abi.ascii_len = 1; | 
 |             } | 
 |           else | 
 |             { | 
 |               abi = parse_ident (rdm); | 
 |               if (!abi.ascii || abi.punycode) | 
 |                 { | 
 |                   rdm->errored = 1; | 
 |                   goto restore; | 
 |                 } | 
 |             } | 
 |  | 
 |           PRINT ("extern \""); | 
 |  | 
 |           /* If the ABI had any `-`, they were replaced with `_`, | 
 |              so the parts between `_` have to be re-joined with `-`. */ | 
 |           for (i = 0; i < abi.ascii_len; i++) | 
 |             { | 
 |               if (abi.ascii[i] == '_') | 
 |                 { | 
 |                   print_str (rdm, abi.ascii, i); | 
 |                   PRINT ("-"); | 
 |                   abi.ascii += i + 1; | 
 |                   abi.ascii_len -= i + 1; | 
 |                   i = 0; | 
 |                 } | 
 |             } | 
 |           print_str (rdm, abi.ascii, abi.ascii_len); | 
 |  | 
 |           PRINT ("\" "); | 
 |         } | 
 |  | 
 |       PRINT ("fn("); | 
 |       for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++) | 
 |         { | 
 |           if (i > 0) | 
 |             PRINT (", "); | 
 |           demangle_type (rdm); | 
 |         } | 
 |       PRINT (")"); | 
 |  | 
 |       if (eat (rdm, 'u')) | 
 |         { | 
 |           /* Skip printing the return type if it's 'u', i.e. `()`. */ | 
 |         } | 
 |       else | 
 |         { | 
 |           PRINT (" -> "); | 
 |           demangle_type (rdm); | 
 |         } | 
 |  | 
 |     /* Restore `bound_lifetime_depth` to outside the binder. */ | 
 |     restore: | 
 |       rdm->bound_lifetime_depth = old_bound_lifetime_depth; | 
 |       break; | 
 |     case 'D': | 
 |       PRINT ("dyn "); | 
 |  | 
 |       old_bound_lifetime_depth = rdm->bound_lifetime_depth; | 
 |       demangle_binder (rdm); | 
 |  | 
 |       for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++) | 
 |         { | 
 |           if (i > 0) | 
 |             PRINT (" + "); | 
 |           demangle_dyn_trait (rdm); | 
 |         } | 
 |  | 
 |       /* Restore `bound_lifetime_depth` to outside the binder. */ | 
 |       rdm->bound_lifetime_depth = old_bound_lifetime_depth; | 
 |  | 
 |       if (!eat (rdm, 'L')) | 
 |         { | 
 |           rdm->errored = 1; | 
 |           return; | 
 |         } | 
 |       lt = parse_integer_62 (rdm); | 
 |       if (lt) | 
 |         { | 
 |           PRINT (" + "); | 
 |           print_lifetime_from_index (rdm, lt); | 
 |         } | 
 |       break; | 
 |     case 'B': | 
 |       backref = parse_integer_62 (rdm); | 
 |       if (!rdm->skipping_printing) | 
 |         { | 
 |           old_next = rdm->next; | 
 |           rdm->next = backref; | 
 |           demangle_type (rdm); | 
 |           rdm->next = old_next; | 
 |         } | 
 |       break; | 
 |     default: | 
 |       /* Go back to the tag, so `demangle_path` also sees it. */ | 
 |       rdm->next--; | 
 |       demangle_path (rdm, 0); | 
 |     } | 
 |  | 
 |   if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | 
 |     -- rdm->recursion; | 
 | } | 
 |  | 
 | /* A trait in a trait object may have some "existential projections" | 
 |    (i.e. associated type bindings) after it, which should be printed | 
 |    in the `<...>` of the trait, e.g. `dyn Trait<T, U, Assoc=X>`. | 
 |    To this end, this method will keep the `<...>` of an 'I' path | 
 |    open, by omitting the `>`, and return `Ok(true)` in that case. */ | 
 | static int | 
 | demangle_path_maybe_open_generics (struct rust_demangler *rdm) | 
 | { | 
 |   int open; | 
 |   size_t i, old_next, backref; | 
 |  | 
 |   open = 0; | 
 |  | 
 |   if (rdm->errored) | 
 |     return open; | 
 |  | 
 |   if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | 
 |     { | 
 |       ++ rdm->recursion; | 
 |       if (rdm->recursion > RUST_MAX_RECURSION_COUNT) | 
 | 	{ | 
 | 	  /* FIXME: There ought to be a way to report | 
 | 	     that the recursion limit has been reached.  */ | 
 | 	  rdm->errored = 1; | 
 | 	  goto end_of_func; | 
 | 	} | 
 |     } | 
 |  | 
 |   if (eat (rdm, 'B')) | 
 |     { | 
 |       backref = parse_integer_62 (rdm); | 
 |       if (!rdm->skipping_printing) | 
 |         { | 
 |           old_next = rdm->next; | 
 |           rdm->next = backref; | 
 |           open = demangle_path_maybe_open_generics (rdm); | 
 |           rdm->next = old_next; | 
 |         } | 
 |     } | 
 |   else if (eat (rdm, 'I')) | 
 |     { | 
 |       demangle_path (rdm, 0); | 
 |       PRINT ("<"); | 
 |       open = 1; | 
 |       for (i = 0; !rdm->errored && !eat (rdm, 'E'); i++) | 
 |         { | 
 |           if (i > 0) | 
 |             PRINT (", "); | 
 |           demangle_generic_arg (rdm); | 
 |         } | 
 |     } | 
 |   else | 
 |     demangle_path (rdm, 0); | 
 |  | 
 |  end_of_func: | 
 |   if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | 
 |     -- rdm->recursion; | 
 |  | 
 |   return open; | 
 | } | 
 |  | 
 | static void | 
 | demangle_dyn_trait (struct rust_demangler *rdm) | 
 | { | 
 |   int open; | 
 |   struct rust_mangled_ident name; | 
 |  | 
 |   if (rdm->errored) | 
 |     return; | 
 |  | 
 |   open = demangle_path_maybe_open_generics (rdm); | 
 |  | 
 |   while (eat (rdm, 'p')) | 
 |     { | 
 |       if (!open) | 
 |         PRINT ("<"); | 
 |       else | 
 |         PRINT (", "); | 
 |       open = 1; | 
 |  | 
 |       name = parse_ident (rdm); | 
 |       print_ident (rdm, name); | 
 |       PRINT (" = "); | 
 |       demangle_type (rdm); | 
 |     } | 
 |  | 
 |   if (open) | 
 |     PRINT (">"); | 
 | } | 
 |  | 
 | static void | 
 | demangle_const (struct rust_demangler *rdm) | 
 | { | 
 |   char ty_tag; | 
 |   size_t old_next, backref; | 
 |  | 
 |   if (rdm->errored) | 
 |     return; | 
 |  | 
 |   if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | 
 |     { | 
 |       ++ rdm->recursion; | 
 |       if (rdm->recursion > RUST_MAX_RECURSION_COUNT) | 
 | 	/* FIXME: There ought to be a way to report | 
 | 	   that the recursion limit has been reached.  */ | 
 | 	goto fail_return; | 
 |     } | 
 |  | 
 |   if (eat (rdm, 'B')) | 
 |     { | 
 |       backref = parse_integer_62 (rdm); | 
 |       if (!rdm->skipping_printing) | 
 |         { | 
 |           old_next = rdm->next; | 
 |           rdm->next = backref; | 
 |           demangle_const (rdm); | 
 |           rdm->next = old_next; | 
 |         } | 
 |       goto pass_return; | 
 |     } | 
 |  | 
 |   ty_tag = next (rdm); | 
 |   switch (ty_tag) | 
 |     { | 
 |     /* Placeholder. */ | 
 |     case 'p': | 
 |       PRINT ("_"); | 
 |       goto pass_return; | 
 |  | 
 |     /* Unsigned integer types. */ | 
 |     case 'h': | 
 |     case 't': | 
 |     case 'm': | 
 |     case 'y': | 
 |     case 'o': | 
 |     case 'j': | 
 |       demangle_const_uint (rdm); | 
 |       break; | 
 |  | 
 |     /* Signed integer types. */ | 
 |     case 'a': | 
 |     case 's': | 
 |     case 'l': | 
 |     case 'x': | 
 |     case 'n': | 
 |     case 'i': | 
 |       demangle_const_int (rdm); | 
 |       break; | 
 |  | 
 |     /* Boolean. */ | 
 |     case 'b': | 
 |       demangle_const_bool (rdm); | 
 |       break; | 
 |  | 
 |     /* Character. */ | 
 |     case 'c': | 
 |       demangle_const_char (rdm); | 
 |       break; | 
 |  | 
 |     default: | 
 |       goto fail_return; | 
 |     } | 
 |  | 
 |   if (!rdm->errored && rdm->verbose) | 
 |     { | 
 |       PRINT (": "); | 
 |       PRINT (basic_type (ty_tag)); | 
 |     } | 
 |   goto pass_return; | 
 |  | 
 |  fail_return: | 
 |   rdm->errored = 1; | 
 |  pass_return: | 
 |   if (rdm->recursion != RUST_NO_RECURSION_LIMIT) | 
 |     -- rdm->recursion; | 
 | } | 
 |  | 
 | static void | 
 | demangle_const_uint (struct rust_demangler *rdm) | 
 | { | 
 |   size_t hex_len; | 
 |   uint64_t value; | 
 |  | 
 |   if (rdm->errored) | 
 |     return; | 
 |  | 
 |   hex_len = parse_hex_nibbles (rdm, &value); | 
 |  | 
 |   if (hex_len > 16) | 
 |     { | 
 |       /* Print anything that doesn't fit in `uint64_t` verbatim. */ | 
 |       PRINT ("0x"); | 
 |       print_str (rdm, rdm->sym + (rdm->next - hex_len), hex_len); | 
 |     } | 
 |   else if (hex_len > 0) | 
 |     print_uint64 (rdm, value); | 
 |   else | 
 |     rdm->errored = 1; | 
 | } | 
 |  | 
 | static void | 
 | demangle_const_int (struct rust_demangler *rdm) | 
 | { | 
 |   if (eat (rdm, 'n')) | 
 |     PRINT ("-"); | 
 |   demangle_const_uint (rdm); | 
 | } | 
 |  | 
 | static void | 
 | demangle_const_bool (struct rust_demangler *rdm) | 
 | { | 
 |   uint64_t value; | 
 |  | 
 |   if (parse_hex_nibbles (rdm, &value) != 1) | 
 |     { | 
 |       rdm->errored = 1; | 
 |       return; | 
 |     } | 
 |  | 
 |   if (value == 0) | 
 |     PRINT ("false"); | 
 |   else if (value == 1) | 
 |     PRINT ("true"); | 
 |   else | 
 |     rdm->errored = 1; | 
 | } | 
 |  | 
 | static void | 
 | demangle_const_char (struct rust_demangler *rdm) | 
 | { | 
 |   size_t hex_len; | 
 |   uint64_t value; | 
 |  | 
 |   hex_len = parse_hex_nibbles (rdm, &value); | 
 |  | 
 |   if (hex_len == 0 || hex_len > 8) | 
 |     { | 
 |       rdm->errored = 1; | 
 |       return; | 
 |     } | 
 |  | 
 |   /* Match Rust's character "debug" output as best as we can. */ | 
 |   PRINT ("'"); | 
 |   if (value == '\t') | 
 |     PRINT ("\\t"); | 
 |   else if (value == '\r') | 
 |     PRINT ("\\r"); | 
 |   else if (value == '\n') | 
 |     PRINT ("\\n"); | 
 |   else if (value > ' ' && value < '~') | 
 |     { | 
 |       /* Rust also considers many non-ASCII codepoints to be printable, but | 
 | 	 that logic is not easily ported to C. */ | 
 |       char c = value; | 
 |       print_str (rdm, &c, 1); | 
 |     } | 
 |   else | 
 |     { | 
 |       PRINT ("\\u{"); | 
 |       print_uint64_hex (rdm, value); | 
 |       PRINT ("}"); | 
 |     } | 
 |   PRINT ("'"); | 
 | } | 
 |  | 
 | /* A legacy hash is the prefix "h" followed by 16 lowercase hex digits. | 
 |    The hex digits must contain at least 5 distinct digits. */ | 
 | static int | 
 | is_legacy_prefixed_hash (struct rust_mangled_ident ident) | 
 | { | 
 |   uint16_t seen; | 
 |   int nibble; | 
 |   size_t i, count; | 
 |  | 
 |   if (ident.ascii_len != 17 || ident.ascii[0] != 'h') | 
 |     return 0; | 
 |  | 
 |   seen = 0; | 
 |   for (i = 0; i < 16; i++) | 
 |     { | 
 |       nibble = decode_lower_hex_nibble (ident.ascii[1 + i]); | 
 |       if (nibble < 0) | 
 |         return 0; | 
 |       seen |= (uint16_t)1 << nibble; | 
 |     } | 
 |  | 
 |   /* Count how many distinct digits were seen. */ | 
 |   count = 0; | 
 |   while (seen) | 
 |     { | 
 |       if (seen & 1) | 
 |         count++; | 
 |       seen >>= 1; | 
 |     } | 
 |  | 
 |   return count >= 5; | 
 | } | 
 |  | 
 | int | 
 | rust_demangle_callback (const char *mangled, int options, | 
 |                         demangle_callbackref callback, void *opaque) | 
 | { | 
 |   const char *p; | 
 |   struct rust_demangler rdm; | 
 |   struct rust_mangled_ident ident; | 
 |  | 
 |   rdm.sym = mangled; | 
 |   rdm.sym_len = 0; | 
 |  | 
 |   rdm.callback_opaque = opaque; | 
 |   rdm.callback = callback; | 
 |  | 
 |   rdm.next = 0; | 
 |   rdm.errored = 0; | 
 |   rdm.skipping_printing = 0; | 
 |   rdm.verbose = (options & DMGL_VERBOSE) != 0; | 
 |   rdm.version = 0; | 
 |   rdm.recursion = (options & DMGL_NO_RECURSE_LIMIT) ? RUST_NO_RECURSION_LIMIT : 0; | 
 |   rdm.bound_lifetime_depth = 0; | 
 |  | 
 |   /* Rust symbols always start with _R (v0) or _ZN (legacy). */ | 
 |   if (rdm.sym[0] == '_' && rdm.sym[1] == 'R') | 
 |     rdm.sym += 2; | 
 |   else if (rdm.sym[0] == '_' && rdm.sym[1] == 'Z' && rdm.sym[2] == 'N') | 
 |     { | 
 |       rdm.sym += 3; | 
 |       rdm.version = -1; | 
 |     } | 
 |   else | 
 |     return 0; | 
 |  | 
 |   /* Paths (v0) always start with uppercase characters. */ | 
 |   if (rdm.version != -1 && !ISUPPER (rdm.sym[0])) | 
 |     return 0; | 
 |  | 
 |   /* Rust symbols (v0) use only [_0-9a-zA-Z] characters. */ | 
 |   for (p = rdm.sym; *p; p++) | 
 |     { | 
 |       /* Rust v0 symbols can have '.' suffixes, ignore those.  */ | 
 |       if (rdm.version == 0 && *p == '.') | 
 |         break; | 
 |  | 
 |       rdm.sym_len++; | 
 |  | 
 |       if (*p == '_' || ISALNUM (*p)) | 
 |         continue; | 
 |  | 
 |       /* Legacy Rust symbols can also contain [.:$] characters. | 
 |          Or @ in the .suffix (which will be skipped, see below). */ | 
 |       if (rdm.version == -1 && (*p == '$' || *p == '.' || *p == ':' | 
 |                                 || *p == '@')) | 
 |         continue; | 
 |  | 
 |       return 0; | 
 |     } | 
 |  | 
 |   /* Legacy Rust symbols need to be handled separately. */ | 
 |   if (rdm.version == -1) | 
 |     { | 
 |       /* Legacy Rust symbols always end with E.  But can be followed by a | 
 |          .suffix (which we want to ignore).  */ | 
 |       int dot_suffix = 1; | 
 |       while (rdm.sym_len > 0 && | 
 |              !(dot_suffix && rdm.sym[rdm.sym_len - 1] == 'E')) | 
 |         { | 
 |           dot_suffix = rdm.sym[rdm.sym_len - 1] == '.'; | 
 |           rdm.sym_len--; | 
 |         } | 
 |  | 
 |       if (!(rdm.sym_len > 0 && rdm.sym[rdm.sym_len - 1] == 'E')) | 
 |         return 0; | 
 |       rdm.sym_len--; | 
 |  | 
 |       /* Legacy Rust symbols also always end with a path segment | 
 |          that encodes a 16 hex digit hash, i.e. '17h[a-f0-9]{16}'. | 
 |          This early check, before any parse_ident calls, should | 
 |          quickly filter out most C++ symbols unrelated to Rust. */ | 
 |       if (!(rdm.sym_len > 19 | 
 |             && !memcmp (&rdm.sym[rdm.sym_len - 19], "17h", 3))) | 
 |         return 0; | 
 |  | 
 |       do | 
 |         { | 
 |           ident = parse_ident (&rdm); | 
 |           if (rdm.errored || !ident.ascii) | 
 |             return 0; | 
 |         } | 
 |       while (rdm.next < rdm.sym_len); | 
 |  | 
 |       /* The last path segment should be the hash. */ | 
 |       if (!is_legacy_prefixed_hash (ident)) | 
 |         return 0; | 
 |  | 
 |       /* Reset the state for a second pass, to print the symbol. */ | 
 |       rdm.next = 0; | 
 |       if (!rdm.verbose && rdm.sym_len > 19) | 
 |         { | 
 |           /* Hide the last segment, containing the hash, if not verbose. */ | 
 |           rdm.sym_len -= 19; | 
 |         } | 
 |  | 
 |       do | 
 |         { | 
 |           if (rdm.next > 0) | 
 |             print_str (&rdm, "::", 2); | 
 |  | 
 |           ident = parse_ident (&rdm); | 
 |           print_ident (&rdm, ident); | 
 |         } | 
 |       while (rdm.next < rdm.sym_len); | 
 |     } | 
 |   else | 
 |     { | 
 |       demangle_path (&rdm, 1); | 
 |  | 
 |       /* Skip instantiating crate. */ | 
 |       if (!rdm.errored && rdm.next < rdm.sym_len) | 
 |         { | 
 |           rdm.skipping_printing = 1; | 
 |           demangle_path (&rdm, 0); | 
 |         } | 
 |  | 
 |       /* It's an error to not reach the end. */ | 
 |       rdm.errored |= rdm.next != rdm.sym_len; | 
 |     } | 
 |  | 
 |   return !rdm.errored; | 
 | } | 
 |  | 
 | /* Growable string buffers. */ | 
 | struct str_buf | 
 | { | 
 |   char *ptr; | 
 |   size_t len; | 
 |   size_t cap; | 
 |   int errored; | 
 | }; | 
 |  | 
 | static void | 
 | str_buf_reserve (struct str_buf *buf, size_t extra) | 
 | { | 
 |   size_t available, min_new_cap, new_cap; | 
 |   char *new_ptr; | 
 |  | 
 |   /* Allocation failed before. */ | 
 |   if (buf->errored) | 
 |     return; | 
 |  | 
 |   available = buf->cap - buf->len; | 
 |  | 
 |   if (extra <= available) | 
 |     return; | 
 |  | 
 |   min_new_cap = buf->cap + (extra - available); | 
 |  | 
 |   /* Check for overflows. */ | 
 |   if (min_new_cap < buf->cap) | 
 |     { | 
 |       buf->errored = 1; | 
 |       return; | 
 |     } | 
 |  | 
 |   new_cap = buf->cap; | 
 |  | 
 |   if (new_cap == 0) | 
 |     new_cap = 4; | 
 |  | 
 |   /* Double capacity until sufficiently large. */ | 
 |   while (new_cap < min_new_cap) | 
 |     { | 
 |       new_cap *= 2; | 
 |  | 
 |       /* Check for overflows. */ | 
 |       if (new_cap < buf->cap) | 
 |         { | 
 |           buf->errored = 1; | 
 |           return; | 
 |         } | 
 |     } | 
 |  | 
 |   new_ptr = (char *)realloc (buf->ptr, new_cap); | 
 |   if (new_ptr == NULL) | 
 |     { | 
 |       free (buf->ptr); | 
 |       buf->ptr = NULL; | 
 |       buf->len = 0; | 
 |       buf->cap = 0; | 
 |       buf->errored = 1; | 
 |     } | 
 |   else | 
 |     { | 
 |       buf->ptr = new_ptr; | 
 |       buf->cap = new_cap; | 
 |     } | 
 | } | 
 |  | 
 | static void | 
 | str_buf_append (struct str_buf *buf, const char *data, size_t len) | 
 | { | 
 |   str_buf_reserve (buf, len); | 
 |   if (buf->errored) | 
 |     return; | 
 |  | 
 |   memcpy (buf->ptr + buf->len, data, len); | 
 |   buf->len += len; | 
 | } | 
 |  | 
 | static void | 
 | str_buf_demangle_callback (const char *data, size_t len, void *opaque) | 
 | { | 
 |   str_buf_append ((struct str_buf *)opaque, data, len); | 
 | } | 
 |  | 
 | char * | 
 | rust_demangle (const char *mangled, int options) | 
 | { | 
 |   struct str_buf out; | 
 |   int success; | 
 |  | 
 |   out.ptr = NULL; | 
 |   out.len = 0; | 
 |   out.cap = 0; | 
 |   out.errored = 0; | 
 |  | 
 |   success = rust_demangle_callback (mangled, options, | 
 |                                     str_buf_demangle_callback, &out); | 
 |  | 
 |   if (!success) | 
 |     { | 
 |       free (out.ptr); | 
 |       return NULL; | 
 |     } | 
 |  | 
 |   str_buf_append (&out, "\0", 1); | 
 |   return out.ptr; | 
 | } |