rust-demangle.c revision 1.1.1.3 1 1.1 christos /* Demangler for the Rust programming language
2 1.1.1.3 christos Copyright (C) 2016-2020 Free Software Foundation, Inc.
3 1.1 christos Written by David Tolnay (dtolnay (at) gmail.com).
4 1.1 christos
5 1.1 christos This file is part of the libiberty library.
6 1.1 christos Libiberty is free software; you can redistribute it and/or
7 1.1 christos modify it under the terms of the GNU Library General Public
8 1.1 christos License as published by the Free Software Foundation; either
9 1.1 christos version 2 of the License, or (at your option) any later version.
10 1.1 christos
11 1.1 christos In addition to the permissions in the GNU Library General Public
12 1.1 christos License, the Free Software Foundation gives you unlimited permission
13 1.1 christos to link the compiled version of this file into combinations with other
14 1.1 christos programs, and to distribute those combinations without any restriction
15 1.1 christos coming from the use of this file. (The Library Public License
16 1.1 christos restrictions do apply in other respects; for example, they cover
17 1.1 christos modification of the file, and distribution when not linked into a
18 1.1 christos combined executable.)
19 1.1 christos
20 1.1 christos Libiberty is distributed in the hope that it will be useful,
21 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of
22 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
23 1.1 christos Library General Public License for more details.
24 1.1 christos
25 1.1 christos You should have received a copy of the GNU Library General Public
26 1.1 christos License along with libiberty; see the file COPYING.LIB.
27 1.1 christos If not, see <http://www.gnu.org/licenses/>. */
28 1.1 christos
29 1.1 christos
30 1.1 christos #ifdef HAVE_CONFIG_H
31 1.1 christos #include "config.h"
32 1.1 christos #endif
33 1.1 christos
34 1.1 christos #include "safe-ctype.h"
35 1.1 christos
36 1.1.1.3 christos #include <inttypes.h>
37 1.1 christos #include <sys/types.h>
38 1.1 christos #include <string.h>
39 1.1 christos #include <stdio.h>
40 1.1.1.3 christos #include <stdlib.h>
41 1.1 christos
42 1.1 christos #ifdef HAVE_STRING_H
43 1.1 christos #include <string.h>
44 1.1 christos #else
45 1.1 christos extern size_t strlen(const char *s);
46 1.1 christos extern int strncmp(const char *s1, const char *s2, size_t n);
47 1.1 christos extern void *memset(void *s, int c, size_t n);
48 1.1 christos #endif
49 1.1 christos
50 1.1 christos #include <demangle.h>
51 1.1 christos #include "libiberty.h"
52 1.1 christos
53 1.1.1.3 christos struct rust_demangler
54 1.1.1.3 christos {
55 1.1.1.3 christos const char *sym;
56 1.1.1.3 christos size_t sym_len;
57 1.1 christos
58 1.1.1.3 christos void *callback_opaque;
59 1.1.1.3 christos demangle_callbackref callback;
60 1.1 christos
61 1.1.1.3 christos /* Position of the next character to read from the symbol. */
62 1.1.1.3 christos size_t next;
63 1.1 christos
64 1.1.1.3 christos /* Non-zero if any error occurred. */
65 1.1.1.3 christos int errored;
66 1.1 christos
67 1.1.1.3 christos /* Non-zero if printing should be verbose (e.g. include hashes). */
68 1.1.1.3 christos int verbose;
69 1.1 christos
70 1.1.1.3 christos /* Rust mangling version, with legacy mangling being -1. */
71 1.1.1.3 christos int version;
72 1.1.1.3 christos };
73 1.1 christos
74 1.1.1.3 christos /* Parsing functions. */
75 1.1 christos
76 1.1.1.3 christos static char
77 1.1.1.3 christos peek (const struct rust_demangler *rdm)
78 1.1 christos {
79 1.1.1.3 christos if (rdm->next < rdm->sym_len)
80 1.1.1.3 christos return rdm->sym[rdm->next];
81 1.1.1.3 christos return 0;
82 1.1.1.3 christos }
83 1.1 christos
84 1.1.1.3 christos static char
85 1.1.1.3 christos next (struct rust_demangler *rdm)
86 1.1.1.3 christos {
87 1.1.1.3 christos char c = peek (rdm);
88 1.1.1.3 christos if (!c)
89 1.1.1.3 christos rdm->errored = 1;
90 1.1.1.3 christos else
91 1.1.1.3 christos rdm->next++;
92 1.1.1.3 christos return c;
93 1.1.1.3 christos }
94 1.1.1.3 christos
95 1.1.1.3 christos struct rust_mangled_ident
96 1.1.1.3 christos {
97 1.1.1.3 christos /* ASCII part of the identifier. */
98 1.1.1.3 christos const char *ascii;
99 1.1.1.3 christos size_t ascii_len;
100 1.1.1.3 christos };
101 1.1.1.3 christos
102 1.1.1.3 christos static struct rust_mangled_ident
103 1.1.1.3 christos parse_ident (struct rust_demangler *rdm)
104 1.1.1.3 christos {
105 1.1.1.3 christos char c;
106 1.1.1.3 christos size_t start, len;
107 1.1.1.3 christos struct rust_mangled_ident ident;
108 1.1.1.3 christos
109 1.1.1.3 christos ident.ascii = NULL;
110 1.1.1.3 christos ident.ascii_len = 0;
111 1.1.1.3 christos
112 1.1.1.3 christos c = next (rdm);
113 1.1.1.3 christos if (!ISDIGIT (c))
114 1.1.1.3 christos {
115 1.1.1.3 christos rdm->errored = 1;
116 1.1.1.3 christos return ident;
117 1.1.1.3 christos }
118 1.1.1.3 christos len = c - '0';
119 1.1.1.3 christos
120 1.1.1.3 christos if (c != '0')
121 1.1.1.3 christos while (ISDIGIT (peek (rdm)))
122 1.1.1.3 christos len = len * 10 + (next (rdm) - '0');
123 1.1.1.3 christos
124 1.1.1.3 christos start = rdm->next;
125 1.1.1.3 christos rdm->next += len;
126 1.1.1.3 christos /* Check for overflows. */
127 1.1.1.3 christos if ((start > rdm->next) || (rdm->next > rdm->sym_len))
128 1.1.1.3 christos {
129 1.1.1.3 christos rdm->errored = 1;
130 1.1.1.3 christos return ident;
131 1.1.1.3 christos }
132 1.1.1.3 christos
133 1.1.1.3 christos ident.ascii = rdm->sym + start;
134 1.1.1.3 christos ident.ascii_len = len;
135 1.1.1.3 christos
136 1.1.1.3 christos if (ident.ascii_len == 0)
137 1.1.1.3 christos ident.ascii = NULL;
138 1.1.1.3 christos
139 1.1.1.3 christos return ident;
140 1.1.1.3 christos }
141 1.1.1.3 christos
142 1.1.1.3 christos /* Printing functions. */
143 1.1.1.3 christos
144 1.1.1.3 christos static void
145 1.1.1.3 christos print_str (struct rust_demangler *rdm, const char *data, size_t len)
146 1.1.1.3 christos {
147 1.1.1.3 christos if (!rdm->errored)
148 1.1.1.3 christos rdm->callback (data, len, rdm->callback_opaque);
149 1.1.1.3 christos }
150 1.1.1.3 christos
151 1.1.1.3 christos #define PRINT(s) print_str (rdm, s, strlen (s))
152 1.1.1.3 christos
153 1.1.1.3 christos /* Return a 0x0-0xf value if the char is 0-9a-f, and -1 otherwise. */
154 1.1.1.3 christos static int
155 1.1.1.3 christos decode_lower_hex_nibble (char nibble)
156 1.1.1.3 christos {
157 1.1.1.3 christos if ('0' <= nibble && nibble <= '9')
158 1.1.1.3 christos return nibble - '0';
159 1.1.1.3 christos if ('a' <= nibble && nibble <= 'f')
160 1.1.1.3 christos return 0xa + (nibble - 'a');
161 1.1.1.3 christos return -1;
162 1.1.1.3 christos }
163 1.1.1.3 christos
164 1.1.1.3 christos /* Return the unescaped character for a "$...$" escape, or 0 if invalid. */
165 1.1.1.3 christos static char
166 1.1.1.3 christos decode_legacy_escape (const char *e, size_t len, size_t *out_len)
167 1.1.1.3 christos {
168 1.1.1.3 christos char c = 0;
169 1.1.1.3 christos size_t escape_len = 0;
170 1.1.1.3 christos int lo_nibble = -1, hi_nibble = -1;
171 1.1 christos
172 1.1.1.3 christos if (len < 3 || e[0] != '$')
173 1.1 christos return 0;
174 1.1 christos
175 1.1.1.3 christos e++;
176 1.1.1.3 christos len--;
177 1.1.1.3 christos
178 1.1.1.3 christos if (e[0] == 'C')
179 1.1.1.3 christos {
180 1.1.1.3 christos escape_len = 1;
181 1.1.1.3 christos
182 1.1.1.3 christos c = ',';
183 1.1.1.3 christos }
184 1.1.1.3 christos else if (len > 2)
185 1.1.1.3 christos {
186 1.1.1.3 christos escape_len = 2;
187 1.1.1.3 christos
188 1.1.1.3 christos if (e[0] == 'S' && e[1] == 'P')
189 1.1.1.3 christos c = '@';
190 1.1.1.3 christos else if (e[0] == 'B' && e[1] == 'P')
191 1.1.1.3 christos c = '*';
192 1.1.1.3 christos else if (e[0] == 'R' && e[1] == 'F')
193 1.1.1.3 christos c = '&';
194 1.1.1.3 christos else if (e[0] == 'L' && e[1] == 'T')
195 1.1.1.3 christos c = '<';
196 1.1.1.3 christos else if (e[0] == 'G' && e[1] == 'T')
197 1.1.1.3 christos c = '>';
198 1.1.1.3 christos else if (e[0] == 'L' && e[1] == 'P')
199 1.1.1.3 christos c = '(';
200 1.1.1.3 christos else if (e[0] == 'R' && e[1] == 'P')
201 1.1.1.3 christos c = ')';
202 1.1.1.3 christos else if (e[0] == 'u' && len > 3)
203 1.1.1.3 christos {
204 1.1.1.3 christos escape_len = 3;
205 1.1.1.3 christos
206 1.1.1.3 christos hi_nibble = decode_lower_hex_nibble (e[1]);
207 1.1.1.3 christos if (hi_nibble < 0)
208 1.1.1.3 christos return 0;
209 1.1.1.3 christos lo_nibble = decode_lower_hex_nibble (e[2]);
210 1.1.1.3 christos if (lo_nibble < 0)
211 1.1.1.3 christos return 0;
212 1.1.1.3 christos
213 1.1.1.3 christos /* Only allow non-control ASCII characters. */
214 1.1.1.3 christos if (hi_nibble > 7)
215 1.1.1.3 christos return 0;
216 1.1.1.3 christos c = (hi_nibble << 4) | lo_nibble;
217 1.1.1.3 christos if (c < 0x20)
218 1.1.1.3 christos return 0;
219 1.1.1.3 christos }
220 1.1.1.3 christos }
221 1.1.1.3 christos
222 1.1.1.3 christos if (!c || len <= escape_len || e[escape_len] != '$')
223 1.1 christos return 0;
224 1.1 christos
225 1.1.1.3 christos *out_len = 2 + escape_len;
226 1.1.1.3 christos return c;
227 1.1 christos }
228 1.1 christos
229 1.1.1.3 christos static void
230 1.1.1.3 christos print_ident (struct rust_demangler *rdm, struct rust_mangled_ident ident)
231 1.1.1.3 christos {
232 1.1.1.3 christos char unescaped;
233 1.1.1.3 christos size_t len;
234 1.1.1.3 christos
235 1.1.1.3 christos if (rdm->errored)
236 1.1.1.3 christos return;
237 1.1.1.3 christos
238 1.1.1.3 christos if (rdm->version == -1)
239 1.1.1.3 christos {
240 1.1.1.3 christos /* Ignore leading underscores preceding escape sequences.
241 1.1.1.3 christos The mangler inserts an underscore to make sure the
242 1.1.1.3 christos identifier begins with a XID_Start character. */
243 1.1.1.3 christos if (ident.ascii_len >= 2 && ident.ascii[0] == '_'
244 1.1.1.3 christos && ident.ascii[1] == '$')
245 1.1.1.3 christos {
246 1.1.1.3 christos ident.ascii++;
247 1.1.1.3 christos ident.ascii_len--;
248 1.1.1.3 christos }
249 1.1.1.3 christos
250 1.1.1.3 christos while (ident.ascii_len > 0)
251 1.1.1.3 christos {
252 1.1.1.3 christos /* Handle legacy escape sequences ("$...$", ".." or "."). */
253 1.1.1.3 christos if (ident.ascii[0] == '$')
254 1.1.1.3 christos {
255 1.1.1.3 christos unescaped
256 1.1.1.3 christos = decode_legacy_escape (ident.ascii, ident.ascii_len, &len);
257 1.1.1.3 christos if (unescaped)
258 1.1.1.3 christos print_str (rdm, &unescaped, 1);
259 1.1.1.3 christos else
260 1.1.1.3 christos {
261 1.1.1.3 christos /* Unexpected escape sequence, print the rest verbatim. */
262 1.1.1.3 christos print_str (rdm, ident.ascii, ident.ascii_len);
263 1.1.1.3 christos return;
264 1.1.1.3 christos }
265 1.1.1.3 christos }
266 1.1.1.3 christos else if (ident.ascii[0] == '.')
267 1.1.1.3 christos {
268 1.1.1.3 christos if (ident.ascii_len >= 2 && ident.ascii[1] == '.')
269 1.1.1.3 christos {
270 1.1.1.3 christos /* ".." becomes "::" */
271 1.1.1.3 christos PRINT ("::");
272 1.1.1.3 christos len = 2;
273 1.1.1.3 christos }
274 1.1.1.3 christos else
275 1.1.1.3 christos {
276 1.1.1.3 christos /* "." becomes "-" */
277 1.1.1.3 christos PRINT ("-");
278 1.1.1.3 christos len = 1;
279 1.1.1.3 christos }
280 1.1.1.3 christos }
281 1.1.1.3 christos else
282 1.1.1.3 christos {
283 1.1.1.3 christos /* Print everything before the next escape sequence, at once. */
284 1.1.1.3 christos for (len = 0; len < ident.ascii_len; len++)
285 1.1.1.3 christos if (ident.ascii[len] == '$' || ident.ascii[len] == '.')
286 1.1.1.3 christos break;
287 1.1.1.3 christos
288 1.1.1.3 christos print_str (rdm, ident.ascii, len);
289 1.1.1.3 christos }
290 1.1.1.3 christos
291 1.1.1.3 christos ident.ascii += len;
292 1.1.1.3 christos ident.ascii_len -= len;
293 1.1.1.3 christos }
294 1.1 christos
295 1.1.1.3 christos return;
296 1.1.1.3 christos }
297 1.1.1.3 christos }
298 1.1.1.3 christos
299 1.1.1.3 christos /* A legacy hash is the prefix "h" followed by 16 lowercase hex digits.
300 1.1.1.3 christos The hex digits must contain at least 5 distinct digits. */
301 1.1 christos static int
302 1.1.1.3 christos is_legacy_prefixed_hash (struct rust_mangled_ident ident)
303 1.1 christos {
304 1.1.1.3 christos uint16_t seen;
305 1.1.1.3 christos int nibble;
306 1.1.1.3 christos size_t i, count;
307 1.1 christos
308 1.1.1.3 christos if (ident.ascii_len != 17 || ident.ascii[0] != 'h')
309 1.1 christos return 0;
310 1.1 christos
311 1.1.1.3 christos seen = 0;
312 1.1.1.3 christos for (i = 0; i < 16; i++)
313 1.1.1.3 christos {
314 1.1.1.3 christos nibble = decode_lower_hex_nibble (ident.ascii[1 + i]);
315 1.1.1.3 christos if (nibble < 0)
316 1.1.1.3 christos return 0;
317 1.1.1.3 christos seen |= (uint16_t)1 << nibble;
318 1.1.1.3 christos }
319 1.1 christos
320 1.1.1.3 christos /* Count how many distinct digits were seen. */
321 1.1 christos count = 0;
322 1.1.1.3 christos while (seen)
323 1.1.1.3 christos {
324 1.1.1.3 christos if (seen & 1)
325 1.1.1.3 christos count++;
326 1.1.1.3 christos seen >>= 1;
327 1.1.1.3 christos }
328 1.1 christos
329 1.1.1.3 christos return count >= 5;
330 1.1 christos }
331 1.1 christos
332 1.1.1.3 christos int
333 1.1.1.3 christos rust_demangle_callback (const char *mangled, int options,
334 1.1.1.3 christos demangle_callbackref callback, void *opaque)
335 1.1 christos {
336 1.1.1.3 christos const char *p;
337 1.1.1.3 christos struct rust_demangler rdm;
338 1.1.1.3 christos struct rust_mangled_ident ident;
339 1.1.1.3 christos
340 1.1.1.3 christos rdm.sym = mangled;
341 1.1.1.3 christos rdm.sym_len = 0;
342 1.1.1.3 christos
343 1.1.1.3 christos rdm.callback_opaque = opaque;
344 1.1.1.3 christos rdm.callback = callback;
345 1.1.1.3 christos
346 1.1.1.3 christos rdm.next = 0;
347 1.1.1.3 christos rdm.errored = 0;
348 1.1.1.3 christos rdm.verbose = (options & DMGL_VERBOSE) != 0;
349 1.1.1.3 christos rdm.version = 0;
350 1.1.1.3 christos
351 1.1.1.3 christos /* Rust symbols always start with _ZN (legacy). */
352 1.1.1.3 christos if (rdm.sym[0] == '_' && rdm.sym[1] == 'Z' && rdm.sym[2] == 'N')
353 1.1.1.3 christos {
354 1.1.1.3 christos rdm.sym += 3;
355 1.1.1.3 christos rdm.version = -1;
356 1.1.1.3 christos }
357 1.1.1.3 christos else
358 1.1.1.3 christos return 0;
359 1.1.1.3 christos
360 1.1.1.3 christos /* Legacy Rust symbols use only [_0-9a-zA-Z.:$] characters. */
361 1.1.1.3 christos for (p = rdm.sym; *p; p++)
362 1.1.1.3 christos {
363 1.1.1.3 christos rdm.sym_len++;
364 1.1 christos
365 1.1.1.3 christos if (*p == '_' || ISALNUM (*p))
366 1.1.1.3 christos continue;
367 1.1 christos
368 1.1.1.3 christos if (rdm.version == -1 && (*p == '$' || *p == '.' || *p == ':'))
369 1.1.1.3 christos continue;
370 1.1.1.3 christos
371 1.1.1.3 christos return 0;
372 1.1.1.3 christos }
373 1.1.1.3 christos
374 1.1.1.3 christos /* Legacy Rust symbols need to be handled separately. */
375 1.1.1.3 christos if (rdm.version == -1)
376 1.1.1.3 christos {
377 1.1.1.3 christos /* Legacy Rust symbols always end with E. */
378 1.1.1.3 christos if (!(rdm.sym_len > 0 && rdm.sym[rdm.sym_len - 1] == 'E'))
379 1.1.1.3 christos return 0;
380 1.1.1.3 christos rdm.sym_len--;
381 1.1.1.3 christos
382 1.1.1.3 christos /* Legacy Rust symbols also always end with a path segment
383 1.1.1.3 christos that encodes a 16 hex digit hash, i.e. '17h[a-f0-9]{16}'.
384 1.1.1.3 christos This early check, before any parse_ident calls, should
385 1.1.1.3 christos quickly filter out most C++ symbols unrelated to Rust. */
386 1.1.1.3 christos if (!(rdm.sym_len > 19
387 1.1.1.3 christos && !memcmp (&rdm.sym[rdm.sym_len - 19], "17h", 3)))
388 1.1.1.3 christos return 0;
389 1.1.1.3 christos
390 1.1.1.3 christos do
391 1.1.1.3 christos {
392 1.1.1.3 christos ident = parse_ident (&rdm);
393 1.1.1.3 christos if (rdm.errored || !ident.ascii)
394 1.1.1.3 christos return 0;
395 1.1.1.3 christos }
396 1.1.1.3 christos while (rdm.next < rdm.sym_len);
397 1.1.1.3 christos
398 1.1.1.3 christos /* The last path segment should be the hash. */
399 1.1.1.3 christos if (!is_legacy_prefixed_hash (ident))
400 1.1.1.3 christos return 0;
401 1.1.1.3 christos
402 1.1.1.3 christos /* Reset the state for a second pass, to print the symbol. */
403 1.1.1.3 christos rdm.next = 0;
404 1.1.1.3 christos if (!rdm.verbose && rdm.sym_len > 19)
405 1.1.1.3 christos {
406 1.1.1.3 christos /* Hide the last segment, containing the hash, if not verbose. */
407 1.1.1.3 christos rdm.sym_len -= 19;
408 1.1.1.3 christos }
409 1.1.1.3 christos
410 1.1.1.3 christos do
411 1.1.1.3 christos {
412 1.1.1.3 christos if (rdm.next > 0)
413 1.1.1.3 christos print_str (&rdm, "::", 2);
414 1.1.1.3 christos
415 1.1.1.3 christos ident = parse_ident (&rdm);
416 1.1.1.3 christos print_ident (&rdm, ident);
417 1.1.1.3 christos }
418 1.1.1.3 christos while (rdm.next < rdm.sym_len);
419 1.1.1.3 christos }
420 1.1.1.3 christos else
421 1.1.1.3 christos return 0;
422 1.1.1.3 christos
423 1.1.1.3 christos return !rdm.errored;
424 1.1.1.3 christos }
425 1.1.1.3 christos
426 1.1.1.3 christos /* Growable string buffers. */
427 1.1.1.3 christos struct str_buf
428 1.1.1.3 christos {
429 1.1.1.3 christos char *ptr;
430 1.1.1.3 christos size_t len;
431 1.1.1.3 christos size_t cap;
432 1.1.1.3 christos int errored;
433 1.1.1.3 christos };
434 1.1.1.3 christos
435 1.1.1.3 christos static void
436 1.1.1.3 christos str_buf_reserve (struct str_buf *buf, size_t extra)
437 1.1.1.3 christos {
438 1.1.1.3 christos size_t available, min_new_cap, new_cap;
439 1.1.1.3 christos char *new_ptr;
440 1.1.1.3 christos
441 1.1.1.3 christos /* Allocation failed before. */
442 1.1.1.3 christos if (buf->errored)
443 1.1.1.3 christos return;
444 1.1.1.3 christos
445 1.1.1.3 christos available = buf->cap - buf->len;
446 1.1.1.3 christos
447 1.1.1.3 christos if (extra <= available)
448 1.1 christos return;
449 1.1 christos
450 1.1.1.3 christos min_new_cap = buf->cap + (extra - available);
451 1.1.1.3 christos
452 1.1.1.3 christos /* Check for overflows. */
453 1.1.1.3 christos if (min_new_cap < buf->cap)
454 1.1.1.3 christos {
455 1.1.1.3 christos buf->errored = 1;
456 1.1.1.3 christos return;
457 1.1.1.3 christos }
458 1.1.1.3 christos
459 1.1.1.3 christos new_cap = buf->cap;
460 1.1.1.3 christos
461 1.1.1.3 christos if (new_cap == 0)
462 1.1.1.3 christos new_cap = 4;
463 1.1.1.3 christos
464 1.1.1.3 christos /* Double capacity until sufficiently large. */
465 1.1.1.3 christos while (new_cap < min_new_cap)
466 1.1.1.3 christos {
467 1.1.1.3 christos new_cap *= 2;
468 1.1.1.3 christos
469 1.1.1.3 christos /* Check for overflows. */
470 1.1.1.3 christos if (new_cap < buf->cap)
471 1.1.1.3 christos {
472 1.1.1.3 christos buf->errored = 1;
473 1.1.1.3 christos return;
474 1.1.1.3 christos }
475 1.1.1.3 christos }
476 1.1.1.3 christos
477 1.1.1.3 christos new_ptr = (char *)realloc (buf->ptr, new_cap);
478 1.1.1.3 christos if (new_ptr == NULL)
479 1.1.1.3 christos {
480 1.1.1.3 christos free (buf->ptr);
481 1.1.1.3 christos buf->ptr = NULL;
482 1.1.1.3 christos buf->len = 0;
483 1.1.1.3 christos buf->cap = 0;
484 1.1.1.3 christos buf->errored = 1;
485 1.1.1.3 christos }
486 1.1.1.3 christos else
487 1.1.1.3 christos {
488 1.1.1.3 christos buf->ptr = new_ptr;
489 1.1.1.3 christos buf->cap = new_cap;
490 1.1.1.3 christos }
491 1.1 christos }
492 1.1 christos
493 1.1.1.3 christos static void
494 1.1.1.3 christos str_buf_append (struct str_buf *buf, const char *data, size_t len)
495 1.1 christos {
496 1.1.1.3 christos str_buf_reserve (buf, len);
497 1.1.1.3 christos if (buf->errored)
498 1.1.1.3 christos return;
499 1.1 christos
500 1.1.1.3 christos memcpy (buf->ptr + buf->len, data, len);
501 1.1.1.3 christos buf->len += len;
502 1.1.1.3 christos }
503 1.1 christos
504 1.1.1.3 christos static void
505 1.1.1.3 christos str_buf_demangle_callback (const char *data, size_t len, void *opaque)
506 1.1.1.3 christos {
507 1.1.1.3 christos str_buf_append ((struct str_buf *)opaque, data, len);
508 1.1.1.3 christos }
509 1.1.1.3 christos
510 1.1.1.3 christos char *
511 1.1.1.3 christos rust_demangle (const char *mangled, int options)
512 1.1.1.3 christos {
513 1.1.1.3 christos struct str_buf out;
514 1.1.1.3 christos int success;
515 1.1 christos
516 1.1.1.3 christos out.ptr = NULL;
517 1.1.1.3 christos out.len = 0;
518 1.1.1.3 christos out.cap = 0;
519 1.1.1.3 christos out.errored = 0;
520 1.1.1.3 christos
521 1.1.1.3 christos success = rust_demangle_callback (mangled, options,
522 1.1.1.3 christos str_buf_demangle_callback, &out);
523 1.1.1.3 christos
524 1.1.1.3 christos if (!success)
525 1.1.1.3 christos {
526 1.1.1.3 christos free (out.ptr);
527 1.1.1.3 christos return NULL;
528 1.1.1.3 christos }
529 1.1 christos
530 1.1.1.3 christos str_buf_append (&out, "\0", 1);
531 1.1.1.3 christos return out.ptr;
532 1.1 christos }
533