xref: /aosp_15_r20/external/abseil-cpp/absl/strings/internal/str_format/parser.cc (revision 9356374a3709195abf420251b3e825997ff56c0f)
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