• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tstack / lnav / 20284884426-2753

16 Dec 2025 10:23PM UTC coverage: 68.23% (-0.7%) from 68.903%
20284884426-2753

push

github

tstack
[log] show invalid utf hex dump in log view too

25 of 25 new or added lines in 2 files covered. (100.0%)

503 existing lines in 33 files now uncovered.

51170 of 74996 relevant lines covered (68.23%)

433797.6 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.52
/src/base/humanize.cc
1
/**
2
 * Copyright (c) 2019, Timothy Stack
3
 *
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *
9
 * * Redistributions of source code must retain the above copyright notice, this
10
 * list of conditions and the following disclaimer.
11
 * * Redistributions in binary form must reproduce the above copyright notice,
12
 * this list of conditions and the following disclaimer in the documentation
13
 * and/or other materials provided with the distribution.
14
 * * Neither the name of Timothy Stack nor the names of its contributors
15
 * may be used to endorse or promote products derived from this software
16
 * without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29

30
#include <array>
31
#include <cmath>
32

33
#include "humanize.hh"
34

35
#include <lnav_log.hh>
36

37
#include "config.h"
38
#include "fmt/format.h"
39
#include "pcrepp/pcre2pp.hh"
40
#include "scn/scan.h"
41

42
namespace humanize {
43

44
template<>
45
std::optional<double>
46
try_from(const string_fragment& sf)
1,524✔
47
{
48
    enum capture_item {
49
        all,
50
        integer,
51
        real,
52
        file_size,
53
        secs,
54
        hms,
55
        ms,
56
    };
57

58
    static const auto code = lnav::pcre2pp::code::from_const(
59
        R"(^\s*(?:([\-\+]?\d+)|([\-\+]?\d+\.\d+(?:[eE][\-\+]\d+)?)|([\-\+]?\d+(?:\.\d+)?\s*[KMGTPE]?[Bb](?:ps)?)|([\-\+]?\d+(?:\.\d+)?\s*[munpf]?)s|(\d{1,2}:\d{2}:\d{2}(?:\.\d{1,6})?)|(\d{1,2}:\d{2}(?:\.\d{1,6})?))\s*$)");
1,524✔
60
    thread_local auto md = lnav::pcre2pp::match_data::unitialized();
1,524✔
61

62
    if (!code.capture_from(sf).into(md).found_p()) {
1,524✔
63
        return std::nullopt;
187✔
64
    }
65

66
    if (md[integer]) {
1,337✔
67
        auto scan_res = scn::scan_value<int64_t>(md[integer]->to_string_view());
57✔
68

69
        return scan_res->value();
57✔
70
    }
71

72
    if (md[real]) {
1,280✔
73
        auto scan_res = scn::scan_value<double>(md[real]->to_string_view());
142✔
74

75
        return scan_res->value();
142✔
76
    }
77

78
    if (md[file_size]) {
1,138✔
79
        auto scan_res
80
            = scn::scan_value<double>(md[file_size]->to_string_view());
1,018✔
81
        const auto unit_range = scan_res->range();
1,018✔
82
        auto retval = scan_res->value();
1,018✔
83

84
        if (unit_range.size() >= 2) {
1,018✔
85
            size_t start = 0;
1,017✔
86
            while (isspace(unit_range[start])) {
1,672✔
87
                start += 1;
655✔
88
            }
89
            switch (unit_range[start]) {
1,017✔
90
                case 'E':
×
91
                    retval *= 1024.0;
×
92
                case 'P':
×
93
                    retval *= 1024.0;
×
94
                case 'T':
×
95
                    retval *= 1024.0;
×
96
                case 'G':
3✔
97
                    retval *= 1024.0;
3✔
98
                case 'M':
43✔
99
                    retval *= 1024.0;
43✔
100
                case 'K':
631✔
101
                    retval *= 1024.0;
631✔
102
                    break;
631✔
103
            }
104
        }
105
        return retval;
1,018✔
106
    }
107

108
    if (md[secs]) {
120✔
109
        auto scan_res = scn::scan_value<double>(md[secs]->to_string_view());
11✔
110
        const auto unit_range = scan_res->range();
11✔
111
        auto retval = scan_res->value();
11✔
112

113
        if (!unit_range.empty()) {
11✔
114
            size_t start = 0;
8✔
115
            while (isspace(unit_range[start])) {
9✔
116
                start += 1;
1✔
117
            }
118
            switch (unit_range[start]) {
8✔
119
                case 'f':
×
120
                    retval /= 1000.0;
×
121
                case 'p':
×
122
                    retval /= 1000.0;
×
123
                case 'n':
×
124
                    retval /= 1000.0;
×
125
                case 'u':
3✔
126
                    retval /= 1000.0;
3✔
127
                case 'm':
8✔
128
                    retval /= 1000.0;
8✔
129
                    break;
8✔
130
            }
131
        }
132

133
        return retval;
11✔
134
    }
135

136
    if (md[hms]) {
109✔
137
        auto scan_res = scn::scan<int, int, double>(md[hms]->to_string_view(),
46✔
138
                                                    "{}:{}:{}");
92✔
139
        auto [hours, mins, secs] = scan_res->values();
46✔
140

141
        return hours * 3600.0 + mins * 60.0 + secs;
46✔
142
    }
143

144
    if (md[ms]) {
63✔
145
        auto scan_res
146
            = scn::scan<int, double>(md[ms]->to_string_view(), "{}:{}");
63✔
147
        auto [mins, secs] = scan_res->values();
63✔
148

149
        return mins * 60.0 + secs;
63✔
150
    }
151

152
    return std::nullopt;
×
153
}
154

155
std::string
156
file_size(file_ssize_t value, alignment align)
289✔
157
{
158
    static const double LN1024 = std::log(1024.0);
159
    static constexpr std::array<const char*, 7> UNITS = {
160
        " ",
161
        "K",
162
        "M",
163
        "G",
164
        "T",
165
        "P",
166
        "E",
167
    };
168

169
    if (value < 0) {
289✔
170
        return "Unknown";
2✔
171
    }
172

173
    if (value == 0) {
288✔
174
        switch (align) {
25✔
UNCOV
175
            case alignment::none:
×
UNCOV
176
                return "0B";
×
177
            case alignment::columnar:
25✔
178
                return "0.0 B";
50✔
179
        }
180
    }
181

182
    const auto exp
183
        = floor(std::min(log(value) / LN1024, (double) (UNITS.size() - 1)));
263✔
184
    const auto divisor = pow(1024, exp);
263✔
185

186
    if (align == alignment::none && divisor <= 1) {
263✔
187
        return fmt::format(FMT_STRING("{}B"), value, UNITS[exp]);
4✔
188
    }
189
    return fmt::format(FMT_STRING("{:.1f}{}B"),
524✔
190
                       divisor == 0 ? value : value / divisor,
262✔
191
                       UNITS[exp]);
524✔
192
}
193

194
const std::string&
195
sparkline(double value, std::optional<double> upper_opt)
848✔
196
{
197
    static const std::string ZERO = " ";
882✔
198
    static const std::string BARS[] = {
199
        "\u2581",
200
        "\u2582",
201
        "\u2583",
202
        "\u2584",
203
        "\u2585",
204
        "\u2586",
205
        "\u2587",
206
        "\u2588",
207
    };
882✔
208
    static constexpr double BARS_COUNT = std::distance(begin(BARS), end(BARS));
209

210
    if (value <= 0.0) {
848✔
211
        return ZERO;
766✔
212
    }
213

214
    const auto upper = upper_opt.value_or(100.0);
82✔
215
    if (value >= upper) {
82✔
216
        return BARS[(size_t) BARS_COUNT - 1];
17✔
217
    }
218

219
    const size_t index = ceil((value / upper) * BARS_COUNT) - 1;
65✔
220

221
    return BARS[index];
65✔
222
}
223

224
}  // namespace humanize
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc