/proc/self/cwd/external/com_google_absl/absl/strings/internal/str_format/bind.cc
Line | Count | Source (jump to first uncovered line) |
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 | | |
42 | | inline bool BindFromPosition(int position, int* value, |
43 | 0 | absl::Span<const FormatArgImpl> pack) { |
44 | 0 | assert(position > 0); |
45 | 0 | if (static_cast<size_t>(position) > pack.size()) { |
46 | 0 | return false; |
47 | 0 | } |
48 | | // -1 because positions are 1-based |
49 | 0 | return FormatArgImplFriend::ToInt(pack[static_cast<size_t>(position) - 1], |
50 | 0 | value); |
51 | 0 | } |
52 | | |
53 | | class ArgContext { |
54 | | public: |
55 | 4.96k | 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 | | |
69 | | inline bool ArgContext::Bind(const UnboundConversion* unbound, |
70 | 4.96k | BoundConversion* bound) { |
71 | 4.96k | const FormatArgImpl* arg = nullptr; |
72 | 4.96k | int arg_position = unbound->arg_position; |
73 | 4.96k | if (static_cast<size_t>(arg_position - 1) >= pack_.size()) return false; |
74 | 4.96k | arg = &pack_[static_cast<size_t>(arg_position - 1)]; // 1-based |
75 | | |
76 | 4.96k | if (unbound->flags != Flags::kBasic) { |
77 | 0 | int width = unbound->width.value(); |
78 | 0 | bool force_left = false; |
79 | 0 | if (unbound->width.is_from_arg()) { |
80 | 0 | if (!BindFromPosition(unbound->width.get_from_arg(), &width, pack_)) |
81 | 0 | return false; |
82 | 0 | if (width < 0) { |
83 | | // "A negative field width is taken as a '-' flag followed by a |
84 | | // positive field width." |
85 | 0 | force_left = true; |
86 | | // Make sure we don't overflow the width when negating it. |
87 | 0 | width = -std::max(width, -std::numeric_limits<int>::max()); |
88 | 0 | } |
89 | 0 | } |
90 | | |
91 | 0 | int precision = unbound->precision.value(); |
92 | 0 | if (unbound->precision.is_from_arg()) { |
93 | 0 | if (!BindFromPosition(unbound->precision.get_from_arg(), &precision, |
94 | 0 | pack_)) |
95 | 0 | return false; |
96 | 0 | } |
97 | | |
98 | 0 | FormatConversionSpecImplFriend::SetWidth(width, bound); |
99 | 0 | FormatConversionSpecImplFriend::SetPrecision(precision, bound); |
100 | |
|
101 | 0 | if (force_left) { |
102 | 0 | FormatConversionSpecImplFriend::SetFlags(unbound->flags | Flags::kLeft, |
103 | 0 | bound); |
104 | 0 | } else { |
105 | 0 | FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound); |
106 | 0 | } |
107 | |
|
108 | 0 | FormatConversionSpecImplFriend::SetLengthMod(unbound->length_mod, bound); |
109 | 4.96k | } else { |
110 | 4.96k | FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound); |
111 | 4.96k | FormatConversionSpecImplFriend::SetWidth(-1, bound); |
112 | 4.96k | FormatConversionSpecImplFriend::SetPrecision(-1, bound); |
113 | 4.96k | } |
114 | 4.96k | FormatConversionSpecImplFriend::SetConversionChar(unbound->conv, bound); |
115 | 4.96k | bound->set_arg(arg); |
116 | 4.96k | return true; |
117 | 4.96k | } |
118 | | |
119 | | template <typename Converter> |
120 | | class ConverterConsumer { |
121 | | public: |
122 | | ConverterConsumer(Converter converter, absl::Span<const FormatArgImpl> pack) |
123 | 4.96k | : converter_(converter), arg_context_(pack) {} Unexecuted instantiation: bind.cc:absl::str_format_internal::(anonymous namespace)::ConverterConsumer<absl::str_format_internal::(anonymous namespace)::SummarizingConverter>::ConverterConsumer(absl::str_format_internal::(anonymous namespace)::SummarizingConverter, absl::Span<absl::str_format_internal::FormatArgImpl const>) bind.cc:absl::str_format_internal::(anonymous namespace)::ConverterConsumer<absl::str_format_internal::(anonymous namespace)::DefaultConverter>::ConverterConsumer(absl::str_format_internal::(anonymous namespace)::DefaultConverter, absl::Span<absl::str_format_internal::FormatArgImpl const>) Line | Count | Source | 123 | 4.96k | : converter_(converter), arg_context_(pack) {} |
|
124 | | |
125 | 4.96k | bool Append(string_view s) { |
126 | 4.96k | converter_.Append(s); |
127 | 4.96k | return true; |
128 | 4.96k | } Unexecuted instantiation: bind.cc:absl::str_format_internal::(anonymous namespace)::ConverterConsumer<absl::str_format_internal::(anonymous namespace)::SummarizingConverter>::Append(absl::string_view) bind.cc:absl::str_format_internal::(anonymous namespace)::ConverterConsumer<absl::str_format_internal::(anonymous namespace)::DefaultConverter>::Append(absl::string_view) Line | Count | Source | 125 | 4.96k | bool Append(string_view s) { | 126 | 4.96k | converter_.Append(s); | 127 | 4.96k | return true; | 128 | 4.96k | } |
|
129 | 4.96k | bool ConvertOne(const UnboundConversion& conv, string_view conv_string) { |
130 | 4.96k | BoundConversion bound; |
131 | 4.96k | if (!arg_context_.Bind(&conv, &bound)) return false; |
132 | 4.96k | return converter_.ConvertOne(bound, conv_string); |
133 | 4.96k | } Unexecuted instantiation: bind.cc:absl::str_format_internal::(anonymous namespace)::ConverterConsumer<absl::str_format_internal::(anonymous namespace)::SummarizingConverter>::ConvertOne(absl::str_format_internal::UnboundConversion const&, absl::string_view) bind.cc:absl::str_format_internal::(anonymous namespace)::ConverterConsumer<absl::str_format_internal::(anonymous namespace)::DefaultConverter>::ConvertOne(absl::str_format_internal::UnboundConversion const&, absl::string_view) Line | Count | Source | 129 | 4.96k | bool ConvertOne(const UnboundConversion& conv, string_view conv_string) { | 130 | 4.96k | BoundConversion bound; | 131 | 4.96k | if (!arg_context_.Bind(&conv, &bound)) return false; | 132 | 4.96k | return converter_.ConvertOne(bound, conv_string); | 133 | 4.96k | } |
|
134 | | |
135 | | private: |
136 | | Converter converter_; |
137 | | ArgContext arg_context_; |
138 | | }; |
139 | | |
140 | | template <typename Converter> |
141 | | bool ConvertAll(const UntypedFormatSpecImpl format, |
142 | 4.96k | absl::Span<const FormatArgImpl> args, Converter converter) { |
143 | 4.96k | if (format.has_parsed_conversion()) { |
144 | 0 | return format.parsed_conversion()->ProcessFormat( |
145 | 0 | ConverterConsumer<Converter>(converter, args)); |
146 | 4.96k | } else { |
147 | 4.96k | return ParseFormatString(format.str(), |
148 | 4.96k | ConverterConsumer<Converter>(converter, args)); |
149 | 4.96k | } |
150 | 4.96k | } Unexecuted instantiation: bind.cc:bool absl::str_format_internal::(anonymous namespace)::ConvertAll<absl::str_format_internal::(anonymous namespace)::SummarizingConverter>(absl::str_format_internal::UntypedFormatSpecImpl, absl::Span<absl::str_format_internal::FormatArgImpl const>, absl::str_format_internal::(anonymous namespace)::SummarizingConverter) bind.cc:bool absl::str_format_internal::(anonymous namespace)::ConvertAll<absl::str_format_internal::(anonymous namespace)::DefaultConverter>(absl::str_format_internal::UntypedFormatSpecImpl, absl::Span<absl::str_format_internal::FormatArgImpl const>, absl::str_format_internal::(anonymous namespace)::DefaultConverter) Line | Count | Source | 142 | 4.96k | absl::Span<const FormatArgImpl> args, Converter converter) { | 143 | 4.96k | if (format.has_parsed_conversion()) { | 144 | 0 | return format.parsed_conversion()->ProcessFormat( | 145 | 0 | ConverterConsumer<Converter>(converter, args)); | 146 | 4.96k | } else { | 147 | 4.96k | return ParseFormatString(format.str(), | 148 | 4.96k | ConverterConsumer<Converter>(converter, args)); | 149 | 4.96k | } | 150 | 4.96k | } |
|
151 | | |
152 | | class DefaultConverter { |
153 | | public: |
154 | 4.96k | explicit DefaultConverter(FormatSinkImpl* sink) : sink_(sink) {} |
155 | | |
156 | 4.96k | void Append(string_view s) const { sink_->Append(s); } |
157 | | |
158 | 4.96k | bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const { |
159 | 4.96k | return FormatArgImplFriend::Convert(*bound.arg(), bound, sink_); |
160 | 4.96k | } |
161 | | |
162 | | private: |
163 | | FormatSinkImpl* sink_; |
164 | | }; |
165 | | |
166 | | class SummarizingConverter { |
167 | | public: |
168 | 0 | explicit SummarizingConverter(FormatSinkImpl* sink) : sink_(sink) {} |
169 | | |
170 | 0 | void Append(string_view s) const { sink_->Append(s); } |
171 | | |
172 | 0 | bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const { |
173 | 0 | UntypedFormatSpecImpl spec("%d"); |
174 | |
|
175 | 0 | std::ostringstream ss; |
176 | 0 | ss << "{" << Streamable(spec, {*bound.arg()}) << ":" |
177 | 0 | << FormatConversionSpecImplFriend::FlagsToString(bound); |
178 | 0 | if (bound.width() >= 0) ss << bound.width(); |
179 | 0 | if (bound.precision() >= 0) ss << "." << bound.precision(); |
180 | 0 | ss << bound.conversion_char() << "}"; |
181 | 0 | Append(ss.str()); |
182 | 0 | return true; |
183 | 0 | } |
184 | | |
185 | | private: |
186 | | FormatSinkImpl* sink_; |
187 | | }; |
188 | | |
189 | | } // namespace |
190 | | |
191 | | bool BindWithPack(const UnboundConversion* props, |
192 | | absl::Span<const FormatArgImpl> pack, |
193 | 0 | BoundConversion* bound) { |
194 | 0 | return ArgContext(pack).Bind(props, bound); |
195 | 0 | } |
196 | | |
197 | | std::string Summarize(const UntypedFormatSpecImpl format, |
198 | 0 | absl::Span<const FormatArgImpl> args) { |
199 | 0 | typedef SummarizingConverter Converter; |
200 | 0 | std::string out; |
201 | 0 | { |
202 | | // inner block to destroy sink before returning out. It ensures a last |
203 | | // flush. |
204 | 0 | FormatSinkImpl sink(&out); |
205 | 0 | if (!ConvertAll(format, args, Converter(&sink))) { |
206 | 0 | return ""; |
207 | 0 | } |
208 | 0 | } |
209 | 0 | return out; |
210 | 0 | } |
211 | | |
212 | | bool FormatUntyped(FormatRawSinkImpl raw_sink, |
213 | | const UntypedFormatSpecImpl format, |
214 | 4.96k | absl::Span<const FormatArgImpl> args) { |
215 | 4.96k | FormatSinkImpl sink(raw_sink); |
216 | 4.96k | using Converter = DefaultConverter; |
217 | 4.96k | return ConvertAll(format, args, Converter(&sink)); |
218 | 4.96k | } |
219 | | |
220 | 0 | std::ostream& Streamable::Print(std::ostream& os) const { |
221 | 0 | if (!FormatUntyped(&os, format_, args_)) os.setstate(std::ios::failbit); |
222 | 0 | return os; |
223 | 0 | } |
224 | | |
225 | | std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl format, |
226 | 0 | absl::Span<const FormatArgImpl> args) { |
227 | 0 | size_t orig = out->size(); |
228 | 0 | if (ABSL_PREDICT_FALSE(!FormatUntyped(out, format, args))) { |
229 | 0 | out->erase(orig); |
230 | 0 | } |
231 | 0 | return *out; |
232 | 0 | } |
233 | | |
234 | | std::string FormatPack(UntypedFormatSpecImpl format, |
235 | 4.96k | absl::Span<const FormatArgImpl> args) { |
236 | 4.96k | std::string out; |
237 | 4.96k | if (ABSL_PREDICT_FALSE(!FormatUntyped(&out, format, args))) { |
238 | 0 | out.clear(); |
239 | 0 | } |
240 | 4.96k | return out; |
241 | 4.96k | } |
242 | | |
243 | | int FprintF(std::FILE* output, const UntypedFormatSpecImpl format, |
244 | 0 | absl::Span<const FormatArgImpl> args) { |
245 | 0 | FILERawSink sink(output); |
246 | 0 | if (!FormatUntyped(&sink, format, args)) { |
247 | 0 | errno = EINVAL; |
248 | 0 | return -1; |
249 | 0 | } |
250 | 0 | if (sink.error()) { |
251 | 0 | errno = sink.error(); |
252 | 0 | return -1; |
253 | 0 | } |
254 | 0 | if (sink.count() > static_cast<size_t>(std::numeric_limits<int>::max())) { |
255 | 0 | errno = EFBIG; |
256 | 0 | return -1; |
257 | 0 | } |
258 | 0 | return static_cast<int>(sink.count()); |
259 | 0 | } |
260 | | |
261 | | int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl format, |
262 | 0 | absl::Span<const FormatArgImpl> args) { |
263 | 0 | BufferRawSink sink(output, size ? size - 1 : 0); |
264 | 0 | if (!FormatUntyped(&sink, format, args)) { |
265 | 0 | errno = EINVAL; |
266 | 0 | return -1; |
267 | 0 | } |
268 | 0 | size_t total = sink.total_written(); |
269 | 0 | if (size) output[std::min(total, size - 1)] = 0; |
270 | 0 | return static_cast<int>(total); |
271 | 0 | } |
272 | | |
273 | | } // namespace str_format_internal |
274 | | ABSL_NAMESPACE_END |
275 | | } // namespace absl |