aboutsummaryrefslogblamecommitdiffstats
path: root/util/stream/format.h
blob: b033208a1b0027e6087a7eedee5f8f678728c291 (plain) (tree)
1
2
3
4
5
6
7
8
9
            
 

                   
                                
                               
                                
                             
 

                                                                      
  
                                                 
 
                  
                                                                                 
  
                          
                           
                                                                                 
 
               
                                                          
 
                                                                            







                                     
                

                     
                                                                






                          
                                                                        
                                                 
                       








                                                       
                

                     
                                                                 






                          
                                                                         
                                                 
                       





                                                       
 


                                      
 
                                                             




                          
                                      
                                                                                             
 
                                      
                                                                                         
                                                                              
 
                            
                        
         
 
                                    
                                           
                                   
                                           
             
         
                                                                                                                
         
                      
     
 

                                         
 
                                                          
                        
         
      
                                         
                                                                                     
                                                       

                          
                                                                         

                  
 
                         
                            
                                                                                                           




                                
                         
                                                                                 



                                                                                            


                                   
                                                                
                          
         


                               
                           
      
 


                                                                            
                                                                            








                                                                     
                
   
                     
                                                                                                                            
                                                             
 
                                                                                                                                        

                                                                    

                                                                
                                                                             


                          
                                                                      





                                                                     
                     
                                                                                                                              
                                                              
 
                                                                                                                                          

                                                                     

                                                    
                                                                        










                                                                             
                     
                                                                                                                                           
 

                                                    
                                                                        









                                                            
                     
                                                                                                                                    
 
   
                                                   
                                                                        


















                                                                                                                                          
                                                                        

















                                                                                                                                   
                                                                          


                                                  
                                                                           



                                                                                
                         
                                                                                                  

   
                                               
                                                                          


                                             
                                                                                     










                                                                                                 
                                                                           








                                                                                      
                                                                                                            


                                                              

                                                                                
                                                                                





                                                                                 

                                                                                                                     



                                                        
                                                                                                                         
                          
 
                                         
 
   
                                                                       
                                                                              










                                                                                                                      
                     
                                                                                                                                           

                                  
   
                                                                       
                                                                              








                                                                                                     
                     
                                                                                                        
                                          
#pragma once

#include "mem.h"
#include "output.h"

#include <util/datetime/base.h>
#include <util/generic/strbuf.h>
#include <util/generic/flags.h>
#include <util/memory/tempbuf.h>
#include <util/string/cast.h>

enum ENumberFormatFlag {
    HF_FULL = 0x01, /**< Output number with leading zeros. */
    HF_ADDX = 0x02, /**< Output '0x' or '0b' before hex/bin digits. */
};
Y_DECLARE_FLAGS(ENumberFormat, ENumberFormatFlag)
Y_DECLARE_OPERATORS_FOR_FLAGS(ENumberFormat)

enum ESizeFormat {
    SF_QUANTITY, /**< Base 1000, usual suffixes. 1100 gets turned into "1.1K". */
    SF_BYTES,    /**< Base 1024, byte suffix. 1100 gets turned into "1.07KiB". */
};

namespace NFormatPrivate {
    template <size_t Value>
    struct TLog2: std::integral_constant<size_t, TLog2<Value / 2>::value + 1> {};

    template <>
    struct TLog2<1>: std::integral_constant<size_t, 0> {};

    static inline void WriteChars(IOutputStream& os, char c, size_t count) {
        if (count == 0)
            return;
        TTempBuf buf(count);
        memset(buf.Data(), c, count);
        os.Write(buf.Data(), count);
    }

    template <typename T>
    struct TLeftPad {
        T Value;
        size_t Width;
        char Padc;

        inline TLeftPad(const T& value, size_t width, char padc)
            : Value(value)
            , Width(width)
            , Padc(padc)
        {
        }
    };

    template <typename T>
    IOutputStream& operator<<(IOutputStream& o, const TLeftPad<T>& lp) {
        TTempBuf buf;
        TMemoryOutput ss(buf.Data(), buf.Size());
        ss << lp.Value;
        size_t written = buf.Size() - ss.Avail();
        if (lp.Width > written) {
            WriteChars(o, lp.Padc, lp.Width - written);
        }
        o.Write(buf.Data(), written);
        return o;
    }

