2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
13 * Copyright 2017 Jason King
16 #include <sys/debug.h>
17 #include <sys/sysmacros.h>
21 #include "demangle_int.h"
24 #define CHUNK_SIZE (8U)
27 * A name_t is essentially a stack of str_pair_t's. Generally, the parsing
28 * code will push (via name_add() or the like) portions of the demangled
29 * name into a name_t, and periodically combine them via name_join().
31 * As such it should be noted that since items are added at the end of
32 * name_t->nm_items, the numbering of name_at() starts at the end
33 * of name_t->nm_items, i.e. name_at(n, 0) == &n->nm_items[n->nm_len - 1].
35 * It should also be noted that for name_t's, adding is a move operation in
36 * that it takes ownership of the passed in string/str_t/etc
40 name_init(name_t
*n
, sysdem_ops_t
*ops
)
42 (void) memset(n
, 0, sizeof (*n
));
43 n
->nm_ops
= (ops
!= NULL
) ? ops
: sysdem_ops_default
;
53 xfree(n
->nm_ops
, n
->nm_items
, n
->nm_size
);
59 name_len(const name_t
*n
)
65 name_empty(const name_t
*n
)
67 return (name_len(n
) == 0 ? B_TRUE
: B_FALSE
);
76 for (size_t i
= 0; i
< n
->nm_len
; i
++) {
77 str_pair_t
*sp
= &n
->nm_items
[i
];
78 sysdem_ops_t
*ops
= sp
->strp_l
.str_ops
;
81 (void) str_pair_init(sp
, ops
);
88 name_reserve(name_t
*n
, size_t amt
)
90 size_t newlen
= n
->nm_len
+ amt
;
92 if (newlen
== cpp_name_max_depth
) {
97 if (newlen
< n
->nm_size
)
100 size_t newsize
= roundup(newlen
, CHUNK_SIZE
);
101 if (newsize
> cpp_name_max_depth
)
102 newsize
= cpp_name_max_depth
;
104 void *temp
= xrealloc(n
->nm_ops
, n
->nm_items
,
105 n
->nm_size
* sizeof (str_pair_t
), newsize
* sizeof (str_pair_t
));
111 n
->nm_size
= newsize
;
116 name_add(name_t
*n
, const char *l
, size_t l_len
, const char *r
, size_t r_len
)
121 str_init(&sl
, n
->nm_ops
);
122 str_init(&sr
, n
->nm_ops
);
123 str_set(&sl
, l
, l_len
);
124 str_set(&sr
, r
, r_len
);
125 return (name_add_str(n
, &sl
, &sr
));
129 name_add_str(name_t
*n
, str_t
*l
, str_t
*r
)
133 (void) str_pair_init(&sp
, n
->nm_ops
);
135 if (!name_reserve(n
, 1))
140 (void) memset(l
, 0, sizeof (*l
));
145 (void) memset(r
, 0, sizeof (*r
));
148 n
->nm_items
[n
->nm_len
++] = sp
;
154 name_at(const name_t
*n
, size_t idx
)
156 VERIFY(!name_empty(n
));
157 VERIFY3U(idx
, <, n
->nm_len
);
158 return (&n
->nm_items
[n
->nm_len
- idx
- 1]);
164 return (name_at(n
, 0));
168 name_pop(name_t
*n
, str_pair_t
*sp
)
173 str_pair_t
*top
= name_top(n
);
177 (void) memset(top
, 0, sizeof (*top
));
186 name_join(name_t
*n
, size_t amt
, const char *sep
)
188 str_pair_t
*sp
= NULL
;
190 size_t seplen
= strlen(sep
);
192 VERIFY3U(amt
, <=, n
->nm_len
);
195 * A join of 0 elements places an empty string on the stack. This
196 * simplifies code that wants to do things like:
197 * name_join(...); name_fmt(.., "({0})", ...)
200 (void) name_add(n
, "", 0, "", 0);
204 /* A join of 1 element just implies merging the top str_pair_t */
206 VERIFY3U(name_len(n
), >, 0);
207 return (str_pair_merge(name_top(n
)));
210 (void) str_init(&res
, n
->nm_ops
);
212 sp
= name_at(n
, amt
- 1);
213 for (size_t i
= 0; i
< amt
; i
++) {
215 if (!str_append(&res
, sep
, seplen
))
219 if (!str_append_str(&res
, &sp
->strp_l
))
221 if (!str_append_str(&res
, &sp
->strp_r
))
227 for (size_t i
= 0; i
< amt
; i
++)
230 /* since we've removed at least 1 entry, this should always succeed */
231 VERIFY(name_add_str(n
, &res
, NULL
));
240 name_fmt_s(name_t
*n
, str_t
*s
, const char *fmt
, long *maxp
)
248 for (p
= fmt
; *p
!= '\0'; p
++) {
250 (void) str_append_c(s
, *p
);
256 long val
= strtol(p
+ 1, &q
, 10);
258 VERIFY(val
!= 0 || errno
== 0);
259 VERIFY3U(val
, <, n
->nm_len
);
261 str_pair_t
*sp
= name_at(n
, val
);
268 if (!str_append_str(s
, &sp
->strp_l
))
270 if (!str_append_str(s
, &sp
->strp_r
))
277 if (!str_append_str(s
, &sp
->strp_l
))
281 if (!str_append_str(s
, &sp
->strp_r
))
299 * Replace a number of elements in the name stack with a formatted string
300 * for format is a plain string with optional {nnn} or {nnn:L|R} substitutions
301 * where nnn is the stack position of an element and it's contents (both
302 * left and right pieces) are inserted. Optionally, only the left or
303 * right piece can specified using :L|R e.g. {2:L}{3}{2:R} would insert
304 * the left piece of element 2, all of element 3, then the right piece of
307 * Once complete, all elements up to the deepest one references are popped
308 * off the stack, and the resulting formatted string is pushed into n.
310 * This could be done as a sequence of push & pops, but this makes the
311 * intended output far clearer to see.
314 name_fmt(name_t
*n
, const char *fmt_l
, const char *fmt_r
)
319 (void) str_pair_init(&res
, n
->nm_ops
);
321 if (!name_reserve(n
, 1))
324 if (!name_fmt_s(n
, &res
.strp_l
, fmt_l
, &max
))
326 if (!name_fmt_s(n
, &res
.strp_r
, fmt_r
, &max
))
330 for (size_t i
= 0; i
<= max
; i
++)
334 n
->nm_items
[n
->nm_len
++] = res
;
343 * The substitution list is a list of name_t's that get added as the
344 * demangled name is parsed. Adding a name_t to the substitution list
345 * is a copy operation, and likewise inserting a substitution into a name_t
346 * is also a copy operation.
349 sub_init(sub_t
*sub
, sysdem_ops_t
*ops
)
351 (void) memset(sub
, 0, sizeof (*sub
));
352 sub
->sub_ops
= (ops
!= NULL
) ? ops
: sysdem_ops_default
;
362 xfree(sub
->sub_ops
, sub
->sub_items
, sub
->sub_size
);
363 sub
->sub_items
= NULL
;
368 sub_clear(sub_t
*sub
)
373 for (size_t i
= 0; i
< sub
->sub_len
; i
++)
374 name_fini(&sub
->sub_items
[i
]);
380 sub_empty(const sub_t
*sub
)
382 return ((sub
->sub_len
== 0) ? B_TRUE
: B_FALSE
);
386 sub_len(const sub_t
*sub
)
388 return (sub
->sub_len
);
392 sub_reserve(sub_t
*sub
, size_t amt
)
394 if (sub
->sub_len
+ amt
< sub
->sub_size
)
397 size_t newsize
= roundup(sub
->sub_size
+ amt
, CHUNK_SIZE
);
398 void *temp
= xrealloc(sub
->sub_ops
, sub
->sub_items
,
399 sub
->sub_size
* sizeof (name_t
), newsize
* sizeof (name_t
));
404 sub
->sub_items
= temp
;
405 sub
->sub_size
= newsize
;
410 /* save the element of n (up to depth elements deep) as a substitution */
412 sub_save(sub_t
*sub
, const name_t
*n
, size_t depth
)
417 if (!sub_reserve(sub
, 1))
420 name_t
*dest
= &sub
->sub_items
[sub
->sub_len
++];
421 name_init(dest
, sub
->sub_ops
);
423 if (!name_reserve(dest
, depth
)) {
429 const str_pair_t
*src_sp
= name_at(n
, depth
- 1);
431 for (size_t i
= 0; i
< depth
; i
++, src_sp
++) {
432 str_pair_t copy
= { 0 };
433 (void) str_pair_init(©
, n
->nm_ops
);
434 if (!str_pair_copy(src_sp
, ©
)) {
435 str_pair_fini(©
);
440 VERIFY(name_add_str(dest
, ©
.strp_l
, ©
.strp_r
));
446 /* push substitution idx onto n */
448 sub_substitute(const sub_t
*sub
, size_t idx
, name_t
*n
)
450 VERIFY3U(idx
, <, sub
->sub_len
);
452 const name_t
*src
= &sub
->sub_items
[idx
];
453 const str_pair_t
*sp
= src
->nm_items
;
454 size_t save
= name_len(n
);
456 for (size_t i
= 0; i
< src
->nm_len
; i
++, sp
++) {
457 str_pair_t copy
= { 0 };
459 if (!str_pair_copy(sp
, ©
))
462 if (!name_add_str(n
, ©
.strp_l
, ©
.strp_r
))
469 for (size_t i
= 0; i
< name_len(n
) - save
; i
++)
477 name_t
*top
= &sub
->sub_items
[--sub
->sub_len
];
482 * Templates can use substitutions for it's arguments (using T instead of
483 * S). Since templates can nest however, each nesting requires a new
484 * set of substitutions. As such a new, empty list of template substitutions
485 * is pushed onto cpp_templ each time templates are nested, and popped at
486 * the end of the current template argument list.
489 templ_reserve(templ_t
*tpl
, size_t n
)
491 if (tpl
->tpl_len
+ n
< tpl
->tpl_size
)
494 size_t newsize
= tpl
->tpl_size
+ CHUNK_SIZE
;
495 void *temp
= xrealloc(tpl
->tpl_ops
, tpl
->tpl_items
,
496 tpl
->tpl_size
* sizeof (sub_t
), newsize
* sizeof (sub_t
));
501 tpl
->tpl_items
= temp
;
502 tpl
->tpl_size
= newsize
;
507 templ_init(templ_t
*tpl
, sysdem_ops_t
*ops
)
509 (void) memset(tpl
, 0, sizeof (*tpl
));
514 templ_fini(templ_t
*tpl
)
519 for (size_t i
= 0; i
< tpl
->tpl_len
; i
++)
520 sub_fini(&tpl
->tpl_items
[i
]);
522 xfree(tpl
->tpl_ops
, tpl
->tpl_items
, tpl
->tpl_size
* sizeof (sub_t
));
523 sysdem_ops_t
*ops
= tpl
->tpl_ops
;
524 (void) memset(tpl
, 0, sizeof (*tpl
));
529 templ_push(templ_t
*tpl
)
531 if (!templ_reserve(tpl
, 1))
534 sub_t
*sub
= &tpl
->tpl_items
[tpl
->tpl_len
++];
535 sub_init(sub
, tpl
->tpl_ops
);
540 templ_pop(templ_t
*tpl
)
542 VERIFY(!templ_empty(tpl
));
544 sub_t
*sub
= &tpl
->tpl_items
[--tpl
->tpl_len
];
549 templ_top(templ_t
*tpl
)
551 if (tpl
->tpl_len
== 0)
553 return (&tpl
->tpl_items
[tpl
->tpl_len
- 1]);
557 templ_empty(const templ_t
*tpl
)
559 return ((tpl
->tpl_len
== 0) ? B_TRUE
: B_FALSE
);
563 templ_top_len(const templ_t
*tpl
)
565 const sub_t
*sub
= templ_top((templ_t
*)tpl
);
567 return (sub
->sub_len
);
571 templ_sub(const templ_t
*tpl
, size_t idx
, name_t
*n
)
573 const sub_t
*sub
= templ_top((templ_t
*)tpl
);
575 return (sub_substitute(sub
, idx
, n
));
579 templ_save(const name_t
*n
, size_t amt
, templ_t
*tpl
)
581 VERIFY3U(tpl
->tpl_len
, >, 0);
583 sub_t
*s
= templ_top(tpl
);
584 boolean_t res
= B_TRUE
;
586 /* a bit of a hack -- want an 'empty' entry when saving 0 params */
590 name_init(&name
, tpl
->tpl_ops
);
591 res
&= name_add(&name
, "", 0, "", 0);
593 res
&= sub_save(s
, &name
, 1);
596 res
&= sub_save(s
, n
, amt
);