123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779 |
- #ifndef FORMAT_INL_H_
- #error "Direct inclusion of this file is not allowed, include format.h"
- // For the sake of sane code completion.
- #include "format.h"
- #endif
- #include "enum.h"
- #include "string.h"
- #include <library/cpp/yt/assert/assert.h>
- #include <library/cpp/yt/small_containers/compact_vector.h>
- #include <library/cpp/yt/containers/enum_indexed_array.h>
- #include <library/cpp/yt/misc/enum.h>
- #include <util/system/platform.h>
- #include <cctype>
- #include <optional>
- #include <span>
- namespace NYT {
- ////////////////////////////////////////////////////////////////////////////////
- static constexpr char GenericSpecSymbol = 'v';
- inline bool IsQuotationSpecSymbol(char symbol)
- {
- return symbol == 'Q' || symbol == 'q';
- }
- // TStringBuf
- inline void FormatValue(TStringBuilderBase* builder, TStringBuf value, TStringBuf format)
- {
- if (!format) {
- builder->AppendString(value);
- return;
- }
- // Parse alignment.
- bool alignLeft = false;
- const char* current = format.begin();
- if (*current == '-') {
- alignLeft = true;
- ++current;
- }
- bool hasAlign = false;
- int alignSize = 0;
- while (*current >= '0' && *current <= '9') {
- hasAlign = true;
- alignSize = 10 * alignSize + (*current - '0');
- if (alignSize > 1000000) {
- builder->AppendString(TStringBuf("<alignment overflow>"));
- return;
- }
- ++current;
- }
- int padding = 0;
- bool padLeft = false;
- bool padRight = false;
- if (hasAlign) {
- padding = alignSize - value.size();
- if (padding < 0) {
- padding = 0;
- }
- padLeft = !alignLeft;
- padRight = alignLeft;
- }
- bool singleQuotes = false;
- bool doubleQuotes = false;
- bool escape = false;
- while (current < format.end()) {
- switch (*current++) {
- case 'q':
- singleQuotes = true;
- break;
- case 'Q':
- doubleQuotes = true;
- break;
- case 'h':
- escape = true;
- break;
- }
- }
- if (padLeft) {
- builder->AppendChar(' ', padding);
- }
- if (singleQuotes || doubleQuotes || escape) {
- for (const char* valueCurrent = value.begin(); valueCurrent < value.end(); ++valueCurrent) {
- char ch = *valueCurrent;
- if (ch == '\n') {
- builder->AppendString("\\n");
- } else if (ch == '\t') {
- builder->AppendString("\\t");
- } else if (ch == '\\') {
- builder->AppendString("\\\\");
- } else if (ch < PrintableASCIILow || ch > PrintableASCIIHigh) {
- builder->AppendString("\\x");
- builder->AppendChar(IntToHexLowercase[static_cast<ui8>(ch) >> 4]);
- builder->AppendChar(IntToHexLowercase[static_cast<ui8>(ch) & 0xf]);
- } else if ((singleQuotes && ch == '\'') || (doubleQuotes && ch == '\"')) {
- builder->AppendChar('\\');
- builder->AppendChar(ch);
- } else {
- builder->AppendChar(ch);
- }
- }
- } else {
- builder->AppendString(value);
- }
- if (padRight) {
- builder->AppendChar(' ', padding);
- }
- }
- // TString
- inline void FormatValue(TStringBuilderBase* builder, const TString& value, TStringBuf format)
- {
- FormatValue(builder, TStringBuf(value), format);
- }
- // const char*
- inline void FormatValue(TStringBuilderBase* builder, const char* value, TStringBuf format)
- {
- FormatValue(builder, TStringBuf(value), format);
- }
- // char*
- inline void FormatValue(TStringBuilderBase* builder, char* value, TStringBuf format)
- {
- FormatValue(builder, TStringBuf(value), format);
- }
- // char
- inline void FormatValue(TStringBuilderBase* builder, char value, TStringBuf format)
- {
- FormatValue(builder, TStringBuf(&value, 1), format);
- }
- // bool
- inline void FormatValue(TStringBuilderBase* builder, bool value, TStringBuf format)
- {
- // Parse custom flags.
- bool lowercase = false;
- const char* current = format.begin();
- while (current != format.end()) {
- if (*current == 'l') {
- ++current;
- lowercase = true;
- } else if (IsQuotationSpecSymbol(*current)) {
- ++current;
- } else
- break;
- }
- auto str = lowercase
- ? (value ? TStringBuf("true") : TStringBuf("false"))
- : (value ? TStringBuf("True") : TStringBuf("False"));
- builder->AppendString(str);
- }
- // Fallback to ToString
- struct TToStringFallbackValueFormatterTag
- { };
- template <class TValue, class = void>
- struct TValueFormatter
- {
- static TToStringFallbackValueFormatterTag Do(TStringBuilderBase* builder, const TValue& value, TStringBuf format)
- {
- using ::ToString;
- FormatValue(builder, ToString(value), format);
- return {};
- }
- };
- // Enum
- template <class TEnum>
- struct TValueFormatter<TEnum, typename std::enable_if<TEnumTraits<TEnum>::IsEnum>::type>
- {
- static void Do(TStringBuilderBase* builder, TEnum value, TStringBuf format)
- {
- // Parse custom flags.
- bool lowercase = false;
- const char* current = format.begin();
- while (current != format.end()) {
- if (*current == 'l') {
- ++current;
- lowercase = true;
- } else if (IsQuotationSpecSymbol(*current)) {
- ++current;
- } else {
- break;
- }
- }
- FormatEnum(builder, value, lowercase);
- }
- };
- template <class TRange, class TFormatter>
- typename TFormattableView<TRange, TFormatter>::TBegin TFormattableView<TRange, TFormatter>::begin() const
- {
- return RangeBegin;
- }
- template <class TRange, class TFormatter>
- typename TFormattableView<TRange, TFormatter>::TEnd TFormattableView<TRange, TFormatter>::end() const
- {
- return RangeEnd;
- }
- template <class TRange, class TFormatter>
- TFormattableView<TRange, TFormatter> MakeFormattableView(
- const TRange& range,
- TFormatter&& formatter)
- {
- return TFormattableView<TRange, std::decay_t<TFormatter>>{range.begin(), range.end(), std::forward<TFormatter>(formatter)};
- }
- template <class TRange, class TFormatter>
- TFormattableView<TRange, TFormatter> MakeShrunkFormattableView(
- const TRange& range,
- TFormatter&& formatter,
- size_t limit)
- {
- return TFormattableView<TRange, std::decay_t<TFormatter>>{range.begin(), range.end(), std::forward<TFormatter>(formatter), limit};
- }
- template <class TRange, class TFormatter>
- void FormatRange(TStringBuilderBase* builder, const TRange& range, const TFormatter& formatter, size_t limit = std::numeric_limits<size_t>::max())
- {
- builder->AppendChar('[');
- size_t index = 0;
- for (const auto& item : range) {
- if (index > 0) {
- builder->AppendString(DefaultJoinToStringDelimiter);
- }
- if (index == limit) {
- builder->AppendString(DefaultRangeEllipsisFormat);
- break;
- }
- formatter(builder, item);
- ++index;
- }
- builder->AppendChar(']');
- }
- template <class TRange, class TFormatter>
- void FormatKeyValueRange(TStringBuilderBase* builder, const TRange& range, const TFormatter& formatter, size_t limit = std::numeric_limits<size_t>::max())
- {
- builder->AppendChar('{');
- size_t index = 0;
- for (const auto& item : range) {
- if (index > 0) {
- builder->AppendString(DefaultJoinToStringDelimiter);
- }
- if (index == limit) {
- builder->AppendString(DefaultRangeEllipsisFormat);
- break;
- }
- formatter(builder, item.first);
- builder->AppendString(DefaultKeyValueDelimiter);
- formatter(builder, item.second);
- ++index;
- }
- builder->AppendChar('}');
- }
- // TFormattableView
- template <class TRange, class TFormatter>
- struct TValueFormatter<TFormattableView<TRange, TFormatter>>
- {
- static void Do(TStringBuilderBase* builder, const TFormattableView<TRange, TFormatter>& range, TStringBuf /*format*/)
- {
- FormatRange(builder, range, range.Formatter, range.Limit);
- }
- };
- template <class TFormatter>
- TFormatterWrapper<TFormatter> MakeFormatterWrapper(
- TFormatter&& formatter)
- {
- return TFormatterWrapper<TFormatter>{
- .Formatter = std::move(formatter)
- };
- }
- // TFormatterWrapper
- template <class TFormatter>
- struct TValueFormatter<TFormatterWrapper<TFormatter>>
- {
- static void Do(TStringBuilderBase* builder, const TFormatterWrapper<TFormatter>& wrapper, TStringBuf /*format*/)
- {
- wrapper.Formatter(builder);
- }
- };
- // std::vector
- template <class T, class TAllocator>
- struct TValueFormatter<std::vector<T, TAllocator>>
- {
- static void Do(TStringBuilderBase* builder, const std::vector<T, TAllocator>& collection, TStringBuf /*format*/)
- {
- FormatRange(builder, collection, TDefaultFormatter());
- }
- };
- // std::span
- template <class T, size_t Extent>
- struct TValueFormatter<std::span<T, Extent>>
- {
- static void Do(TStringBuilderBase* builder, const std::span<T, Extent>& collection, TStringBuf /*format*/)
- {
- FormatRange(builder, collection, TDefaultFormatter());
- }
- };
- // TCompactVector
- template <class T, unsigned N>
- struct TValueFormatter<TCompactVector<T, N>>
- {
- static void Do(TStringBuilderBase* builder, const TCompactVector<T, N>& collection, TStringBuf /*format*/)
- {
- FormatRange(builder, collection, TDefaultFormatter());
- }
- };
- // std::set
- template <class T>
- struct TValueFormatter<std::set<T>>
- {
- static void Do(TStringBuilderBase* builder, const std::set<T>& collection, TStringBuf /*format*/)
- {
- FormatRange(builder, collection, TDefaultFormatter());
- }
- };
- // std::map
- template <class K, class V>
- struct TValueFormatter<std::map<K, V>>
- {
- static void Do(TStringBuilderBase* builder, const std::map<K, V>& collection, TStringBuf /*format*/)
- {
- FormatKeyValueRange(builder, collection, TDefaultFormatter());
- }
- };
- // std::multimap
- template <class K, class V>
- struct TValueFormatter<std::multimap<K, V>>
- {
- static void Do(TStringBuilderBase* builder, const std::multimap<K, V>& collection, TStringBuf /*format*/)
- {
- FormatKeyValueRange(builder, collection, TDefaultFormatter());
- }
- };
- // THashSet
- template <class T>
- struct TValueFormatter<THashSet<T>>
- {
- static void Do(TStringBuilderBase* builder, const THashSet<T>& collection, TStringBuf /*format*/)
- {
- FormatRange(builder, collection, TDefaultFormatter());
- }
- };
- // THashMultiSet
- template <class T>
- struct TValueFormatter<THashMultiSet<T>>
- {
- static void Do(TStringBuilderBase* builder, const THashMultiSet<T>& collection, TStringBuf /*format*/)
- {
- FormatRange(builder, collection, TDefaultFormatter());
- }
- };
- // THashMap
- template <class K, class V>
- struct TValueFormatter<THashMap<K, V>>
- {
- static void Do(TStringBuilderBase* builder, const THashMap<K, V>& collection, TStringBuf /*format*/)
- {
- FormatKeyValueRange(builder, collection, TDefaultFormatter());
- }
- };
- // THashMultiMap
- template <class K, class V>
- struct TValueFormatter<THashMultiMap<K, V>>
- {
- static void Do(TStringBuilderBase* builder, const THashMultiMap<K, V>& collection, TStringBuf /*format*/)
- {
- FormatKeyValueRange(builder, collection, TDefaultFormatter());
- }
- };
- // TEnumIndexedArray
- template <class E, class T>
- struct TValueFormatter<TEnumIndexedArray<E, T>>
- {
- static void Do(TStringBuilderBase* builder, const TEnumIndexedArray<E, T>& collection, TStringBuf format)
- {
- builder->AppendChar('{');
- bool firstItem = true;
- for (const auto& index : TEnumTraits<E>::GetDomainValues()) {
- if (!firstItem) {
- builder->AppendString(DefaultJoinToStringDelimiter);
- }
- FormatValue(builder, index, format);
- builder->AppendString(": ");
- FormatValue(builder, collection[index], format);
- firstItem = false;
- }
- builder->AppendChar('}');
- }
- };
- // std::pair
- template <class T1, class T2>
- struct TValueFormatter<std::pair<T1, T2>>
- {
- static void Do(TStringBuilderBase* builder, const std::pair<T1, T2>& value, TStringBuf format)
- {
- builder->AppendChar('{');
- FormatValue(builder, value.first, format);
- builder->AppendString(TStringBuf(", "));
- FormatValue(builder, value.second, format);
- builder->AppendChar('}');
- }
- };
- // std::optional
- inline void FormatValue(TStringBuilderBase* builder, std::nullopt_t, TStringBuf /*format*/)
- {
- builder->AppendString(TStringBuf("<null>"));
- }
- template <class T>
- struct TValueFormatter<std::optional<T>>
- {
- static void Do(TStringBuilderBase* builder, const std::optional<T>& value, TStringBuf format)
- {
- if (value) {
- FormatValue(builder, *value, format);
- } else {
- FormatValue(builder, std::nullopt, format);
- }
- }
- };
- template <class TValue>
- auto FormatValue(TStringBuilderBase* builder, const TValue& value, TStringBuf format) ->
- decltype(TValueFormatter<TValue>::Do(builder, value, format))
- {
- return TValueFormatter<TValue>::Do(builder, value, format);
- }
- namespace NDetail {
- template <class TValue>
- void FormatValueViaSprintf(
- TStringBuilderBase* builder,
- TValue value,
- TStringBuf format,
- TStringBuf genericSpec);
- template <class TValue>
- void FormatIntValue(
- TStringBuilderBase* builder,
- TValue value,
- TStringBuf format,
- TStringBuf genericSpec);
- void FormatPointerValue(
- TStringBuilderBase* builder,
- const void* value,
- TStringBuf format);
- } // namespace NDetail
- #define XX(valueType, castType, genericSpec) \
- inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf format) \
- { \
- NYT::NDetail::FormatIntValue(builder, static_cast<castType>(value), format, genericSpec); \
- }
- XX(i8, i32, TStringBuf("d"))
- XX(ui8, ui32, TStringBuf("u"))
- XX(i16, i32, TStringBuf("d"))
- XX(ui16, ui32, TStringBuf("u"))
- XX(i32, i32, TStringBuf("d"))
- XX(ui32, ui32, TStringBuf("u"))
- #ifdef _win_
- XX(long long, i64, TStringBuf("lld"))
- XX(unsigned long long, ui64, TStringBuf("llu"))
- #else
- XX(long, i64, TStringBuf("ld"))
- XX(unsigned long, ui64, TStringBuf("lu"))
- #endif
- #undef XX
- #define XX(valueType, castType, genericSpec) \
- inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf format) \
- { \
- NYT::NDetail::FormatValueViaSprintf(builder, static_cast<castType>(value), format, genericSpec); \
- }
- XX(double, double, TStringBuf("lf"))
- XX(float, float, TStringBuf("f"))
- #undef XX
- // Pointer
- template <class T>
- void FormatValue(TStringBuilderBase* builder, T* value, TStringBuf format)
- {
- NYT::NDetail::FormatPointerValue(builder, static_cast<const void*>(value), format);
- }
- // TDuration (specialize for performance reasons)
- inline void FormatValue(TStringBuilderBase* builder, TDuration value, TStringBuf /*format*/)
- {
- builder->AppendFormat("%vus", value.MicroSeconds());
- }
- // TInstant (specialize for TFormatTraits)
- inline void FormatValue(TStringBuilderBase* builder, TInstant value, TStringBuf format)
- {
- // TODO(babenko): optimize
- builder->AppendFormat("%v", ToString(value), format);
- }
- ////////////////////////////////////////////////////////////////////////////////
- namespace NDetail {
- template <class TArgFormatter>
- void FormatImpl(
- TStringBuilderBase* builder,
- TStringBuf format,
- const TArgFormatter& argFormatter)
- {
- size_t argIndex = 0;
- auto current = format.begin();
- while (true) {
- // Scan verbatim part until stop symbol.
- auto verbatimBegin = current;
- auto verbatimEnd = verbatimBegin;
- while (verbatimEnd != format.end() && *verbatimEnd != '%') {
- ++verbatimEnd;
- }
- // Copy verbatim part, if any.
- size_t verbatimSize = verbatimEnd - verbatimBegin;
- if (verbatimSize > 0) {
- builder->AppendString(TStringBuf(verbatimBegin, verbatimSize));
- }
- // Handle stop symbol.
- current = verbatimEnd;
- if (current == format.end()) {
- break;
- }
- YT_ASSERT(*current == '%');
- ++current;
- if (*current == '%') {
- // Verbatim %.
- builder->AppendChar('%');
- ++current;
- } else {
- // Scan format part until stop symbol.
- auto argFormatBegin = current;
- auto argFormatEnd = argFormatBegin;
- bool singleQuotes = false;
- bool doubleQuotes = false;
- while (
- argFormatEnd != format.end() &&
- *argFormatEnd != GenericSpecSymbol && // value in generic format
- *argFormatEnd != 'd' && // others are standard specifiers supported by printf
- *argFormatEnd != 'i' &&
- *argFormatEnd != 'u' &&
- *argFormatEnd != 'o' &&
- *argFormatEnd != 'x' &&
- *argFormatEnd != 'X' &&
- *argFormatEnd != 'f' &&
- *argFormatEnd != 'F' &&
- *argFormatEnd != 'e' &&
- *argFormatEnd != 'E' &&
- *argFormatEnd != 'g' &&
- *argFormatEnd != 'G' &&
- *argFormatEnd != 'a' &&
- *argFormatEnd != 'A' &&
- *argFormatEnd != 'c' &&
- *argFormatEnd != 's' &&
- *argFormatEnd != 'p' &&
- *argFormatEnd != 'n')
- {
- switch (*argFormatEnd) {
- case 'q':
- singleQuotes = true;
- break;
- case 'Q':
- doubleQuotes = true;
- break;
- case 'h':
- break;
- }
- ++argFormatEnd;
- }
- // Handle end of format string.
- if (argFormatEnd != format.end()) {
- ++argFormatEnd;
- }
- // 'n' means 'nothing'; skip the argument.
- if (*argFormatBegin != 'n') {
- // Format argument.
- TStringBuf argFormat(argFormatBegin, argFormatEnd);
- if (singleQuotes) {
- builder->AppendChar('\'');
- }
- if (doubleQuotes) {
- builder->AppendChar('"');
- }
- argFormatter(argIndex++, builder, argFormat);
- if (singleQuotes) {
- builder->AppendChar('\'');
- }
- if (doubleQuotes) {
- builder->AppendChar('"');
- }
- }
- current = argFormatEnd;
- }
- }
- }
- } // namespace NDetail
- ////////////////////////////////////////////////////////////////////////////////
- template <class... TArgs>
- TLazyMultiValueFormatter<TArgs...>::TLazyMultiValueFormatter(
- TStringBuf format,
- TArgs&&... args)
- : Format_(format)
- , Args_(std::forward<TArgs>(args)...)
- { }
- template <class... TArgs>
- void FormatValue(
- TStringBuilderBase* builder,
- const TLazyMultiValueFormatter<TArgs...>& value,
- TStringBuf /*format*/)
- {
- std::apply(
- [&] <class... TInnerArgs> (TInnerArgs&&... args) {
- builder->AppendFormat(value.Format_, std::forward<TInnerArgs>(args)...);
- },
- value.Args_);
- }
- template <class... TArgs>
- auto MakeLazyMultiValueFormatter(TStringBuf format, TArgs&&... args)
- {
- return TLazyMultiValueFormatter<TArgs...>(format, std::forward<TArgs>(args)...);
- }
- ////////////////////////////////////////////////////////////////////////////////
- template <class T>
- struct TFormatTraits
- {
- static constexpr bool HasCustomFormatValue = !std::is_same_v<
- decltype(FormatValue(
- static_cast<TStringBuilderBase*>(nullptr),
- *static_cast<const T*>(nullptr),
- TStringBuf())),
- TToStringFallbackValueFormatterTag>;
- };
- ////////////////////////////////////////////////////////////////////////////////
- template <size_t IndexBase, class... TArgs>
- struct TArgFormatterImpl;
- template <size_t IndexBase>
- struct TArgFormatterImpl<IndexBase>
- {
- void operator() (size_t /*index*/, TStringBuilderBase* builder, TStringBuf /*format*/) const
- {
- builder->AppendString(TStringBuf("<missing argument>"));
- }
- };
- template <size_t IndexBase, class THeadArg, class... TTailArgs>
- struct TArgFormatterImpl<IndexBase, THeadArg, TTailArgs...>
- {
- explicit TArgFormatterImpl(const THeadArg& headArg, const TTailArgs&... tailArgs)
- : HeadArg(headArg)
- , TailFormatter(tailArgs...)
- { }
- const THeadArg& HeadArg;
- TArgFormatterImpl<IndexBase + 1, TTailArgs...> TailFormatter;
- void operator() (size_t index, TStringBuilderBase* builder, TStringBuf format) const
- {
- YT_ASSERT(index >= IndexBase);
- if (index == IndexBase) {
- FormatValue(builder, HeadArg, format);
- } else {
- TailFormatter(index, builder, format);
- }
- }
- };
- ////////////////////////////////////////////////////////////////////////////////
- template <size_t Length, class... TArgs>
- void Format(
- TStringBuilderBase* builder,
- const char (&format)[Length],
- TArgs&&... args)
- {
- Format(builder, TStringBuf(format, Length - 1), std::forward<TArgs>(args)...);
- }
- template <class... TArgs>
- void Format(
- TStringBuilderBase* builder,
- TStringBuf format,
- TArgs&&... args)
- {
- TArgFormatterImpl<0, TArgs...> argFormatter(args...);
- NYT::NDetail::FormatImpl(builder, format, argFormatter);
- }
- template <size_t Length, class... TArgs>
- TString Format(
- const char (&format)[Length],
- TArgs&&... args)
- {
- TStringBuilder builder;
- Format(&builder, format, std::forward<TArgs>(args)...);
- return builder.Flush();
- }
- template <class... TArgs>
- TString Format(
- TStringBuf format,
- TArgs&&... args)
- {
- TStringBuilder builder;
- Format(&builder, format, std::forward<TArgs>(args)...);
- return builder.Flush();
- }
- ////////////////////////////////////////////////////////////////////////////////
- } // namespace NYT
|