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/arg.h | 434 +++++++++++++++++++++++++++++++++ 1 file changed, 434 insertions(+) create mode 100644 absl/strings/internal/str_format/arg.h (limited to 'absl/strings/internal/str_format/arg.h') diff --git a/absl/strings/internal/str_format/arg.h b/absl/strings/internal/str_format/arg.h new file mode 100644 index 0000000..a956218 --- /dev/null +++ b/absl/strings/internal/str_format/arg.h @@ -0,0 +1,434 @@ +#ifndef ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ +#define ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "absl/base/port.h" +#include "absl/meta/type_traits.h" +#include "absl/numeric/int128.h" +#include "absl/strings/internal/str_format/extension.h" +#include "absl/strings/string_view.h" + +class Cord; +class CordReader; + +namespace absl { + +class FormatCountCapture; +class FormatSink; + +namespace str_format_internal { + +template +struct HasUserDefinedConvert : std::false_type {}; + +template +struct HasUserDefinedConvert< + T, void_t(), std::declval(), + std::declval()))>> : std::true_type {}; +template +class StreamedWrapper; + +// If 'v' can be converted (in the printf sense) according to 'conv', +// then convert it, appending to `sink` and return `true`. +// Otherwise fail and return `false`. +// Raw pointers. +struct VoidPtr { + VoidPtr() = default; + template (std::declval())) = 0> + VoidPtr(T* ptr) // NOLINT + : value(ptr ? reinterpret_cast(ptr) : 0) {} + uintptr_t value; +}; +ConvertResult FormatConvertImpl(VoidPtr v, const ConversionSpec& conv, + FormatSinkImpl* sink); + +// Strings. +ConvertResult FormatConvertImpl(const std::string& v, + const ConversionSpec& conv, + FormatSinkImpl* sink); +ConvertResult FormatConvertImpl(string_view v, + const ConversionSpec& conv, + FormatSinkImpl* sink); +ConvertResult FormatConvertImpl(const char* v, + const ConversionSpec& conv, + FormatSinkImpl* sink); +template ::value>::type* = nullptr, + class AbslCordReader = ::CordReader> +ConvertResult FormatConvertImpl(const AbslCord& value, + const ConversionSpec& conv, + FormatSinkImpl* sink) { + if (conv.conv().id() != ConversionChar::s) return {false}; + + bool is_left = conv.flags().left; + size_t space_remaining = 0; + + int width = conv.width(); + if (width >= 0) space_remaining = width; + + size_t to_write = value.size(); + + int precision = conv.precision(); + if (precision >= 0) + to_write = std::min(to_write, static_cast(precision)); + + space_remaining = Excess(to_write, space_remaining); + + if (space_remaining > 0 && !is_left) sink->Append(space_remaining, ' '); + + string_view piece; + for (AbslCordReader reader(value); + to_write > 0 && reader.ReadFragment(&piece); to_write -= piece.size()) { + if (piece.size() > to_write) piece.remove_suffix(piece.size() - to_write); + sink->Append(piece); + } + + if (space_remaining > 0 && is_left) sink->Append(space_remaining, ' '); + return {true}; +} + +using IntegralConvertResult = + ConvertResult; +using FloatingConvertResult = ConvertResult; + +// Floats. +FloatingConvertResult FormatConvertImpl(float v, const ConversionSpec& conv, + FormatSinkImpl* sink); +FloatingConvertResult FormatConvertImpl(double v, const ConversionSpec& conv, + FormatSinkImpl* sink); +FloatingConvertResult FormatConvertImpl(long double v, + const ConversionSpec& conv, + FormatSinkImpl* sink); + +// Chars. +IntegralConvertResult FormatConvertImpl(char v, const ConversionSpec& conv, + FormatSinkImpl* sink); +IntegralConvertResult FormatConvertImpl(signed char v, + const ConversionSpec& conv, + FormatSinkImpl* sink); +IntegralConvertResult FormatConvertImpl(unsigned char v, + const ConversionSpec& conv, + FormatSinkImpl* sink); + +// Ints. +IntegralConvertResult FormatConvertImpl(short v, // NOLINT + const ConversionSpec& conv, + FormatSinkImpl* sink); +IntegralConvertResult FormatConvertImpl(unsigned short v, // NOLINT + const ConversionSpec& conv, + FormatSinkImpl* sink); +IntegralConvertResult FormatConvertImpl(int v, const ConversionSpec& conv, + FormatSinkImpl* sink); +IntegralConvertResult FormatConvertImpl(unsigned v, const ConversionSpec& conv, + FormatSinkImpl* sink); +IntegralConvertResult FormatConvertImpl(long v, // NOLINT + const ConversionSpec& conv, + FormatSinkImpl* sink); +IntegralConvertResult FormatConvertImpl(unsigned long v, // NOLINT + const ConversionSpec& conv, + FormatSinkImpl* sink); +IntegralConvertResult FormatConvertImpl(long long v, // NOLINT + const ConversionSpec& conv, + FormatSinkImpl* sink); +IntegralConvertResult FormatConvertImpl(unsigned long long v, // NOLINT + const ConversionSpec& conv, + FormatSinkImpl* sink); +IntegralConvertResult FormatConvertImpl(uint128 v, const ConversionSpec& conv, + FormatSinkImpl* sink); +template ::value, int> = 0> +IntegralConvertResult FormatConvertImpl(T v, const ConversionSpec& conv, + FormatSinkImpl* sink) { + return FormatConvertImpl(static_cast(v), conv, sink); +} + +// We provide this function to help the checker, but it is never defined. +// FormatArgImpl will use the underlying Convert functions instead. +template +typename std::enable_if::value && + !HasUserDefinedConvert::value, + IntegralConvertResult>::type +FormatConvertImpl(T v, const ConversionSpec& conv, FormatSinkImpl* sink); + +template +ConvertResult FormatConvertImpl(const StreamedWrapper& v, + const ConversionSpec& conv, + FormatSinkImpl* out) { + std::ostringstream oss; + oss << v.v_; + if (!oss) return {false}; + return str_format_internal::FormatConvertImpl(oss.str(), conv, out); +} + +// Use templates and dependent types to delay evaluation of the function +// until after FormatCountCapture is fully defined. +struct FormatCountCaptureHelper { + template + static ConvertResult ConvertHelper(const FormatCountCapture& v, + const ConversionSpec& conv, + FormatSinkImpl* sink) { + const absl::enable_if_t& v2 = v; + + if (conv.conv().id() != str_format_internal::ConversionChar::n) + return {false}; + *v2.p_ = static_cast(sink->size()); + return {true}; + } +}; + +template +ConvertResult FormatConvertImpl(const FormatCountCapture& v, + const ConversionSpec& conv, + FormatSinkImpl* sink) { + return FormatCountCaptureHelper::ConvertHelper(v, conv, sink); +} + +// Helper friend struct to hide implementation details from the public API of +// FormatArgImpl. +struct FormatArgImplFriend { + template + static bool ToInt(Arg arg, int* out) { + if (!arg.vtbl_->to_int) return false; + *out = arg.vtbl_->to_int(arg.data_); + return true; + } + + template + static bool Convert(Arg arg, const str_format_internal::ConversionSpec& conv, + FormatSinkImpl* out) { + return arg.vtbl_->convert(arg.data_, conv, out); + } + + template + static const void* GetVTablePtrForTest(Arg arg) { + return arg.vtbl_; + } +}; + +// A type-erased handle to a format argument. +class FormatArgImpl { + private: + enum { kInlinedSpace = 8 }; + + using VoidPtr = str_format_internal::VoidPtr; + + union Data { + const void* ptr; + const volatile void* volatile_ptr; + char buf[kInlinedSpace]; + }; + + struct VTable { + bool (*convert)(Data, const str_format_internal::ConversionSpec& conv, + FormatSinkImpl* out); + int (*to_int)(Data); + }; + + template + struct store_by_value + : std::integral_constant::value || + std::is_floating_point::value || + std::is_pointer::value || + std::is_same::value)> {}; + + enum StoragePolicy { ByPointer, ByVolatilePointer, ByValue }; + template + struct storage_policy + : std::integral_constant::value + ? ByVolatilePointer + : (store_by_value::value ? ByValue + : ByPointer))> { + }; + + // An instance of an FormatArgImpl::VTable suitable for 'T'. + template + struct TypedVTable; + + // To reduce the number of vtables we will decay values before hand. + // Anything with a user-defined Convert will get its own vtable. + // For everything else: + // - Decay char* and char arrays into `const char*` + // - Decay any other pointer to `const void*` + // - Decay all enums to their underlying type. + // - Decay function pointers to void*. + template + struct DecayType { + static constexpr bool kHasUserDefined = + str_format_internal::HasUserDefinedConvert::value; + using type = typename std::conditional< + !kHasUserDefined && std::is_convertible::value, + const char*, + typename std::conditional::value, + VoidPtr, const T&>::type>::type; + }; + template + struct DecayType::value && + std::is_enum::value>::type> { + using type = typename std::underlying_type::type; + }; + + public: + template + explicit FormatArgImpl(const T& value) { + using D = typename DecayType::type; + static_assert( + std::is_same::value || storage_policy::value == ByValue, + "Decayed types must be stored by value"); + Init(static_cast(value)); + } + + private: + friend struct str_format_internal::FormatArgImplFriend; + template ::value> + struct Manager; + + template + struct Manager { + static Data SetValue(const T& value) { + Data data; + data.ptr = &value; + return data; + } + + static const T& Value(Data arg) { return *static_cast(arg.ptr); } + }; + + template + struct Manager { + static Data SetValue(const T& value) { + Data data; + data.volatile_ptr = &value; + return data; + } + + static const T& Value(Data arg) { + return *static_cast(arg.volatile_ptr); + } + }; + + template + struct Manager { + static Data SetValue(const T& value) { + Data data; + memcpy(data.buf, &value, sizeof(value)); + return data; + } + + static T Value(Data arg) { + T value; + memcpy(&value, arg.buf, sizeof(T)); + return value; + } + }; + + template + void Init(const T& value); + + template + static int ToIntVal(const T& val) { + using CommonType = typename std::conditional::value, + int64_t, uint64_t>::type; + if (static_cast(val) > + static_cast(std::numeric_limits::max())) { + return std::numeric_limits::max(); + } else if (std::is_signed::value && + static_cast(val) < + static_cast(std::numeric_limits::min())) { + return std::numeric_limits::min(); + } + return static_cast(val); + } + + Data data_; + const VTable* vtbl_; +}; + +template +struct FormatArgImpl::TypedVTable { + private: + static bool ConvertImpl(Data arg, + const str_format_internal::ConversionSpec& conv, + FormatSinkImpl* out) { + return str_format_internal::FormatConvertImpl(Manager::Value(arg), conv, + out) + .value; + } + + template + struct ToIntImpl { + static constexpr int (*value)(Data) = nullptr; + }; + + template + struct ToIntImpl::value>::type> { + static int Invoke(Data arg) { return ToIntVal(Manager::Value(arg)); } + static constexpr int (*value)(Data) = &Invoke; + }; + + template + struct ToIntImpl::value>::type> { + static int Invoke(Data arg) { + return ToIntVal(static_cast::type>( + Manager::Value(arg))); + } + static constexpr int (*value)(Data) = &Invoke; + }; + + public: + static constexpr VTable value{&ConvertImpl, ToIntImpl<>::value}; +}; + +template +constexpr FormatArgImpl::VTable FormatArgImpl::TypedVTable::value; + +template +void FormatArgImpl::Init(const T& value) { + data_ = Manager::SetValue(value); + vtbl_ = &TypedVTable::value; +} + +extern template struct FormatArgImpl::TypedVTable; + +extern template struct FormatArgImpl::TypedVTable; +extern template struct FormatArgImpl::TypedVTable; +extern template struct FormatArgImpl::TypedVTable; +extern template struct FormatArgImpl::TypedVTable; +extern template struct FormatArgImpl::TypedVTable; // NOLINT +extern template struct FormatArgImpl::TypedVTable; // NOLINT +extern template struct FormatArgImpl::TypedVTable; +extern template struct FormatArgImpl::TypedVTable; +extern template struct FormatArgImpl::TypedVTable; // NOLINT +extern template struct FormatArgImpl::TypedVTable; // NOLINT +extern template struct FormatArgImpl::TypedVTable; // NOLINT +extern template struct FormatArgImpl::TypedVTable< + unsigned long long>; // NOLINT +extern template struct FormatArgImpl::TypedVTable; + +extern template struct FormatArgImpl::TypedVTable; +extern template struct FormatArgImpl::TypedVTable; +extern template struct FormatArgImpl::TypedVTable; + +extern template struct FormatArgImpl::TypedVTable; +extern template struct FormatArgImpl::TypedVTable; +extern template struct FormatArgImpl::TypedVTable; +} // namespace str_format_internal +} // namespace absl + +#endif // ABSL_STRINGS_INTERNAL_STR_FORMAT_ARG_H_ -- cgit v1.2.3