1 /* Demangler for the Rust programming language
2 Copyright (C) 2016-2024 Free Software Foundation, Inc.
3 Written by David Tolnay (dtolnay@gmail.com).
4 Rewritten by Eduard-Mihai Burtescu (eddyb@lyken.rs) for v0 support.
6 This file is part of the libiberty library.
7 Libiberty is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
12 In addition to the permissions in the GNU Library General Public
13 License, the Free Software Foundation gives you unlimited permission
14 to link the compiled version of this file into combinations with other
15 programs, and to distribute those combinations without any restriction
16 coming from the use of this file. (The Library Public License
17 restrictions do apply in other respects; for example, they cover
18 modification of the file, and distribution when not linked into a
21 Libiberty is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 Library General Public License for more details.
26 You should have received a copy of the GNU Library General Public
27 License along with libiberty; see the file COPYING.LIB.
28 If not, see <http://www.gnu.org/licenses/>. */
31 #if 0 /* in valgrind */
35 #endif /* ! in valgrind */
37 #if 0 /* in valgrind */
38 #include "safe-ctype.h"
39 #endif /* ! in valgrind */
41 #if 0 /* in valgrind */
43 #include <sys/types.h>
47 #endif /* ! in valgrind */
49 #if 0 /* in valgrind */
53 extern size_t strlen(const char *s
);
54 extern int strncmp(const char *s1
, const char *s2
, size_t n
);
55 extern void *memset(void *s
, int c
, size_t n
);
57 #endif /* ! in valgrind */
59 #if 0 /* in valgrind */
61 #include "libiberty.h"
62 #endif /* ! in valgrind */
64 #include "vg_libciface.h"
68 #include "safe-ctype.h"
70 typedef UChar
uint8_t;
72 typedef UShort
uint16_t;
73 typedef Short
int16_t;
74 typedef UInt
uint32_t;
76 typedef ULong
uint64_t;
86 void *callback_opaque
;
87 demangle_callbackref callback
;
89 /* Position of the next character to read from the symbol. */
92 /* Non-zero if any error occurred. */
95 /* Non-zero if nothing should be printed. */
96 int skipping_printing
;
98 /* Non-zero if printing should be verbose (e.g. include hashes). */
101 /* Rust mangling version, with legacy mangling being -1. */
104 /* Recursion depth. */
105 unsigned int recursion
;
106 /* Maximum number of times demangle_path may be called recursively. */
107 #define RUST_MAX_RECURSION_COUNT 1024
108 #define RUST_NO_RECURSION_LIMIT ((unsigned int) -1)
110 uint64_t bound_lifetime_depth
;
113 /* Parsing functions. */
116 peek (const struct rust_demangler
*rdm
)
118 if (rdm
->next
< rdm
->sym_len
)
119 return rdm
->sym
[rdm
->next
];
124 eat (struct rust_demangler
*rdm
, char c
)
136 next (struct rust_demangler
*rdm
)
147 parse_integer_62 (struct rust_demangler
*rdm
)
156 while (!eat (rdm
, '_') && !rdm
->errored
)
162 else if (ISLOWER (c
))
164 else if (ISUPPER (c
))
165 x
+= 10 + 26 + (c
- 'A');
176 parse_opt_integer_62 (struct rust_demangler
*rdm
, char tag
)
180 return 1 + parse_integer_62 (rdm
);
184 parse_disambiguator (struct rust_demangler
*rdm
)
186 return parse_opt_integer_62 (rdm
, 's');
190 parse_hex_nibbles (struct rust_demangler
*rdm
, uint64_t *value
)
198 while (!eat (rdm
, '_'))
205 else if (c
>= 'a' && c
<= 'f')
206 *value
|= 10 + (c
- 'a');
218 struct rust_mangled_ident
220 /* ASCII part of the identifier. */
224 /* Punycode insertion codes for Unicode codepoints, if any. */
225 const char *punycode
;
229 static struct rust_mangled_ident
230 parse_ident (struct rust_demangler
*rdm
)
235 struct rust_mangled_ident ident
;
239 ident
.punycode
= NULL
;
240 ident
.punycode_len
= 0;
242 if (rdm
->version
!= -1)
243 is_punycode
= eat (rdm
, 'u');
254 while (ISDIGIT (peek (rdm
)))
255 len
= len
* 10 + (next (rdm
) - '0');
257 /* Skip past the optional `_` separator (v0). */
258 if (rdm
->version
!= -1)
263 /* Check for overflows. */
264 if ((start
> rdm
->next
) || (rdm
->next
> rdm
->sym_len
))
270 ident
.ascii
= rdm
->sym
+ start
;
271 ident
.ascii_len
= len
;
275 ident
.punycode_len
= 0;
276 while (ident
.ascii_len
> 0)
280 /* The last '_' is a separator between ascii & punycode. */
281 if (ident
.ascii
[ident
.ascii_len
] == '_')
284 ident
.punycode_len
++;
286 if (!ident
.punycode_len
)
291 ident
.punycode
= ident
.ascii
+ (len
- ident
.punycode_len
);
294 if (ident
.ascii_len
== 0)
300 /* Printing functions. */
303 print_str (struct rust_demangler
*rdm
, const char *data
, size_t len
)
305 if (!rdm
->errored
&& !rdm
->skipping_printing
)
306 rdm
->callback (data
, len
, rdm
->callback_opaque
);
309 #define PRINT(s) print_str (rdm, s, strlen (s))
312 print_uint64 (struct rust_demangler
*rdm
, uint64_t x
)
315 snprintf (s
, 21, "%" PRIu64
, x
);
320 print_uint64_hex (struct rust_demangler
*rdm
, uint64_t x
)
323 snprintf (s
, 17, "%" PRIx64
, x
);
327 /* Return a 0x0-0xf value if the char is 0-9a-f, and -1 otherwise. */
329 decode_lower_hex_nibble (char nibble
)
331 if ('0' <= nibble
&& nibble
<= '9')
333 if ('a' <= nibble
&& nibble
<= 'f')
334 return 0xa + (nibble
- 'a');
338 /* Return the unescaped character for a "$...$" escape, or 0 if invalid. */
340 decode_legacy_escape (const char *e
, size_t len
, size_t *out_len
)
343 size_t escape_len
= 0;
344 int lo_nibble
= -1, hi_nibble
= -1;
346 if (len
< 3 || e
[0] != '$')
362 if (e
[0] == 'S' && e
[1] == 'P')
364 else if (e
[0] == 'B' && e
[1] == 'P')
366 else if (e
[0] == 'R' && e
[1] == 'F')
368 else if (e
[0] == 'L' && e
[1] == 'T')
370 else if (e
[0] == 'G' && e
[1] == 'T')
372 else if (e
[0] == 'L' && e
[1] == 'P')
374 else if (e
[0] == 'R' && e
[1] == 'P')
376 else if (e
[0] == 'u' && len
> 3)
380 hi_nibble
= decode_lower_hex_nibble (e
[1]);
383 lo_nibble
= decode_lower_hex_nibble (e
[2]);
387 /* Only allow non-control ASCII characters. */
390 c
= (hi_nibble
<< 4) | lo_nibble
;
396 if (!c
|| len
<= escape_len
|| e
[escape_len
] != '$')
399 *out_len
= 2 + escape_len
;
404 print_ident (struct rust_demangler
*rdm
, struct rust_mangled_ident ident
)
408 size_t len
, cap
, punycode_pos
, j
;
409 /* Punycode parameters and state. */
411 size_t base
, t_min
, t_max
, skew
, damp
, bias
, i
;
412 size_t delta
, w
, k
, t
;
414 if (rdm
->errored
|| rdm
->skipping_printing
)
417 if (rdm
->version
== -1)
419 /* Ignore leading underscores preceding escape sequences.
420 The mangler inserts an underscore to make sure the
421 identifier begins with a XID_Start character. */
422 if (ident
.ascii_len
>= 2 && ident
.ascii
[0] == '_'
423 && ident
.ascii
[1] == '$')
429 while (ident
.ascii_len
> 0)
431 /* Handle legacy escape sequences ("$...$", ".." or "."). */
432 if (ident
.ascii
[0] == '$')
435 = decode_legacy_escape (ident
.ascii
, ident
.ascii_len
, &len
);
437 print_str (rdm
, &unescaped
, 1);
440 /* Unexpected escape sequence, print the rest verbatim. */
441 print_str (rdm
, ident
.ascii
, ident
.ascii_len
);
445 else if (ident
.ascii
[0] == '.')
447 if (ident
.ascii_len
>= 2 && ident
.ascii
[1] == '.')
449 /* ".." becomes "::" */
461 /* Print everything before the next escape sequence, at once. */
462 for (len
= 0; len
< ident
.ascii_len
; len
++)
463 if (ident
.ascii
[len
] == '$' || ident
.ascii
[len
] == '.')
466 print_str (rdm
, ident
.ascii
, len
);
470 ident
.ascii_len
-= len
;
478 print_str (rdm
, ident
.ascii
, ident
.ascii_len
);
484 while (cap
< ident
.ascii_len
)
487 /* Check for overflows. */
488 if ((cap
* 4) / 4 != cap
)
495 /* Store the output codepoints as groups of 4 UTF-8 bytes. */
496 out
= (uint8_t *)xmalloc (cap
* 4);
503 /* Populate initial output from ASCII fragment. */
504 for (len
= 0; len
< ident
.ascii_len
; len
++)
510 p
[3] = ident
.ascii
[len
];
513 /* Punycode parameters and initial state. */
524 while (punycode_pos
< ident
.punycode_len
)
526 /* Read one delta value. */
533 t
= k
< bias
? 0 : (k
- bias
);
539 if (punycode_pos
>= ident
.punycode_len
)
541 d
= ident
.punycode
[punycode_pos
++];
545 else if (ISDIGIT (d
))
558 /* Compute the new insert position and character. */
564 /* Ensure enough space is available. */
568 /* Check for overflows. */
569 if ((cap
* 4) / 4 != cap
|| cap
< len
)
575 p
= (uint8_t *)xrealloc (out
, cap
* 4);
583 /* Move the characters after the insert position. */
585 memmove (p
+ 4, p
, (len
- i
- 1) * 4);
587 /* Insert the new character, as UTF-8 bytes. */
588 p
[0] = c
>= 0x10000 ? 0xf0 | (c
>> 18) : 0;
589 p
[1] = c
>= 0x800 ? (c
< 0x10000 ? 0xe0 : 0x80) | ((c
>> 12) & 0x3f) : 0;
590 p
[2] = (c
< 0x800 ? 0xc0 : 0x80) | ((c
>> 6) & 0x3f);
591 p
[3] = 0x80 | (c
& 0x3f);
593 /* If there are no more deltas, decoding is complete. */
594 if (punycode_pos
== ident
.punycode_len
)
599 /* Perform bias adaptation. */
603 delta
+= delta
/ len
;
605 while (delta
> ((base
- t_min
) * t_max
) / 2)
607 delta
/= base
- t_min
;
610 bias
= k
+ ((base
- t_min
+ 1) * delta
) / (delta
+ skew
);
613 /* Remove all the 0 bytes to leave behind an UTF-8 string. */
614 for (i
= 0, j
= 0; i
< len
* 4; i
++)
618 print_str (rdm
, (const char *)out
, j
);
624 /* Print the lifetime according to the previously decoded index.
625 An index of `0` always refers to `'_`, but starting with `1`,
626 indices refer to late-bound lifetimes introduced by a binder. */
628 print_lifetime_from_index (struct rust_demangler
*rdm
, uint64_t lt
)
640 depth
= rdm
->bound_lifetime_depth
- lt
;
641 /* Try to print lifetimes alphabetically first. */
645 print_str (rdm
, &c
, 1);
649 /* Use `'_123` after running out of letters. */
651 print_uint64 (rdm
, depth
);
655 /* Demangling functions. */
657 static void demangle_binder (struct rust_demangler
*rdm
);
658 static void demangle_path (struct rust_demangler
*rdm
, int in_value
);
659 static void demangle_generic_arg (struct rust_demangler
*rdm
);
660 static void demangle_type (struct rust_demangler
*rdm
);
661 static int demangle_path_maybe_open_generics (struct rust_demangler
*rdm
);
662 static void demangle_dyn_trait (struct rust_demangler
*rdm
);
663 static void demangle_const (struct rust_demangler
*rdm
);
664 static void demangle_const_uint (struct rust_demangler
*rdm
);
665 static void demangle_const_int (struct rust_demangler
*rdm
);
666 static void demangle_const_bool (struct rust_demangler
*rdm
);
667 static void demangle_const_char (struct rust_demangler
*rdm
);
669 /* Optionally enter a binder ('G') for late-bound lifetimes,
670 printing e.g. `for<'a, 'b> `, and make those lifetimes visible
671 to the caller (via depth level, which the caller should reset). */
673 demangle_binder (struct rust_demangler
*rdm
)
675 uint64_t i
, bound_lifetimes
;
680 bound_lifetimes
= parse_opt_integer_62 (rdm
, 'G');
681 if (bound_lifetimes
> 0)
684 for (i
= 0; i
< bound_lifetimes
; i
++)
688 rdm
->bound_lifetime_depth
++;
689 print_lifetime_from_index (rdm
, 1);
696 demangle_path (struct rust_demangler
*rdm
, int in_value
)
699 int was_skipping_printing
;
700 size_t i
, backref
, old_next
;
702 struct rust_mangled_ident name
;
707 if (rdm
->recursion
!= RUST_NO_RECURSION_LIMIT
)
710 if (rdm
->recursion
> RUST_MAX_RECURSION_COUNT
)
711 /* FIXME: There ought to be a way to report
712 that the recursion limit has been reached. */
716 switch (tag
= next (rdm
))
719 dis
= parse_disambiguator (rdm
);
720 name
= parse_ident (rdm
);
722 print_ident (rdm
, name
);
726 print_uint64_hex (rdm
, dis
);
732 if (!ISLOWER (ns
) && !ISUPPER (ns
))
735 demangle_path (rdm
, in_value
);
737 dis
= parse_disambiguator (rdm
);
738 name
= parse_ident (rdm
);
742 /* Special namespaces, like closures and shims. */
753 print_str (rdm
, &ns
, 1);
755 if (name
.ascii
|| name
.punycode
)
758 print_ident (rdm
, name
);
761 print_uint64 (rdm
, dis
);
766 /* Implementation-specific/unspecified namespaces. */
768 if (name
.ascii
|| name
.punycode
)
771 print_ident (rdm
, name
);
777 /* Ignore the `impl`'s own path.*/
778 parse_disambiguator (rdm
);
779 was_skipping_printing
= rdm
->skipping_printing
;
780 rdm
->skipping_printing
= 1;
781 demangle_path (rdm
, in_value
);
782 rdm
->skipping_printing
= was_skipping_printing
;
790 demangle_path (rdm
, 0);
795 demangle_path (rdm
, in_value
);
799 for (i
= 0; !rdm
->errored
&& !eat (rdm
, 'E'); i
++)
803 demangle_generic_arg (rdm
);
808 backref
= parse_integer_62 (rdm
);
809 if (!rdm
->skipping_printing
)
811 old_next
= rdm
->next
;
813 demangle_path (rdm
, in_value
);
814 rdm
->next
= old_next
;
825 if (rdm
->recursion
!= RUST_NO_RECURSION_LIMIT
)
830 demangle_generic_arg (struct rust_demangler
*rdm
)
835 lt
= parse_integer_62 (rdm
);
836 print_lifetime_from_index (rdm
, lt
);
838 else if (eat (rdm
, 'K'))
839 demangle_const (rdm
);
845 basic_type (char tag
)
898 demangle_type (struct rust_demangler
*rdm
)
901 size_t i
, old_next
, backref
;
902 uint64_t lt
, old_bound_lifetime_depth
;
904 struct rust_mangled_ident abi
;
911 basic
= basic_type (tag
);
918 if (rdm
->recursion
!= RUST_NO_RECURSION_LIMIT
)
921 if (rdm
->recursion
> RUST_MAX_RECURSION_COUNT
)
922 /* FIXME: There ought to be a way to report
923 that the recursion limit has been reached. */
938 lt
= parse_integer_62 (rdm
);
941 print_lifetime_from_index (rdm
, lt
);
965 demangle_const (rdm
);
971 for (i
= 0; !rdm
->errored
&& !eat (rdm
, 'E'); i
++)
982 old_bound_lifetime_depth
= rdm
->bound_lifetime_depth
;
983 demangle_binder (rdm
);
997 abi
= parse_ident (rdm
);
998 if (!abi
.ascii
|| abi
.punycode
)
1005 PRINT ("extern \"");
1007 /* If the ABI had any `-`, they were replaced with `_`,
1008 so the parts between `_` have to be re-joined with `-`. */
1009 for (i
= 0; i
< abi
.ascii_len
; i
++)
1011 if (abi
.ascii
[i
] == '_')
1013 print_str (rdm
, abi
.ascii
, i
);
1016 abi
.ascii_len
-= i
+ 1;
1020 print_str (rdm
, abi
.ascii
, abi
.ascii_len
);
1026 for (i
= 0; !rdm
->errored
&& !eat (rdm
, 'E'); i
++)
1030 demangle_type (rdm
);
1036 /* Skip printing the return type if it's 'u', i.e. `()`. */
1041 demangle_type (rdm
);
1044 /* Restore `bound_lifetime_depth` to outside the binder. */
1046 rdm
->bound_lifetime_depth
= old_bound_lifetime_depth
;
1051 old_bound_lifetime_depth
= rdm
->bound_lifetime_depth
;
1052 demangle_binder (rdm
);
1054 for (i
= 0; !rdm
->errored
&& !eat (rdm
, 'E'); i
++)
1058 demangle_dyn_trait (rdm
);
1061 /* Restore `bound_lifetime_depth` to outside the binder. */
1062 rdm
->bound_lifetime_depth
= old_bound_lifetime_depth
;
1064 if (!eat (rdm
, 'L'))
1069 lt
= parse_integer_62 (rdm
);
1073 print_lifetime_from_index (rdm
, lt
);
1077 backref
= parse_integer_62 (rdm
);
1078 if (!rdm
->skipping_printing
)
1080 old_next
= rdm
->next
;
1081 rdm
->next
= backref
;
1082 demangle_type (rdm
);
1083 rdm
->next
= old_next
;
1087 /* Go back to the tag, so `demangle_path` also sees it. */
1089 demangle_path (rdm
, 0);
1092 if (rdm
->recursion
!= RUST_NO_RECURSION_LIMIT
)
1096 /* A trait in a trait object may have some "existential projections"
1097 (i.e. associated type bindings) after it, which should be printed
1098 in the `<...>` of the trait, e.g. `dyn Trait<T, U, Assoc=X>`.
1099 To this end, this method will keep the `<...>` of an 'I' path
1100 open, by omitting the `>`, and return `Ok(true)` in that case. */
1102 demangle_path_maybe_open_generics (struct rust_demangler
*rdm
)
1105 size_t i
, old_next
, backref
;
1112 if (rdm
->recursion
!= RUST_NO_RECURSION_LIMIT
)
1115 if (rdm
->recursion
> RUST_MAX_RECURSION_COUNT
)
1117 /* FIXME: There ought to be a way to report
1118 that the recursion limit has been reached. */
1126 backref
= parse_integer_62 (rdm
);
1127 if (!rdm
->skipping_printing
)
1129 old_next
= rdm
->next
;
1130 rdm
->next
= backref
;
1131 open
= demangle_path_maybe_open_generics (rdm
);
1132 rdm
->next
= old_next
;
1135 else if (eat (rdm
, 'I'))
1137 demangle_path (rdm
, 0);
1140 for (i
= 0; !rdm
->errored
&& !eat (rdm
, 'E'); i
++)
1144 demangle_generic_arg (rdm
);
1148 demangle_path (rdm
, 0);
1151 if (rdm
->recursion
!= RUST_NO_RECURSION_LIMIT
)
1158 demangle_dyn_trait (struct rust_demangler
*rdm
)
1161 struct rust_mangled_ident name
;
1166 open
= demangle_path_maybe_open_generics (rdm
);
1168 while (eat (rdm
, 'p'))
1176 name
= parse_ident (rdm
);
1177 print_ident (rdm
, name
);
1179 demangle_type (rdm
);
1187 demangle_const (struct rust_demangler
*rdm
)
1190 size_t old_next
, backref
;
1195 if (rdm
->recursion
!= RUST_NO_RECURSION_LIMIT
)
1198 if (rdm
->recursion
> RUST_MAX_RECURSION_COUNT
)
1199 /* FIXME: There ought to be a way to report
1200 that the recursion limit has been reached. */
1206 backref
= parse_integer_62 (rdm
);
1207 if (!rdm
->skipping_printing
)
1209 old_next
= rdm
->next
;
1210 rdm
->next
= backref
;
1211 demangle_const (rdm
);
1212 rdm
->next
= old_next
;
1217 ty_tag
= next (rdm
);
1225 /* Unsigned integer types. */
1232 demangle_const_uint (rdm
);
1235 /* Signed integer types. */
1242 demangle_const_int (rdm
);
1247 demangle_const_bool (rdm
);
1252 demangle_const_char (rdm
);
1259 if (!rdm
->errored
&& rdm
->verbose
)
1262 PRINT (basic_type (ty_tag
));
1269 if (rdm
->recursion
!= RUST_NO_RECURSION_LIMIT
)
1274 demangle_const_uint (struct rust_demangler
*rdm
)
1282 hex_len
= parse_hex_nibbles (rdm
, &value
);
1286 /* Print anything that doesn't fit in `uint64_t` verbatim. */
1288 print_str (rdm
, rdm
->sym
+ (rdm
->next
- hex_len
), hex_len
);
1290 else if (hex_len
> 0)
1291 print_uint64 (rdm
, value
);
1297 demangle_const_int (struct rust_demangler
*rdm
)
1301 demangle_const_uint (rdm
);
1305 demangle_const_bool (struct rust_demangler
*rdm
)
1309 if (parse_hex_nibbles (rdm
, &value
) != 1)
1317 else if (value
== 1)
1324 demangle_const_char (struct rust_demangler
*rdm
)
1329 hex_len
= parse_hex_nibbles (rdm
, &value
);
1331 if (hex_len
== 0 || hex_len
> 8)
1337 /* Match Rust's character "debug" output as best as we can. */
1341 else if (value
== '\r')
1343 else if (value
== '\n')
1345 else if (value
> ' ' && value
< '~')
1347 /* Rust also considers many non-ASCII codepoints to be printable, but
1348 that logic is not easily ported to C. */
1350 print_str (rdm
, &c
, 1);
1355 print_uint64_hex (rdm
, value
);
1361 /* A legacy hash is the prefix "h" followed by 16 lowercase hex digits.
1362 The hex digits must contain at least 5 distinct digits. */
1364 is_legacy_prefixed_hash (struct rust_mangled_ident ident
)
1370 if (ident
.ascii_len
!= 17 || ident
.ascii
[0] != 'h')
1374 for (i
= 0; i
< 16; i
++)
1376 nibble
= decode_lower_hex_nibble (ident
.ascii
[1 + i
]);
1379 seen
|= (uint16_t)1 << nibble
;
1382 /* Count how many distinct digits were seen. */
1395 rust_demangle_callback (const char *mangled
, int options
,
1396 demangle_callbackref callback
, void *opaque
)
1399 struct rust_demangler rdm
;
1400 struct rust_mangled_ident ident
;
1405 rdm
.callback_opaque
= opaque
;
1406 rdm
.callback
= callback
;
1410 rdm
.skipping_printing
= 0;
1411 rdm
.verbose
= (options
& DMGL_VERBOSE
) != 0;
1413 rdm
.recursion
= (options
& DMGL_NO_RECURSE_LIMIT
) ? RUST_NO_RECURSION_LIMIT
: 0;
1414 rdm
.bound_lifetime_depth
= 0;
1416 /* Rust symbols always start with _R (v0) or _ZN (legacy). */
1417 if (rdm
.sym
[0] == '_' && rdm
.sym
[1] == 'R')
1419 else if (rdm
.sym
[0] == '_' && rdm
.sym
[1] == 'Z' && rdm
.sym
[2] == 'N')
1427 /* Paths (v0) always start with uppercase characters. */
1428 if (rdm
.version
!= -1 && !ISUPPER (rdm
.sym
[0]))
1431 /* Rust symbols (v0) use only [_0-9a-zA-Z] characters. */
1432 for (p
= rdm
.sym
; *p
; p
++)
1434 /* Rust v0 symbols can have '.' suffixes, ignore those. */
1435 if (rdm
.version
== 0 && *p
== '.')
1440 if (*p
== '_' || ISALNUM (*p
))
1443 /* Legacy Rust symbols can also contain [.:$] characters.
1444 Or @ in the .suffix (which will be skipped, see below). */
1445 if (rdm
.version
== -1 && (*p
== '$' || *p
== '.' || *p
== ':'
1452 /* Legacy Rust symbols need to be handled separately. */
1453 if (rdm
.version
== -1)
1455 /* Legacy Rust symbols always end with E. But can be followed by a
1456 .suffix (which we want to ignore). */
1458 while (rdm
.sym_len
> 0 &&
1459 !(dot_suffix
&& rdm
.sym
[rdm
.sym_len
- 1] == 'E'))
1461 dot_suffix
= rdm
.sym
[rdm
.sym_len
- 1] == '.';
1465 if (!(rdm
.sym_len
> 0 && rdm
.sym
[rdm
.sym_len
- 1] == 'E'))
1469 /* Legacy Rust symbols also always end with a path segment
1470 that encodes a 16 hex digit hash, i.e. '17h[a-f0-9]{16}'.
1471 This early check, before any parse_ident calls, should
1472 quickly filter out most C++ symbols unrelated to Rust. */
1473 if (!(rdm
.sym_len
> 19
1474 && !memcmp (&rdm
.sym
[rdm
.sym_len
- 19], "17h", 3)))
1479 ident
= parse_ident (&rdm
);
1480 if (rdm
.errored
|| !ident
.ascii
)
1483 while (rdm
.next
< rdm
.sym_len
);
1485 /* The last path segment should be the hash. */
1486 if (!is_legacy_prefixed_hash (ident
))
1489 /* Reset the state for a second pass, to print the symbol. */
1491 if (!rdm
.verbose
&& rdm
.sym_len
> 19)
1493 /* Hide the last segment, containing the hash, if not verbose. */
1500 print_str (&rdm
, "::", 2);
1502 ident
= parse_ident (&rdm
);
1503 print_ident (&rdm
, ident
);
1505 while (rdm
.next
< rdm
.sym_len
);
1509 demangle_path (&rdm
, 1);
1511 /* Skip instantiating crate. */
1512 if (!rdm
.errored
&& rdm
.next
< rdm
.sym_len
)
1514 rdm
.skipping_printing
= 1;
1515 demangle_path (&rdm
, 0);
1518 /* It's an error to not reach the end. */
1519 rdm
.errored
|= rdm
.next
!= rdm
.sym_len
;
1522 return !rdm
.errored
;
1525 /* Growable string buffers. */
1535 str_buf_reserve (struct str_buf
*buf
, size_t extra
)
1537 size_t available
, min_new_cap
, new_cap
;
1540 /* Allocation failed before. */
1544 available
= buf
->cap
- buf
->len
;
1546 if (extra
<= available
)
1549 min_new_cap
= buf
->cap
+ (extra
- available
);
1551 /* Check for overflows. */
1552 if (min_new_cap
< buf
->cap
)
1563 /* Double capacity until sufficiently large. */
1564 while (new_cap
< min_new_cap
)
1568 /* Check for overflows. */
1569 if (new_cap
< buf
->cap
)
1576 new_ptr
= (char *)xrealloc (buf
->ptr
, new_cap
);
1577 if (new_ptr
== NULL
)
1593 str_buf_append (struct str_buf
*buf
, const char *data
, size_t len
)
1595 str_buf_reserve (buf
, len
);
1599 memcpy (buf
->ptr
+ buf
->len
, data
, len
);
1604 str_buf_demangle_callback (const char *data
, size_t len
, void *opaque
)
1606 str_buf_append ((struct str_buf
*)opaque
, data
, len
);
1610 rust_demangle (const char *mangled
, int options
)
1620 success
= rust_demangle_callback (mangled
, options
,
1621 str_buf_demangle_callback
, &out
);
1629 str_buf_append (&out
, "\0", 1);