aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/clickhouse/src/Common/UnicodeBar.cpp
blob: 253d720e8a1ce31903ca2072706caf801468282e (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
#include <cstring>
#include <cmath>
#include <string>
#include <base/types.h>
#include <base/arithmeticOverflow.h>
#include <Common/Exception.h>
#include <Common/UnicodeBar.h>
#include <Common/NaNUtils.h>


namespace DB
{
    namespace ErrorCodes
    {
        extern const int LOGICAL_ERROR;
    }
}

namespace UnicodeBar
{
    double getWidth(double x, double min, double max, double max_width)
    {
        if (isNaN(x) || isNaN(min) || isNaN(max))
            return 0;

        if (x <= min)
            return 0;

        if (x >= max)
            return max_width;

        return (x - min) / (max - min) * max_width;
    }

    namespace
    {
        /// We use the following Unicode characters to draw the bar:
        /// U+2588 "█" Full block
        /// U+2589 "▉" Left seven eighths block
        /// U+258A "▊" Left three quarters block
        /// U+258B "▋" Left five eighths block
        /// U+258C "▌" Left half block
        /// U+258D "▍" Left three eighths block
        /// U+258E "▎" Left one quarter block
        /// U+258F "▏" Left one eighth block
        constexpr size_t GRADES_IN_FULL_BAR = 8;
        constexpr char FULL_BAR[] = "█";
        constexpr char FRACTIONAL_BARS[] = "▏▎▍▌▋▊▉";   /// 7 elements: 1/8, 2/8, 3/8, 4/8, 5/8, 6/8, 7/8
    }

    size_t getWidthInBytes(double width)
    {
        Int64 int_width = static_cast<Int64>(width * GRADES_IN_FULL_BAR);
        return (int_width / GRADES_IN_FULL_BAR) * UNICODE_BAR_CHAR_SIZE + (int_width % GRADES_IN_FULL_BAR ? UNICODE_BAR_CHAR_SIZE : 0);
    }

    static char* checkedCopy(const char * src, size_t src_size, char * dst, const char * dst_end)
    {
        if (dst + src_size > dst_end)
            throw DB::Exception(
                DB::ErrorCodes::LOGICAL_ERROR,
                "Not enough space in buffer for UnicodeBar::render, required: {}, got: {}",
                src_size, dst_end - dst);

        memcpy(dst, src, src_size);
        return dst + src_size;
    }

    void render(double width, char * dst, const char * dst_end)
    {
        Int64 int_width = static_cast<Int64>(width * GRADES_IN_FULL_BAR);
        size_t floor_width = (int_width / GRADES_IN_FULL_BAR);

        for (size_t i = 0; i < floor_width; ++i)
        {
            dst = checkedCopy(FULL_BAR, UNICODE_BAR_CHAR_SIZE, dst, dst_end);
        }

        size_t remainder = int_width % GRADES_IN_FULL_BAR;

        if (remainder)
        {
            dst = checkedCopy(&FRACTIONAL_BARS[(remainder - 1) * UNICODE_BAR_CHAR_SIZE], UNICODE_BAR_CHAR_SIZE, dst, dst_end);
        }

        checkedCopy("\0", 1, dst, dst_end);
    }

    std::string render(double width)
    {
        std::string res(getWidthInBytes(width) + 1, '\0');
        render(width, res.data(), res.data() + res.size());
        return res;
    }
}