#include "format.h"
#include "output.h"
#include <util/generic/ymath.h>
#include <util/string/cast.h>
namespace NFormatPrivate {
static inline i64 Round(double value) {
double res1 = floor(value);
double res2 = ceil(value);
return (value - res1 < res2 - value) ? (i64)res1 : (i64)res2;
}
static inline IOutputStream& PrintDoubleShortly(IOutputStream& os, const double& d) {
// General case: request 3 significant digits
// Side-effect: allows exponential representation
EFloatToStringMode mode = PREC_NDIGITS;
int ndigits = 3;
if (IsValidFloat(d) && Abs(d) < 1e12) {
// For reasonably-sized finite values, it's better to avoid
// exponential representation.
// Use compact fixed representation and determine
// precision based on magnitude.
mode = PREC_POINT_DIGITS_STRIP_ZEROES;
if (i64(Abs(d) * 100) < 1000) {
ndigits = 2;
} else if (i64(Abs(d) * 10) < 1000) {
ndigits = 1;
} else {
ndigits = 0;
}
}
return os << Prec(d, mode, ndigits);
}
}
template <>
void Out<NFormatPrivate::THumanReadableSize>(IOutputStream& stream, const NFormatPrivate::THumanReadableSize& value) {
ui64 base = value.Format == SF_BYTES ? 1024 : 1000;
ui64 base2 = base * base;
ui64 base3 = base * base2;
ui64 base4 = base * base3;
double v = value.Value;
if (v < 0) {
stream << "-";
v = -v;
}
if (v < base) {
NFormatPrivate::PrintDoubleShortly(stream, v);
} else if (v < base2) {
NFormatPrivate::PrintDoubleShortly(stream, v / (double)base) << 'K';
} else if (v < base3) {
NFormatPrivate::PrintDoubleShortly(stream, v / (double)base2) << 'M';
} else if (v < base4) {
NFormatPrivate::PrintDoubleShortly(stream, v / (double)base3) << 'G';
} else {
NFormatPrivate::PrintDoubleShortly(stream, v / (double)base4) << 'T';
}
if (value.Format == SF_BYTES) {
if (v < base) {
stream << "B";
} else {
stream << "iB";
}
}
}
template <>
void Out<NFormatPrivate::THumanReadableDuration>(IOutputStream& os, const NFormatPrivate::THumanReadableDuration& hr) {
TTempBuf buf;
TMemoryOutput ss(buf.Data(), buf.Size());
do {
ui64 microSeconds = hr.Value.MicroSeconds();
if (microSeconds < 1000) {
ss << microSeconds << "us";
break;
}
if (microSeconds < 1000 * 1000) {
NFormatPrivate::PrintDoubleShortly(ss, (double)microSeconds / 1000.0) << "ms";
break;
}
double seconds = (double)(hr.Value.MilliSeconds()) / 1000.0;
if (seconds < 60) {
NFormatPrivate::PrintDoubleShortly(ss, seconds) << 's';
break;
}
ui64 s = NFormatPrivate::Round(seconds * 1000 + 0.5) / 1000;
ui64 m = s / 60;
s = s % 60;
ui64 h = m / 60;
m = m % 60;
ui64 d = h / 24;
h = h % 24;
ui64 times[] = {d, h, m, s};
char names[] = {'d', 'h', 'm', 's'};
bool first = true;
for (size_t i = 0; i < Y_ARRAY_SIZE(times); ++i) {
if (times[i] > 0) {
if (!first) {
ss << ' ';
}
ss << times[i] << names[i];
first = false;
}
}
} while (false);
size_t written = buf.Size() - ss.Avail();
os.Write(buf.Data(), written);
}
void Time(IOutputStream& l) {
l << millisec();
}
void TimeHumanReadable(IOutputStream& l) {
char timeStr[30];
const time_t t = time(nullptr);
l << ctime_r(&t, timeStr);
}