Home | History | Annotate | Line # | Download | only in libiberty
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