123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- // Copyright 2020 The Abseil Authors.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // https://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #include "absl/strings/internal/str_format/bind.h"
- #include <algorithm>
- #include <cassert>
- #include <cerrno>
- #include <cstddef>
- #include <cstdio>
- #include <ios>
- #include <limits>
- #include <ostream>
- #include <sstream>
- #include <string>
- #include "absl/base/config.h"
- #include "absl/base/optimization.h"
- #include "absl/strings/internal/str_format/arg.h"
- #include "absl/strings/internal/str_format/constexpr_parser.h"
- #include "absl/strings/internal/str_format/extension.h"
- #include "absl/strings/internal/str_format/output.h"
- #include "absl/strings/string_view.h"
- #include "absl/types/span.h"
- namespace absl {
- ABSL_NAMESPACE_BEGIN
- namespace str_format_internal {
- namespace {
- inline bool BindFromPosition(int position, int* value,
- absl::Span<const FormatArgImpl> pack) {
- assert(position > 0);
- if (static_cast<size_t>(position) > pack.size()) {
- return false;
- }
- // -1 because positions are 1-based
- return FormatArgImplFriend::ToInt(pack[static_cast<size_t>(position) - 1],
- value);
- }
- class ArgContext {
- public:
- explicit ArgContext(absl::Span<const FormatArgImpl> pack) : pack_(pack) {}
- // Fill 'bound' with the results of applying the context's argument pack
- // to the specified 'unbound'. 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* unbound, BoundConversion* bound);
- private:
- absl::Span<const FormatArgImpl> pack_;
- };
- inline bool ArgContext::Bind(const UnboundConversion* unbound,
- BoundConversion* bound) {
- const FormatArgImpl* arg = nullptr;
- int arg_position = unbound->arg_position;
- if (static_cast<size_t>(arg_position - 1) >= pack_.size()) return false;
- arg = &pack_[static_cast<size_t>(arg_position - 1)]; // 1-based
- if (unbound->flags != Flags::kBasic) {
- 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;
- // Make sure we don't overflow the width when negating it.
- width = -std::max(width, -std::numeric_limits<int>::max());
- }
- }
- int precision = unbound->precision.value();
- if (unbound->precision.is_from_arg()) {
- if (!BindFromPosition(unbound->precision.get_from_arg(), &precision,
- pack_))
- return false;
- }
- FormatConversionSpecImplFriend::SetWidth(width, bound);
- FormatConversionSpecImplFriend::SetPrecision(precision, bound);
- if (force_left) {
- FormatConversionSpecImplFriend::SetFlags(unbound->flags | Flags::kLeft,
- bound);
- } else {
- FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound);
- }
- FormatConversionSpecImplFriend::SetLengthMod(unbound->length_mod, bound);
- } else {
- FormatConversionSpecImplFriend::SetFlags(unbound->flags, bound);
- FormatConversionSpecImplFriend::SetWidth(-1, bound);
- FormatConversionSpecImplFriend::SetPrecision(-1, bound);
- }
- FormatConversionSpecImplFriend::SetConversionChar(unbound->conv, bound);
- bound->set_arg(arg);
- return true;
- }
- template <typename Converter>
- class ConverterConsumer {
- public:
- ConverterConsumer(Converter converter, absl::Span<const FormatArgImpl> 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 <typename Converter>
- bool ConvertAll(const UntypedFormatSpecImpl format,
- absl::Span<const FormatArgImpl> args, Converter converter) {
- if (format.has_parsed_conversion()) {
- return format.parsed_conversion()->ProcessFormat(
- ConverterConsumer<Converter>(converter, args));
- } else {
- return ParseFormatString(format.str(),
- ConverterConsumer<Converter>(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()}) << ":"
- << FormatConversionSpecImplFriend::FlagsToString(bound);
- if (bound.width() >= 0) ss << bound.width();
- if (bound.precision() >= 0) ss << "." << bound.precision();
- ss << bound.conversion_char() << "}";
- Append(ss.str());
- return true;
- }
- private:
- FormatSinkImpl* sink_;
- };
- } // namespace
- bool BindWithPack(const UnboundConversion* props,
- absl::Span<const FormatArgImpl> pack,
- BoundConversion* bound) {
- return ArgContext(pack).Bind(props, bound);
- }
- std::string Summarize(const UntypedFormatSpecImpl format,
- absl::Span<const FormatArgImpl> 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))) {
- return "";
- }
- }
- return out;
- }
- bool FormatUntyped(FormatRawSinkImpl raw_sink,
- const UntypedFormatSpecImpl format,
- absl::Span<const FormatArgImpl> args) {
- FormatSinkImpl sink(raw_sink);
- using Converter = DefaultConverter;
- return ConvertAll(format, args, Converter(&sink));
- }
- 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<const FormatArgImpl> args) {
- size_t orig = out->size();
- if (ABSL_PREDICT_FALSE(!FormatUntyped(out, format, args))) {
- out->erase(orig);
- }
- return *out;
- }
- std::string FormatPack(UntypedFormatSpecImpl format,
- absl::Span<const FormatArgImpl> args) {
- std::string out;
- if (ABSL_PREDICT_FALSE(!FormatUntyped(&out, format, args))) {
- out.clear();
- }
- return out;
- }
- int FprintF(std::FILE* output, const UntypedFormatSpecImpl format,
- absl::Span<const FormatArgImpl> args) {
- FILERawSink sink(output);
- if (!FormatUntyped(&sink, format, args)) {
- errno = EINVAL;
- return -1;
- }
- if (sink.error()) {
- errno = sink.error();
- return -1;
- }
- if (sink.count() > static_cast<size_t>(std::numeric_limits<int>::max())) {
- errno = EFBIG;
- return -1;
- }
- return static_cast<int>(sink.count());
- }
- int SnprintF(char* output, size_t size, const UntypedFormatSpecImpl format,
- absl::Span<const FormatArgImpl> 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<int>(total);
- }
- } // namespace str_format_internal
- ABSL_NAMESPACE_END
- } // namespace absl
|