My Project 3.7.1
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
49template<size_t N>
50class Hash
51{
52public:
53 using T = std::array<uint8_t, N>;
54 typedef typename T::iterator iterator;
55 typedef typename T::const_iterator const_iterator;
56
57 Hash() noexcept { data_.fill(0); }
58 Hash(const uint8_t* h, size_t data_len)
59 {
60 if (data_len < N)
61 data_.fill(0);
62 else
63 std::copy_n(h, N, data_.begin());
64 }
70 explicit Hash(std::string_view hex)
71 {
72 if (hex.size() < 2 * N)
73 data_.fill(0);
74 else
75 fromString(hex.data());
76 }
77
78 Hash(const msgpack::object& o) { msgpack_unpack(o); }
79
80 static constexpr size_t size() noexcept { return N; }
81 const uint8_t* data() const { return data_.data(); }
82 uint8_t* data() { return data_.data(); }
83 iterator begin() { return data_.begin(); }
84 const_iterator cbegin() const { return data_.cbegin(); }
85 iterator end() { return data_.end(); }
86 const_iterator cend() const { return data_.cend(); }
87
88 static constexpr inline Hash zero() noexcept { return Hash {}; }
89
90 bool operator==(const Hash& h) const { return data_ == h.data_; }
91 bool operator!=(const Hash& h) const { return !(*this == h); }
92
93 bool operator<(const Hash& o) const
94 {
95 for (unsigned i = 0; i < N; i++) {
96 if (data_[i] != o.data_[i])
97 return data_[i] < o.data_[i];
98 }
99 return false;
100 }
101
102 Hash operator^(const Hash& o) const
103 {
104 Hash result;
105 for (auto i = 0u; i < N; i++) {
106 result[i] = data_[i] ^ o.data_[i];
107 }
108 return result;
109 }
110
111 explicit operator bool() const
112 {
113 auto a = reinterpret_cast<const uint32_t*>(data_.data());
114 auto b = reinterpret_cast<const uint32_t*>(data_.data() + N);
115 for (; a != b; a++) {
116 if (*a)
117 return true;
118 }
119 return false;
120 }
121
122 uint8_t& operator[](size_t index) { return data_[index]; }
123 const uint8_t& operator[](size_t index) const { return data_[index]; }
124
129 inline int lowbit() const
130 {
131 int i, j;
132 for (i = N - 1; i >= 0; i--)
133 if (data_[i] != 0)
134 break;
135 if (i < 0)
136 return -1;
137 for (j = 7; j >= 0; j--)
138 if ((data_[i] & (0x80 >> j)) != 0)
139 break;
140 return 8 * i + j;
141 }
142
143 static inline int cmp(const Hash& id1, const Hash& id2)
144 {
145 return std::memcmp(id1.data_.data(), id2.data_.data(), N);
146 }
147
149 static inline unsigned commonBits(const Hash& id1, const Hash& id2)
150 {
151 unsigned i, j;
152 uint8_t x;
153 for (i = 0; i < N; i++) {
154 if (id1.data_[i] != id2.data_[i])
155 break;
156 }
157
158 if (i == N)
159 return 8 * N;
160
161 x = id1.data_[i] ^ id2.data_[i];
162
163 j = 0;
164 while ((x & 0x80) == 0) {
165 x <<= 1;
166 j++;
167 }
168
169 return 8 * i + j;
170 }
171
173 int xorCmp(const Hash& id1, const Hash& id2) const
174 {
175 for (unsigned i = 0; i < N; i++) {
176 if (id1.data_[i] == id2.data_[i])
177 continue;
178 uint8_t xor1 = id1.data_[i] ^ data_[i];
179 uint8_t xor2 = id2.data_[i] ^ data_[i];
180 return (xor1 < xor2) ? -1 : 1;
181 }
182 return 0;
183 }
184
185 bool getBit(unsigned nbit) const
186 {
187 auto& num = *(data_.cbegin() + (nbit / 8));
188 unsigned bit = 7 - (nbit % 8);
189 return (num >> bit) & 1;
190 }
191
192 void setBit(unsigned nbit, bool b)
193 {
194 auto& num = data_[nbit / 8];
195 unsigned bit = 7 - (nbit % 8);
196 num ^= (-b ^ num) & (1 << bit);
197 }
198
199 double toFloat() const
200 {
201 using D = size_t;
202 double v = 0.;
203 for (size_t i = 0; i < std::min<size_t>(N, sizeof(D) - 1); i++)
204 v += *(data_.cbegin() + i) / (double) ((D) 1 << 8 * (i + 1));
205 return v;
206 }
207
208 static inline Hash get(std::string_view data) { return get((const uint8_t*) data.data(), data.size()); }
209
210 static inline Hash get(const std::vector<uint8_t>& data) { return get(data.data(), data.size()); }
211
212 template<size_t H>
213 static Hash get(const Hash<H>& o)
214 {
215 return get(o.data(), o.size());
216 }
217
221 static Hash get(const uint8_t* data, size_t data_len)
222 {
223 Hash ret;
224 crypto::hash(data, data_len, ret.data(), N);
225 return ret;
226 }
227
228 static Hash getRandom();
229
230 template<typename Rd>
231 static Hash getRandom(Rd&);
232
233 template<size_t M>
234 friend std::ostream& operator<<(std::ostream& s, const Hash<M>& h);
235
236 template<size_t M>
237 friend std::istream& operator>>(std::istream& s, Hash<M>& h);
238
240 std::string_view to_view() const { return std::string_view(to_c_str(), N * 2); }
241 const char* to_c_str() const;
242
243 std::string toString() const;
244
245 template<typename Packer>
246 void msgpack_pack(Packer& pk) const
247 {
248 pk.pack_bin(N);
249 pk.pack_bin_body((char*) data_.data(), N);
250 }
251
252 void msgpack_unpack(msgpack::object o)
253 {
254 if (o.type != msgpack::type::BIN or o.via.bin.size != N)
255 throw msgpack::type_error();
256 std::copy_n(o.via.bin.ptr, N, data_.data());
257 }
258
259private:
260 T data_;
261 void fromString(const char*);
262};
263
264#define HASH_LEN 20u
265using InfoHash = Hash<HASH_LEN>;
266using h256 = Hash<32>;
267using PkId = h256;
268
269template<size_t N>
270std::ostream&
271operator<<(std::ostream& s, const Hash<N>& h)
272{
273 s.write(h.to_c_str(), N * 2);
274 return s;
275}
276
277template<size_t N>
278std::istream&
279operator>>(std::istream& s, Hash<N>& h)
280{
281 std::array<char, h.size() * 2> dat;
282 s.exceptions(std::istream::eofbit | std::istream::failbit);
283 s.read(&(*dat.begin()), dat.size());
284 fromString(dat.data());
285 return s;
286}
287
288template<size_t N>
289void
290Hash<N>::fromString(const char* in)
291{
292 auto hex2bin = [](char c) -> uint8_t {
293 if (c >= 'a' and c <= 'f')
294 return 10 + c - 'a';
295 else if (c >= 'A' and c <= 'F')
296 return 10 + c - 'A';
297 else if (c >= '0' and c <= '9')
298 return c - '0';
299 else
300 throw std::domain_error("not an hex character");
301 };
302 try {
303 for (size_t i = 0; i < N; i++)
304 data_[i] = (hex2bin(in[2 * i]) << 4) | hex2bin(in[2 * i + 1]);
305 } catch (const std::domain_error&) {
306 data_.fill(0);
307 }
308}
309
310template<size_t N>
312Hash<N>::getRandom()
313{
314 Hash h;
315 std::random_device rdev;
316 std::uniform_int_distribution<uint32_t> rand_int;
317 auto a = reinterpret_cast<uint32_t*>(h.data());
318 auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
319 std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
320 return h;
321}
322
323template<size_t N>
324template<typename Rd>
326Hash<N>::getRandom(Rd& rdev)
327{
328 Hash h;
329 std::uniform_int_distribution<uint32_t> rand_int;
330 auto a = reinterpret_cast<uint32_t*>(h.data());
331 auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
332 std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
333 return h;
334}
335
336struct alignas(std::max_align_t) HexMap : public std::array<std::array<char, 2>, 256>
337{
338 constexpr HexMap()
339 : std::array<std::array<char, 2>, 256>()
340 {
341 for (size_t i = 0; i < size(); i++) {
342 auto& e = (*this)[i];
343 e[0] = hex_digits[(i >> 4) & 0x0F];
344 e[1] = hex_digits[i & 0x0F];
345 }
346 }
347
348private:
349 static constexpr const char* hex_digits = "0123456789abcdef";
350};
351
352inline constexpr HexMap hex_map {};
353
354inline std::string
355toHex(const uint8_t* data, size_t size)
356{
357 std::string ret(size * 2, '\0');
358 for (size_t i = 0; i < size; i++) {
359 auto b = ret.data() + i * 2;
360 const auto& m = hex_map[data[i]];
361 *((uint16_t*) b) = *((uint16_t*) &m);
362 }
363 return ret;
364}
365
366inline std::string
367toHex(const std::vector<uint8_t>& data)
368{
369 return toHex(data.data(), data.size());
370}
371
372template<size_t N>
373const char*
374Hash<N>::to_c_str() const
375{
376 alignas(std::max_align_t) thread_local std::array<char, N * 2 + 1> buf;
377 for (size_t i = 0; i < N; i++) {
378 auto b = buf.data() + i * 2;
379 const auto& m = hex_map[data_[i]];
380 *((uint16_t*) b) = *((uint16_t*) &m);
381 }
382 buf[N * 2] = '\0';
383 return buf.data();
384}
385
386template<size_t N>
387std::string
388Hash<N>::toString() const
389{
390 return std::string(to_c_str(), N * 2);
391}
392
393} // namespace dht
394
395template<size_t N>
396struct fmt::formatter<dht::Hash<N>> : formatter<string_view>
397{
398 constexpr auto format(const dht::Hash<N>& c, format_context& ctx) const
399 {
400 return formatter<string_view>::format(c.to_view(), ctx);
401 }
402};
static unsigned commonBits(const Hash &id1, const Hash &id2)
Definition infohash.h:149
Hash(std::string_view hex)
Definition infohash.h:70
static Hash get(const uint8_t *data, size_t data_len)
Definition infohash.h:221
int lowbit() const
Definition infohash.h:129
std::string_view to_view() const
Definition infohash.h:240
int xorCmp(const Hash &id1, const Hash &id2) const
Definition infohash.h:173
OPENDHT_PUBLIC Blob hash(const Blob &data, size_t hash_length=512/8)