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

tstack / lnav / 25560458570-3031

08 May 2026 02:05PM UTC coverage: 70.162% (+0.08%) from 70.083%
25560458570-3031

push

github

tstack
[stats] compute cardinality for identifiers using hyperloglog

90 of 120 new or added lines in 6 files covered. (75.0%)

7 existing lines in 4 files now uncovered.

57376 of 81777 relevant lines covered (70.16%)

632883.24 hits per line

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

87.1
/src/spectro_source.hh
1
/**
2
 * Copyright (c) 2016, 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
 * @file spectroview_curses.hh
30
 */
31

32
#ifndef spectro_source_hh
33
#define spectro_source_hh
34

35
#include <chrono>
36
#include <functional>
37
#include <memory>
38
#include <unordered_map>
39
#include <vector>
40

41
#include <math.h>
42

43
#include "digestible/digestible.h"
44
#include "hasher.hh"
45
#include "statusview_curses.hh"
46
#include "textview_curses.hh"
47

48
struct exec_context;
49

50
struct spectrogram_bounds {
51
    std::chrono::microseconds sb_begin_time{0};
52
    std::chrono::microseconds sb_end_time{0};
53
    double sb_min_value_out{0.0};
54
    double sb_max_value_out{0.0};
55
    int64_t sb_count{0};
56
    size_t sb_mark_generation{0};
57
    digestible::tdigest<double> sb_tdigest{200};
58
};
59

60
struct spectrogram_thresholds {
61
    int st_thresholds[7]{};
62
};
63

64
struct spectrogram_request {
65
    explicit spectrogram_request(spectrogram_bounds& sb) : sr_bounds(sb) {}
186✔
66

67
    spectrogram_bounds& sr_bounds;
68
    unsigned long sr_width{0};
69
    std::chrono::microseconds sr_begin_time{0};
62✔
70
    std::chrono::microseconds sr_end_time{0};
62✔
71
    double sr_column_size{0};
72
};
73

74
struct spectrogram_row {
75
    spectrogram_row() = default;
8✔
76
    spectrogram_row(const spectrogram_row&) = delete;
77

78
    struct row_bucket {
79
        int rb_counter{0};
80
        int rb_marks{0};
81
    };
82

83
    enum class value_type : uint8_t {
84
        integer,
85
        real,
86
    };
87

88
    std::vector<row_bucket> sr_values;
89
    value_type sr_value_type{value_type::integer};
90
    unsigned long sr_width{0};
91
    double sr_column_size{0.0};
92
    std::function<std::unique_ptr<text_sub_source>(
93
        const spectrogram_request&, double range_min, double range_max)>
94
        sr_details_source_provider;
95
    digestible::tdigest<double> sr_tdigest{200};
96
    int sr_thresholds[7]{};
97

98
    void add_value(spectrogram_request& sr,
227✔
99
                   value_type vt,
100
                   double value,
101
                   bool marked)
102
    {
103
        if (vt != value_type::integer) {
227✔
104
            this->sr_value_type = vt;
9✔
105
        }
106
        long index = std::floor((value - sr.sr_bounds.sb_min_value_out)
227✔
107
                                / sr.sr_column_size);
227✔
108

109
        this->sr_values[index].rb_counter += 1;
227✔
110
        if (marked) {
227✔
111
            this->sr_values[index].rb_marks += 1;
2✔
112
        }
113
    }
227✔
114

115
    std::optional<size_t> nearest_column(size_t current) const;
116
};
117

118
class spectrogram_value_source {
119
public:
120
    virtual ~spectrogram_value_source() = default;
11✔
121

122
    virtual void spectro_bounds(spectrogram_bounds& sb_out) = 0;
123

124
    virtual void spectro_row(spectrogram_request& sr, spectrogram_row& row_out)
125
        = 0;
126

127
    virtual bool spectro_is_marked(spectrogram_request& sr) { return false; }
1✔
128

129
    enum class mark_op_t {
130
        add,
131
        clear,
132
    };
133

134
    virtual void spectro_mark(textview_curses& tc,
135
                              std::chrono::microseconds begin_time,
136
                              std::chrono::microseconds end_time,
137
                              double range_min,
138
                              double range_max,
139
                              mark_op_t op)
140
        = 0;
141

142
    // Optional unit suffix for the spectrogram's value axis.  Used by
143
    // the header renderer to humanize Min/Max (e.g. "1.2 MB" instead
144
    // of "1258291") when the source column declares a unit.  Empty
145
    // string means "no humanization — render raw number".
146
    //
147
    // Sources are responsible for emitting bounds and row values in
148
    // the same units the suffix describes — any divisor between the
149
    // raw column representation and the display unit is applied
150
    // inside the source, not here.
UNCOV
151
    virtual std::string spectro_value_suffix() const { return {}; }
×
152
};
153

