bind.cc (8696B)
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 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: 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 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: 122 ConverterConsumer(Converter converter, absl::Span<const FormatArgImpl> pack) 123 : converter_(converter), arg_context_(pack) {} 124 125 bool Append(string_view s) { 126 converter_.Append(s); 127 return true; 128 } 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> 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: 154 explicit DefaultConverter(FormatSinkImpl* sink) : sink_(sink) {} 155 156 void Append(string_view s) const { sink_->Append(s); } 157 158 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: 168 explicit SummarizingConverter(FormatSinkImpl* sink) : sink_(sink) {} 169 170 void Append(string_view s) const { sink_->Append(s); } 171 172 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 191 bool BindWithPack(const UnboundConversion* props, 192 absl::Span<const FormatArgImpl> pack, 193 BoundConversion* bound) { 194 return ArgContext(pack).Bind(props, bound); 195 } 196 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 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 220 std::ostream& Streamable::Print(std::ostream& os) const { 221 if (!FormatUntyped(&os, format_, args_)) os.setstate(std::ios::failbit); 222 return os; 223 } 224 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 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 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 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