format-inl.h 34 KB

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