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