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