|  | /* Demangler for the Rust programming language | 
|  | Copyright (C) 2016-2023 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; | 
|  | } |