    template <typename T>
    struct TRightPad {
        T Value;
        size_t Width;
        char Padc;

        inline TRightPad(const T& value, size_t width, char padc)
            : Value(value)
            , Width(width)
            , Padc(padc)
        {
        }
    };

    template <typename T>
    IOutputStream& operator<<(IOutputStream& o, const TRightPad<T>& lp) {
        TTempBuf buf;
        TMemoryOutput ss(buf.Data(), buf.Size());
        ss << lp.Value;
        size_t written = buf.Size() - ss.Avail();
        o.Write(buf.Data(), written);
        if (lp.Width > written) {
            WriteChars(o, lp.Padc, lp.Width - written);
        }
        return o;
    }

    template <typename T, size_t Base>
    struct TBaseNumber {
        T Value;
        ENumberFormat Flags;

        template <typename OtherT>
        inline TBaseNumber(OtherT value, ENumberFormat flags)
            : Value(value)
            , Flags(flags)
        {
        }
    };

    template <typename T, size_t Base>
    using TUnsignedBaseNumber = TBaseNumber<std::make_unsigned_t<std::remove_cv_t<T>>, Base>;

    template <typename T, size_t Base>
    IOutputStream& operator<<(IOutputStream& stream, const TBaseNumber<T, Base>& value) {
        char buf[8 * sizeof(T) + 1]; /* Add 1 for sign. */
        TStringBuf str(buf, IntToString<Base>(value.Value, buf, sizeof(buf)));

        if (str[0] == '-') {
            stream << '-';
            str.Skip(1);
        }

        if (value.Flags & HF_ADDX) {
            if (Base == 16) {
                stream << TStringBuf("0x");
            } else if (Base == 2) {
                stream << TStringBuf("0b");
            }
        }

        if (value.Flags & HF_FULL) {
            WriteChars(stream, '0', (8 * sizeof(T) + TLog2<Base>::value - 1) / TLog2<Base>::value - str.size());
        }

        stream << str;
        return stream;
    }

    template <typename Char, size_t Base>
    struct TBaseText {
        TBasicStringBuf<Char> Text;

        inline TBaseText(const TBasicStringBuf<Char> text)
            : Text(text)
        {
        }
    };

    template <typename Char, size_t Base>
    IOutputStream& operator<<(IOutputStream& os, const TBaseText<Char, Base>& text) {
        for (size_t i = 0; i < text.Text.size(); ++i) {
            if (i != 0) {
                os << ' ';
            }
            os << TUnsignedBaseNumber<Char, Base>(text.Text[i], HF_FULL);
        }
        return os;
    }

    template <typename T>
    struct TFloatPrecision {
        using TdVal = std::remove_cv_t<T>;
        static_assert(std::is_floating_point<TdVal>::value, "expect std::is_floating_point<TdVal>::value");

        TdVal Value;
        EFloatToStringMode Mode;
        int NDigits;
    };

    template <typename T>
    IOutputStream& operator<<(IOutputStream& o, const TFloatPrecision<T>& prec) {
        char buf[512];
        size_t count = FloatToString(prec.Value, buf, sizeof(buf), prec.Mode, prec.NDigits);
        o << TStringBuf(buf, count);
        return o;
    }

    struct THumanReadableDuration {
        TDuration Value;

        constexpr THumanReadableDuration(const TDuration& value)
            : Value(value)
        {
        }
    };

    struct THumanReadableSize {
        double Value;
        ESizeFormat Format;
    };
}

/**
 * Output manipulator basically equivalent to `std::setw` and `std::setfill`
 * combined.
 *
 * When written into a `IOutputStream`, writes out padding characters first,
 * and then provided value.
 *
 * Example usage:
 * @code
 * stream << LeftPad(12345, 10, '0'); // Will output "0000012345"
 * @endcode
 *
 * @param value                         Value to output.
 * @param width                         Target total width.
 * @param padc                          Character to use for padding.
 * @see RightPad
 */
template <typename T>
static constexpr ::NFormatPrivate::TLeftPad<T> LeftPad(const T& value, const size_t width, const char padc = ' ') noexcept {
    return ::NFormatPrivate::TLeftPad<T>(value, width, padc);
}

