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