summaryrefslogtreecommitdiff
path: root/matching/include/spdlog/fmt/bin_to_hex.h
blob: 3523380204e096681830ae22993fd19718976eee (plain)
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//

#pragma once

//
// Support for logging binary data as hex
// format flags:
// {:X} - print in uppercase.
// {:s} - don't separate each byte with space.
// {:p} - don't print the position on each line start.
// {:n} - don't split the output to lines.

//
// Examples:
//
// std::vector<char> v(200, 0x0b);
// logger->info("Some buffer {}", spdlog::to_hex(v));
// char buf[128];
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));

namespace spdlog {
namespace details {

template<typename It>
class bytes_range
{
public:
    bytes_range(It range_begin, It range_end)
        : begin_(range_begin)
        , end_(range_end)
    {
    }

    It begin() const
    {
        return begin_;
    }
    It end() const
    {
        return end_;
    }

private:
    It begin_, end_;
};
} // namespace details

// create a bytes_range that wraps the given container
template<typename Container>
inline details::bytes_range<typename Container::const_iterator> to_hex(const Container &container)
{
    static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
    using Iter = typename Container::const_iterator;
    return details::bytes_range<Iter>(std::begin(container), std::end(container));
}

// create bytes_range from ranges
template<typename It>
inline details::bytes_range<It> to_hex(const It range_begin, const It range_end)
{
    return details::bytes_range<It>(range_begin, range_end);
}

} // namespace spdlog

namespace fmt {

template<typename T>
struct formatter<spdlog::details::bytes_range<T>>
{
    const std::size_t line_size = 100;
    const char delimiter = ' ';

    bool put_newlines = true;
    bool put_delimiters = true;
    bool use_uppercase = false;
    bool put_positions = true; // position on start of each line

    // parse the format string flags
    template<typename ParseContext>
    auto parse(ParseContext &ctx) -> decltype(ctx.begin())
    {
        auto it = ctx.begin();
        while (*it && *it != '}')
        {
            switch (*it)
            {
            case 'X':
                use_uppercase = true;
                break;
            case 's':
                put_delimiters = false;
                break;
            case 'p':
                put_positions = false;
                break;
            case 'n':
                put_newlines = false;
                break;
            }

            ++it;
        }
        return it;
    }

    // format the given bytes range as hex
    template<typename FormatContext, typename Container>
    auto format(const spdlog::details::bytes_range<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
    {
        SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
        SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
        const char *hex_chars = use_uppercase ? hex_upper : hex_lower;

        std::size_t pos = 0;
        std::size_t column = line_size;
        auto inserter = ctx.begin();

        for (auto &item : the_range)
        {
            auto ch = static_cast<unsigned char>(item);
            pos++;

            if (put_newlines && column >= line_size)
            {
                column = put_newline(inserter, pos);

                // put first byte without delimiter in front of it
                *inserter++ = hex_chars[(ch >> 4) & 0x0f];
                *inserter++ = hex_chars[ch & 0x0f];
                column += 2;
                continue;
            }

            if (put_delimiters)
            {
                *inserter++ = delimiter;
                ++column;
            }

            *inserter++ = hex_chars[(ch >> 4) & 0x0f];
            *inserter++ = hex_chars[ch & 0x0f];
            column += 2;
        }
        return inserter;
    }

    // put newline(and position header)
    // return the next column
    template<typename It>
    std::size_t put_newline(It inserter, std::size_t pos)
    {
#ifdef _WIN32
        *inserter++ = '\r';
#endif
        *inserter++ = '\n';

        if (put_positions)
        {
            fmt::format_to(inserter, "{:<04X}: ", pos - 1);
            return 7;
        }
        else
        {
            return 1;
        }
    }
};
} // namespace fmt