Skip to content

Commit c2018fa

Browse files
authored
[NFC][Support] Refactor FormatVariadic code. (#106610)
- Rename `Align` field in ReplacementItem/FmtAlign to `Width` to accurately reflect its use. - Change both `Width` and `Index` in ReplacementItem to 32-bit int instead of size_t (as 64-bits seems excessive in this context). - Eliminate the use of `Empty` ReplacementType, and use the existing std::optional<> instead to indicate that. - Eliminate some boilerplate type code in formatv(). - Eliminate the loop in `splitLiteralAndReplacement`. The existing code will never loop back. - Directly use constructor instead of std::make_pair.
1 parent fc3e6a8 commit c2018fa

File tree

4 files changed

+90
-105
lines changed

4 files changed

+90
-105
lines changed

llvm/include/llvm/Support/FormatCommon.h

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,43 +16,45 @@
1616
namespace llvm {
1717
enum class AlignStyle { Left, Center, Right };
1818

19+
/// Helper class to format to a \p Width wide field, with alignment \p Where
20+
/// within that field.
1921
struct FmtAlign {
2022
support::detail::format_adapter &Adapter;
2123
AlignStyle Where;
22-
size_t Amount;
24+
unsigned Width;
2325
char Fill;
2426

2527
FmtAlign(support::detail::format_adapter &Adapter, AlignStyle Where,
26-
size_t Amount, char Fill = ' ')
27-
: Adapter(Adapter), Where(Where), Amount(Amount), Fill(Fill) {}
28+
unsigned Width, char Fill = ' ')
29+
: Adapter(Adapter), Where(Where), Width(Width), Fill(Fill) {}
2830

2931
void format(raw_ostream &S, StringRef Options) {
3032
// If we don't need to align, we can format straight into the underlying
3133
// stream. Otherwise we have to go through an intermediate stream first
3234
// in order to calculate how long the output is so we can align it.
3335
// TODO: Make the format method return the number of bytes written, that
3436
// way we can also skip the intermediate stream for left-aligned output.
35-
if (Amount == 0) {
37+
if (Width == 0) {
3638
Adapter.format(S, Options);
3739
return;
3840
}
3941
SmallString<64> Item;
4042
raw_svector_ostream Stream(Item);
4143

4244
Adapter.format(Stream, Options);
43-
if (Amount <= Item.size()) {
45+
if (Width <= Item.size()) {
4446
S << Item;
4547
return;
4648
}
4749

48-
size_t PadAmount = Amount - Item.size();
50+
unsigned PadAmount = Width - static_cast<unsigned>(Item.size());
4951
switch (Where) {
5052
case AlignStyle::Left:
5153
S << Item;
5254
fill(S, PadAmount);
5355
break;
5456
case AlignStyle::Center: {
55-
size_t X = PadAmount / 2;
57+
unsigned X = PadAmount / 2;
5658
fill(S, X);
5759
S << Item;
5860
fill(S, PadAmount - X);
@@ -66,8 +68,8 @@ struct FmtAlign {
6668
}
6769

6870
private:
69-
void fill(llvm::raw_ostream &S, size_t Count) {
70-
for (size_t I = 0; I < Count; ++I)
71+
void fill(llvm::raw_ostream &S, unsigned Count) {
72+
for (unsigned I = 0; I < Count; ++I)
7173
S << Fill;
7274
}
7375
};

llvm/include/llvm/Support/FormatVariadic.h

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,21 +43,20 @@
4343

4444
namespace llvm {
4545

46-
enum class ReplacementType { Empty, Format, Literal };
46+
enum class ReplacementType { Format, Literal };
4747

4848
struct ReplacementItem {
49-
ReplacementItem() = default;
5049
explicit ReplacementItem(StringRef Literal)
5150
: Type(ReplacementType::Literal), Spec(Literal) {}
52-
ReplacementItem(StringRef Spec, size_t Index, size_t Align, AlignStyle Where,
53-
char Pad, StringRef Options)
54-
: Type(ReplacementType::Format), Spec(Spec), Index(Index), Align(Align),
51+
ReplacementItem(StringRef Spec, unsigned Index, unsigned Width,
52+
AlignStyle Where, char Pad, StringRef Options)
53+
: Type(ReplacementType::Format), Spec(Spec), Index(Index), Width(Width),
5554
Where(Where), Pad(Pad), Options(Options) {}
5655

57-
ReplacementType Type = ReplacementType::Empty;
56+
ReplacementType Type;
5857
StringRef Spec;
59-
size_t Index = 0;
60-
size_t Align = 0;
58+
unsigned Index = 0;
59+
unsigned Width = 0;
6160
AlignStyle Where = AlignStyle::Right;
6261
char Pad = 0;
6362
StringRef Options;
@@ -81,8 +80,6 @@ class formatv_object_base {
8180
void format(raw_ostream &S) const {
8281
const auto Replacements = parseFormatString(Fmt, Adapters.size(), Validate);
8382
for (const auto &R : Replacements) {
84-
if (R.Type == ReplacementType::Empty)
85-
continue;
8683
if (R.Type == ReplacementType::Literal) {
8784
S << R.Spec;
8885
continue;
@@ -94,7 +91,7 @@ class formatv_object_base {
9491

9592
auto *W = Adapters[R.Index];
9693

97-
FmtAlign Align(*W, R.Where, R.Align, R.Pad);
94+
FmtAlign Align(*W, R.Where, R.Width, R.Pad);
9895
Align.format(S, R.Options);
9996
}
10097
}
@@ -248,14 +245,10 @@ template <typename Tuple> class formatv_object : public formatv_object_base {
248245

249246
// formatv() with validation enable/disable controlled by the first argument.
250247
template <typename... Ts>
251-
inline auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
252-
-> formatv_object<decltype(std::make_tuple(
253-
support::detail::build_format_adapter(std::forward<Ts>(Vals))...))> {
254-
using ParamTuple = decltype(std::make_tuple(
255-
support::detail::build_format_adapter(std::forward<Ts>(Vals))...));
248+
inline auto formatv(bool Validate, const char *Fmt, Ts &&...Vals) {
256249
auto Params = std::make_tuple(
257250
support::detail::build_format_adapter(std::forward<Ts>(Vals))...);
258-
return formatv_object<ParamTuple>(Fmt, std::move(Params), Validate);
251+
return formatv_object<decltype(Params)>(Fmt, std::move(Params), Validate);
259252
}
260253

261254
// formatv() with validation enabled.

llvm/lib/Support/FormatVariadic.cpp

Lines changed: 49 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ static std::optional<AlignStyle> translateLocChar(char C) {
2626
}
2727

2828
static bool consumeFieldLayout(StringRef &Spec, AlignStyle &Where,
29-
size_t &Align, char &Pad) {
29+
unsigned &Align, char &Pad) {
3030
Where = AlignStyle::Right;
3131
Align = 0;
3232
Pad = ' ';
@@ -60,14 +60,14 @@ static std::optional<ReplacementItem> parseReplacementItem(StringRef Spec) {
6060
// If the replacement sequence does not start with a non-negative integer,
6161
// this is an error.
6262
char Pad = ' ';
63-
std::size_t Align = 0;
63+
unsigned Align = 0;
6464
AlignStyle Where = AlignStyle::Right;
6565
StringRef Options;
66-
size_t Index = 0;
66+
unsigned Index = 0;
6767
RepString = RepString.trim();
6868
if (RepString.consumeInteger(0, Index)) {
6969
assert(false && "Invalid replacement sequence index!");
70-
return ReplacementItem{};
70+
return std::nullopt;
7171
}
7272
RepString = RepString.trim();
7373
if (RepString.consume_front(",")) {
@@ -83,61 +83,50 @@ static std::optional<ReplacementItem> parseReplacementItem(StringRef Spec) {
8383
assert(RepString.empty() &&
8484
"Unexpected characters found in replacement string!");
8585

86-
return ReplacementItem{Spec, Index, Align, Where, Pad, Options};
86+
return ReplacementItem(Spec, Index, Align, Where, Pad, Options);
8787
}
8888

89-
static std::pair<ReplacementItem, StringRef>
89+
static std::pair<std::optional<ReplacementItem>, StringRef>
9090
splitLiteralAndReplacement(StringRef Fmt) {
91-
while (!Fmt.empty()) {
92-
// Everything up until the first brace is a literal.
93-
if (Fmt.front() != '{') {
94-
std::size_t BO = Fmt.find_first_of('{');
95-
return std::make_pair(ReplacementItem{Fmt.substr(0, BO)}, Fmt.substr(BO));
96-
}
97-
98-
StringRef Braces = Fmt.take_while([](char C) { return C == '{'; });
99-
// If there is more than one brace, then some of them are escaped. Treat
100-
// these as replacements.
101-
if (Braces.size() > 1) {
102-
size_t NumEscapedBraces = Braces.size() / 2;
103-
StringRef Middle = Fmt.take_front(NumEscapedBraces);
104-
StringRef Right = Fmt.drop_front(NumEscapedBraces * 2);
105-
return std::make_pair(ReplacementItem{Middle}, Right);
106-
}
107-
// An unterminated open brace is undefined. Assert to indicate that this is
108-
// undefined and that we consider it an error. When asserts are disabled,
109-
// build a replacement item with an error message.
110-
std::size_t BC = Fmt.find_first_of('}');
111-
if (BC == StringRef::npos) {
112-
assert(
113-
false &&
114-
"Unterminated brace sequence. Escape with {{ for a literal brace.");
115-
return std::make_pair(
116-
ReplacementItem{"Unterminated brace sequence. Escape with {{ for a "
117-
"literal brace."},
118-
StringRef());
119-
}
91+
assert(!Fmt.empty());
92+
// Everything up until the first brace is a literal.
93+
if (Fmt.front() != '{') {
94+
size_t BO = Fmt.find_first_of('{');
95+
return {ReplacementItem{Fmt.substr(0, BO)}, Fmt.substr(BO)};
96+
}
12097

121-
// Even if there is a closing brace, if there is another open brace before
122-
// this closing brace, treat this portion as literal, and try again with the
123-
// next one.
124-
std::size_t BO2 = Fmt.find_first_of('{', 1);
125-
if (BO2 < BC)
126-
return std::make_pair(ReplacementItem{Fmt.substr(0, BO2)},
127-
Fmt.substr(BO2));
98+
StringRef Braces = Fmt.take_while([](char C) { return C == '{'; });
99+
// If there is more than one brace, then some of them are escaped. Treat
100+
// these as replacements.
101+
if (Braces.size() > 1) {
102+
size_t NumEscapedBraces = Braces.size() / 2;
103+
StringRef Middle = Fmt.take_front(NumEscapedBraces);
104+
StringRef Right = Fmt.drop_front(NumEscapedBraces * 2);
105+
return {ReplacementItem(Middle), Right};
106+
}
107+
// An unterminated open brace is undefined. Assert to indicate that this is
108+
// undefined and that we consider it an error. When asserts are disabled,
109+
// build a replacement item with an error message.
110+
size_t BC = Fmt.find_first_of('}');
111+
if (BC == StringRef::npos) {
112+
assert(false &&
113+
"Unterminated brace sequence. Escape with {{ for a literal brace.");
114+
return {ReplacementItem("Unterminated brace sequence. Escape with {{ for a "
115+
"literal brace."),
116+
StringRef()};
117+
}
128118

129-
StringRef Spec = Fmt.slice(1, BC);
130-
StringRef Right = Fmt.substr(BC + 1);
119+
// Even if there is a closing brace, if there is another open brace before
120+
// this closing brace, treat this portion as literal, and try again with the
121+
// next one.
122+
size_t BO2 = Fmt.find_first_of('{', 1);
123+
if (BO2 < BC)
124+
return {ReplacementItem(Fmt.substr(0, BO2)), Fmt.substr(BO2)};
131125

132-
auto RI = parseReplacementItem(Spec);
133-
if (RI)
134-
return std::make_pair(*RI, Right);
126+
StringRef Spec = Fmt.slice(1, BC);
127+
StringRef Right = Fmt.substr(BC + 1);
135128

136-
// If there was an error parsing the replacement item, treat it as an
137-
// invalid replacement spec, and just continue.
138-
Fmt = Fmt.drop_front(BC + 1);
139-
}
140-
return std::make_pair(ReplacementItem{Fmt}, StringRef());
129+
return {parseReplacementItem(Spec), Right};
141130
}
142131

143132
#ifndef NDEBUG
@@ -153,17 +142,18 @@ formatv_object_base::parseFormatString(StringRef Fmt, size_t NumArgs,
153142

154143
#if ENABLE_VALIDATION
155144
const StringRef SavedFmtStr = Fmt;
156-
size_t NumExpectedArgs = 0;
145+
unsigned NumExpectedArgs = 0;
157146
#endif
158147

159148
while (!Fmt.empty()) {
160-
ReplacementItem I;
149+
std::optional<ReplacementItem> I;
161150
std::tie(I, Fmt) = splitLiteralAndReplacement(Fmt);
162-
if (I.Type != ReplacementType::Empty)
163-
Replacements.push_back(I);
151+
if (!I)
152+
continue;
153+
Replacements.emplace_back(*I);
164154
#if ENABLE_VALIDATION
165-
if (I.Type == ReplacementType::Format)
166-
NumExpectedArgs = std::max(NumExpectedArgs, I.Index + 1);
155+
if (I->Type == ReplacementType::Format)
156+
NumExpectedArgs = std::max(NumExpectedArgs, I->Index + 1);
167157
#endif
168158
}
169159

@@ -195,7 +185,7 @@ formatv_object_base::parseFormatString(StringRef Fmt, size_t NumArgs,
195185
// Find the number of unique indices seen. All replacement indices
196186
// are < NumExpectedArgs.
197187
SmallVector<bool> Indices(NumExpectedArgs);
198-
size_t Count = 0;
188+
unsigned Count = 0;
199189
for (const ReplacementItem &I : Replacements) {
200190
if (I.Type != ReplacementType::Format || Indices[I.Index])
201191
continue;

0 commit comments

Comments
 (0)