diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 17:44:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 17:44:55 +0000 |
commit | 5068d34c08f951a7ea6257d305a1627b09a95817 (patch) | |
tree | 08213e2be853396a3b07ce15dbe222644dcd9a89 /src/hist_source.hh | |
parent | Initial commit. (diff) | |
download | lnav-upstream.tar.xz lnav-upstream.zip |
Adding upstream version 0.11.1.upstream/0.11.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | src/hist_source.hh | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/src/hist_source.hh b/src/hist_source.hh new file mode 100644 index 0000000..a6399ad --- /dev/null +++ b/src/hist_source.hh @@ -0,0 +1,408 @@ +/** + * Copyright (c) 2007-2012, Timothy Stack + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Timothy Stack nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @file hist_source.hh + */ + +#ifndef hist_source_hh +#define hist_source_hh + +#include <cmath> +#include <limits> +#include <map> +#include <string> +#include <unordered_map> +#include <vector> + +#include "base/lnav_log.hh" +#include "mapbox/variant.hpp" +#include "strong_int.hh" +#include "textview_curses.hh" + +/** Type for indexes into a group of buckets. */ +STRONG_INT_TYPE(int, bucket_group); + +/** Type used to differentiate values added to the same row in the histogram */ +STRONG_INT_TYPE(int, bucket_type); + +struct stacked_bar_chart_base { + virtual ~stacked_bar_chart_base() = default; + + struct show_none {}; + struct show_all {}; + struct show_one { + size_t so_index; + + explicit show_one(int so_index) : so_index(so_index) {} + }; + + using show_state = mapbox::util::variant<show_none, show_all, show_one>; + + enum class direction { + forward, + backward, + }; +}; + +template<typename T> +class stacked_bar_chart : public stacked_bar_chart_base { +public: + stacked_bar_chart& with_stacking_enabled(bool enabled) + { + this->sbc_do_stacking = enabled; + return *this; + } + + stacked_bar_chart& with_attrs_for_ident(const T& ident, text_attrs attrs) + { + auto& ci = this->find_ident(ident); + ci.ci_attrs = attrs; + return *this; + } + + stacked_bar_chart& with_margins(unsigned long left, unsigned long right) + { + this->sbc_left = left; + this->sbc_right = right; + return *this; + } + + bool attrs_in_use(const text_attrs& attrs) const { + for (const auto& ident : this->sbc_idents) { + if (ident.ci_attrs == attrs) { + return true; + } + } + return false; + } + + show_state show_next_ident(direction dir) + { + bool single_ident = this->sbc_idents.size() == 1; + + if (this->sbc_idents.empty()) { + return this->sbc_show_state; + } + + this->sbc_show_state = this->sbc_show_state.match( + [&](show_none) -> show_state { + switch (dir) { + case direction::forward: + if (single_ident) { + return show_all(); + } + return show_one(0); + case direction::backward: + return show_all(); + } + + return show_all(); + }, + [&](show_one& one) -> show_state { + switch (dir) { + case direction::forward: + if (one.so_index + 1 == this->sbc_idents.size()) { + return show_all(); + } + return show_one(one.so_index + 1); + case direction::backward: + if (one.so_index == 0) { + return show_none(); + } + return show_one(one.so_index - 1); + } + + return show_all(); + }, + [&](show_all) -> show_state { + switch (dir) { + case direction::forward: + return show_none(); + case direction::backward: + if (single_ident) { + return show_none(); + } + return show_one(this->sbc_idents.size() - 1); + } + + return show_all(); + }); + + return this->sbc_show_state; + } + + void get_ident_to_show(T& ident_out) const + { + this->sbc_show_state.match( + [](const show_none) {}, + [](const show_all) {}, + [&](const show_one& one) { + ident_out = this->sbc_idents[one.so_index].ci_ident; + }); + } + + void chart_attrs_for_value(const listview_curses& lc, + int& left, + const T& ident, + double value, + string_attrs_t& value_out) const + { + auto ident_iter = this->sbc_ident_lookup.find(ident); + + require(ident_iter != this->sbc_ident_lookup.end()); + + size_t ident_index = ident_iter->second; + unsigned long width, avail_width; + bucket_stats_t overall_stats; + struct line_range lr; + vis_line_t height; + + lr.lr_unit = line_range::unit::codepoint; + + size_t ident_to_show = this->sbc_show_state.match( + [](const show_none) { return -1; }, + [ident_index](const show_all) { return ident_index; }, + [](const show_one& one) { return one.so_index; }); + + if (ident_to_show != ident_index) { + return; + } + + lc.get_dimensions(height, width); + + for (size_t lpc = 0; lpc < this->sbc_idents.size(); lpc++) { + if (this->sbc_show_state.template is<show_all>() + || lpc == (size_t) ident_to_show) + { + overall_stats.merge(this->sbc_idents[lpc].ci_stats, + this->sbc_do_stacking); + } + } + + if (this->sbc_show_state.template is<show_all>()) { + avail_width = width - this->sbc_idents.size(); + } else { + avail_width = width - 1; + } + avail_width -= this->sbc_left + this->sbc_right; + + lr.lr_start = left; + + const auto& ci = this->sbc_idents[ident_index]; + int amount; + + if (value == 0.0) { + amount = 0; + } else if ((overall_stats.bs_max_value - 0.01) <= value + && value <= (overall_stats.bs_max_value + 0.01)) + { + amount = avail_width; + } else { + double percent + = (value - overall_stats.bs_min_value) / overall_stats.width(); + amount = (int) rint(percent * avail_width); + amount = std::max(1, amount); + } + lr.lr_end = left = lr.lr_start + amount; + + if (!ci.ci_attrs.empty() && !lr.empty()) { + auto rev_attrs = ci.ci_attrs; + rev_attrs.ta_attrs |= A_REVERSE; + value_out.emplace_back(lr, VC_STYLE.value(rev_attrs)); + } + } + + void clear() + { + this->sbc_idents.clear(); + this->sbc_ident_lookup.clear(); + this->sbc_show_state = show_all(); + } + + void add_value(const T& ident, double amount = 1.0) + { + struct chart_ident& ci = this->find_ident(ident); + ci.ci_stats.update(amount); + } + + struct bucket_stats_t { + bucket_stats_t() + : bs_min_value(std::numeric_limits<double>::max()), bs_max_value(0) + { + } + + void merge(const bucket_stats_t& rhs, bool do_stacking) + { + this->bs_min_value = std::min(this->bs_min_value, rhs.bs_min_value); + if (do_stacking) { + this->bs_max_value += rhs.bs_max_value; + } else { + this->bs_max_value + = std::max(this->bs_max_value, rhs.bs_max_value); + } + } + + double width() const + { + return std::fabs(this->bs_max_value - this->bs_min_value); + } + + void update(double value) + { + this->bs_max_value = std::max(this->bs_max_value, value); + this->bs_min_value = std::min(this->bs_min_value, value); + } + + double bs_min_value; + double bs_max_value; + }; + + const bucket_stats_t& get_stats_for(const T& ident) + { + const chart_ident& ci = this->find_ident(ident); + + return ci.ci_stats; + } + +protected: + struct chart_ident { + explicit chart_ident(const T& ident) : ci_ident(ident) {} + + T ci_ident; + text_attrs ci_attrs; + bucket_stats_t ci_stats; + }; + + struct chart_ident& find_ident(const T& ident) + { + auto iter = this->sbc_ident_lookup.find(ident); + if (iter == this->sbc_ident_lookup.end()) { + this->sbc_ident_lookup[ident] = this->sbc_idents.size(); + this->sbc_idents.emplace_back(ident); + return this->sbc_idents.back(); + } + return this->sbc_idents[iter->second]; + } + + bool sbc_do_stacking{true}; + unsigned long sbc_left{0}, sbc_right{0}; + std::vector<struct chart_ident> sbc_idents; + std::unordered_map<T, unsigned int> sbc_ident_lookup; + show_state sbc_show_state{show_all()}; +}; + +class hist_source2 + : public text_sub_source + , public text_time_translator { +public: + typedef enum { + HT_NORMAL, + HT_WARNING, + HT_ERROR, + HT_MARK, + + HT__MAX + } hist_type_t; + + hist_source2() { this->clear(); } + + ~hist_source2() override = default; + + void init(); + + void set_time_slice(int64_t slice) { this->hs_time_slice = slice; } + + int64_t get_time_slice() const { return this->hs_time_slice; } + + size_t text_line_count() override { return this->hs_line_count; } + + size_t text_line_width(textview_curses& curses) override + { + return 48 + 8 * 4; + } + + void clear(); + + void add_value(time_t row, hist_type_t htype, double value = 1.0); + + void end_of_row(); + + void text_value_for_line(textview_curses& tc, + int row, + std::string& value_out, + line_flags_t flags) override; + + void text_attrs_for_line(textview_curses& tc, + int row, + string_attrs_t& value_out) override; + + size_t text_size_for_line(textview_curses& tc, + int row, + line_flags_t flags) override + { + return 0; + } + + nonstd::optional<struct timeval> time_for_row(vis_line_t row) override; + + nonstd::optional<vis_line_t> row_for_time( + struct timeval tv_bucket) override; + +private: + struct hist_value { + double hv_value; + }; + + struct bucket_t { + time_t b_time; + hist_value b_values[HT__MAX]; + }; + + static const int64_t BLOCK_SIZE = 100; + + struct bucket_block { + bucket_block() + { + memset(this->bb_buckets, 0, sizeof(this->bb_buckets)); + } + + unsigned int bb_used{0}; + bucket_t bb_buckets[BLOCK_SIZE]; + }; + + bucket_t& find_bucket(int64_t index); + + int64_t hs_time_slice{10 * 60}; + int64_t hs_line_count; + int64_t hs_last_bucket; + time_t hs_last_row; + std::map<int64_t, struct bucket_block> hs_blocks; + stacked_bar_chart<hist_type_t> hs_chart; +}; + +#endif |