Skip to content

Commit dde59a0

Browse files
njames93memfrob
authored andcommitted
[ADT] Add methods to SmallString for efficient concatenation
A common pattern when using SmallString is to repeatedly call append to build a larger string. The issue here is the optimizer can't see through this and often has to check there is enough space in the storage for each string you try to append. This results in lots of conditional branches and potentially multiple calls to grow needing to be emitted if the buffer wasn't large enough. By taking an initializer_list of StringRefs, SmallString can preallocate the storage it needs for all of the StringRefs which only need to grow one time at most, then use a fast path of copying all the strings into its storage knowing there is guaranteed to be enough capacity. By using StringRefs, this also means you can append different string like types in one go as they will all be implicitly converted to a StringRef. Reviewed By: dblaikie Differential Revision: https://reviews.llvm.org/D90386
1 parent ca5d9d6 commit dde59a0

File tree

2 files changed

+45
-0
lines changed

2 files changed

+45
-0
lines changed

llvm/include/llvm/ADT/SmallString.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ class SmallString : public SmallVector<char, InternalLen> {
3030
/// Initialize from a StringRef.
3131
SmallString(StringRef S) : SmallVector<char, InternalLen>(S.begin(), S.end()) {}
3232

33+
/// Initialize by concatenating a list of StringRefs.
34+
SmallString(std::initializer_list<StringRef> Refs)
35+
: SmallVector<char, InternalLen>() {
36+
this->append(Refs);
37+
}
38+
3339
/// Initialize with a range.
3440
template<typename ItTy>
3541
SmallString(ItTy S, ItTy E) : SmallVector<char, InternalLen>(S, E) {}
@@ -65,6 +71,12 @@ class SmallString : public SmallVector<char, InternalLen> {
6571
SmallVectorImpl<char>::append(RHS.begin(), RHS.end());
6672
}
6773

74+
/// Assign from a list of StringRefs.
75+
void assign(std::initializer_list<StringRef> Refs) {
76+
this->clear();
77+
append(Refs);
78+
}
79+
6880
/// @}
6981
/// @name String Concatenation
7082
/// @{
@@ -89,6 +101,20 @@ class SmallString : public SmallVector<char, InternalLen> {
89101
SmallVectorImpl<char>::append(RHS.begin(), RHS.end());
90102
}
91103

104+
/// Append from a list of StringRefs.
105+
void append(std::initializer_list<StringRef> Refs) {
106+
size_t SizeNeeded = this->size();
107+
for (const StringRef &Ref : Refs)
108+
SizeNeeded += Ref.size();
109+
this->reserve(SizeNeeded);
110+
auto CurEnd = this->end();
111+
for (const StringRef &Ref : Refs) {
112+
this->uninitialized_copy(Ref.begin(), Ref.end(), CurEnd);
113+
CurEnd += Ref.size();
114+
}
115+
this->set_size(SizeNeeded);
116+
}
117+
92118
/// @}
93119
/// @name String Comparison
94120
/// @{

llvm/unittests/ADT/SmallStringTest.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ TEST_F(SmallStringTest, AssignSmallVector) {
7171
EXPECT_STREQ("abc", theString.c_str());
7272
}
7373

74+
TEST_F(SmallStringTest, AssignStringRefs) {
75+
theString.assign({"abc", "def", "ghi"});
76+
EXPECT_EQ(9u, theString.size());
77+
EXPECT_STREQ("abcdefghi", theString.c_str());
78+
}
79+
7480
TEST_F(SmallStringTest, AppendIterPair) {
7581
StringRef abc = "abc";
7682
theString.append(abc.begin(), abc.end());
@@ -96,6 +102,19 @@ TEST_F(SmallStringTest, AppendSmallVector) {
96102
EXPECT_STREQ("abcabc", theString.c_str());
97103
}
98104

105+
TEST_F(SmallStringTest, AppendStringRefs) {
106+
theString.append({"abc", "def", "ghi"});
107+
EXPECT_EQ(9u, theString.size());
108+
EXPECT_STREQ("abcdefghi", theString.c_str());
109+
StringRef Jkl = "jkl";
110+
std::string Mno = "mno";
111+
SmallString<4> Pqr("pqr");
112+
const char *Stu = "stu";
113+
theString.append({Jkl, Mno, Pqr, Stu});
114+
EXPECT_EQ(21u, theString.size());
115+
EXPECT_STREQ("abcdefghijklmnopqrstu", theString.c_str());
116+
}
117+
99118
TEST_F(SmallStringTest, StringRefConversion) {
100119
StringRef abc = "abc";
101120
theString.assign(abc.begin(), abc.end());

0 commit comments

Comments
 (0)