/proc/self/cwd/external/com_google_absl/absl/strings/internal/str_format/parser.cc
Line | Count | Source (jump to first uncovered line) |
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 | | |
37 | | ABSL_ATTRIBUTE_NOINLINE const char* ConsumeUnboundConversionNoInline( |
38 | 0 | const char* p, const char* end, UnboundConversion* conv, int* next_arg) { |
39 | 0 | return ConsumeUnboundConversion(p, end, conv, next_arg); |
40 | 0 | } |
41 | | |
42 | 0 | std::string LengthModToString(LengthMod v) { |
43 | 0 | switch (v) { |
44 | 0 | case LengthMod::h: |
45 | 0 | return "h"; |
46 | 0 | case LengthMod::hh: |
47 | 0 | return "hh"; |
48 | 0 | case LengthMod::l: |
49 | 0 | return "l"; |
50 | 0 | case LengthMod::ll: |
51 | 0 | return "ll"; |
52 | 0 | case LengthMod::L: |
53 | 0 | return "L"; |
54 | 0 | case LengthMod::j: |
55 | 0 | return "j"; |
56 | 0 | case LengthMod::z: |
57 | 0 | return "z"; |
58 | 0 | case LengthMod::t: |
59 | 0 | return "t"; |
60 | 0 | case LengthMod::q: |
61 | 0 | return "q"; |
62 | 0 | case LengthMod::none: |
63 | 0 | return ""; |
64 | 0 | } |
65 | 0 | return ""; |
66 | 0 | } |
67 | | |
68 | | struct ParsedFormatBase::ParsedFormatConsumer { |
69 | | explicit ParsedFormatConsumer(ParsedFormatBase *parsedformat) |
70 | 0 | : parsed(parsedformat), data_pos(parsedformat->data_.get()) {} |
71 | | |
72 | 0 | bool Append(string_view s) { |
73 | 0 | if (s.empty()) return true; |
74 | | |
75 | 0 | size_t text_end = AppendText(s); |
76 | |
|
77 | 0 | if (!parsed->items_.empty() && !parsed->items_.back().is_conversion) { |
78 | | // Let's extend the existing text run. |
79 | 0 | parsed->items_.back().text_end = text_end; |
80 | 0 | } else { |
81 | | // Let's make a new text run. |
82 | 0 | parsed->items_.push_back({false, text_end, {}}); |
83 | 0 | } |
84 | 0 | return true; |
85 | 0 | } |
86 | | |
87 | 0 | bool ConvertOne(const UnboundConversion &conv, string_view s) { |
88 | 0 | size_t text_end = AppendText(s); |
89 | 0 | parsed->items_.push_back({true, text_end, conv}); |
90 | 0 | return true; |
91 | 0 | } |
92 | | |
93 | 0 | size_t AppendText(string_view s) { |
94 | 0 | memcpy(data_pos, s.data(), s.size()); |
95 | 0 | data_pos += s.size(); |
96 | 0 | return static_cast<size_t>(data_pos - parsed->data_.get()); |
97 | 0 | } |
98 | | |
99 | | ParsedFormatBase *parsed; |
100 | | char* data_pos; |
101 | | }; |
102 | | |
103 | | ParsedFormatBase::ParsedFormatBase( |
104 | | string_view format, bool allow_ignored, |
105 | | std::initializer_list<FormatConversionCharSet> convs) |
106 | 0 | : data_(format.empty() ? nullptr : new char[format.size()]) { |
107 | 0 | has_error_ = !ParseFormatString(format, ParsedFormatConsumer(this)) || |
108 | 0 | !MatchesConversions(allow_ignored, convs); |
109 | 0 | } |
110 | | |
111 | | bool ParsedFormatBase::MatchesConversions( |
112 | | bool allow_ignored, |
113 | 0 | std::initializer_list<FormatConversionCharSet> convs) const { |
114 | 0 | std::unordered_set<int> used; |
115 | 0 | auto add_if_valid_conv = [&](int pos, char c) { |
116 | 0 | if (static_cast<size_t>(pos) > convs.size() || |
117 | 0 | !Contains(convs.begin()[pos - 1], c)) |
118 | 0 | return false; |
119 | 0 | used.insert(pos); |
120 | 0 | return true; |
121 | 0 | }; |
122 | 0 | for (const ConversionItem &item : items_) { |
123 | 0 | if (!item.is_conversion) continue; |
124 | 0 | auto &conv = item.conv; |
125 | 0 | if (conv.precision.is_from_arg() && |
126 | 0 | !add_if_valid_conv(conv.precision.get_from_arg(), '*')) |
127 | 0 | return false; |
128 | 0 | if (conv.width.is_from_arg() && |
129 | 0 | !add_if_valid_conv(conv.width.get_from_arg(), '*')) |
130 | 0 | return false; |
131 | 0 | if (!add_if_valid_conv(conv.arg_position, |
132 | 0 | FormatConversionCharToChar(conv.conv))) |
133 | 0 | return false; |
134 | 0 | } |
135 | 0 | return used.size() == convs.size() || allow_ignored; |
136 | 0 | } |
137 | | |
138 | | } // namespace str_format_internal |
139 | | ABSL_NAMESPACE_END |
140 | | } // namespace absl |