template <typename T, int N>
static constexpr ::NFormatPrivate::TLeftPad<const T*> LeftPad(const T (&value)[N], const size_t width, const char padc = ' ') noexcept {
    return ::NFormatPrivate::TLeftPad<const T*>(value, width, padc);
}

/**
 * Output manipulator similar to `std::setw` and `std::setfill`.
 *
 * When written into a `IOutputStream`, writes provided value first, and then
 * the padding characters.
 *
 * Example usage:
 * @code
 * stream << RightPad("column1", 10, ' '); // Will output "column1   "
 * @endcode
 *
 * @param value                         Value to output.
 * @param width                         Target total width.
 * @param padc                          Character to use for padding.
 * @see LeftPad
 */
template <typename T>
static constexpr ::NFormatPrivate::TRightPad<T> RightPad(const T& value, const size_t width, const char padc = ' ') noexcept {
    return ::NFormatPrivate::TRightPad<T>(value, width, padc);
}

template <typename T, int N>
static constexpr ::NFormatPrivate::TRightPad<const T*> RightPad(const T (&value)[N], const size_t width, const char padc = ' ') noexcept {
    return ::NFormatPrivate::TRightPad<const T*>(value, width, padc);
}

/**
 * Output manipulator similar to `std::setbase(16)`.
 *
 * When written into a `IOutputStream`, writes out the provided value in
 * hexadecimal form. The value is treated as unsigned, even if its type is in
 * fact signed.
 *
 * Example usage:
 * @code
 * stream << Hex(-1);   // Will output "0xFFFFFFFF"
 * stream << Hex(1ull); // Will output "0x0000000000000001"
 * @endcode
 *
 * @param value                         Value to output.
 * @param flags                         Output flags.
 */
template <typename T>
static constexpr ::NFormatPrivate::TUnsignedBaseNumber<T, 16> Hex(const T& value, const ENumberFormat flags = HF_FULL | HF_ADDX) noexcept {
    return {value, flags};
}

/**
 * Output manipulator similar to `std::setbase(16)`.
 *
 * When written into a `IOutputStream`, writes out the provided value in
 * hexadecimal form.
 *
 * Example usage:
 * @code
 * stream << SHex(-1);   // Will output "-0x00000001"
 * stream << SHex(1ull); // Will output "0x0000000000000001"
 * @endcode
 *
 * @param value                         Value to output.
 * @param flags                         Output flags.
 */
template <typename T>
static constexpr ::NFormatPrivate::TBaseNumber<T, 16> SHex(const T& value, const ENumberFormat flags = HF_FULL | HF_ADDX) noexcept {
    return {value, flags};
}

/**
 * Output manipulator similar to `std::setbase(2)`.
 *
 * When written into a `IOutputStream`, writes out the provided value in
 * binary form. The value is treated as unsigned, even if its type is in
 * fact signed.
 *
 * Example usage:
 * @code
 * stream << Bin(-1);   // Will output "0b11111111111111111111111111111111"
 * stream << Bin(1);    // Will output "0b00000000000000000000000000000001"
 * @endcode
 *
 * @param value                         Value to output.
 * @param flags                         Output flags.
 */
template <typename T>
static constexpr ::NFormatPrivate::TUnsignedBaseNumber<T, 2> Bin(const T& value, const ENumberFormat flags = HF_FULL | HF_ADDX) noexcept {
    return {value, flags};
}

/**
 * Output manipulator similar to `std::setbase(2)`.
 *
 * When written into a `IOutputStream`, writes out the provided value in
 * binary form.
 *
 * Example usage:
 * @code
 * stream << SBin(-1);   // Will output "-0b00000000000000000000000000000001"
 * stream << SBin(1);    // Will output "0b00000000000000000000000000000001"
 * @endcode
 *
 * @param value                         Value to output.
 * @param flags                         Output flags.
 */
template <typename T>
static constexpr ::NFormatPrivate::TBaseNumber<T, 2> SBin(const T& value, const ENumberFormat flags = HF_FULL | HF_ADDX) noexcept {
    return {value, flags};
}

/**
 * Output manipulator for hexadecimal string output.
 *
 * When written into a `IOutputStream`, writes out the provided characters
 * in hexadecimal form divided by space character.
 *
 * Example usage:
 * @code
 * stream << HexText(TStringBuf("abcи"));  // Will output "61 62 63 D0 B8"
 * stream << HexText(TWtringBuf(u"abcи")); // Will output "0061 0062 0063 0438"
 * @endcode
 *
 * @param value                         String to output.
 */
