Abseil Common Libraries (C++) (grcp 依赖)
https://abseil.io/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
259 lines
8.0 KiB
259 lines
8.0 KiB
// 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 <cerrno> |
|
#include <limits> |
|
#include <sstream> |
|
#include <string> |
|
|
|
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); |
|
} |
|
} 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(const 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
|
|
|