My Project 2.4.4
C++ Distributed Hash Table
Loading...
Searching...
No Matches
infohash.h
1/*
2 * Copyright (C) 2014-2022 Savoir-faire Linux Inc.
3 * Author : Adrien Béraud <adrien.beraud@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include "def.h"
22#include "rng.h"
23
24#include <msgpack.hpp>
25
26#ifndef _WIN32
27#include <netinet/in.h>
28#include <netdb.h>
29#ifdef __ANDROID__
30typedef uint16_t in_port_t;
31#endif
32#else
33#include <iso646.h>
34#include <ws2tcpip.h>
35typedef uint16_t sa_family_t;
36typedef uint16_t in_port_t;
37#endif
38
39#include <iostream>
40#include <iomanip>
41#include <array>
42#include <vector>
43#include <string_view>
44#include <algorithm>
45#include <stdexcept>
46#include <sstream>
47#include <cstring>
48#include <cstddef>
49
50namespace dht {
51
52using byte = uint8_t;
53
54namespace crypto {
55 OPENDHT_PUBLIC void hash(const uint8_t* data, size_t data_length, uint8_t* hash, size_t hash_length);
56}
57
63template <size_t N>
64class OPENDHT_PUBLIC Hash {
65public:
66 using T = std::array<uint8_t, N>;
67 typedef typename T::iterator iterator;
68 typedef typename T::const_iterator const_iterator;
69
70 Hash() noexcept {
71 data_.fill(0);
72 }
73 Hash(const uint8_t* h, size_t data_len) {
74 if (data_len < N)
75 data_.fill(0);
76 else
77 std::copy_n(h, N, data_.begin());
78 }
84 explicit Hash(std::string_view hex) {
85 if (hex.size() < 2*N)
86 data_.fill(0);
87 else
88 fromString(hex.data());
89 }
90
91 Hash(const msgpack::object& o) {
92 msgpack_unpack(o);
93 }
94
95 static constexpr size_t size() noexcept { return N; }
96 const uint8_t* data() const { return data_.data(); }
97 uint8_t* data() { return data_.data(); }
98 iterator begin() { return data_.begin(); }
99 const_iterator cbegin() const { return data_.cbegin(); }
100 iterator end() { return data_.end(); }
101 const_iterator cend() const { return data_.cend(); }
102
103 static constexpr inline Hash zero() noexcept { return Hash{}; }
104
105 bool operator==(const Hash& h) const {
106 auto a = reinterpret_cast<const uint32_t*>(data_.data());
107 auto b = reinterpret_cast<const uint32_t*>(h.data_.data());
108 constexpr unsigned n = N / sizeof(uint32_t);
109 for (unsigned i=0; i < n; i++)
110 if (a[i] != b[i])
111 return false;
112 return true;
113 }
114 bool operator!=(const Hash& h) const { return !(*this == h); }
115
116 bool operator<(const Hash& o) const {
117 for(unsigned i = 0; i < N; i++) {
118 if(data_[i] != o.data_[i])
119 return data_[i] < o.data_[i];
120 }
121 return false;
122 }
123
124 Hash operator^(const Hash& o) const {
125 Hash result;
126 for(auto i = 0u; i < N; i++) {
127 result[i] = data_[i] ^ o.data_[i];
128 }
129 return result;
130 }
131
132 explicit operator bool() const {
133 auto a = reinterpret_cast<const uint32_t*>(data_.data());
134 auto b = reinterpret_cast<const uint32_t*>(data_.data() + N);
135 for (; a != b; a++) {
136 if (*a)
137 return true;
138 }
139 return false;
140 }
141
142 uint8_t& operator[](size_t index) { return data_[index]; }
143 const uint8_t& operator[](size_t index) const { return data_[index]; }
144
149 inline int lowbit() const {
150 int i, j;
151 for(i = N-1; i >= 0; i--)
152 if(data_[i] != 0)
153 break;
154 if(i < 0)
155 return -1;
156 for(j = 7; j >= 0; j--)
157 if((data_[i] & (0x80 >> j)) != 0)
158 break;
159 return 8 * i + j;
160 }
161
162 static inline int cmp(const Hash& id1, const Hash& id2) {
163 return std::memcmp(id1.data_.data(), id2.data_.data(), N);
164 }
165
167 static inline unsigned
168 commonBits(const Hash& id1, const Hash& id2)
169 {
170 unsigned i, j;
171 uint8_t x;
172 for(i = 0; i < N; i++) {
173 if(id1.data_[i] != id2.data_[i])
174 break;
175 }
176
177 if(i == N)
178 return 8*N;
179
180 x = id1.data_[i] ^ id2.data_[i];
181
182 j = 0;
183 while((x & 0x80) == 0) {
184 x <<= 1;
185 j++;
186 }
187
188 return 8 * i + j;
189 }
190
192 int
193 xorCmp(const Hash& id1, const Hash& id2) const
194 {
195 for (unsigned i = 0; i < N; i++) {
196 if(id1.data_[i] == id2.data_[i])
197 continue;
198 uint8_t xor1 = id1.data_[i] ^ data_[i];
199 uint8_t xor2 = id2.data_[i] ^ data_[i];
200 return (xor1 < xor2) ? -1 : 1;
201 }
202 return 0;
203 }
204
205 bool
206 getBit(unsigned nbit) const
207 {
208 auto& num = *(data_.cbegin()+(nbit/8));
209 unsigned bit = 7 - (nbit % 8);
210 return (num >> bit) & 1;
211 }
212
213 void
214 setBit(unsigned nbit, bool b)
215 {
216 auto& num = data_[nbit/8];
217 unsigned bit = 7 - (nbit % 8);
218 num ^= (-b ^ num) & (1 << bit);
219 }
220
221 double toFloat() const {
222 using D = size_t;
223 double v = 0.;
224 for (size_t i = 0; i < std::min<size_t>(N, sizeof(D)-1); i++)
225 v += *(data_.cbegin()+i) / (double)((D)1 << 8*(i+1));
226 return v;
227 }
228
229 static inline Hash get(std::string_view data) {
230 return get((const uint8_t*)data.data(), data.size());
231 }
232
233 static inline Hash get(const std::vector<uint8_t>& data) {
234 return get(data.data(), data.size());
235 }
236
240 static Hash get(const uint8_t* data, size_t data_len)
241 {
242 Hash ret;
243 crypto::hash(data, data_len, ret.data(), N);
244 return ret;
245 }
246
247 static Hash getRandom();
248
249 template <typename Rd>
250 static Hash getRandom(Rd&);
251
252 template <size_t M>
253 OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const Hash<M>& h);
254
255 template <size_t M>
256 OPENDHT_PUBLIC friend std::istream& operator>> (std::istream& s, Hash<M>& h);
257
258 const char* to_c_str() const;
259
260 std::string toString() const;
261
262 template <typename Packer>
263 void msgpack_pack(Packer& pk) const
264 {
265 pk.pack_bin(N);
266 pk.pack_bin_body((char*)data_.data(), N);
267 }
268
269 void msgpack_unpack(msgpack::object o) {
270 if (o.type != msgpack::type::BIN or o.via.bin.size != N)
271 throw msgpack::type_error();
272 std::copy_n(o.via.bin.ptr, N, data_.data());
273 }
274private:
275 T data_;
276 void fromString(const char*);
277};
278
279#define HASH_LEN 20u
280using InfoHash = Hash<HASH_LEN>;
281using h256 = Hash<32>;
282using PkId = h256;
283
284template <size_t N>
285std::ostream& operator<< (std::ostream& s, const Hash<N>& h)
286{
287 s.write(h.to_c_str(), N*2);
288 return s;
289}
290
291template <size_t N>
292std::istream& operator>> (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 fromString(dat.data());
298 return s;
299}
300
301template <size_t N>
302void
303Hash<N>::fromString(const char* in) {
304 auto hex2bin = [](char c) -> uint8_t {
305 if (c >= 'a' and c <= 'f') return 10 + c - 'a';
306 else if (c >= 'A' and c <= 'F') return 10 + c - 'A';
307 else if (c >= '0' and c <= '9') return c - '0';
308 else throw std::domain_error("not an hex character");
309 };
310 try {
311 for (size_t i=0; i<N; i++)
312 data_[i] = (hex2bin(in[2*i]) << 4) | hex2bin(in[2*i+1]);
313 } catch (const std::domain_error&) {
314 data_.fill(0);
315 }
316}
317
318template <size_t N>
319Hash<N>
320Hash<N>::getRandom()
321{
322 Hash h;
323 crypto::random_device rdev;
324 std::uniform_int_distribution<uint32_t> rand_int;
325 auto a = reinterpret_cast<uint32_t*>(h.data());
326 auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
327 std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
328 return h;
329}
330
331template <size_t N>
332template <typename Rd>
333Hash<N>
334Hash<N>::getRandom(Rd& rdev)
335{
336 Hash h;
337 std::uniform_int_distribution<uint32_t> rand_int;
338 auto a = reinterpret_cast<uint32_t*>(h.data());
339 auto b = reinterpret_cast<uint32_t*>(h.data() + h.size());
340 std::generate(a, b, std::bind(rand_int, std::ref(rdev)));
341 return h;
342}
343
344struct alignas(std::max_align_t) HexMap : public std::array<std::array<char, 2>, 256> {
345 HexMap() {
346 for (size_t i=0; i<size(); i++) {
347 auto& e = (*this)[i];
348 e[0] = hex_digits[(i >> 4) & 0x0F];
349 e[1] = hex_digits[i & 0x0F];
350 }
351 }
352private:
353 static constexpr const char* hex_digits = "0123456789abcdef";
354};
355
356OPENDHT_PUBLIC extern const HexMap hex_map;
357
358inline std::string
359toHex(const uint8_t* data, size_t size) {
360 std::string ret(size * 2, '\0');
361 for (size_t i=0; i<size; i++) {
362 auto b = ret.data()+i*2;
363 const auto& m = hex_map[data[i]];
364 *((uint16_t*)b) = *((uint16_t*)&m);
365 }
366 return ret;
367}
368
369inline std::string
370toHex(const std::vector<uint8_t>& data) {
371 return toHex(data.data(), data.size());
372}
373
374template <size_t N>
375const char*
376Hash<N>::to_c_str() const
377{
378 alignas(std::max_align_t) thread_local std::array<char, N*2+1> buf;
379 for (size_t i=0; i<N; i++) {
380 auto b = buf.data()+i*2;
381 const auto& m = hex_map[data_[i]];
382 *((uint16_t*)b) = *((uint16_t*)&m);
383 }
384 return buf.data();
385}
386
387template <size_t N>
388std::string
389Hash<N>::toString() const
390{
391 return std::string(to_c_str(), N*2);
392}
393
394struct OPENDHT_PUBLIC NodeExport {
395 InfoHash id;
396 sockaddr_storage ss;
397 socklen_t sslen;
398
399 template <typename Packer>
400 void msgpack_pack(Packer& pk) const
401 {
402 pk.pack_map(2);
403 pk.pack(std::string("id"));
404 pk.pack(id);
405 pk.pack(std::string("addr"));
406 pk.pack_bin(sslen);
407 pk.pack_bin_body((char*)&ss, sslen);
408 }
409
410 void msgpack_unpack(msgpack::object o);
411
412 OPENDHT_PUBLIC friend std::ostream& operator<< (std::ostream& s, const NodeExport& h);
413};
414
415}
static unsigned commonBits(const Hash &id1, const Hash &id2)
Definition: infohash.h:168
Hash(std::string_view hex)
Definition: infohash.h:84
static Hash get(const uint8_t *data, size_t data_len)
Definition: infohash.h:240
int lowbit() const
Definition: infohash.h:149
int xorCmp(const Hash &id1, const Hash &id2) const
Definition: infohash.h:193
OPENDHT_PUBLIC Blob hash(const Blob &data, size_t hash_length=512/8)
Definition: callbacks.h:35