xref: /aosp_15_r20/external/abseil-cpp/absl/strings/internal/str_format/bind.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/bind.h"
16 
17 #include <algorithm>
18 #include <cassert>
19 #include <cerrno>
20 #include <cstddef>
21 #include <cstdio>
22 #include <ios>
23 #include <limits>
24 #include <ostream>
25 #include <sstream>
26 #include <string>
27 #include "absl/base/config.h"
28 #include "absl/base/optimization.h"
29 #include "absl/strings/internal/str_format/arg.h"
30 #include "absl/strings/internal/str_format/constexpr_parser.h"
31 #include "absl/strings/internal/str_format/extension.h"
32 #include "absl/strings/internal/str_format/output.h"
33 #include "absl/strings/string_view.h"
34 #include "absl/types/span.h"
35 
36 namespace absl {
37 ABSL_NAMESPACE_BEGIN
38 namespace str_format_internal {
39 
40 namespace {
41 
BindFromPosition(int position,int * value,absl::Span<const FormatArgImpl> pack)42 inline bool BindFromPosition(int position, int* value,
43                              absl::Span<const FormatArgImpl> pack) {
44   assert(position > 0);
45   if (static_cast<size_t>(position) > pack.size()) {
46     return false;
47   }
48   // -1 because positions are 1-based
49   return FormatArgImplFriend::ToInt(pack[static_cast<size_t>(position) - 1],
50                                     value);
51 }
52 
53 class ArgContext {
54  public:
ArgContext(absl::Span<const FormatArgImpl> pack)55   explicit ArgContext(absl::Span<const FormatArgImpl> pack) : pack_(pack) {}
56 
57   // Fill 'bound' with the results of applying the context's argument pack
58   // to the specified 'unbound'. We synthesize a BoundConversion by
59   // lining up a UnboundConversion with a user argument. We also
60   // resolve any '*' specifiers for width and precision, so after
61   // this call, 'bound' has all the information it needs to be formatted.
62   // Returns false on failure.
63   bool Bind(const UnboundConversion* unbound, BoundConversion* bound);
64 
65  private:
66   absl::Span<const FormatArgImpl> pack_;
67 };
68 
Bind(const UnboundConversion * unbound,BoundConversion * bound)69 inline bool ArgContext::Bind(const UnboundConversion* unbound,
70                              BoundConversion* bound) {
71   const FormatArgImpl* arg = nullptr;
72   int arg_position = unbound->arg_position;
73   if (static_cast<size_t>(arg_position - 1) >= pack_.size()) return false;
74   arg = &pack_[static_cast<size_t>(arg_position - 1)];  // 1-based
75 
76   if (unbound->flags != Flags::kBasic) {
77     int width = unbound->width.value();
78     bool force_left = false;
79     if (unbound->width.is_from_arg()) {
80       if (!BindFromPosition(unbound->width.get_from_arg(), &width, pack_))
81         return false;
82       if (width < 0) {
83         // "A negative field width is taken as a '-' flag followed by a
84         // positive field width."
85         force_left = true;
86         // Make sure we don't overflow the width when negating it.
87         width = -std::max(width, -std::numeric_limits<int>::max());
88       }
89     }
90 
91     int precision = unbound->precision.value();
92     if (unbound->precision.is_from_arg()) {
93       if (!BindFromPosition(unbound->precision.get_from_arg(), &precision,
94                             pack_))
95         return false;
96     }
97 
98     FormatConversionSpecImplFriend::SetWidth(width, bound);
99     FormatConversionSpecImplFriend::SetPrecision(precision, bound);
100 
101     if (force_left) {
102       FormatConversionSpecImplFriend::SetFlags(unbound->flags | Flags::kLeft,
103                                                bound);
104     } else {
105       FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound);
106     }
107 
108     FormatConversionSpecImplFriend::SetLengthMod(unbound->length_mod, bound);
109   } else {
110     FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound);
111     FormatConversionSpecImplFriend::SetWidth(-1, bound);
112     FormatConversionSpecImplFriend::SetPrecision(-1, bound);
113   }
114   FormatConversionSpecImplFriend::SetConversionChar(unbound->conv, bound);
115   bound->set_arg(arg);
116   return true;
117 }
118 
119 template <typename Converter>
120 class ConverterConsumer {
121  public:
ConverterConsumer(Converter converter,absl::Span<const FormatArgImpl> pack)122   ConverterConsumer(Converter converter, absl::Span<const FormatArgImpl> pack)
123       : converter_(converter), arg_context_(pack) {}
124 
Append(string_view s)125   bool Append(string_view s) {
126     converter_.Append(s);
127     return true;
128   }
ConvertOne(const UnboundConversion & conv,string_view conv_string)129   bool ConvertOne(const UnboundConversion& conv, string_view conv_string) {
130     BoundConversion bound;
131     if (!arg_context_.Bind(&conv, &bound)) return false;
132     return converter_.ConvertOne(bound, conv_string);
133   }
134 
135  private:
136   Converter converter_;
137   ArgContext arg_context_;
138 };
139 
140 template <typename Converter>
ConvertAll(const UntypedFormatSpecImpl format,absl::Span<const FormatArgImpl> args,Converter converter)141 bool ConvertAll(const UntypedFormatSpecImpl format,
142                 absl::Span<const FormatArgImpl> args, Converter converter) {
143   if (format.has_parsed_conversion()) {
144     return format.parsed_conversion()->ProcessFormat(
145         ConverterConsumer<Converter>(converter, args));
146   } else {
147     return ParseFormatString(format.str(),
148                              ConverterConsumer<Converter>(converter, args));
149   }
150 }
151 
152 class DefaultConverter {
153  public:
DefaultConverter(FormatSinkImpl * sink)154   explicit DefaultConverter(FormatSinkImpl* sink) : sink_(sink) {}
155 
Append(string_view s) const156   void Append(string_view s) const { sink_->Append(s); }
157 
ConvertOne(const BoundConversion & bound,string_view) const158   bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const {
159     return FormatArgImplFriend::Convert(*bound.arg(), bound, sink_);
160   }
161 
162  private:
163   FormatSinkImpl* sink_;
164 };
165 
166 class SummarizingConverter {
167  public:
SummarizingConverter(FormatSinkImpl * sink)168   explicit SummarizingConverter(FormatSinkImpl* sink) : sink_(sink) {}
169 
Append(string_view s) const170   void Append(string_view s) const { sink_->Append(s); }
171 
ConvertOne(const BoundConversion & bound,string_view) const172   bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const {
173     UntypedFormatSpecImpl spec("%d");
174 
175     std::ostringstream ss;
176     ss << "{" << Streamable(spec, {*bound.arg()}) << ":"
177        << FormatConversionSpecImplFriend::FlagsToString(bound);
178     if (bound.width() >= 0) ss << bound.width();
179     if (bound.precision() >= 0) ss << "." << bound.precision();
180     ss << bound.conversion_char() << "}";
181     Append(ss.str());
182     return true;
183   }
184 
185  private:
186   FormatSinkImpl* sink_;
187 };
188 
189 }  // namespace
190 
BindWithPack(const UnboundConversion * props,absl::Span<const FormatArgImpl> pack,BoundConversion * bound)191 bool BindWithPack(const UnboundConversion* props,
192                   absl::Span<const FormatArgImpl> pack,
193                   BoundConversion* bound) {
194   return ArgContext(pack).Bind(props, bound);
195 }
196 
Summarize(const UntypedFormatSpecImpl format,absl::Span<const FormatArgImpl> args)197 std::string Summarize(const UntypedFormatSpecImpl format,
198                       absl::Span<const FormatArgImpl> args) {
199   typedef SummarizingConverter Converter;
200   std::string out;
201   {
202     // inner block to destroy sink before returning out. It ensures a last
203     // flush.
204     FormatSinkImpl sink(&out);
205     if (!ConvertAll(format, args, Converter(&sink))) {
206       return "";
207     }
208   }
209   return out;
210 }
211 
FormatUntyped(FormatRawSinkImpl raw_sink,const UntypedFormatSpecImpl format,absl::Span<const FormatArgImpl> args)212 bool FormatUntyped(FormatRawSinkImpl raw_sink,
213                    const UntypedFormatSpecImpl format,
214                    absl::Span<const FormatArgImpl> args) {
215   FormatSinkImpl sink(raw_sink);
216   using Converter = DefaultConverter;
217   return ConvertAll(format, args, Converter(&sink));
218 }
219 
Print(std::ostream & os) const220 std::ostream& Streamable::Print(std::ostream& os) const {
221   if (!FormatUntyped(&os, format_, args_)) os.setstate(std::ios::failbit);
222   return os;
223 }
224 
AppendPack(std::string * out,const UntypedFormatSpecImpl format,absl::Span<const FormatArgImpl> args)225 std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl format,
226                         absl::Span<const FormatArgImpl> args) {
227   size_t orig = out->size();
228   if (ABSL_PREDICT_FALSE(!FormatUntyped(out, format, args))) {
229     out->erase(orig);
230   }
231   return *out;
232 }
233 
FormatPack(UntypedFormatSpecImpl format,absl::Span<const FormatArgImpl> args)234 std::string FormatPack(UntypedFormatSpecImpl format,
235                        absl::Span<const FormatArgImpl> args) {
236   std::string out;
237   if (ABSL_PREDICT_FALSE(!FormatUntyped(&out, format, args))) {
238     out.clear();
239   }
240   return out;
241 }
242 
FprintF(std::FILE * output,const UntypedFormatSpecImpl format,absl::Span<const FormatArgImpl> args)243 int FprintF(std::FILE* output, const UntypedFormatSpecImpl format,
244             absl::Span<const FormatArgImpl> args) {
245   FILERawSink sink(output);
246   if (!FormatUntyped(&sink, format, args)) {
247     errno = EINVAL;
248     return -1;
249   }
250   if (sink.error()) {
251     errno = sink.error();
252     return -1;
253   }
254   if (sink.count() > static_cast<size_t>(std::numeric_limits<int>::max())) {
255     errno = EFBIG;
256     return -1;
257   }
258   return static_cast<int>(sink.count());
259 }
260 
SnprintF(char * output,size_t size,const UntypedFormatSpecImpl format,absl::Span<const FormatArgImpl> args)261 int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl format,
262              absl::Span<const FormatArgImpl> args) {
263   BufferRawSink sink(output, size ? size - 1 : 0);
264   if (!FormatUntyped(&sink, format, args)) {
265     errno = EINVAL;
266     return -1;
267   }
268   size_t total = sink.total_written();
269   if (size) output[std::min(total, size - 1)] = 0;
270   return static_cast<int>(total);
271 }
272 
273 }  // namespace str_format_internal
274 ABSL_NAMESPACE_END
275 }  // namespace absl
276