From 4491d606df34c44efda47b6d17b605262f17e182 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Thu, 21 Jun 2018 12:55:12 -0700 Subject: Export of internal Abseil changes. -- 70f43a482d7d4ae4a255f17ca02b0106653dd600 by Shaindel Schwartz : Internal change PiperOrigin-RevId: 201571193 -- 93e6e9c2e683158be49d9dd1f5cb1a91d0c0f556 by Abseil Team : Internal change. PiperOrigin-RevId: 201567108 -- fbd8ee94fbe9f2448e5adf5e88706f9c8216048f by Juemin Yang : str_format release PiperOrigin-RevId: 201565129 -- 387faa301555a8a888c4429df52734aa806dca46 by Abseil Team : Adds a defaulted allocator parameter to the size_type constructor of InlinedVector PiperOrigin-RevId: 201558711 -- 39b15ea2c68d7129d70cbde7e71af900032595ec by Matt Calabrese : Update the variant implementation to eliminate unnecessary checking on alternative access when the index is known or required to be correct. PiperOrigin-RevId: 201529535 -- adab77f1f7bb363aa534297f22aae2b0f08889ea by Abseil Team : Import of CCTZ from GitHub. PiperOrigin-RevId: 201458388 -- a701dc0ba62e3cadf0de14203415b91df4ee8151 by Greg Falcon : Internal cleanup PiperOrigin-RevId: 201394836 -- 8a7191410b8f440fdfa27f722ff05e451502ab61 by Abseil Team : Import of CCTZ from GitHub. PiperOrigin-RevId: 201369269 GitOrigin-RevId: 70f43a482d7d4ae4a255f17ca02b0106653dd600 Change-Id: I8ab073b30b4e27405a3b6da2c826bb4f3f0b9af6 --- absl/strings/internal/str_format/bind.cc | 232 +++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 absl/strings/internal/str_format/bind.cc (limited to 'absl/strings/internal/str_format/bind.cc') diff --git a/absl/strings/internal/str_format/bind.cc b/absl/strings/internal/str_format/bind.cc new file mode 100644 index 000000000000..33e8641558ab --- /dev/null +++ b/absl/strings/internal/str_format/bind.cc @@ -0,0 +1,232 @@ +#include "absl/strings/internal/str_format/bind.h" + +#include +#include +#include +#include + +namespace absl { +namespace str_format_internal { + +namespace { + +inline bool BindFromPosition(int position, int* value, + absl::Span pack) { + assert(position > 0); + if (static_cast(position) > pack.size()) { + return false; + } + // -1 because positions are 1-based + return FormatArgImplFriend::ToInt(pack[position - 1], value); +} + +class ArgContext { + public: + explicit ArgContext(absl::Span pack) : pack_(pack) {} + + // Fill 'bound' with the results of applying the context's argument pack + // to the specified 'props'. We synthesize a BoundConversion by + // lining up a UnboundConversion with a user argument. We also + // resolve any '*' specifiers for width and precision, so after + // this call, 'bound' has all the information it needs to be formatted. + // Returns false on failure. + bool Bind(const UnboundConversion *props, BoundConversion *bound); + + private: + absl::Span pack_; +}; + +inline bool ArgContext::Bind(const UnboundConversion* unbound, + BoundConversion* bound) { + const FormatArgImpl* arg = nullptr; + int arg_position = unbound->arg_position; + if (static_cast(arg_position - 1) >= pack_.size()) return false; + arg = &pack_[arg_position - 1]; // 1-based + + if (!unbound->flags.basic) { + int width = unbound->width.value(); + bool force_left = false; + if (unbound->width.is_from_arg()) { + if (!BindFromPosition(unbound->width.get_from_arg(), &width, pack_)) + return false; + if (width < 0) { + // "A negative field width is taken as a '-' flag followed by a + // positive field width." + force_left = true; + width = -width; + } + } + + int precision = unbound->precision.value(); + if (unbound->precision.is_from_arg()) { + if (!BindFromPosition(unbound->precision.get_from_arg(), &precision, + pack_)) + return false; + } + + bound->set_width(width); + bound->set_precision(precision); + bound->set_flags(unbound->flags); + if (force_left) + bound->set_left(true); + } else { + bound->set_flags(unbound->flags); + bound->set_width(-1); + bound->set_precision(-1); + } + + bound->set_length_mod(unbound->length_mod); + bound->set_conv(unbound->conv); + bound->set_arg(arg); + return true; +} + +template +class ConverterConsumer { + public: + ConverterConsumer(Converter converter, absl::Span pack) + : converter_(converter), arg_context_(pack) {} + + bool Append(string_view s) { + converter_.Append(s); + return true; + } + bool ConvertOne(const UnboundConversion& conv, string_view conv_string) { + BoundConversion bound; + if (!arg_context_.Bind(&conv, &bound)) return false; + return converter_.ConvertOne(bound, conv_string); + } + + private: + Converter converter_; + ArgContext arg_context_; +}; + +template +bool ConvertAll(const UntypedFormatSpecImpl& format, + absl::Span args, + const Converter& converter) { + const ParsedFormatBase* pc = format.parsed_conversion(); + if (pc) + return pc->ProcessFormat(ConverterConsumer(converter, args)); + + return ParseFormatString(format.str(), + ConverterConsumer(converter, args)); +} + +class DefaultConverter { + public: + explicit DefaultConverter(FormatSinkImpl* sink) : sink_(sink) {} + + void Append(string_view s) const { sink_->Append(s); } + + bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const { + return FormatArgImplFriend::Convert(*bound.arg(), bound, sink_); + } + + private: + FormatSinkImpl* sink_; +}; + +class SummarizingConverter { + public: + explicit SummarizingConverter(FormatSinkImpl* sink) : sink_(sink) {} + + void Append(string_view s) const { sink_->Append(s); } + + bool ConvertOne(const BoundConversion& bound, string_view /*conv*/) const { + UntypedFormatSpecImpl spec("%d"); + + std::ostringstream ss; + ss << "{" << Streamable(spec, {*bound.arg()}) << ":" << bound.flags(); + if (bound.width() >= 0) ss << bound.width(); + if (bound.precision() >= 0) ss << "." << bound.precision(); + ss << bound.length_mod() << bound.conv() << "}"; + Append(ss.str()); + return true; + } + + private: + FormatSinkImpl* sink_; +}; + +} // namespace + +bool BindWithPack(const UnboundConversion* props, + absl::Span pack, + BoundConversion* bound) { + return ArgContext(pack).Bind(props, bound); +} + +std::string Summarize(const UntypedFormatSpecImpl& format, + absl::Span args) { + typedef SummarizingConverter Converter; + std::string out; + { + // inner block to destroy sink before returning out. It ensures a last + // flush. + FormatSinkImpl sink(&out); + if (!ConvertAll(format, args, Converter(&sink))) { + sink.Flush(); + out.clear(); + } + } + return out; +} + +bool FormatUntyped(FormatRawSinkImpl raw_sink, + const UntypedFormatSpecImpl& format, + absl::Span args) { + FormatSinkImpl sink(raw_sink); + using Converter = DefaultConverter; + if (!ConvertAll(format, args, Converter(&sink))) { + sink.Flush(); + return false; + } + return true; +} + +std::ostream& Streamable::Print(std::ostream& os) const { + if (!FormatUntyped(&os, format_, args_)) os.setstate(std::ios::failbit); + return os; +} + +std::string& AppendPack(std::string* out, const UntypedFormatSpecImpl& format, + absl::Span args) { + size_t orig = out->size(); + if (!FormatUntyped(out, format, args)) out->resize(orig); + return *out; +} + +int FprintF(std::FILE* output, const UntypedFormatSpecImpl& format, + absl::Span args) { + FILERawSink sink(output); + if (!FormatUntyped(&sink, format, args)) { + errno = EINVAL; + return -1; + } + if (sink.error()) { + errno = sink.error(); + return -1; + } + if (sink.count() > std::numeric_limits::max()) { + errno = EFBIG; + return -1; + } + return static_cast(sink.count()); +} + +int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl& format, + absl::Span args) { + BufferRawSink sink(output, size ? size - 1 : 0); + if (!FormatUntyped(&sink, format, args)) { + errno = EINVAL; + return -1; + } + size_t total = sink.total_written(); + if (size) output[std::min(total, size - 1)] = 0; + return static_cast(total); +} + +} // namespace str_format_internal +} // namespace absl -- cgit 1.4.1