154
class spectrogram_source
155
    : public text_sub_source
156
    , public text_time_translator
157
    , public list_overlay_source
158
    , public text_delegate {
159
public:
160
    ~spectrogram_source() override = default;
1,618✔
161

162
    bool empty() const override { return this->ss_details_source == nullptr; }
×
163

164
    void register_view(textview_curses* tc) override
809✔
165
    {
166
        text_sub_source::register_view(tc);
809✔
167
        tc->tc_mark_style = std::nullopt;
809✔
168
    }
809✔
169

170
    void invalidate()
19✔
171
    {
172
        this->ss_cached_bounds.sb_count = 0;
19✔
173
        this->ss_cached_bounds.sb_mark_generation = 0;
19✔
174
        this->ss_row_cache.clear();
19✔
175
        this->ss_cursor_column = std::nullopt;
19✔
176
        this->ss_cursor_details_checksum.clear();
19✔
177
    }
19✔
178

179
    bool list_input_handle_key(listview_curses& lv, const ncinput& ch) override;
180

181
    bool text_handle_mouse(textview_curses& tc,
182
                           const listview_curses::display_line_content_t&,
183
                           mouse_event& me) override;
184

185
    bool list_static_overlay(const listview_curses& lv,
186
                             media_t media,
187
                             int y,
188
                             int bottom,
189
                             attr_line_t& value_out) override;
190

191
    void list_value_for_overlay(const listview_curses& lv,
192
                                vis_line_t row,
193
                                std::vector<attr_line_t>& value_out) override;
194

195
    size_t text_line_count() override;
196

197
    size_t text_line_width(textview_curses& tc) override;
198

199
    size_t text_size_for_line(textview_curses& tc,
×
200
                              int row,
201
                              line_flags_t flags) override
202
    {
203
        return 0;
×
204
    }
205

206
    bool text_is_row_selectable(textview_curses& tc, vis_line_t row) override;
207

208
    void text_selection_changed(textview_curses& tc) override;
209

210
    std::optional<row_info> time_for_row(vis_line_t row) override;
211

212
    std::optional<vis_line_t> row_for_time(struct timeval time_bucket) override;
213

214
    line_info text_value_for_line(textview_curses& tc,
215
                                  int row,
216
                                  std::string& value_out,
217
                                  line_flags_t flags) override;
218

219
    void text_attrs_for_line(textview_curses& tc,
220
                             int row,
221
                             string_attrs_t& value_out) override;
222

223
    void chart_attrs_for_line(textview_curses& tc,
224
                              int row,
225
                              string_attrs_t& value_out);
226

227
    void text_mark(const bookmark_type_t* bm,
228
                   vis_line_t line,
229
                   bool added) override;
230

231
    void cache_bounds();
232

233
    std::optional<row_info> time_for_row_int(vis_line_t row);
234

235
    const spectrogram_row& load_row(const listview_curses& lv, int row);
236

237
    void reset_details_source();
238

239
    Result<std::string, lnav::console::user_message> text_reload_data(
240
        exec_context& ec) override;
241

242
    textview_curses* ss_details_view{nullptr};
243
    text_sub_source* ss_no_details_source{nullptr};
244
    exec_context* ss_exec_context{nullptr};
245
    std::unique_ptr<text_sub_source> ss_details_source;
246
    spectrogram_value_source* ss_value_source{nullptr};
247
    spectrogram_bounds ss_cached_bounds;
248
    size_t ss_cached_line_count{0};
249
    std::unordered_map<std::chrono::microseconds,
250
                       spectrogram_row,
251
                       lnav::duration_hasher>
252
        ss_row_cache;
253
    std::optional<size_t> ss_cursor_column;
254
    attr_line_t ss_cursor_details;
255
    hasher::array_t ss_cursor_details_checksum;
256
};
257

258
class spectro_status_source : public status_data_source {
259
public:
260
    enum field_t {
261
        F_TITLE,
262
        F_HELP,
263

264
        F_MAX
265
    };
266

267
    spectro_status_source();
268

269
    size_t statusview_fields() override;
270

271
    status_field& statusview_value_for_field(int field) override;
272

273
private:
274
    status_field sss_fields[F_MAX];
275
};
276

277
#endif
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