Index: lib/Demangle/ItaniumDemangle.cpp =================================================================== --- lib/Demangle/ItaniumDemangle.cpp +++ lib/Demangle/ItaniumDemangle.cpp @@ -42,6 +42,363 @@ CV_restrict = (1 << 2), }; +namespace { + +class arena; + +static __thread arena * thread_arena = nullptr; + +class arena { +public: + arena() + { + thread_arena = this; + } + + ~arena(); + + char * alloc(size_t len) + { + if (m_current_end < m_current_next + len) + grow(len); + char * ret = m_current_next; + m_current_last = ret; + m_current_next = ret + len; + return ret; + } + + char * realloc(const char * &old_begin, const char * &old_end, size_t extra) + { + if (old_begin == m_current_last && + old_end == m_current_next && + old_end + extra <= m_current_end) { + char * ret = m_current_next; + old_end = m_current_next = ret + extra; + return ret; + } else { + return slow_realloc(old_begin, old_end, extra); + } + } + +private: + struct arena_page { + arena_page * next; + }; + + void grow(size_t len); + char * slow_realloc(const char * &old_begin, const char * &old_end, size_t extra); + + arena_page * m_current = nullptr; + char * m_current_last = nullptr; + char * m_current_next = nullptr; + char * m_current_end = nullptr; +}; + +arena::~arena() +{ + while (arena_page * cur = m_current) { + m_current = cur->next; + free(cur); + } + thread_arena = nullptr; +} + +void arena::grow(size_t len) +{ + len = std::max(len * 2, // Make sure appends are O(n) + 2048 - sizeof(arena_page)); // Reasonable default + arena_page * elem = static_cast(malloc(sizeof(arena_page) + len)); + elem->next = m_current; + m_current = elem; + m_current_next = reinterpret_cast(elem + 1); + m_current_end = m_current_next + len; +} + +char * arena::slow_realloc(const char * &old_begin, const char * &old_end, size_t extra) +{ + size_t old_len = old_end - old_begin; + char * ret = alloc(old_len + extra); + memcpy(ret, old_begin, old_len); + old_begin = ret; + old_end = ret + old_len + extra; + return ret + old_len; +} + +template +class arena_allocator { +public: + typedef T value_type; + typedef T * pointer; + typedef std::size_t size_type; + + pointer allocate( size_type n, std::allocator::const_pointer hint = 0 ) + { + return reinterpret_cast(thread_arena->alloc(n * sizeof(T))); + } + + void deallocate( T* p, std::size_t n ) + { + // Nothing to do! So cheap! + } +}; + +class demangle_string { +public: + demangle_string() + : m_first(nullptr) + , m_last(nullptr) + { + } + + demangle_string(demangle_string const & rhs) + : m_first(rhs.m_first) + , m_last(rhs.m_last) + { + } + + demangle_string(const char * f, const char * l) + : m_first(f) + , m_last(l) + { + } + + demangle_string(const char * str, size_t len) + : m_first(str) + , m_last(str + len) + { + } + + demangle_string(const char * str) + : m_first(str) + , m_last(str + strlen(str)) + { + } + + void clear() + { + m_last = m_first; + } + + const char & front() const + { + return m_first[0]; + } + + const char & back() const + { + return *(m_last - 1); + } + + const char & operator[](size_t idx) const + { + return m_first[idx]; + } + + void append(demangle_string const & b) + { + if (empty()) { + *this = b; + } else if (!b.empty()) { + size_t blen = b.size(); + memcpy(thread_arena->realloc(m_first, m_last, blen), b.m_first, blen); + } + } + + void append(const char * rhs) + { + append(demangle_string(rhs)); + } + + void append(const char * a, const char * b) + { + append(demangle_string(a, b)); + } + + void push_back(char c) + { + append(demangle_string(&c, 1)); + } + + void replace(const char * cut_first, const char * cut_last, demangle_string const & b) + { + cut_first = std::min(cut_first, m_last); + cut_last = std::min(cut_last, m_last); + const char * last = m_last; + m_last = cut_first; + append(b.m_first, b.m_last); + append(cut_last, last); + } + + void replace(size_t pos, size_t count, demangle_string const & b) + { + replace(m_first + pos, m_first + pos + count, b); + } + + void replace(const char * f, const char * l, const char * b) + { + replace(f, l, demangle_string(b)); + } + + void replace(size_t pos, size_t count, const char * b) + { + replace(m_first + pos, m_first + pos + count, b); + } + + void insert(size_t pos, demangle_string const & b) + { + replace(m_first + pos, m_first + pos, b); + } + + void insert(size_t pos, const char * b) + { + insert(pos, demangle_string(b)); + } + + void insert(size_t pos, const char * a, const char * b) + { + insert(pos, demangle_string(a, b)); + } + + void erase(size_t pos, size_t count) + { + replace(pos, count, demangle_string()); + } + + // NOTE: Not null terminated! + const char * data() const { + return m_first; + } + + size_t size() const { + return m_last - m_first; + } + + bool empty() const { + return m_first == m_last; + } + + bool operator==(demangle_string const & rhs) const + { + size_t alen = m_last - m_first; + size_t blen = rhs.m_last - rhs.m_first; + return alen == blen && !memcmp(m_first, rhs.m_first, alen); + } + + bool operator!=(demangle_string const & rhs) const + { + return !operator==(rhs); + } + + bool operator==(const char * rhs) const + { + return *this == demangle_string(rhs); + } + + bool operator!=(const char * rhs) const + { + return *this != demangle_string(rhs); + } + + demangle_string substr(size_t pos, size_t count) const + { + const char * f = std::min(m_first + pos, m_last); + const char * l = std::min(m_first + pos + count, m_last); + return demangle_string(f, l); + } + + demangle_string & operator=(demangle_string const & src) + { + m_first = src.m_first; + m_last = src.m_last; + return *this; + } + + // This is purposefully deleted to remind developers NOT to do "a += b + c" but instead "a = a + + // b + c", which reduces the number of copies. If you really need to append a single + // demangle_string, call append. + demangle_string & operator+=(demangle_string const & rhs) = delete; + + demangle_string operator+(demangle_string const & rhs) const + { + demangle_string copy(*this); + copy.append(rhs); + return copy; + } + + demangle_string operator+(const char * rhs) const + { + return *this + demangle_string(rhs); + } + + demangle_string operator+(const char rhs) const + { + return *this + demangle_string(&rhs, 1); + } + +private: + const char * m_first = nullptr; // inclusive + const char * m_last = nullptr; // exclusive +}; + +demangle_string operator+(const char * lhs, demangle_string const & rhs) +{ + return demangle_string(lhs) + rhs; +} + +demangle_string & operator+=(demangle_string & lhs, const char * rhs) +{ + lhs.append(rhs); + return lhs; +} + +demangle_string & operator+=(demangle_string & lhs, const char ch) +{ + lhs.push_back(ch); + return lhs; +} + +template struct string_pair { + StrT first; + StrT second; + + string_pair() = default; + string_pair(StrT f) : first(std::move(f)) {} + string_pair(StrT f, StrT s) : first(std::move(f)), second(std::move(s)) {} + template string_pair(const char (&s)[N]) : first(demangle_string(s, N - 1)) {} + + size_t size() const { return first.size() + second.size(); } + StrT full() const { return first + second; } + string_pair move_full() { return *this; } + bool empty() const { return first.empty() && second.empty(); } +}; + +demangle_string & operator+=(demangle_string & lhs, string_pair const & rhs) +{ + lhs.append(rhs.first); + lhs.append(rhs.second); + return lhs; +} + +demangle_string operator+(demangle_string lhs, string_pair const & rhs) +{ + return lhs += rhs; +} + +demangle_string operator+(const char * lhs, string_pair const & rhs) +{ + return demangle_string(lhs) + rhs; +} + +demangle_string operator+(string_pair const & lhs, const char * rhs) +{ + demangle_string ret = lhs.first; + ret.append(lhs.second); + ret.append(rhs); + return ret; +} + +} // namespace + template static const char *parse_type(const char *first, const char *last, C &db); template @@ -144,11 +501,12 @@ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ std::reverse(buf, e); #endif - char num[float_data::max_demangled_size] = {0}; - int n = snprintf(num, sizeof(num), float_data::spec, value); - if (static_cast(n) >= sizeof(num)) + size_t const numlen = float_data::max_demangled_size; + char * num = thread_arena->alloc(numlen); + int n = snprintf(num, numlen, float_data::spec, value); + if (static_cast(n) >= numlen) return first; - db.names.push_back(std::string(num, static_cast(n))); + db.names.push_back(demangle_string(num, static_cast(n))); first = t + 1; } } @@ -171,7 +529,7 @@ return first; } if (static_cast(last - t) >= n) { - std::string r(t, n); + demangle_string r(t, n); if (r.substr(0, 10) == "_GLOBAL__N") db.names.push_back("(anonymous namespace)"); else @@ -492,7 +850,7 @@ db.names.push_back(temp); first = t + 1; } else { - db.names.push_back(std::string(first, t + 1)); + db.names.push_back(demangle_string(first, t + 1)); first = t + 1; db.fix_forward_references = true; } @@ -663,12 +1021,12 @@ const char *t = parse_template_param(first + 2, last, db); size_t k1 = db.names.size(); if (t != first + 2) { - std::string tmp("sizeof...("); + demangle_string tmp("sizeof...("); size_t k = k0; if (k != k1) { tmp += db.names[k].move_full(); for (++k; k != k1; ++k) - tmp += ", " + db.names[k].move_full(); + tmp = tmp + ", " + db.names[k].move_full(); } tmp += ")"; for (; k1 != k0; --k1) @@ -698,7 +1056,7 @@ const char *t = parse_cv_qualifiers(first + 2, last, cv); const char *t1 = parse_number(t, last); if (t1 != last && *t1 == '_') { - db.names.push_back("fp" + std::string(t, t1)); + db.names.push_back("fp" + demangle_string(t, t1)); first = t1 + 1; } } else if (first[1] == 'L') { @@ -709,7 +1067,7 @@ const char *t = parse_cv_qualifiers(t0, last, cv); const char *t1 = parse_number(t, last); if (t1 != last && *t1 == '_') { - db.names.push_back("fp" + std::string(t, t1)); + db.names.push_back("fp" + demangle_string(t, t1)); first = t1 + 1; } } @@ -792,7 +1150,7 @@ return first; auto expr = db.names.back().move_full(); db.names.pop_back(); - db.names.back().first += ".*" + expr; + db.names.back().first = db.names.back().first + ".*" + expr; first = t1; } } @@ -1034,7 +1392,7 @@ return first; auto s = db.names.back().move_full(); db.names.pop_back(); - db.names.back().first += "::" + std::move(s); + db.names.back().first = db.names.back().first + "::" + std::move(s); t = t1; } ++t; @@ -1048,7 +1406,7 @@ return first; auto s = db.names.back().move_full(); db.names.pop_back(); - db.names.back().first += "::" + std::move(s); + db.names.back().first = db.names.back().first + "::" + std::move(s); first = t1; } else { t += 2; @@ -1074,7 +1432,7 @@ return first; auto s = db.names.back().move_full(); db.names.pop_back(); - db.names.back().first += "::" + std::move(s); + db.names.back().first = db.names.back().first + "::" + std::move(s); first = t1; } else { t1 = parse_unresolved_qualifier_level(t, last, db); @@ -1092,7 +1450,7 @@ return first; auto s = db.names.back().move_full(); db.names.pop_back(); - db.names.back().first += "::" + std::move(s); + db.names.back().first = db.names.back().first + "::" + std::move(s); t = t1; } ++t; @@ -1106,7 +1464,7 @@ return first; auto s = db.names.back().move_full(); db.names.pop_back(); - db.names.back().first += "::" + std::move(s); + db.names.back().first = db.names.back().first + "::" + std::move(s); first = t1; } } @@ -1130,7 +1488,7 @@ db.names.pop_back(); if (db.names.empty()) return first; - db.names.back().first += "." + name; + db.names.back().first = db.names.back().first + "." + name; first = t1; } } @@ -1149,8 +1507,8 @@ return first; if (db.names.empty()) return first; - db.names.back().first += db.names.back().second; - db.names.back().second = std::string(); + db.names.back().first.append(db.names.back().second); + db.names.back().second = demangle_string(); db.names.back().first.append("("); bool first_expr = true; while (*t != 'E') { @@ -1168,7 +1526,7 @@ db.names.back().first.append(", "); first_expr = false; } - db.names.back().first.append(tmp); + db.names.back().first += tmp; } t = t1; } @@ -1221,7 +1579,7 @@ if (db.names.empty()) return first; db.names.back().first.append(", "); - db.names.back().first.append(tmp); + db.names.back().first += tmp; first_expr = false; } } @@ -1250,7 +1608,7 @@ if (db.names.empty()) return first; db.names.back().first.append(", "); - db.names.back().first.append(tmp); + db.names.back().first += tmp; first_expr = false; } } @@ -1259,7 +1617,7 @@ } if (*t != 'E') return first; - std::string init_list; + string_pair init_list; if (has_init) { if (db.names.empty()) return first; @@ -1270,14 +1628,14 @@ return first; auto type = db.names.back().move_full(); db.names.pop_back(); - std::string expr_list; + string_pair expr_list; if (has_expr_list) { if (db.names.empty()) return first; expr_list = db.names.back().move_full(); db.names.pop_back(); } - std::string r; + demangle_string r; if (parsed_gs) r = "::"; if (is_array) @@ -1285,10 +1643,10 @@ else r += " "; if (has_expr_list) - r += "(" + expr_list + ") "; + r = r + "(" + expr_list + ") "; r += type; if (has_init) - r += " (" + init_list + ")"; + r = r + " (" + init_list + ")"; db.names.push_back(std::move(r)); first = t + 1; } @@ -1336,7 +1694,7 @@ if (db.names.empty()) return first; db.names.back().first.append(", "); - db.names.back().first.append(tmp); + db.names.back().first += tmp; first_expr = false; } } @@ -1398,7 +1756,7 @@ const char *t1 = parse_type(t, last, db); if (t1 != t) { t = t1; - std::string sig("("); + demangle_string sig("("); int ref_qual = 0; while (true) { if (t == last) { @@ -1516,7 +1874,7 @@ if (db.names.back().second.substr(0, 2) == " [") db.names.back().second.erase(0, 1); db.names.back().second.insert(0, - " [" + std::string(first + 1, t) + "]"); + " [" + demangle_string(first + 1, t) + "]"); first = t2; } } @@ -1589,17 +1947,18 @@ if (t1 != t) { if (db.names.empty()) return first; - db.names.back().first += " vector[" + std::string(num, sz) + "]"; + db.names.back().first = db.names.back().first + " vector[" + + demangle_string(num, sz) + "]"; first = t1; } } else { ++t; - db.names.push_back("pixel vector[" + std::string(num, sz) + "]"); + db.names.push_back("pixel vector[" + demangle_string(num, sz) + "]"); first = t; } } } else { - std::string num; + string_pair num; const char *t1 = first + 2; if (*t1 != '_') { const char *t = parse_expression(t1, last, db); @@ -1616,7 +1975,7 @@ if (t != t1) { if (db.names.empty()) return first; - db.names.back().first += " vector[" + num + "]"; + db.names.back().first = db.names.back().first + " vector[" + num + "]"; first = t; } } @@ -1861,7 +2220,7 @@ if (db.names.back().first.substr(0, 9) != "objcproto") { db.names.back() = type + " " + db.names.back().move_full(); } else { - auto proto = db.names.back().move_full(); + auto proto = db.names.back().full(); db.names.pop_back(); t = parse_source_name(proto.data() + 9, proto.data() + proto.size(), db); @@ -2304,11 +2663,12 @@ template static const char *parse_integer_literal(const char *first, const char *last, - const std::string &lit, C &db) { + const char * lit, C &db) { const char *t = parse_number(first, last); if (t != first && t != last && *t == 'E') { - if (lit.size() > 3) - db.names.push_back("(" + lit + ")"); + bool is_short_lit = strlen(lit) <= 3; + if (!is_short_lit) + db.names.push_back(demangle_string("(") + lit + ")"); else db.names.emplace_back(); if (*first == 'n') { @@ -2316,7 +2676,7 @@ ++first; } db.names.back().first.append(first, t); - if (lit.size() <= 3) + if (is_short_lit) db.names.back().first += lit; first = t + 1; } @@ -2466,7 +2826,7 @@ if (db.names.empty()) return first; db.names.back() = - "(" + db.names.back().move_full() + ")" + std::string(t, n); + "(" + db.names.back().move_full() + ")" + demangle_string(t, n); first = n + 1; break; } @@ -2481,7 +2841,7 @@ return first; } -static std::string base_name(std::string &s) { +static demangle_string base_name(demangle_string &s) { if (s.empty()) return s; if (s == "std::string") { @@ -2507,7 +2867,7 @@ unsigned c = 1; while (true) { if (--pe == pf) - return std::string(); + return demangle_string(); if (pe[-1] == '<') { if (--c == 0) { --pe; @@ -2518,7 +2878,7 @@ } } if (pe - pf <= 1) - return std::string(); + return demangle_string(); const char *p0 = pe - 1; for (; p0 != pf; --p0) { if (*p0 == ':') { @@ -2526,7 +2886,7 @@ break; } } - return std::string(p0, pe); + return demangle_string(p0, pe); } // ::= C1 # complete object constructor @@ -2591,7 +2951,7 @@ char type = first[1]; switch (type) { case 't': { - db.names.push_back(std::string("'unnamed")); + db.names.push_back("'unnamed"); const char *t0 = first + 2; if (t0 == last) { db.names.pop_back(); @@ -2612,7 +2972,7 @@ first = t0 + 1; } break; case 'l': { - db.names.push_back(std::string("'lambda'(")); + db.names.push_back("'lambda'("); const char *t0 = first + 2; if (first[2] == 'v') { db.names.back().first += ')'; @@ -2628,7 +2988,7 @@ return first; auto tmp = db.names.back().move_full(); db.names.pop_back(); - db.names.back().first.append(tmp); + db.names.back().first += tmp; t0 = t1; while (true) { t1 = parse_type(t0, last, db); @@ -2640,7 +3000,7 @@ db.names.pop_back(); if (!tmp.empty()) { db.names.back().first.append(", "); - db.names.back().first.append(tmp); + db.names.back().first += tmp; } t0 = t1; } @@ -2663,7 +3023,7 @@ const char *t1 = t0 + 1; while (t1 != last && std::isdigit(*t1)) ++t1; - db.names.back().first.insert(db.names.back().first.begin() + 7, t0, t1); + db.names.back().first.insert(7, t0, t1); t0 = t1; } if (t0 == last || *t0 != '_') { @@ -2802,13 +3162,13 @@ template static const char *parse_prefix_expression(const char *first, const char *last, - const std::string &op, + const char * op, C &db) { const char *t1 = parse_expression(first, last, db); if (t1 != first) { if (db.names.empty()) return first; - db.names.back().first = op + "(" + db.names.back().move_full() + ")"; + db.names.back().first = demangle_string(op) + "(" + db.names.back().move_full() + ")"; first = t1; } return first; @@ -2816,7 +3176,7 @@ template static const char *parse_binary_expression(const char *first, const char *last, - const std::string &op, + const char * op, C &db) { const char *t1 = parse_expression(first, last, db); if (t1 != first) { @@ -2829,10 +3189,11 @@ auto op1 = db.names.back().move_full(); auto &nm = db.names.back().first; nm.clear(); - if (op == ">") + bool is_greater_than = (op[0] == '>' && op[1] == 0); + if (is_greater_than) nm += '('; - nm += "(" + op1 + ") " + op + " (" + op2 + ")"; - if (op == ">") + nm = nm + "(" + op1 + ") " + op + " (" + op2 + ")"; + if (is_greater_than) nm += ')'; first = t2; } else if (!db.names.empty()) @@ -3007,7 +3368,7 @@ if (db.names.empty()) return first; db.names.back().first = - (parsed_gs ? std::string("::") : std::string()) + "delete[] " + + (parsed_gs ? demangle_string("::delete[] ") : demangle_string("delete[] ")) + db.names.back().move_full(); first = t1; } @@ -3026,7 +3387,7 @@ if (db.names.empty()) return first; db.names.back().first = - (parsed_gs ? std::string("::") : std::string()) + "delete " + + (parsed_gs ? demangle_string("::delete ") : demangle_string("delete ")) + db.names.back().move_full(); first = t1; } @@ -3435,7 +3796,7 @@ if (db.tag_templates) db.template_param.back().clear(); const char *t = first + 1; - std::string args("<"); + demangle_string args("<"); while (*t != 'E') { if (db.tag_templates) db.template_param.emplace_back(); @@ -3530,10 +3891,10 @@ if (db.names.empty()) return first; if (!db.names.back().first.empty()) { - db.names.back().first += "::" + name; + db.names.back().first = db.names.back().first + "::" + name; db.subs.push_back(typename C::sub_type(1, db.names.back())); } else - db.names.back().first = name; + db.names.back().first = name.full(); pop_subs = true; t0 = t1; } else @@ -3547,9 +3908,9 @@ if (db.names.empty()) return first; if (!db.names.back().first.empty()) - db.names.back().first += "::" + name; + db.names.back().first = db.names.back().first + "::" + name; else - db.names.back().first = name; + db.names.back().first = name.full(); db.subs.push_back(typename C::sub_type(1, db.names.back())); pop_subs = true; t0 = t1; @@ -3566,9 +3927,9 @@ if (db.names.empty()) return first; if (!db.names.back().first.empty()) - db.names.back().first += "::" + name; + db.names.back().first = db.names.back().first + "::" + name; else - db.names.back().first = name; + db.names.back().first = name.full(); db.subs.push_back(typename C::sub_type(1, db.names.back())); pop_subs = true; t0 = t1; @@ -3602,9 +3963,9 @@ if (db.names.empty()) return first; if (!db.names.back().first.empty()) - db.names.back().first += "::" + name; + db.names.back().first = db.names.back().first + "::" + name; else - db.names.back().first = name; + db.names.back().first = name.full(); db.subs.push_back(typename C::sub_type(1, db.names.back())); pop_subs = true; t0 = t1; @@ -3684,7 +4045,7 @@ if (db.names.empty()) return first; db.names.back().first.append("::"); - db.names.back().first.append(name); + db.names.back().first += name; first = t1; } else if (!db.names.empty()) db.names.pop_back(); @@ -3703,7 +4064,7 @@ if (db.names.empty()) return first; db.names.back().first.append("::"); - db.names.back().first.append(name); + db.names.back().first += name; } else if (!db.names.empty()) db.names.pop_back(); } break; @@ -4045,11 +4406,10 @@ save_value sb2(db.tag_templates); db.tag_templates = false; const char *t2; - std::string ret2; + demangle_string ret2; if (db.names.empty()) return first; - const std::string &nm = db.names.back().first; - if (nm.empty()) + if (db.names.back().first.empty()) return first; if (!db.parsed_ctor_dtor_cv && ends_with_template_args) { t2 = parse_type(t, last, db); @@ -4080,7 +4440,7 @@ if (t2 == t) break; if (k1 > k0) { - std::string tmp; + demangle_string tmp; for (size_t k = k0; k < k1; ++k) { if (!tmp.empty()) tmp += ", "; @@ -4098,7 +4458,7 @@ db.names.back().first += ", "; else first_arg = false; - db.names.back().first += tmp; + db.names.back().first.append(tmp); } } t = t2; @@ -4117,7 +4477,7 @@ db.names.back().first.append(" &"); else if (ref == 2) db.names.back().first.append(" &&"); - db.names.back().first += ret2; + db.names.back().first.append(ret2); first = t; } else first = t; @@ -4171,7 +4531,7 @@ if (first != last && *first == '.') { if (db.names.empty()) return first; - db.names.back().first += " (" + std::string(first, last) + ")"; + db.names.back().first = db.names.back().first + " (" + demangle_string(first, last) + ")"; first = last; } return first; @@ -4219,26 +4579,14 @@ } namespace { -template struct string_pair { - StrT first; - StrT second; - - string_pair() = default; - string_pair(StrT f) : first(std::move(f)) {} - string_pair(StrT f, StrT s) : first(std::move(f)), second(std::move(s)) {} - template string_pair(const char (&s)[N]) : first(s, N - 1) {} - - size_t size() const { return first.size() + second.size(); } - StrT full() const { return first + second; } - StrT move_full() { return std::move(first) + std::move(second); } -}; - struct Db { - typedef std::vector> sub_type; - typedef std::vector template_param_type; + arena arena; // Declare before vectors that may use it. + typedef std::vector, + arena_allocator>> sub_type; + typedef std::vector> template_param_type; sub_type names; template_param_type subs; - std::vector template_param; + std::vector> template_param; unsigned cv = 0; unsigned ref = 0; unsigned encoding_depth = 0; @@ -4288,7 +4636,7 @@ } } if (buf != nullptr) { - db.names.back().first += db.names.back().second; + db.names.back().first.append(db.names.back().second); std::memcpy(buf, db.names.back().first.data(), sz - 1); buf[sz - 1] = char(0); }