aboutsummaryrefslogtreecommitdiffstats
path: root/util/stream/format.cpp
blob: 3996130df5d00c5bf2aae366567bda6a53658248 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#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);
}