format-inl.h 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125
  1. #ifndef FORMAT_INL_H_
  2. #error "Direct inclusion of this file is not allowed, include format.h"
  3. // For the sake of sane code completion.
  4. #include "format.h"
  5. #endif
  6. #include "guid.h"
  7. #include "enum.h"
  8. #include "string.h"
  9. #include <library/cpp/yt/assert/assert.h>
  10. #include <library/cpp/yt/small_containers/compact_vector.h>
  11. #include <library/cpp/yt/containers/enum_indexed_array.h>
  12. #include <library/cpp/yt/misc/concepts.h>
  13. #include <library/cpp/yt/misc/enum.h>
  14. #include <library/cpp/yt/misc/wrapper_traits.h>
  15. #include <util/generic/maybe.h>
  16. #include <util/system/platform.h>
  17. #include <cctype>
  18. #include <optional>
  19. #include <span>
  20. #if __cplusplus >= 202302L
  21. #include <filesystem>
  22. #endif
  23. namespace NYT {
  24. ////////////////////////////////////////////////////////////////////////////////
  25. inline char* TStringBuilderBase::Preallocate(size_t size)
  26. {
  27. Reserve(size + GetLength());
  28. return Current_;
  29. }
  30. inline void TStringBuilderBase::Reserve(size_t size)
  31. {
  32. if (Y_UNLIKELY(End_ - Begin_ < static_cast<ssize_t>(size))) {
  33. size_t length = GetLength();
  34. auto newLength = std::max(size, MinBufferLength);
  35. DoReserve(newLength);
  36. Current_ = Begin_ + length;
  37. }
  38. }
  39. inline size_t TStringBuilderBase::GetLength() const
  40. {
  41. return Current_ ? Current_ - Begin_ : 0;
  42. }
  43. inline TStringBuf TStringBuilderBase::GetBuffer() const
  44. {
  45. return TStringBuf(Begin_, Current_);
  46. }
  47. inline void TStringBuilderBase::Advance(size_t size)
  48. {
  49. Current_ += size;
  50. YT_ASSERT(Current_ <= End_);
  51. }
  52. inline void TStringBuilderBase::AppendChar(char ch)
  53. {
  54. *Preallocate(1) = ch;
  55. Advance(1);
  56. }
  57. inline void TStringBuilderBase::AppendChar(char ch, int n)
  58. {
  59. YT_ASSERT(n >= 0);
  60. if (Y_LIKELY(0 != n)) {
  61. char* dst = Preallocate(n);
  62. ::memset(dst, ch, n);
  63. Advance(n);
  64. }
  65. }
  66. inline void TStringBuilderBase::AppendString(TStringBuf str)
  67. {
  68. if (Y_LIKELY(str)) {
  69. char* dst = Preallocate(str.length());
  70. ::memcpy(dst, str.begin(), str.length());
  71. Advance(str.length());
  72. }
  73. }
  74. inline void TStringBuilderBase::AppendString(const char* str)
  75. {
  76. AppendString(TStringBuf(str));
  77. }
  78. inline void TStringBuilderBase::Reset()
  79. {
  80. Begin_ = Current_ = End_ = nullptr;
  81. DoReset();
  82. }
  83. template <class... TArgs>
  84. void TStringBuilderBase::AppendFormat(TStringBuf format, TArgs&& ... args)
  85. {
  86. Format(this, TRuntimeFormat{format}, std::forward<TArgs>(args)...);
  87. }
  88. template <size_t Length, class... TArgs>
  89. void TStringBuilderBase::AppendFormat(const char (&format)[Length], TArgs&& ... args)
  90. {
  91. Format(this, TRuntimeFormat{format}, std::forward<TArgs>(args)...);
  92. }
  93. ////////////////////////////////////////////////////////////////////////////////
  94. inline TString TStringBuilder::Flush()
  95. {
  96. Buffer_.resize(GetLength());
  97. auto result = std::move(Buffer_);
  98. Reset();
  99. return result;
  100. }
  101. inline void TStringBuilder::DoReset()
  102. {
  103. Buffer_ = {};
  104. }
  105. inline void TStringBuilder::DoReserve(size_t newLength)
  106. {
  107. Buffer_.ReserveAndResize(newLength);
  108. auto capacity = Buffer_.capacity();
  109. Buffer_.ReserveAndResize(capacity);
  110. Begin_ = &*Buffer_.begin();
  111. End_ = Begin_ + capacity;
  112. }
  113. inline void FormatValue(TStringBuilderBase* builder, const TStringBuilder& value, TStringBuf /*spec*/)
  114. {
  115. builder->AppendString(value.GetBuffer());
  116. }
  117. ////////////////////////////////////////////////////////////////////////////////
  118. template <class T>
  119. TString ToStringViaBuilder(const T& value, TStringBuf spec)
  120. {
  121. TStringBuilder builder;
  122. FormatValue(&builder, value, spec);
  123. return builder.Flush();
  124. }
  125. ////////////////////////////////////////////////////////////////////////////////
  126. // Compatibility for users of NYT::ToString(nyt_type).
  127. template <CFormattable T>
  128. TString ToString(const T& t)
  129. {
  130. return ToStringViaBuilder(t);
  131. }
  132. // Sometime we want to implement
  133. // FormatValue using util's ToString
  134. // However, if we inside the FormatValue
  135. // we cannot just call the ToString since
  136. // in this scope T is already CFormattable
  137. // and ToString will call the very
  138. // FormatValue we are implementing,
  139. // causing an infinite recursion loop.
  140. // This method is basically a call to
  141. // util's ToString default implementation.
  142. template <class T>
  143. TString ToStringIgnoringFormatValue(const T& t)
  144. {
  145. TString s;
  146. ::TStringOutput o(s);
  147. o << t;
  148. return s;
  149. }
  150. ////////////////////////////////////////////////////////////////////////////////
  151. // Helper functions for formatting.
  152. namespace NDetail {
  153. constexpr inline char IntroductorySymbol = '%';
  154. constexpr inline char GenericSpecSymbol = 'v';
  155. inline bool IsQuotationSpecSymbol(char symbol)
  156. {
  157. return symbol == 'Q' || symbol == 'q';
  158. }
  159. ////////////////////////////////////////////////////////////////////////////////
  160. template <class TValue>
  161. void FormatValueViaSprintf(
  162. TStringBuilderBase* builder,
  163. TValue value,
  164. TStringBuf spec,
  165. TStringBuf genericSpec);
  166. template <class TValue>
  167. void FormatIntValue(
  168. TStringBuilderBase* builder,
  169. TValue value,
  170. TStringBuf spec,
  171. TStringBuf genericSpec);
  172. void FormatPointerValue(
  173. TStringBuilderBase* builder,
  174. const void* value,
  175. TStringBuf spec);
  176. ////////////////////////////////////////////////////////////////////////////////
  177. // Helper concepts for matching the correct overload.
  178. // NB(arkady-e1ppa): We prefer to hardcode the known types
  179. // so that someone doesn't accidentally implement the
  180. // "SimpleRange" concept and have a non-trivial
  181. // formatting procedure at the same time.
  182. // Sadly, clang is bugged and thus we must do implementation by hand
  183. // if we want to use this concept in class specializations.
  184. template <class R>
  185. constexpr bool CKnownRange = false;
  186. template <class T>
  187. requires requires (T* t) { [] <class... Ts> (std::vector<Ts...>*) {} (t); }
  188. constexpr bool CKnownRange<T> = true;
  189. template <class T, size_t E>
  190. constexpr bool CKnownRange<std::span<T, E>> = true;
  191. template <class T, size_t N>
  192. constexpr bool CKnownRange<std::array<T, N>> = true;
  193. template <class T, size_t N>
  194. constexpr bool CKnownRange<TCompactVector<T, N>> = true;
  195. template <class T>
  196. requires requires (T* t) { [] <class... Ts> (std::set<Ts...>*) {} (t); }
  197. constexpr bool CKnownRange<T> = true;
  198. template <class T>
  199. requires requires (T* t) { [] <class... Ts> (std::multiset<Ts...>*) {} (t); }
  200. constexpr bool CKnownRange<T> = true;
  201. template <class... Ts>
  202. constexpr bool CKnownRange<THashSet<Ts...>> = true;
  203. template <class... Ts>
  204. constexpr bool CKnownRange<THashMultiSet<Ts...>> = true;
  205. ////////////////////////////////////////////////////////////////////////////////
  206. template <class R>
  207. constexpr bool CKnownKVRange = false;
  208. template <class T>
  209. requires requires (T* t) { [] <class... Ts> (std::map<Ts...>*) {} (t); }
  210. constexpr bool CKnownKVRange<T> = true;
  211. template <class T>
  212. requires requires (T* t) { [] <class... Ts> (std::multimap<Ts...>*) {} (t); }
  213. constexpr bool CKnownKVRange<T> = true;
  214. template <class... Ts>
  215. constexpr bool CKnownKVRange<THashMap<Ts...>> = true;
  216. template <class... Ts>
  217. constexpr bool CKnownKVRange<THashMultiMap<Ts...>> = true;
  218. // TODO(arkady-e1ppa): Uncomment me when
  219. // https://github.com/llvm/llvm-project/issues/58534 is shipped.
  220. // template <class R>
  221. // concept CKnownRange =
  222. // requires (R r) { [] <class... Ts> (std::vector<Ts...>) { } (r); } ||
  223. // requires (R r) { [] <class T, size_t E> (std::span<T, E>) { } (r); } ||
  224. // requires (R r) { [] <class T, size_t N> (TCompactVector<T, N>) { } (r); } ||
  225. // requires (R r) { [] <class... Ts> (std::set<Ts...>) { } (r); } ||
  226. // requires (R r) { [] <class... Ts> (THashSet<Ts...>) { } (r); } ||
  227. // requires (R r) { [] <class... Ts> (THashMultiSet<Ts...>) { } (r); };
  228. // ////////////////////////////////////////////////////////////////////////////////
  229. // template <class R>
  230. // concept CKnownKVRange =
  231. // requires (R r) { [] <class... Ts> (std::map<Ts...>) { } (r); } ||
  232. // requires (R r) { [] <class... Ts> (std::multimap<Ts...>) { } (r); } ||
  233. // requires (R r) { [] <class... Ts> (THashMap<Ts...>) { } (r); } ||
  234. // requires (R r) { [] <class... Ts> (THashMultiMap<Ts...>) { } (r); };
  235. } // namespace NDetail
  236. ////////////////////////////////////////////////////////////////////////////////
  237. template <class TRange, class TFormatter>
  238. void FormatRange(TStringBuilderBase* builder, const TRange& range, const TFormatter& formatter, size_t limit = std::numeric_limits<size_t>::max())
  239. {
  240. builder->AppendChar('[');
  241. size_t index = 0;
  242. for (const auto& item : range) {
  243. if (index > 0) {
  244. builder->AppendString(DefaultJoinToStringDelimiter);
  245. }
  246. if (index == limit) {
  247. builder->AppendString(DefaultRangeEllipsisFormat);
  248. break;
  249. }
  250. formatter(builder, item);
  251. ++index;
  252. }
  253. builder->AppendChar(']');
  254. }
  255. ////////////////////////////////////////////////////////////////////////////////
  256. template <class TRange, class TFormatter>
  257. void FormatKeyValueRange(TStringBuilderBase* builder, const TRange& range, const TFormatter& formatter, size_t limit = std::numeric_limits<size_t>::max())
  258. {
  259. builder->AppendChar('{');
  260. size_t index = 0;
  261. for (const auto& item : range) {
  262. if (index > 0) {
  263. builder->AppendString(DefaultJoinToStringDelimiter);
  264. }
  265. if (index == limit) {
  266. builder->AppendString(DefaultRangeEllipsisFormat);
  267. break;
  268. }
  269. formatter(builder, item.first);
  270. builder->AppendString(DefaultKeyValueDelimiter);
  271. formatter(builder, item.second);
  272. ++index;
  273. }
  274. builder->AppendChar('}');
  275. }
  276. ////////////////////////////////////////////////////////////////////////////////
  277. template <class R>
  278. concept CFormattableRange =
  279. NDetail::CKnownRange<R> &&
  280. CFormattable<typename R::value_type>;
  281. template <class R>
  282. concept CFormattableKVRange =
  283. NDetail::CKnownKVRange<R> &&
  284. CFormattable<typename R::key_type> &&
  285. CFormattable<typename R::value_type>;
  286. ////////////////////////////////////////////////////////////////////////////////
  287. // Specializations of TFormatArg for ranges
  288. template <class R>
  289. requires CFormattableRange<std::remove_cvref_t<R>>
  290. struct TFormatArg<R>
  291. : public TFormatArgBase
  292. {
  293. using TUnderlying = typename std::remove_cvref_t<R>::value_type;
  294. static constexpr auto ConversionSpecifiers = TFormatArg<TUnderlying>::ConversionSpecifiers;
  295. static constexpr auto FlagSpecifiers = TFormatArg<TUnderlying>::FlagSpecifiers;
  296. };
  297. ////////////////////////////////////////////////////////////////////////////////
  298. template <class TRange, class TFormatter>
  299. typename TFormattableView<TRange, TFormatter>::TBegin TFormattableView<TRange, TFormatter>::begin() const
  300. {
  301. return RangeBegin;
  302. }
  303. template <class TRange, class TFormatter>
  304. typename TFormattableView<TRange, TFormatter>::TEnd TFormattableView<TRange, TFormatter>::end() const
  305. {
  306. return RangeEnd;
  307. }
  308. template <class TRange, class TFormatter>
  309. TFormattableView<TRange, TFormatter> MakeFormattableView(
  310. const TRange& range,
  311. TFormatter&& formatter)
  312. {
  313. return TFormattableView<TRange, std::decay_t<TFormatter>>{range.begin(), range.end(), std::forward<TFormatter>(formatter)};
  314. }
  315. template <class TRange, class TFormatter>
  316. TFormattableView<TRange, TFormatter> MakeShrunkFormattableView(
  317. const TRange& range,
  318. TFormatter&& formatter,
  319. size_t limit)
  320. {
  321. return TFormattableView<TRange, std::decay_t<TFormatter>>{
  322. range.begin(),
  323. range.end(),
  324. std::forward<TFormatter>(formatter),
  325. limit};
  326. }
  327. template <class TFormatter>
  328. TFormatterWrapper<TFormatter> MakeFormatterWrapper(
  329. TFormatter&& formatter)
  330. {
  331. return TFormatterWrapper<TFormatter>{
  332. .Formatter = std::move(formatter)
  333. };
  334. }
  335. template <class... TArgs>
  336. TLazyMultiValueFormatter<TArgs...>::TLazyMultiValueFormatter(
  337. TStringBuf format,
  338. TArgs&&... args)
  339. : Format_(format)
  340. , Args_(std::forward<TArgs>(args)...)
  341. { }
  342. template <class... TArgs>
  343. auto MakeLazyMultiValueFormatter(TStringBuf format, TArgs&&... args)
  344. {
  345. return TLazyMultiValueFormatter<TArgs...>(format, std::forward<TArgs>(args)...);
  346. }
  347. ////////////////////////////////////////////////////////////////////////////////
  348. // Non-container objects.
  349. #define XX(valueType, castType, genericSpec) \
  350. inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf spec) \
  351. { \
  352. NYT::NDetail::FormatIntValue(builder, static_cast<castType>(value), spec, genericSpec); \
  353. }
  354. XX(i8, i32, TStringBuf("d"))
  355. XX(ui8, ui32, TStringBuf("u"))
  356. XX(i16, i32, TStringBuf("d"))
  357. XX(ui16, ui32, TStringBuf("u"))
  358. XX(i32, i32, TStringBuf("d"))
  359. XX(ui32, ui32, TStringBuf("u"))
  360. XX(long, i64, TStringBuf(PRIdLEAST64))
  361. XX(long long, i64, TStringBuf(PRIdLEAST64))
  362. XX(unsigned long, ui64, TStringBuf(PRIuLEAST64))
  363. XX(unsigned long long, ui64, TStringBuf(PRIuLEAST64))
  364. #undef XX
  365. #define XX(valueType, castType, genericSpec) \
  366. inline void FormatValue(TStringBuilderBase* builder, valueType value, TStringBuf spec) \
  367. { \
  368. NYT::NDetail::FormatValueViaSprintf(builder, static_cast<castType>(value), spec, genericSpec); \
  369. }
  370. XX(double, double, TStringBuf("lf"))
  371. XX(float, float, TStringBuf("f"))
  372. #undef XX
  373. // Pointer
  374. template <class T>
  375. void FormatValue(TStringBuilderBase* builder, T* value, TStringBuf spec)
  376. {
  377. NYT::NDetail::FormatPointerValue(builder, static_cast<const void*>(value), spec);
  378. }
  379. // TStringBuf
  380. inline void FormatValue(TStringBuilderBase* builder, TStringBuf value, TStringBuf spec)
  381. {
  382. if (!spec) {
  383. builder->AppendString(value);
  384. return;
  385. }
  386. // Parse alignment.
  387. bool alignLeft = false;
  388. const char* current = spec.begin();
  389. if (*current == '-') {
  390. alignLeft = true;
  391. ++current;
  392. }
  393. bool hasAlign = false;
  394. int alignSize = 0;
  395. while (*current >= '0' && *current <= '9') {
  396. hasAlign = true;
  397. alignSize = 10 * alignSize + (*current - '0');
  398. if (alignSize > 1000000) {
  399. builder->AppendString(TStringBuf("<alignment overflow>"));
  400. return;
  401. }
  402. ++current;
  403. }
  404. int padding = 0;
  405. bool padLeft = false;
  406. bool padRight = false;
  407. if (hasAlign) {
  408. padding = alignSize - value.size();
  409. if (padding < 0) {
  410. padding = 0;
  411. }
  412. padLeft = !alignLeft;
  413. padRight = alignLeft;
  414. }
  415. bool singleQuotes = false;
  416. bool doubleQuotes = false;
  417. bool escape = false;
  418. while (current < spec.end()) {
  419. switch (*current++) {
  420. case 'q':
  421. singleQuotes = true;
  422. break;
  423. case 'Q':
  424. doubleQuotes = true;
  425. break;
  426. case 'h':
  427. escape = true;
  428. break;
  429. }
  430. }
  431. if (padLeft) {
  432. builder->AppendChar(' ', padding);
  433. }
  434. if (singleQuotes || doubleQuotes || escape) {
  435. for (const char* valueCurrent = value.begin(); valueCurrent < value.end(); ++valueCurrent) {
  436. char ch = *valueCurrent;
  437. if (ch == '\n') {
  438. builder->AppendString("\\n");
  439. } else if (ch == '\t') {
  440. builder->AppendString("\\t");
  441. } else if (ch == '\\') {
  442. builder->AppendString("\\\\");
  443. } else if (ch < PrintableASCIILow || ch > PrintableASCIIHigh) {
  444. builder->AppendString("\\x");
  445. builder->AppendChar(IntToHexLowercase[static_cast<ui8>(ch) >> 4]);
  446. builder->AppendChar(IntToHexLowercase[static_cast<ui8>(ch) & 0xf]);
  447. } else if ((singleQuotes && ch == '\'') || (doubleQuotes && ch == '\"')) {
  448. builder->AppendChar('\\');
  449. builder->AppendChar(ch);
  450. } else {
  451. builder->AppendChar(ch);
  452. }
  453. }
  454. } else {
  455. builder->AppendString(value);
  456. }
  457. if (padRight) {
  458. builder->AppendChar(' ', padding);
  459. }
  460. }
  461. // TString
  462. inline void FormatValue(TStringBuilderBase* builder, const TString& value, TStringBuf spec)
  463. {
  464. FormatValue(builder, TStringBuf(value), spec);
  465. }
  466. // const char*
  467. inline void FormatValue(TStringBuilderBase* builder, const char* value, TStringBuf spec)
  468. {
  469. FormatValue(builder, TStringBuf(value), spec);
  470. }
  471. template <size_t N>
  472. inline void FormatValue(TStringBuilderBase* builder, const char (&value)[N], TStringBuf spec)
  473. {
  474. FormatValue(builder, TStringBuf(value), spec);
  475. }
  476. // char*
  477. inline void FormatValue(TStringBuilderBase* builder, char* value, TStringBuf spec)
  478. {
  479. FormatValue(builder, TStringBuf(value), spec);
  480. }
  481. // std::string
  482. inline void FormatValue(TStringBuilderBase* builder, const std::string& value, TStringBuf spec)
  483. {
  484. FormatValue(builder, TStringBuf(value), spec);
  485. }
  486. // std::string_view
  487. inline void FormatValue(TStringBuilderBase* builder, const std::string_view& value, TStringBuf spec)
  488. {
  489. FormatValue(builder, TStringBuf(value), spec);
  490. }
  491. #if __cplusplus >= 202302L
  492. // std::filesystem::path
  493. inline void FormatValue(TStringBuilderBase* builder, const std::filesystem::path& value, TStringBuf spec)
  494. {
  495. FormatValue(builder, std::string(value), spec);
  496. }
  497. #endif
  498. // char
  499. inline void FormatValue(TStringBuilderBase* builder, char value, TStringBuf spec)
  500. {
  501. FormatValue(builder, TStringBuf(&value, 1), spec);
  502. }
  503. // bool
  504. inline void FormatValue(TStringBuilderBase* builder, bool value, TStringBuf spec)
  505. {
  506. // Parse custom flags.
  507. bool lowercase = false;
  508. const char* current = spec.begin();
  509. while (current != spec.end()) {
  510. if (*current == 'l') {
  511. ++current;
  512. lowercase = true;
  513. } else if (NYT::NDetail::IsQuotationSpecSymbol(*current)) {
  514. ++current;
  515. } else
  516. break;
  517. }
  518. auto str = lowercase
  519. ? (value ? TStringBuf("true") : TStringBuf("false"))
  520. : (value ? TStringBuf("True") : TStringBuf("False"));
  521. builder->AppendString(str);
  522. }
  523. // TDuration
  524. inline void FormatValue(TStringBuilderBase* builder, TDuration value, TStringBuf /*spec*/)
  525. {
  526. builder->AppendFormat("%vus", value.MicroSeconds());
  527. }
  528. // TInstant
  529. inline void FormatValue(TStringBuilderBase* builder, TInstant value, TStringBuf spec)
  530. {
  531. // TODO(babenko): Optimize.
  532. FormatValue(builder, NYT::ToStringIgnoringFormatValue(value), spec);
  533. }
  534. // Enum
  535. template <class TEnum>
  536. requires (TEnumTraits<TEnum>::IsEnum)
  537. void FormatValue(TStringBuilderBase* builder, TEnum value, TStringBuf spec)
  538. {
  539. // Parse custom flags.
  540. bool lowercase = false;
  541. const char* current = spec.begin();
  542. while (current != spec.end()) {
  543. if (*current == 'l') {
  544. ++current;
  545. lowercase = true;
  546. } else if (NYT::NDetail::IsQuotationSpecSymbol(*current)) {
  547. ++current;
  548. } else {
  549. break;
  550. }
  551. }
  552. FormatEnum(builder, value, lowercase);
  553. }
  554. template <class TArcadiaEnum>
  555. requires (std::is_enum_v<TArcadiaEnum> && !TEnumTraits<TArcadiaEnum>::IsEnum)
  556. void FormatValue(TStringBuilderBase* builder, TArcadiaEnum value, TStringBuf /*spec*/)
  557. {
  558. // NB(arkady-e1ppa): This can catch normal enums which
  559. // just want to be serialized as numbers.
  560. // Unfortunately, we have no way of determining that other than
  561. // marking every relevant arcadia enum in the code by trait
  562. // or writing their complete trait and placing such trait in
  563. // every single file where it is formatted.
  564. // We gotta figure something out but until that
  565. // we will just have to make a string for such enums.
  566. // If only arcadia enums provided compile-time check
  567. // if enum is serializable :(((((.
  568. builder->AppendString(NYT::ToStringIgnoringFormatValue(value));
  569. }
  570. // Container objects.
  571. // NB(arkady-e1ppa): In order to support container combinations
  572. // we forward-declare them before defining.
  573. // TMaybe
  574. template <class T, class TPolicy>
  575. void FormatValue(TStringBuilderBase* builder, const TMaybe<T, TPolicy>& value, TStringBuf spec);
  576. // std::optional
  577. template <CFormattable T>
  578. void FormatValue(TStringBuilderBase* builder, const std::optional<T>& value, TStringBuf spec);
  579. // std::pair
  580. template <CFormattable A, CFormattable B>
  581. void FormatValue(TStringBuilderBase* builder, const std::pair<A, B>& value, TStringBuf spec);
  582. // std::tuple
  583. template <CFormattable... Ts>
  584. void FormatValue(TStringBuilderBase* builder, const std::tuple<Ts...>& value, TStringBuf spec);
  585. // TEnumIndexedArray
  586. template <class E, CFormattable T>
  587. void FormatValue(TStringBuilderBase* builder, const TEnumIndexedArray<E, T>& collection, TStringBuf spec);
  588. // One-valued ranges
  589. template <CFormattableRange TRange>
  590. void FormatValue(TStringBuilderBase* builder, const TRange& collection, TStringBuf spec);
  591. // Two-valued ranges
  592. template <CFormattableKVRange TRange>
  593. void FormatValue(TStringBuilderBase* builder, const TRange& collection, TStringBuf spec);
  594. // FormattableView
  595. template <class TRange, class TFormatter>
  596. void FormatValue(
  597. TStringBuilderBase* builder,
  598. const TFormattableView<TRange, TFormatter>& formattableView,
  599. TStringBuf spec);
  600. // TFormatterWrapper
  601. template <class TFormatter>
  602. void FormatValue(
  603. TStringBuilderBase* builder,
  604. const TFormatterWrapper<TFormatter>& wrapper,
  605. TStringBuf spec);
  606. // TLazyMultiValueFormatter
  607. template <class... TArgs>
  608. void FormatValue(
  609. TStringBuilderBase* builder,
  610. const TLazyMultiValueFormatter<TArgs...>& value,
  611. TStringBuf /*spec*/);
  612. // TMaybe
  613. template <class T, class TPolicy>
  614. void FormatValue(TStringBuilderBase* builder, const TMaybe<T, TPolicy>& value, TStringBuf spec)
  615. {
  616. FormatValue(builder, NYT::ToStringIgnoringFormatValue(value), spec);
  617. }
  618. // std::optional: nullopt
  619. inline void FormatValue(TStringBuilderBase* builder, std::nullopt_t, TStringBuf /*spec*/)
  620. {
  621. builder->AppendString(TStringBuf("<null>"));
  622. }
  623. // std::optional: generic T
  624. template <CFormattable T>
  625. void FormatValue(TStringBuilderBase* builder, const std::optional<T>& value, TStringBuf spec)
  626. {
  627. if (value.has_value()) {
  628. FormatValue(builder, *value, spec);
  629. } else {
  630. FormatValue(builder, std::nullopt, spec);
  631. }
  632. }
  633. // std::pair
  634. template <CFormattable A, CFormattable B>
  635. void FormatValue(TStringBuilderBase* builder, const std::pair<A, B>& value, TStringBuf spec)
  636. {
  637. builder->AppendChar('{');
  638. FormatValue(builder, value.first, spec);
  639. builder->AppendString(TStringBuf(", "));
  640. FormatValue(builder, value.second, spec);
  641. builder->AppendChar('}');
  642. }
  643. // std::tuple
  644. template <CFormattable... Ts>
  645. void FormatValue(TStringBuilderBase* builder, const std::tuple<Ts...>& value, TStringBuf spec)
  646. {
  647. builder->AppendChar('{');
  648. [&] <size_t... Idx> (std::index_sequence<Idx...>) {
  649. ([&] {
  650. FormatValue(builder, std::get<Idx>(value), spec);
  651. if constexpr (Idx != sizeof...(Ts)) {
  652. builder->AppendString(TStringBuf(", "));
  653. }
  654. } (), ...);
  655. } (std::index_sequence_for<Ts...>());
  656. builder->AppendChar('}');
  657. }
  658. // TEnumIndexedArray
  659. template <class E, CFormattable T>
  660. void FormatValue(TStringBuilderBase* builder, const TEnumIndexedArray<E, T>& collection, TStringBuf spec)
  661. {
  662. builder->AppendChar('{');
  663. bool firstItem = true;
  664. for (const auto& index : TEnumTraits<E>::GetDomainValues()) {
  665. if (!firstItem) {
  666. builder->AppendString(DefaultJoinToStringDelimiter);
  667. }
  668. FormatValue(builder, index, spec);
  669. builder->AppendString(": ");
  670. FormatValue(builder, collection[index], spec);
  671. firstItem = false;
  672. }
  673. builder->AppendChar('}');
  674. }
  675. // One-valued ranges
  676. template <CFormattableRange TRange>
  677. void FormatValue(TStringBuilderBase* builder, const TRange& collection, TStringBuf spec)
  678. {
  679. NYT::FormatRange(builder, collection, TSpecBoundFormatter(spec));
  680. }
  681. // Two-valued ranges
  682. template <CFormattableKVRange TRange>
  683. void FormatValue(TStringBuilderBase* builder, const TRange& collection, TStringBuf /*spec*/)
  684. {
  685. NYT::FormatKeyValueRange(builder, collection, TDefaultFormatter());
  686. }
  687. // FormattableView
  688. template <class TRange, class TFormatter>
  689. void FormatValue(
  690. TStringBuilderBase* builder,
  691. const TFormattableView<TRange, TFormatter>& formattableView,
  692. TStringBuf /*spec*/)
  693. {
  694. NYT::FormatRange(builder, formattableView, formattableView.Formatter, formattableView.Limit);
  695. }
  696. // TFormatterWrapper
  697. template <class TFormatter>
  698. void FormatValue(
  699. TStringBuilderBase* builder,
  700. const TFormatterWrapper<TFormatter>& wrapper,
  701. TStringBuf /*spec*/)
  702. {
  703. wrapper.Formatter(builder);
  704. }
  705. // TLazyMultiValueFormatter
  706. template <class... TArgs>
  707. void FormatValue(
  708. TStringBuilderBase* builder,
  709. const TLazyMultiValueFormatter<TArgs...>& value,
  710. TStringBuf /*spec*/)
  711. {
  712. std::apply(
  713. [&] <class... TInnerArgs> (TInnerArgs&&... args) {
  714. builder->AppendFormat(value.Format_, std::forward<TInnerArgs>(args)...);
  715. },
  716. value.Args_);
  717. }
  718. ////////////////////////////////////////////////////////////////////////////////
  719. namespace NDetail {
  720. template <size_t HeadPos, class... TArgs>
  721. class TValueFormatter;
  722. template <size_t HeadPos>
  723. class TValueFormatter<HeadPos>
  724. {
  725. public:
  726. void operator() (size_t /*index*/, TStringBuilderBase* builder, TStringBuf /*spec*/) const
  727. {
  728. builder->AppendString(TStringBuf("<missing argument>"));
  729. }
  730. };
  731. template <size_t HeadPos, class THead, class... TTail>
  732. class TValueFormatter<HeadPos, THead, TTail...>
  733. {
  734. public:
  735. explicit TValueFormatter(const THead& head, const TTail&... tail) noexcept
  736. : Head_(head)
  737. , TailFormatter_(tail...)
  738. { }
  739. void operator() (size_t index, TStringBuilderBase* builder, TStringBuf spec) const
  740. {
  741. YT_ASSERT(index >= HeadPos);
  742. if (index == HeadPos) {
  743. FormatValue(builder, Head_, spec);
  744. } else {
  745. TailFormatter_(index, builder, spec);
  746. }
  747. }
  748. private:
  749. const THead& Head_;
  750. TValueFormatter<HeadPos + 1, TTail...> TailFormatter_;
  751. };
  752. ////////////////////////////////////////////////////////////////////////////////
  753. template <class TRangeValue>
  754. class TRangeFormatter
  755. {
  756. public:
  757. template <class... TArgs>
  758. requires std::constructible_from<std::span<const TRangeValue>, TArgs...>
  759. explicit TRangeFormatter(TArgs&&... args) noexcept
  760. : Span_(std::forward<TArgs>(args)...)
  761. { }
  762. void operator() (size_t index, TStringBuilderBase* builder, TStringBuf spec) const
  763. {
  764. if (index >= Span_.size()) {
  765. builder->AppendString(TStringBuf("<missing argument>"));
  766. } else {
  767. FormatValue(builder, *(Span_.begin() + index), spec);
  768. }
  769. }
  770. private:
  771. std::span<const TRangeValue> Span_;
  772. };
  773. ////////////////////////////////////////////////////////////////////////////////
  774. template <class T>
  775. concept CFormatter = CInvocable<T, void(size_t, TStringBuilderBase*, TStringBuf)>;
  776. ////////////////////////////////////////////////////////////////////////////////
  777. template <CFormatter TFormatter>
  778. void RunFormatter(
  779. TStringBuilderBase* builder,
  780. TStringBuf format,
  781. const TFormatter& formatter)
  782. {
  783. size_t argIndex = 0;
  784. auto current = std::begin(format);
  785. auto end = std::end(format);
  786. while (true) {
  787. // Scan verbatim part until stop symbol.
  788. auto verbatimBegin = current;
  789. auto verbatimEnd = std::find(current, end, IntroductorySymbol);
  790. // Copy verbatim part, if any.
  791. size_t verbatimSize = verbatimEnd - verbatimBegin;
  792. if (verbatimSize > 0) {
  793. builder->AppendString(TStringBuf(verbatimBegin, verbatimSize));
  794. }
  795. // Handle stop symbol.
  796. current = verbatimEnd;
  797. if (current == end) {
  798. break;
  799. }
  800. YT_ASSERT(*current == IntroductorySymbol);
  801. ++current;
  802. if (*current == IntroductorySymbol) {
  803. // Verbatim %.
  804. builder->AppendChar(IntroductorySymbol);
  805. ++current;
  806. continue;
  807. }
  808. // Scan format part until stop symbol.
  809. auto argFormatBegin = current;
  810. auto argFormatEnd = argFormatBegin;
  811. bool singleQuotes = false;
  812. bool doubleQuotes = false;
  813. while (
  814. argFormatEnd != end &&
  815. *argFormatEnd != GenericSpecSymbol && // value in generic format
  816. *argFormatEnd != 'd' && // others are standard specifiers supported by printf
  817. *argFormatEnd != 'i' &&
  818. *argFormatEnd != 'u' &&
  819. *argFormatEnd != 'o' &&
  820. *argFormatEnd != 'x' &&
  821. *argFormatEnd != 'X' &&
  822. *argFormatEnd != 'f' &&
  823. *argFormatEnd != 'F' &&
  824. *argFormatEnd != 'e' &&
  825. *argFormatEnd != 'E' &&
  826. *argFormatEnd != 'g' &&
  827. *argFormatEnd != 'G' &&
  828. *argFormatEnd != 'a' &&
  829. *argFormatEnd != 'A' &&
  830. *argFormatEnd != 'c' &&
  831. *argFormatEnd != 's' &&
  832. *argFormatEnd != 'p' &&
  833. *argFormatEnd != 'n')
  834. {
  835. switch (*argFormatEnd) {
  836. case 'q':
  837. singleQuotes = true;
  838. break;
  839. case 'Q':
  840. doubleQuotes = true;
  841. break;
  842. case 'h':
  843. break;
  844. }
  845. ++argFormatEnd;
  846. }
  847. // Handle end of format string.
  848. if (argFormatEnd != end) {
  849. ++argFormatEnd;
  850. }
  851. // 'n' means 'nothing'; skip the argument.
  852. if (*argFormatBegin != 'n') {
  853. // Format argument.
  854. TStringBuf argFormat(argFormatBegin, argFormatEnd);
  855. if (singleQuotes) {
  856. builder->AppendChar('\'');
  857. }
  858. if (doubleQuotes) {
  859. builder->AppendChar('"');
  860. }
  861. formatter(argIndex++, builder, argFormat);
  862. if (singleQuotes) {
  863. builder->AppendChar('\'');
  864. }
  865. if (doubleQuotes) {
  866. builder->AppendChar('"');
  867. }
  868. }
  869. current = argFormatEnd;
  870. }
  871. }
  872. } // namespace NDetail
  873. ////////////////////////////////////////////////////////////////////////////////
  874. template <class... TArgs>
  875. void Format(TStringBuilderBase* builder, TFormatString<TArgs...> format, TArgs&&... args)
  876. {
  877. // NB(arkady-e1ppa): "if constexpr" is done in order to prevent
  878. // compiler from emitting "No matching function to call"
  879. // when arguments are not formattable.
  880. // Compiler would crash in TFormatString ctor
  881. // anyway (e.g. program would not compile) but
  882. // for some reason it does look ahead and emits
  883. // a second error.
  884. if constexpr ((CFormattable<TArgs> && ...)) {
  885. NYT::NDetail::TValueFormatter<0, TArgs...> formatter(args...);
  886. NYT::NDetail::RunFormatter(builder, format.Get(), formatter);
  887. }
  888. }
  889. template <class... TArgs>
  890. TString Format(TFormatString<TArgs...> format, TArgs&&... args)
  891. {
  892. TStringBuilder builder;
  893. Format(&builder, format, std::forward<TArgs>(args)...);
  894. return builder.Flush();
  895. }
  896. ////////////////////////////////////////////////////////////////////////////////
  897. template <size_t Length, class TVector>
  898. void FormatVector(
  899. TStringBuilderBase* builder,
  900. const char (&format)[Length],
  901. const TVector& vec)
  902. {
  903. NYT::NDetail::TRangeFormatter<typename TVector::value_type> formatter(vec);
  904. NYT::NDetail::RunFormatter(builder, format, formatter);
  905. }
  906. template <class TVector>
  907. void FormatVector(
  908. TStringBuilderBase* builder,
  909. TStringBuf format,
  910. const TVector& vec)
  911. {
  912. NYT::NDetail::TRangeFormatter<typename TVector::value_type> formatter(vec);
  913. NYT::NDetail::RunFormatter(builder, format, formatter);
  914. }
  915. template <size_t Length, class TVector>
  916. TString FormatVector(
  917. const char (&format)[Length],
  918. const TVector& vec)
  919. {
  920. TStringBuilder builder;
  921. FormatVector(&builder, format, vec);
  922. return builder.Flush();
  923. }
  924. template <class TVector>
  925. TString FormatVector(
  926. TStringBuf format,
  927. const TVector& vec)
  928. {
  929. TStringBuilder builder;
  930. FormatVector(&builder, format, vec);
  931. return builder.Flush();
  932. }
  933. ////////////////////////////////////////////////////////////////////////////////
  934. } // namespace NYT
  935. #include <util/string/cast.h>
  936. // util/string/cast.h extension for yt and std types only
  937. // TODO(arkady-e1ppa): Abolish ::ToString in
  938. // favour of either NYT::ToString or
  939. // automatic formatting wherever it is needed.
  940. namespace NPrivate {
  941. ////////////////////////////////////////////////////////////////////////////////
  942. template <class T>
  943. requires (
  944. (NYT::NDetail::IsNYTName<T>() ||
  945. NYT::NDetail::IsStdName<T>()) &&
  946. NYT::CFormattable<T>)
  947. struct TToString<T, false>
  948. {
  949. static TString Cvt(const T& t)
  950. {
  951. return NYT::ToStringViaBuilder(t);
  952. }
  953. };
  954. ////////////////////////////////////////////////////////////////////////////////
  955. } // namespace NPrivate