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