aboutsummaryrefslogblamecommitdiffstats
path: root/util/string/cast.h
blob: 564baa93d5c2557f089e205cc47b06d199525936 (plain) (tree)
1
2
3
4
5
6
7
8
9
            
 
                                 
                            
                               
                                
                                
                                    
                                    






                                                
   


                                                                                      
                                                           
                                                       


                                             

                                                                                    
                         








                                                                                        


                                                                 



                                                                                                            
                     
                                                                                                
                                                                                                         
                               
 

                                     
                                               
                          
                                                                  



                                
                                               


                               






                                 
                                     
                                   
 
                                                                                       
 
                                                                            

             
                                                                  
 
                                        

             
                                  


             
                            
                  
                                                
 
                                                                                      

             
                                                                            
 
 

                                                       
  
                   
        










                      
                                                
 
                                                    

                                          
                                        
                                                                        

                  
                                          
                                               

                  
                                       
                                               





                                               
           
                                                      

             
                  
                                          
                                                                               

                  
                                            
                                                        
 

























                                                                                 
                                                                                       


                                                                          
                                                                          
 
















                                                                       
                                                                                 
 







                                                                                         
                                                           
                                                        

                  
                                                        
                                                        

                  



                                                            
                                                           
                                                        

                  
                                                             
                                                        
 




























                                                          
                                                                                     
                                                             
 



                                                                              
                                                                       









                                                                        
                                                                    
                                                  
 



                                                             
                                                      

                                            

                                                       


                                               
                                 
                                                                                                                           

                  
                                                                
 

                                                                   
                                                                  
                                                              
 



                                                 
                                                                                
 
                                                   
                                                             
 










                                                                     
 
                                                              

                                                      
                                                                                                                           










                                                                                            
 
                                                 
       

                                                                                                                          
                                                                        
                                               







                                                                                                         




                                                    
                                                                   



















                                                                                                            
                                                                                                                     
                 
                                                                                         






                                                              




                                          


                 
                           
  
#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];
};