/src/WasmEdge/lib/common/hash.cpp
Line | Count | Source |
1 | | #include "common/hash.h" |
2 | | |
3 | | #include <array> |
4 | | #include <cstring> |
5 | | |
6 | | namespace { |
7 | | |
8 | | using namespace WasmEdge; |
9 | | |
10 | | // rapidhash v3 secrets — only the 4 used by the Nano variant. |
11 | | // Indices match upstream rapid_secret[]: [0], [1], [2], [7]. |
12 | | static constexpr std::array<uint64_t, 4> Secret = { |
13 | | 0x2d358dccaa6c78a5ull, // rapid_secret[0] |
14 | | 0x8bb84b93962eacc9ull, // rapid_secret[1] |
15 | | 0x4b33a62ed433d4a3ull, // rapid_secret[2] |
16 | | 0xaaaaaaaaaaaaaaaaull, // rapid_secret[7] |
17 | | }; |
18 | | |
19 | | static const uint64_t RandomSeed = Hash::RandEngine(); |
20 | | |
21 | | // Intentional: assumes little-endian. Trades platform-independent hash |
22 | | // values for speed by skipping byte-swap on big-endian targets. |
23 | 3.25M | inline uint64_t read(Span<const std::byte, 8> Data) noexcept { |
24 | 3.25M | uint64_t V; |
25 | 3.25M | std::memcpy(&V, Data.data(), 8); |
26 | 3.25M | return V; |
27 | 3.25M | } |
28 | 24.4k | inline uint64_t read(Span<const std::byte, 4> Data) noexcept { |
29 | 24.4k | uint32_t V; |
30 | 24.4k | std::memcpy(&V, Data.data(), 4); |
31 | 24.4k | return V; |
32 | 24.4k | } |
33 | | |
34 | | } // namespace |
35 | | |
36 | | namespace WasmEdge::Hash { |
37 | | |
38 | 825k | WASMEDGE_EXPORT uint64_t Hash::rapidHash(Span<const std::byte> Data) noexcept { |
39 | 825k | uint64_t Seed = RandomSeed; |
40 | 825k | Seed ^= rapidMix(Seed ^ Secret[2], Secret[1]); |
41 | 825k | uint64_t A = 0, B = 0; |
42 | 825k | if (likely(Data.size() <= 16)) { |
43 | 799k | if (Data.size() >= 4) { |
44 | 99.4k | Seed ^= Data.size(); |
45 | 99.4k | if (Data.size() >= 8) { |
46 | 87.2k | A = read(Data.first<8>()); |
47 | 87.2k | B = read(Data.last<8>()); |
48 | 87.2k | } else { |
49 | 12.2k | A = read(Data.first<4>()); |
50 | 12.2k | B = read(Data.last<4>()); |
51 | 12.2k | } |
52 | 699k | } else if (Data.size() > 0) { |
53 | 699k | A = (static_cast<uint64_t>(Data[0]) << 45) | |
54 | 699k | static_cast<uint64_t>(Data[Data.size() - 1]); |
55 | 699k | B = static_cast<uint64_t>(Data[Data.size() >> 1]); |
56 | 699k | } else { |
57 | 173 | A = B = 0; |
58 | 173 | } |
59 | 799k | } else { |
60 | 26.0k | const Span<const std::byte> Orig = Data; |
61 | 26.0k | if (Data.size() > 48) { |
62 | 174 | uint64_t See1 = Seed, See2 = Seed; |
63 | 495k | do { |
64 | 495k | Seed = rapidMix(read(Data.first<8>()) ^ Secret[0], |
65 | 495k | read(Data.subspan<8>().first<8>()) ^ Seed); |
66 | 495k | See1 = rapidMix(read(Data.subspan<16>().first<8>()) ^ Secret[1], |
67 | 495k | read(Data.subspan<24>().first<8>()) ^ See1); |
68 | 495k | See2 = rapidMix(read(Data.subspan<32>().first<8>()) ^ Secret[2], |
69 | 495k | read(Data.subspan<40>().first<8>()) ^ See2); |
70 | 495k | Data = Data.subspan<48>(); |
71 | 495k | } while (Data.size() > 48); |
72 | 174 | Seed ^= See1; |
73 | 174 | Seed ^= See2; |
74 | 174 | } |
75 | 26.0k | if (Data.size() > 16) { |
76 | 25.9k | Seed = rapidMix(read(Data.first<8>()) ^ Secret[2], |
77 | 25.9k | read(Data.subspan<8>().first<8>()) ^ Seed); |
78 | 25.9k | if (Data.size() > 32) { |
79 | 1.99k | Seed = rapidMix(read(Data.subspan<16>().first<8>()) ^ Secret[2], |
80 | 1.99k | read(Data.subspan<24>().first<8>()) ^ Seed); |
81 | 1.99k | } |
82 | 25.9k | } |
83 | 26.0k | A = read(Orig.last<16>().first<8>()) ^ Data.size(); |
84 | 26.0k | B = read(Orig.last<8>()); |
85 | 26.0k | } |
86 | 825k | A ^= Secret[1]; |
87 | 825k | B ^= Seed; |
88 | 825k | rapidMum(A, B); |
89 | 825k | return rapidMix(A ^ Secret[3], B ^ Secret[1] ^ Data.size()); |
90 | 825k | } |
91 | | |
92 | | } // namespace WasmEdge::Hash |