tvl-depot/absl/strings/internal/str_format/bind.cc
Abseil Team 2901ec32a9 Export of internal Abseil changes.
--
5da9755667df37e38ccaf6938c9f408e294110bb by Shaindel Schwartz <shaindel@google.com>:

Import of CCTZ from GitHub.

PiperOrigin-RevId: 232942734

--
b6fb275769c66fdd2bd92b119198c59e9a7dd737 by Samuel Benzaquen <sbenza@google.com>:

Fix integral underflow when from-arg width is INT_MIN.

PiperOrigin-RevId: 232888037

--
4135dbba4a26c4642277fc2a7e2a833d593daa1c by Abseil Team <absl-team@google.com>:

Add the insert_return_type alias to raw_hash_set.

PiperOrigin-RevId: 232683892

--
0b120b7d3693800bbb886f6fc607ae54a9338cb1 by Abseil Team <absl-team@google.com>:

Macros to detect and disabled SafeStack
https://clang.llvm.org/docs/SafeStack.html

PiperOrigin-RevId: 232680114

--
a77b3fb533a9e37966d1d6ef5ccd09c73fff2ca1 by Abseil Team <absl-team@google.com>:

Avoid potential red zone clobber

Pushing on the stack on x86-64 may clobber local variables held below
%rsp in the red zone. Avoid this by using lea on x86-64.

PiperOrigin-RevId: 232592478

--
bf326a0eefa92f4e704287563df0c5a5b1873b6d by Eric Fiselier <ericwf@google.com>:

Add additional tests for AbslHashValue.

PiperOrigin-RevId: 232344325

--
816e4f98fd7632c944c779db87b7dac4e138afcf by Eric Fiselier <ericwf@google.com>:

Avoid upcoming GCC 9.0 warnings about base class init.

Currently, in trunk, GCC has a new warning under -Wextra
that diagnoses when a derived class fails to explicitly
initialize the base class in a constructor initializer list.

This patch avoids this warning.

PiperOrigin-RevId: 232327626

--
779c0f44b3c2b7a04d4bdf978641eb8180515bf6 by Eric Fiselier <ericwf@google.com>:

Guard against C++2a char8_t change.

PiperOrigin-RevId: 232326178

--
41e5395b85bbbfb5bf418cc21b04ad4ccb15a284 by Eric Fiselier <ericwf@google.com>:

Avoid Clang Warning

PiperOrigin-RevId: 232138866
GitOrigin-RevId: 5da9755667df37e38ccaf6938c9f408e294110bb
Change-Id: I49ee4f58db177b81b039d7d949f671c97c5a7933
2019-02-07 17:48:22 -05:00

230 lines
6.7 KiB
C++

#include "absl/strings/internal/str_format/bind.h"
#include <cerrno>
#include <limits>
#include <sstream>
#include <string>
namespace absl {
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[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_[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;
// 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;
}
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 <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()}) << ":" << 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<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;
}
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() > 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
} // namespace absl