My Project 3.7.7
C++ Distributed Hash Table
Loading...
Searching...
No Matches
infohash.h
1// Copyright (c) 2014-2026 Savoir-faire Linux Inc.
2// SPDX-License-Identifier: MIT
3#pragma once
4
5#include "def.h"
6#include "rng.h"
7
8#include <msgpack.hpp>
9#include <fmt/core.h>
10
11#ifndef _WIN32
12#include <netinet/in.h>
13#include <netdb.h>
14#ifdef __ANDROID__
15typedef uint16_t in_port_t;
16#endif
17#else
18#include <iso646.h>
19#include <ws2tcpip.h>
20typedef uint16_t sa_family_t;
21typedef uint16_t in_port_t;
22#endif
23
24#include <iostream>
25#include <iomanip>
26#include <array>
27#include <vector>
28#include <string_view>
29#include <algorithm>
30#include <stdexcept>
31#include <sstream>
32
33#include <cstring>
34#include <cstddef>
35
36namespace dht {
37
38using byte = uint8_t;
39
40namespace crypto {
41OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length);
42}
43
48template<size_t N>
49class Hash
50{
51public:
52 using T = std::array<uint8_t, N>;
53 typedef typename T::iterator iterator;
54 typedef typename T::const_iterator const_iterator;
55
56 constexpr Hash() noexcept
57 : data_ {}
58 {}
59 constexpr Hash(const uint8_t* h, size_t data_len)
60 : data_(fromBytes(h, data_len))
61 {}
67 explicit constexpr Hash(std::string_view hex)
68 : data_(fromString(hex))
69 {}
70
71 Hash(const msgpack::object& o) { msgpack_unpack(o); }
72
73 static constexpr size_t size() noexcept { return N; }
74 constexpr const uint8_t* data() const { return data_.data(); }
75 uint8_t* data() { return data_.data(); }
76 iterator begin() { return data_.begin(); }
77 const_iterator cbegin() const { return data_.cbegin(); }
78 iterator end() { return data_.end(); }
79 const_iterator cend() const { return data_.cend(); }
80
81 static constexpr inline Hash zero() noexcept { return Hash {}; }
82
83 constexpr bool operator==(const Hash& h) const
84 {
85#if __cplusplus >= 202002L
86 return data_ == h.data_;
87#else
88 for (size_t i = 0; i < N; ++i) {
89 if (data_[i] != h.data_[i])
90 return false;
91 }
92 return true;
93#endif
94 }
95 constexpr bool operator!=(const Hash& h) const { return !(*this == h); }
96
97 constexpr bool operator<(const Hash& o) const
98 {
99 for (unsigned i = 0; i < N; i++) {
100 if (data_[i] != o.data_[i])
101 return data_[i] < o.data_[i];
102 }
103 return false;
104 }
105
106 constexpr Hash operator^(const Hash& o) const
107 {
108 Hash result;
109 for (auto i = 0u; i < N; i++) {
110 result[i] = data_[i] ^ o.data_[i];
111 }
112 return result;
113 }
114
115 explicit constexpr operator bool() const
116 {
117#if __cplusplus >= 202002L
118 auto a = reinterpret_cast<const uint32_t*>(data_.data());
119 auto b = reinterpret_cast<const uint32_t*>(data_.data() + N);
120 for (; a != b; a++) {
121 if (*a)
122 return true;
123 }
124 return false;
125#else
126 for (size_t i = 0; i < N; ++i) {
127 if (data_[i] != 0)
128 return true;
129 }
130 return false;
131#endif
132 }
133
134 uint8_t& operator[](size_t index) { return data_[index]; }
135 constexpr const uint8_t& operator[](size_t index) const { return data_[index]; }
136
141 constexpr int lowbit() const
142 {
143 int i = N - 1;
144 for (; i >= 0; i--)
145 if (data_[i] != 0)
146 break;
147 if (i < 0)
148 return -1;
149 int j = 7;
150 for (; j >= 0; j--)
151 if ((data_[i] & (0x80 >> j)) != 0)
152 break;
153 return 8 * i + j;
154 }
155
156 static inline int cmp(const Hash& id1, const Hash& id2)
157 {
158 return std::memcmp(id1.data_.data(), id2.data_.data(), N);
159 }
160
162 static constexpr inline unsigned commonBits(const Hash& id1, const Hash& id2)
163 {
164 unsigned i = 0;
165 for (; i < N; i++) {
166 if (id1.data_[i] != id2.data_[i])
167 break;
168 }
169
170 if (i == N)
171 return 8 * N;
172
173 uint8_t x = id1.data_[i] ^ id2.data_[i];
174
175 unsigned j = 0;
176 while ((x & 0x80) == 0) {
177 x <<= 1;
178 j++;
179 }
180
181 return 8 * i + j;
182 }
183
185 constexpr int xorCmp(const Hash& id1, const Hash& id2) const
186 {
187 for (unsigned i = 0; i < N; i++) {
188 if (id1.data_[i] == id2.data_[i])
189 continue;
190 uint8_t xor1 = id1.data_[i] ^ data_[i];
191 uint8_t xor2 = id2.data_[i] ^ data_[i];
192 return (xor1 < xor2) ? -1 : 1;
193 }
194 return 0;
195 }
196
197 constexpr bool getBit(unsigned nbit) const
198 {
199 auto& num = *(data_.cbegin() + (nbit / 8));
200 unsigned bit = 7 - (nbit % 8);
201 return (num >> bit) & 1;
202 }
203
204 void setBit(unsigned nbit, bool b)
205 {
206 auto& num = data_[nbit / 8];
207 unsigned bit = 7 - (nbit % 8);
208 num ^= (-b ^ num) & (1 << bit);
209 }
210
211 double toFloat() const
212 {
213 using D = size_t;
214 double v = 0.;
215 for (size_t i = 0; i < std::min<size_t>(N, sizeof(D) - 1); i++)
216 v += *(data_.cbegin() + i) / (double) ((D) 1 << 8 * (i + 1));
217 return v;
218 }
219
220 static inline Hash get(std::string_view data) { return get((const uint8_t*) data.data(), data.size()); }
221
222 static inline Hash get(const std::vector<uint8_t>& data) { return get(data.data(), data.size()); }
223
224 template<size_t H>
225 static Hash get(const Hash<H>& o)
226 {
227 return get(o.data(), o.size());
228 }
229
233 static Hash get(const uint8_t* data, size_t data_len)
234 {
235 Hash ret;
236 crypto::hash(data, data_len, ret.data(), N);
237 return ret;
238 }
239
240 static Hash getRandom();
241
242 template<typename Rd>
243 static Hash getRandom(Rd&);
244
245 template<size_t M>
246 friend std::ostream& operator<<(std::ostream& s, const Hash<M>& h);
247
248 template<size_t M>
249 friend std::istream& operator>>(std::istream& s, Hash<M>& h);
250
252 std::string_view to_view() const { return std::string_view(to_c_str(), N * 2); }
253 const char* to_c_str() const;
254
255 std::string toString() const;
256
257 template<typename Packer>
258 void msgpack_pack(Packer& pk) const
259 {
260 pk.pack_bin(N);
261 pk.pack_bin_body((char*) data_.data(), N);
262 }
263
264 void msgpack_unpack(msgpack::object o)
265 {
266 if (o.type != msgpack::type::BIN or o.via.bin.size != N)
267 throw msgpack::type_error();
268 std::copy_n(o.via.bin.ptr, N, data_.data());
269 }
270
271private:
272 T data_;
273 static constexpr T fromBytes(const uint8_t*, size_t);
274 static constexpr T fromString(std::string_view);
275};
276
277#define HASH_LEN 20u
278using InfoHash = Hash<HASH_LEN>;
279using h256 = Hash<32>;
280using PkId = h256;
281
282template<size_t N>
283std::ostream&
284operator<<(std::ostream& s, const Hash<N>& h)
285{
286 s.write(h.to_c_str(), N * 2);
287 return s;
288}
289
290template<size_t N>
291std::istream&
292operator>>(std::istream& s, Hash<N>& h)
293{
294 std::array<char, h.size() * 2> dat;
295 s.exceptions(std::istream::eofbit | std::istream::failbit);
296 s.read(&(*dat.begin()), dat.size());
297 h = Hash<N>(std::string_view(dat.data(), dat.size()));
298 return s;
299}
300
301template<size_t N>
302constexpr typename Hash<N>::T
303Hash<N>::fromBytes(const uint8_t* in, size_t size)
304{
305 T data {};
306 if (size < N)
307 return data;
308 for (size_t i = 0; i < N; ++i)
309 data[i] = in[i];
310 return data;
311}
312
313template<size_t N>
314constexpr typename Hash<N>::T
315Hash<N>::fromString(std::string_view in)
316{
317 T data {};
318 if (in.size() < 2 * N)
319 return data;
320
321 constexpr auto hex2bin = [](char c, bool& valid) -> uint8_t {
322 if (c >= 'a' and c <= 'f')
323 return 10 + c - 'a';
324 else if (c >= 'A' and c <= 'F')
325 return 10 + c - 'A';
326 else if (c >= '0' and c <= '9')
327 return c - '0';
328 else {
329 valid = false;
330 return 0;
331 }
332 };
333 bool valid = true;
334 for (size_t i = 0; i < N && valid; i++) {
335 uint8_t high = hex2bin(in[2 * i], valid);
336 uint8_t low = hex2bin(in[2 * i + 1], valid);
337 data[i] = (high << 4) | low;
338 }
339 if (!valid)
340 return {};
341 return data;
342}
343
344template<size_t N>
346Hash<N>::getRandom()
347{
348 Hash h;
349 std::random_device rdev;
350 std::uniform_int_distribution<uint32_t> rand_int;
351 auto a = reinterpret_cast<uint32_t*>(h.data());
352 auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
353 std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
354 return h;
355}
356
357template<size_t N>
358template<typename Rd>
360Hash<N>::getRandom(Rd& rdev)
361{
362 Hash h;
363 std::uniform_int_distribution<uint32_t> rand_int;
364 auto a = reinterpret_cast<uint32_t*>(h.data());
365 auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
366 std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
367 return h;
368}
369
370struct alignas(std::max_align_t) HexMap : public std::array<std::array<char, 2>, 256>
371{
372 constexpr HexMap()
373 : std::array<std::array<char, 2>, 256>()
374 {
375 for (size_t i = 0; i < size(); i++) {
376 auto& e = (*this)[i];
377 e[0] = hex_digits[(i >> 4) & 0x0F];
378 e[1] = hex_digits[i & 0x0F];
379 }
380 }
381
382private:
383 static constexpr const char* hex_digits = "0123456789abcdef";
384};
385
386inline constexpr HexMap hex_map {};
387
388inline std::string
389toHex(const uint8_t* data, size_t size)
390{
391 std::string ret(size * 2, '\0');
392 for (size_t i = 0; i < size; i++) {
393 auto b = ret.data() + i * 2;
394 const auto& m = hex_map[data[i]];
395 *((uint16_t*) b) = *((uint16_t*) &m);
396 }
397 return ret;
398}
399
400inline std::string
401toHex(const std::vector<uint8_t>& data)
402{
403 return toHex(data.data(), data.size());
404}
405
406template<size_t N>
407constexpr std::array<char, N * 2>
408toHexArray(const Hash<N>& h)
409{
410 std::array<char, N * 2> ret {};
411 for (size_t i = 0; i < N; i++) {
412 const auto& m = hex_map[h[i]];
413 ret[i * 2] = m[0];
414 ret[i * 2 + 1] = m[1];
415 }
416 return ret;
417}
418
419template<size_t N>
420const char*
421Hash<N>::to_c_str() const
422{
423 alignas(std::max_align_t) thread_local std::array<char, N * 2 + 1> buf;
424 for (size_t i = 0; i < N; i++) {
425 auto b = buf.data() + i * 2;
426 const auto& m = hex_map[data_[i]];
427 *((uint16_t*) b) = *((uint16_t*) &m);
428 }
429 buf[N * 2] = '\0';
430 return buf.data();
431}
432
433template<size_t N>
434std::string
435Hash<N>::toString() const
436{
437 return std::string(to_c_str(), N * 2);
438}
439
440} // namespace dht
441
442template<size_t N>
443struct fmt::formatter<dht::Hash<N>> : formatter<string_view>
444{
445 constexpr auto format(const dht::Hash<N>& c, format_context& ctx) const
446 {
447 return formatter<string_view>::format(c.to_view(), ctx);
448 }
449};
constexpr int lowbit() const
Definition infohash.h:141
static Hash get(const uint8_t *data, size_t data_len)
Definition infohash.h:233
static constexpr unsigned commonBits(const Hash &id1, const Hash &id2)
Definition infohash.h:162
constexpr Hash(std::string_view hex)
Definition infohash.h:67
constexpr int xorCmp(const Hash &id1, const Hash &id2) const
Definition infohash.h:185
std::string_view to_view() const
Definition infohash.h:252
OPENDHT_PUBLIC Blob hash(const Blob &data, size_t hash_length=512/8)