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