| 390 | |
| 391 | template <typename T, unsigned radix = 10, class TChar = char> |
| 392 | class TIntStringBuf { |
| 393 | private: |
| 394 | // inline constexprs are not supported by CUDA yet |
| 395 | static constexpr char IntToChar[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; |
| 396 | static_assert(1 < radix && radix < 17, "expect 1 < radix && radix < 17"); |
| 397 | |
| 398 | // auxiliary recursive template used to calculate maximum buffer size for the given type |
| 399 | template <T v> |
| 400 | struct TBufSizeRec { |
| 401 | // MSVC is tries to evaluate both sides of ?: operator and doesn't break recursion |
| 402 | static constexpr ui32 GetValue() { |
| 403 | if (v == 0) { |
| 404 | return 1; |
| 405 | } |
| 406 | return 1 + TBufSizeRec<v / radix>::value; |
| 407 | } |
| 408 | |
| 409 | static constexpr ui32 value = GetValue(); |
| 410 | }; |
| 411 | |
| 412 | public: |
| 413 | static constexpr ui32 bufferSize = (std::is_signed<T>::value ? 1 : 0) + |
| 414 | ((radix == 2) ? sizeof(T) * 8 : TBufSizeRec<std::numeric_limits<T>::max()>::value); |
| 415 | |
| 416 | template <std::enable_if_t<std::is_integral<T>::value, bool> = true> |
| 417 | explicit constexpr TIntStringBuf(T t) { |
| 418 | Size_ = Convert(t, Buf_, sizeof(Buf_)); |
| 419 | #if __cplusplus >= 202002L // is_constant_evaluated is not supported by CUDA yet |
| 420 | if (std::is_constant_evaluated()) { |
| 421 | #endif |
| 422 | // Init the rest of the array, |
| 423 | // otherwise constexpr copy and move constructors don't work due to uninitialized data access |
| 424 | std::fill(Buf_ + Size_, Buf_ + sizeof(Buf_), '\0'); |
| 425 | #if __cplusplus >= 202002L |
| 426 | } |
| 427 | #endif |
| 428 | } |
| 429 | |
| 430 | constexpr operator TStringBuf() const noexcept { |
| 431 | return TStringBuf(Buf_, Size_); |
| 432 | } |
| 433 | |
| 434 | constexpr static ui32 Convert(T t, TChar* buf, size_t bufLen) { |
| 435 | bufLen = std::min<size_t>(bufferSize, bufLen); |
| 436 | if (std::is_signed<T>::value && t < 0) { |
| 437 | Y_ENSURE(bufLen >= 2, TStringBuf("not enough room in buffer")); |
| 438 | buf[0] = '-'; |
| 439 | const auto mt = std::make_unsigned_t<T>(-(t + 1)) + std::make_unsigned_t<T>(1); |
| 440 | return ConvertUnsigned(mt, &buf[1], bufLen - 1) + 1; |
| 441 | } else { |
| 442 | return ConvertUnsigned(t, buf, bufLen); |
| 443 | } |
| 444 | } |
| 445 | |
| 446 | private: |
| 447 | constexpr static ui32 ConvertUnsigned(typename std::make_unsigned<T>::type t, TChar* buf, ui32 bufLen) { |
| 448 | Y_ENSURE(bufLen, TStringBuf("zero length")); |
| 449 | |