1 // Copyright 2020 The Abseil Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "absl/strings/internal/str_format/parser.h"
16
17 #include <assert.h>
18 #include <string.h>
19 #include <wchar.h>
20 #include <cctype>
21 #include <cstdint>
22
23 #include <algorithm>
24 #include <initializer_list>
25 #include <limits>
26 #include <ostream>
27 #include <string>
28 #include <unordered_set>
29
30 namespace absl {
31 ABSL_NAMESPACE_BEGIN
32 namespace str_format_internal {
33
34 // Define the array for non-constexpr uses.
35 constexpr ConvTag ConvTagHolder::value[256];
36
ConsumeUnboundConversionNoInline(const char * p,const char * end,UnboundConversion * conv,int * next_arg)37 ABSL_ATTRIBUTE_NOINLINE const char* ConsumeUnboundConversionNoInline(
38 const char* p, const char* end, UnboundConversion* conv, int* next_arg) {
39 return ConsumeUnboundConversion(p, end, conv, next_arg);
40 }
41
LengthModToString(LengthMod v)42 std::string LengthModToString(LengthMod v) {
43 switch (v) {
44 case LengthMod::h:
45 return "h";
46 case LengthMod::hh:
47 return "hh";
48 case LengthMod::l:
49 return "l";
50 case LengthMod::ll:
51 return "ll";
52 case LengthMod::L:
53 return "L";
54 case LengthMod::j:
55 return "j";
56 case LengthMod::z:
57 return "z";
58 case LengthMod::t:
59 return "t";
60 case LengthMod::q:
61 return "q";
62 case LengthMod::none:
63 return "";
64 }
65 return "";
66 }
67
68 struct ParsedFormatBase::ParsedFormatConsumer {
ParsedFormatConsumerabsl::str_format_internal::ParsedFormatBase::ParsedFormatConsumer69 explicit ParsedFormatConsumer(ParsedFormatBase *parsedformat)
70 : parsed(parsedformat), data_pos(parsedformat->data_.get()) {}
71
Appendabsl::str_format_internal::ParsedFormatBase::ParsedFormatConsumer72 bool Append(string_view s) {
73 if (s.empty()) return true;
74
75 size_t text_end = AppendText(s);
76
77 if (!parsed->items_.empty() && !parsed->items_.back().is_conversion) {
78 // Let's extend the existing text run.
79 parsed->items_.back().text_end = text_end;
80 } else {
81 // Let's make a new text run.
82 parsed->items_.push_back({false, text_end, {}});
83 }
84 return true;
85 }
86
ConvertOneabsl::str_format_internal::ParsedFormatBase::ParsedFormatConsumer87 bool ConvertOne(const UnboundConversion &conv, string_view s) {
88 size_t text_end = AppendText(s);
89 parsed->items_.push_back({true, text_end, conv});
90 return true;
91 }
92
AppendTextabsl::str_format_internal::ParsedFormatBase::ParsedFormatConsumer93 size_t AppendText(string_view s) {
94 memcpy(data_pos, s.data(), s.size());
95 data_pos += s.size();
96 return static_cast<size_t>(data_pos - parsed->data_.get());
97 }
98
99 ParsedFormatBase *parsed;
100 char* data_pos;
101 };
102
ParsedFormatBase(string_view format,bool allow_ignored,std::initializer_list<FormatConversionCharSet> convs)103 ParsedFormatBase::ParsedFormatBase(
104 string_view format, bool allow_ignored,
105 std::initializer_list<FormatConversionCharSet> convs)
106 : data_(format.empty() ? nullptr : new char[format.size()]) {
107 has_error_ = !ParseFormatString(format, ParsedFormatConsumer(this)) ||
108 !MatchesConversions(allow_ignored, convs);
109 }
110
MatchesConversions(bool allow_ignored,std::initializer_list<FormatConversionCharSet> convs) const111 bool ParsedFormatBase::MatchesConversions(
112 bool allow_ignored,
113 std::initializer_list<FormatConversionCharSet> convs) const {
114 std::unordered_set<int> used;
115 auto add_if_valid_conv = [&](int pos, char c) {
116 if (static_cast<size_t>(pos) > convs.size() ||
117 !Contains(convs.begin()[pos - 1], c))
118 return false;
119 used.insert(pos);
120 return true;
121 };
122 for (const ConversionItem &item : items_) {
123 if (!item.is_conversion) continue;
124 auto &conv = item.conv;
125 if (conv.precision.is_from_arg() &&
126 !add_if_valid_conv(conv.precision.get_from_arg(), '*'))
127 return false;
128 if (conv.width.is_from_arg() &&
129 !add_if_valid_conv(conv.width.get_from_arg(), '*'))
130 return false;
131 if (!add_if_valid_conv(conv.arg_position,
132 FormatConversionCharToChar(conv.conv)))
133 return false;
134 }
135 return used.size() == convs.size() || allow_ignored;
136 }
137
138 } // namespace str_format_internal
139 ABSL_NAMESPACE_END
140 } // namespace absl
141