#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_