#pragma once
#include <util/system/defaults.h>
#include <util/stream/str.h>
#include <util/generic/maybe.h>
#include <util/generic/string.h>
#include <util/generic/strbuf.h>
#include <util/generic/typetraits.h>
#include <util/generic/yexception.h>
/*
* specialized for all arithmetic types
*/
template <class T>
size_t ToStringImpl(T t, char* buf, size_t len);
/**
* Converts @c t to string writing not more than @c len bytes to output buffer @c buf.
* No NULL terminator appended! Throws exception on buffer overflow.
* @return number of bytes written
*/
template <class T>
inline size_t ToString(const T& t, char* buf, size_t len) {
using TParam = typename TTypeTraits<T>::TFuncParam;
return ToStringImpl<TParam>(t, buf, len);
}
/**
* Floating point to string conversion mode, values are enforced by `dtoa_impl.cpp`.
*/
enum EFloatToStringMode {
/** 0.1f -> "0.1", 0.12345678f -> "0.12345678", ignores ndigits. */
PREC_AUTO = 0,
/** "%g" mode, writes up to the given number of significant digits:
* 0.1f -> "0.1", 0.12345678f -> "0.123457" for ndigits=6, 1.2e-06f -> "1.2e-06" */
PREC_NDIGITS = 2,
/** "%f" mode, writes the given number of digits after decimal point:
* 0.1f -> "0.100000", 1.2e-06f -> "0.000001" for ndigits=6 */
PREC_POINT_DIGITS = 3,
/** same as PREC_POINT_DIGITS, but stripping trailing zeroes:
* 0.1f for ndgigits=6 -> "0.1" */
PREC_POINT_DIGITS_STRIP_ZEROES = 4
};
size_t FloatToString(float t, char* buf, size_t len, EFloatToStringMode mode = PREC_AUTO, int ndigits = 0);
size_t FloatToString(double t, char* buf, size_t len, EFloatToStringMode mode = PREC_AUTO, int ndigits = 0);
template <typename T>
inline TString FloatToString(const T& t, EFloatToStringMode mode = PREC_AUTO, int ndigits = 0) {
char buf[512]; // Max<double>() with mode = PREC_POINT_DIGITS has 309 digits before the decimal point
size_t count = FloatToString(t, buf, sizeof(buf), mode, ndigits);
return TString(buf, count);
}
namespace NPrivate {
template <class T, bool isSimple>
struct TToString {
static inline TString Cvt(const T& t) {
char buf[512];
return TString(buf, ToString<T>(t, buf, sizeof(buf)));
}
};
template <class T>
struct TToString<T, false> {
static inline TString Cvt(const T& t) {
TString s;
TStringOutput o(s);
o << t;
return s;
}
};
}
/*
* some clever implementations...
*/
template <class T>
inline TString ToString(const T& t) {
using TR = std::remove_cv_t<T>;
return ::NPrivate::TToString<TR, std::is_arithmetic<TR>::value>::Cvt((const TR&)t);
}
inline const TString& ToString(const TString& s Y_LIFETIME_BOUND) noexcept {
return s;
}
inline TString&& ToString(TString&& s Y_LIFETIME_BOUND) noexcept {
return std::move(s);
}
inline TString ToString(const char* s) {
return s;
}
inline TString ToString(char* s) {
return s;
}
/*
* Wrapper for wide strings.
*/
template <class T>
inline TUtf16String ToWtring(const T& t) {
return TUtf16String::FromAscii(ToString(t));
}
inline const TUtf16String& ToWtring(const TUtf16String& w Y_LIFETIME_BOUND) noexcept {
return w;
}
inline TUtf16String&& ToWtring(TUtf16String&& w Y_LIFETIME_BOUND) noexcept {
return std::move(w);
}
struct TFromStringException: public TBadCastException {
};
/*
* specialized for:
* bool
* short
* unsigned short
* int
* unsigned int
* long
* unsigned long
* long long
* unsigned long long
* float
* double
* long double
*/
template <typename T, typename TChar>
T FromStringImpl(const TChar* data, size_t len);
template <typename T, typename TChar>
inline T FromString(const TChar* data, size_t len) {
return ::FromStringImpl<T>(data, len);
}
template <typename T, typename TChar>
inline T FromString(const TChar* data) {
return ::FromString<T>(data, std::char_traits<TChar>::length(data));
}
template <class T>
inline T FromString(const TStringBuf& s) {
return ::FromString<T>(s.data(), s.size());
}
template <class T>
inline T FromString(const TString& s) {
return ::FromString<T>(s.data(), s.size());
}
template <class T>
inline T FromString(const std::string& s) {
return ::FromString<T>(s.data(), s.size());
}
template <>
inline TString FromString<TString>(const TString& s) {
return s;
}
template <class T>
inline T FromString(const TWtringBuf& s) {
return ::FromString<T, typename TWtringBuf::char_type>(s.data(), s.size());
}
template <class T>
inline T FromString(const TUtf16String& s) {
return ::FromString<T, wchar16>(s.data(), s.size());
}
namespace NPrivate {
template <typename TChar>
class TFromString {
const TChar* const Data;
const size_t Len;
public:
inline TFromString(const TChar* data, size_t len)
: Data(data)
, Len(len)
{
}
template <typename T>
inline operator T() const {
return FromString<T, TChar>(Data, Len);
}
};
}
template <typename TChar>
inline ::NPrivate::TFromString<TChar> FromString(const TChar* data, size_t len) {
return ::NPrivate::TFromString<TChar>(data, len);
}
template <typename TChar>
inline ::NPrivate::TFromString<TChar> FromString(const TChar* data) {
return ::NPrivate::TFromString<TChar>(data, std::char_traits<TChar>::length(data));
}
template <typename T>
inline ::NPrivate::TFromString<typename T::TChar> FromString(const T& s) {
return ::NPrivate::TFromString<typename T::TChar>(s.data(), s.size());
}
// Conversion exception free versions
template <typename T, typename TChar>
bool TryFromStringImpl(const TChar* data, size_t len, T& result);
/**
* @param data Source string buffer pointer
* @param len Source string length, in characters
* @param result Place to store conversion result value.
* If conversion error occurs, no value stored in @c result
* @return @c true in case of successful conversion, @c false otherwise
**/
template <typename T, typename TChar>
inline bool TryFromString(const TChar* data, size_t len, T& result) {
return TryFromStringImpl<T>(data, len, result);
}
template <typename T, typename TChar>
inline bool TryFromString(const TChar* data, T& result) {
return TryFromString<T>(data, std::char_traits<TChar>::length(data), result);
}
template <class T, class TChar>
inline bool TryFromString(const TChar* data, const size_t len, T& result, const T& def) {
if (TryFromString<T>(data, len, result)) {
return true;
}
result = def;
return false;
}
template <class T>
inline bool TryFromString(const TStringBuf& s, T& result) {
return TryFromString<T>(s.data(), s.size(), result);
}
template <class T>
inline bool TryFromString(const TString& s, T& result) {
return TryFromString<T>(s.data(), s.size(), result);
}
template <class T>
inline bool TryFromString(const std::string& s, T& result) {
return TryFromString<T>(s.data(), s.size(), result);
}
template <class T>
inline bool TryFromString(const TWtringBuf& s, T& result) {
return TryFromString<T>(s.data(), s.size(), result);
}
template <class T>
inline bool TryFromString(const TUtf16String& s, T& result) {
return TryFromString<T>(s.data(), s.size(), result);
}
template <class T, class TChar>
inline TMaybe<T> TryFromString(TBasicStringBuf<TChar> s) {
TMaybe<T> result{NMaybe::TInPlace{}};
if (!TryFromString<T>(s, *result)) {
result.Clear();
}
return result;
}
template <class T, class TChar>
inline TMaybe<T> TryFromString(const TChar* data) {
return TryFromString<T>(TBasicStringBuf<TChar>(data));
}
template <class T>
inline TMaybe<T> TryFromString(const TString& s) {
return TryFromString<T>(TStringBuf(s));
}
template <class T>
inline TMaybe<T> TryFromString(const std::string& s) {
return TryFromString<T>(TStringBuf(s));
}
template <class T>
inline TMaybe<T> TryFromString(const TUtf16String& s) {
return TryFromString<T>(TWtringBuf(s));
}
template <class T, class TStringType>
inline bool TryFromStringWithDefault(const TStringType& s, T& result, const T& def) {
return TryFromString<T>(s.data(), s.size(), result, def);
}
template <class T>
inline bool TryFromStringWithDefault(const char* s, T& result, const T& def) {
return TryFromStringWithDefault<T>(TStringBuf(s), result, def);
}
template <class T, class TStringType>
inline bool TryFromStringWithDefault(const TStringType& s, T& result) {
return TryFromStringWithDefault<T>(s, result, T());
}
// FromString methods with default value if data is invalid
template <class T, class TChar>
inline T FromString(const TChar* data, const size_t len, const T& def) {
T result;
TryFromString<T>(data, len, result, def);
return result;
}
template <class T, class TStringType>
inline T FromStringWithDefault(const TStringType& s, const T& def) {
return FromString<T>(s.data(), s.size(), def);
}
template <class T>
inline T FromStringWithDefault(const char* s, const T& def) {
return FromStringWithDefault<T>(TStringBuf(s), def);
}
template <class T, class TStringType>
inline T FromStringWithDefault(const TStringType& s) {
return FromStringWithDefault<T>(s, T());
}
double StrToD(const char* b, char** se);
double StrToD(const char* b, const char* e, char** se);
template <int base, class T>
size_t IntToString(T t, char* buf, size_t len);
template <int base, class T>
inline TString IntToString(T t) {
static_assert(std::is_arithmetic<std::remove_cv_t<T>>::value, "expect std::is_arithmetic<std::remove_cv_t<T>>::value");
char buf[256];
return TString(buf, IntToString<base>(t, buf, sizeof(buf)));
}
template <int base, class TInt, class TChar>
bool TryIntFromString(const TChar* data, size_t len, TInt& result);
template <int base, class TInt, class TStringType>
inline bool TryIntFromString(const TStringType& s, TInt& result) {
return TryIntFromString<base>(s.data(), s.size(), result);
}
template <class TInt, int base, class TChar>
TInt IntFromString(const TChar* str, size_t len);
template <class TInt, int base, class TChar>
inline TInt IntFromString(const TChar* str) {
return IntFromString<TInt, base>(str, std::char_traits<TChar>::length(str));
}
template <class TInt, int base, class TStringType>
inline TInt IntFromString(const TStringType& str) {
return IntFromString<TInt, base>(str.data(), str.size());
}
static inline TString ToString(const TStringBuf str) {
return TString(str);
}
static inline TUtf16String ToWtring(const TWtringBuf wtr) {
return TUtf16String(wtr);
}
static inline TUtf32String ToUtf32String(const TUtf32StringBuf wtr) {
return TUtf32String(wtr);
}
template <typename T, unsigned radix = 10, class TChar = char>
class TIntStringBuf {
private:
// inline constexprs are not supported by CUDA yet
static constexpr char IntToChar[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
static_assert(1 < radix && radix < 17, "expect 1 < radix && radix < 17");
// auxiliary recursive template used to calculate maximum buffer size for the given type
template <T v>
struct TBufSizeRec {
// MSVC is tries to evaluate both sides of ?: operator and doesn't break recursion
static constexpr ui32 GetValue() {
if (v == 0) {
return 1;
}
return 1 + TBufSizeRec<v / radix>::value;
}
static constexpr ui32 value = GetValue();
};
public:
static constexpr ui32 bufferSize = (std::is_signed<T>::value ? 1 : 0) +
((radix == 2) ? sizeof(T) * 8 : TBufSizeRec<std::numeric_limits<T>::max()>::value);
template <std::enable_if_t<std::is_integral<T>::value, bool> = true>
explicit constexpr TIntStringBuf(T t) {
Size_ = Convert(t, Buf_, sizeof(Buf_));
#if __cplusplus >= 202002L // is_constant_evaluated is not supported by CUDA yet
if (std::is_constant_evaluated()) {
#endif
// Init the rest of the array,
// otherwise constexpr copy and move constructors don't work due to uninitialized data access
std::fill(Buf_ + Size_, Buf_ + sizeof(Buf_), '\0');
#if __cplusplus >= 202002L
}
#endif
}
constexpr operator TStringBuf() const noexcept {
return TStringBuf(Buf_, Size_);
}
constexpr static ui32 Convert(T t, TChar* buf, size_t bufLen) {
bufLen = std::min<size_t>(bufferSize, bufLen);
if (std::is_signed<T>::value && t < 0) {
Y_ENSURE(bufLen >= 2, TStringBuf("not enough room in buffer"));
buf[0] = '-';
const auto mt = std::make_unsigned_t<T>(-(t + 1)) + std::make_unsigned_t<T>(1);
return ConvertUnsigned(mt, &buf[1], bufLen - 1) + 1;
} else {
return ConvertUnsigned(t, buf, bufLen);
}
}
private:
constexpr static ui32 ConvertUnsigned(typename std::make_unsigned<T>::type t, TChar* buf, ui32 bufLen) {
Y_ENSURE(bufLen, TStringBuf("zero length"));
if (t == 0) {
*buf = '0';
return 1;
}
auto* be = buf + bufLen;
ui32 l = 0;
while (t > 0 && be > buf) {
const auto v = t / radix;
const auto r = (radix == 2 || radix == 4 || radix == 8 || radix == 16) ? t & (radix - 1) : t - radix * v;
--be;
if /*constexpr*/ (radix <= 10) { // if constexpr is not supported by CUDA yet
*be = r + '0';
} else {
*be = IntToChar[r];
}
++l;
t = v;
}
Y_ENSURE(!t, TStringBuf("not enough room in buffer"));
if (buf != be) {
for (ui32 i = 0; i < l; ++i) {
*buf = *be;
++buf;
++be;
}
}
return l;
}
ui32 Size_;
TChar Buf_[bufferSize];
};