forked from scylladb/scylladb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
bytes.hh
185 lines (164 loc) · 5.37 KB
/
bytes.hh
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
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
* Copyright (C) 2015-present ScyllaDB
*/
/*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include "seastarx.hh"
#include <fmt/format.h>
#include <seastar/core/sstring.hh>
#include "utils/hashing.hh"
#include <optional>
#include <iosfwd>
#include <functional>
#include <compare>
#include "utils/mutable_view.hh"
#include "utils/simple_hashers.hh"
using bytes = basic_sstring<int8_t, uint32_t, 31, false>;
using bytes_view = std::basic_string_view<int8_t>;
using bytes_mutable_view = basic_mutable_view<bytes_view::value_type>;
using bytes_opt = std::optional<bytes>;
using sstring_view = std::string_view;
inline bytes to_bytes(bytes&& b) {
return std::move(b);
}
inline sstring_view to_sstring_view(bytes_view view) {
return {reinterpret_cast<const char*>(view.data()), view.size()};
}
inline bytes_view to_bytes_view(sstring_view view) {
return {reinterpret_cast<const int8_t*>(view.data()), view.size()};
}
struct fmt_hex {
const bytes_view& v;
fmt_hex(const bytes_view& v) noexcept : v(v) {}
};
std::ostream& operator<<(std::ostream& os, const fmt_hex& hex);
bytes from_hex(sstring_view s);
sstring to_hex(bytes_view b);
sstring to_hex(const bytes& b);
sstring to_hex(const bytes_opt& b);
std::ostream& operator<<(std::ostream& os, const bytes& b);
std::ostream& operator<<(std::ostream& os, const bytes_opt& b);
template <>
struct fmt::formatter<fmt_hex> {
size_t _group_size_in_bytes = 0;
char _delimiter = ' ';
public:
// format_spec := [group_size[delimiter]]
// group_size := a char from '0' to '9'
// delimiter := a char other than '{' or '}'
//
// by default, the given bytes are printed without delimiter, just
// like a string. so a string view of {0x20, 0x01, 0x0d, 0xb8} is
// printed like:
// "20010db8".
//
// but the format specifier can be used to customize how the bytes
// are printed. for instance, to print an bytes_view like IPv6. so
// the format specfier would be "{:2:}", where
// - "2": bytes are printed in groups of 2 bytes
// - ":": each group is delimited by ":"
// and the formatted output will look like:
// "2001:0db8:0000"
//
// or we can mimic how the default format of used by hexdump using
// "{:2 }", where
// - "2": bytes are printed in group of 2 bytes
// - " ": each group is delimited by " "
// and the formatted output will look like:
// "2001 0db8 0000"
//
// or we can just print each bytes and separate them by a dash using
// "{:1-}"
// and the formatted output will look like:
// "20-01-0b-b8-00-00"
constexpr auto parse(fmt::format_parse_context& ctx) {
// get the delimiter if any
auto it = ctx.begin();
auto end = ctx.end();
if (it != end) {
int group_size = *it++ - '0';
if (group_size < 0 ||
static_cast<size_t>(group_size) > sizeof(uint64_t)) {
throw format_error("invalid group_size");
}
_group_size_in_bytes = group_size;
if (it != end) {
// optional delimiter
_delimiter = *it++;
}
}
if (it != end && *it != '}') {
throw format_error("invalid format");
}
return it;
}
template <typename FormatContext>
auto format(const ::fmt_hex& s, FormatContext& ctx) const {
auto out = ctx.out();
const auto& v = s.v;
if (_group_size_in_bytes > 0) {
for (size_t i = 0, size = v.size(); i < size; i++) {
if (i != 0 && i % _group_size_in_bytes == 0) {
fmt::format_to(out, "{}{:02x}", _delimiter, std::byte(v[i]));
} else {
fmt::format_to(out, "{:02x}", std::byte(v[i]));
}
}
} else {
for (auto b : v) {
fmt::format_to(out, "{:02x}", std::byte(b));
}
}
return out;
}
};
template <>
struct fmt::formatter<bytes> : fmt::formatter<fmt_hex> {
template <typename FormatContext>
auto format(const ::bytes& s, FormatContext& ctx) const {
return fmt::formatter<::fmt_hex>::format(::fmt_hex(bytes_view(s)), ctx);
}
};
namespace std {
// Must be in std:: namespace, or ADL fails
std::ostream& operator<<(std::ostream& os, const bytes_view& b);
}
template<>
struct appending_hash<bytes> {
template<typename Hasher>
void operator()(Hasher& h, const bytes& v) const {
feed_hash(h, v.size());
h.update(reinterpret_cast<const char*>(v.cbegin()), v.size() * sizeof(bytes::value_type));
}
};
template<>
struct appending_hash<bytes_view> {
template<typename Hasher>
void operator()(Hasher& h, bytes_view v) const {
feed_hash(h, v.size());
h.update(reinterpret_cast<const char*>(v.begin()), v.size() * sizeof(bytes_view::value_type));
}
};
using bytes_view_hasher = simple_xx_hasher;
namespace std {
template <>
struct hash<bytes_view> {
size_t operator()(bytes_view v) const {
bytes_view_hasher h;
appending_hash<bytes_view>{}(h, v);
return h.finalize();
}
};
} // namespace std
inline std::strong_ordering compare_unsigned(bytes_view v1, bytes_view v2) {
auto size = std::min(v1.size(), v2.size());
if (size) {
auto n = memcmp(v1.begin(), v2.begin(), size);
if (n) {
return n <=> 0;
}
}
return v1.size() <=> v2.size();
}