Coverage Report

Created: 2026-11-09 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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.43M
inline uint64_t read(Span<const std::byte, 8> Data) noexcept {
24
3.43M
  uint64_t V;
25
3.43M
  std::memcpy(&V, Data.data(), 8);
26
3.43M
  return V;
27
3.43M
}
28
24.5k
inline uint64_t read(Span<const std::byte, 4> Data) noexcept {
29
24.5k
  uint32_t V;
30
24.5k
  std::memcpy(&V, Data.data(), 4);
31
24.5k
  return V;
32
24.5k
}
33
34
} // namespace
35
36
namespace WasmEdge::Hash {
37
38
566k
WASMEDGE_EXPORT uint64_t Hash::rapidHash(Span<const std::byte> Data) noexcept {
39
566k
  uint64_t Seed = RandomSeed;
40
566k
  Seed ^= rapidMix(Seed ^ Secret[2], Secret[1]);
41
566k
  uint64_t A = 0, B = 0;
42
566k
  if (likely(Data.size() <= 16)) {
43
539k
    if (Data.size() >= 4) {
44
98.9k
      Seed ^= Data.size();
45
98.9k
      if (Data.size() >= 8) {
46
86.6k
        A = read(Data.first<8>());
47
86.6k
        B = read(Data.last<8>());
48
86.6k
      } else {
49
12.2k
        A = read(Data.first<4>());
50
12.2k
        B = read(Data.last<4>());
51
12.2k
      }
52
440k
    } else if (Data.size() > 0) {
53
440k
      A = (static_cast<uint64_t>(Data[0]) << 45) |
54
440k
          static_cast<uint64_t>(Data[Data.size() - 1]);
55
440k
      B = static_cast<uint64_t>(Data[Data.size() >> 1]);
56
440k
    } else {
57
333
      A = B = 0;
58
333
    }
59
539k
  } else {
60
26.6k
    const Span<const std::byte> Orig = Data;
61
26.6k
    if (Data.size() > 48) {
62
319
      uint64_t See1 = Seed, See2 = Seed;
63
525k
      do {
64
525k
        Seed = rapidMix(read(Data.first<8>()) ^ Secret[0],
65
525k
                        read(Data.subspan<8>().first<8>()) ^ Seed);
66
525k
        See1 = rapidMix(read(Data.subspan<16>().first<8>()) ^ Secret[1],
67
525k
                        read(Data.subspan<24>().first<8>()) ^ See1);
68
525k
        See2 = rapidMix(read(Data.subspan<32>().first<8>()) ^ Secret[2],
69
525k
                        read(Data.subspan<40>().first<8>()) ^ See2);
70
525k
        Data = Data.subspan<48>();
71
525k
      } while (Data.size() > 48);
72
319
      Seed ^= See1;
73
319
      Seed ^= See2;
74
319
    }
75
26.6k
    if (Data.size() > 16) {
76
26.5k
      Seed = rapidMix(read(Data.first<8>()) ^ Secret[2],
77
26.5k
                      read(Data.subspan<8>().first<8>()) ^ Seed);
78
26.5k
      if (Data.size() > 32) {
79
2.03k
        Seed = rapidMix(read(Data.subspan<16>().first<8>()) ^ Secret[2],
80
2.03k
                        read(Data.subspan<24>().first<8>()) ^ Seed);
81
2.03k
      }
82
26.5k
    }
83
26.6k
    A = read(Orig.last<16>().first<8>()) ^ Data.size();
84
26.6k
    B = read(Orig.last<8>());
85
26.6k
  }
86
566k
  A ^= Secret[1];
87
566k
  B ^= Seed;
88
566k
  rapidMum(A, B);
89
566k
  return rapidMix(A ^ Secret[3], B ^ Secret[1] ^ Data.size());
90
566k
}
91
92
} // namespace WasmEdge::Hash