template <typename TChar>
static inline ::NFormatPrivate::TBaseText<TChar, 16> HexText(const TBasicStringBuf<TChar> value) {
    return ::NFormatPrivate::TBaseText<TChar, 16>(value);
}

/**
 * Output manipulator for binary string output.
 *
 * When written into a `IOutputStream`, writes out the provided characters
 * in binary form divided by space character.
 *
 * Example usage:
 * @code
 * stream << BinText(TStringBuf("aaa"));  // Will output "01100001 01100001 01100001"
 * @endcode
 *
 * @param value                         String to output.
 */
template <typename TChar>
static inline ::NFormatPrivate::TBaseText<TChar, 2> BinText(const TBasicStringBuf<TChar> value) {
    return ::NFormatPrivate::TBaseText<TChar, 2>(value);
}

/**
 * Output manipulator for printing `TDuration` values.
 *
 * When written into a `IOutputStream`, writes out the provided `TDuration`
 * in auto-adjusted human-readable format.
 *
 * Example usage:
 * @code
 * stream << HumanReadable(TDuration::MicroSeconds(100));   // Will output "100us"
 * stream << HumanReadable(TDuration::Seconds(3672));       // Will output "1h 1m 12s"
 * @endcode
 *
 * @param value                         Value to output.
 */
static constexpr ::NFormatPrivate::THumanReadableDuration HumanReadable(const TDuration duration) noexcept {
    return ::NFormatPrivate::THumanReadableDuration(duration);
}

/**
 * Output manipulator for writing out human-readable number of elements / memory
 * amount in `ls -h` style.
 *
 * When written into a `IOutputStream`, writes out the provided unsigned integer
 * variable with small precision and a suffix (like 'K', 'M', 'G' for numbers, or
 * 'B', 'KiB', 'MiB', 'GiB' for bytes).
 *
 * For quantities, base 1000 is used. For bytes, base is 1024.
 *
 * Example usage:
 * @code
 * stream <<  HumanReadableSize(1024, SF_QUANTITY);                          // Will output    "1.02K"
 * stream <<  HumanReadableSize(1024, SF_BYTES);                             // Will output    "1KiB"
 * stream << "average usage " << HumanReadableSize(100 / 3., SF_BYTES);     // Will output    "average usage "33.3B""
 * @endcode
 *
 * @param value                         Value to output.
 * @param format                        Format to use.
 */
static constexpr ::NFormatPrivate::THumanReadableSize HumanReadableSize(const double size, ESizeFormat format) noexcept {
    return {size, format};
}

void Time(IOutputStream& l);
void TimeHumanReadable(IOutputStream& l);

/**
 * Output manipulator for adjusting precision of floating point values.
 *
 * When written into a `IOutputStream`, writes out the provided floating point
 * variable with given precision. The behavior depends on provided `mode`.
 *
 * Example usage:
 * @code
 * stream <<  Prec(1.2345678901234567, PREC_AUTO);   // Will output "1.2345678901234567"
 * @endcode
 *
 * @param value                         float or double to output.
 * @param mode                          Output mode.
 * @param ndigits                       Number of significant digits (in `PREC_NDIGITS` and `PREC_POINT_DIGITS` mode).
 * @see EFloatToStringMode
 */
template <typename T>
static constexpr ::NFormatPrivate::TFloatPrecision<T> Prec(const T& value, const EFloatToStringMode mode, const int ndigits = 0) noexcept {
    return {value, mode, ndigits};
}

/**
 * Output manipulator for adjusting precision of floating point values.
 *
 * When written into a `IOutputStream`, writes out the provided floating point
 * variable with given precision. The behavior is equivalent to `Prec(value, PREC_NDIGITS, ndigits)`.
 *
 * Example usage:
 * @code
 * stream <<  Prec(1.2345678901234567, 3);   // Will output "1.23"
 * @endcode
 *
 * @param value                         float or double to output.
 * @param ndigits                       Number of significant digits.
 */
template <typename T>
static constexpr ::NFormatPrivate::TFloatPrecision<T> Prec(const T& value, const int ndigits) noexcept {
    return {value, PREC_NDIGITS, ndigits};
}