Coverage Report

Created: 2023-09-25 06:27

/src/abseil-cpp/absl/hash/internal/low_level_hash.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright 2020 The Abseil Authors
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include "absl/hash/internal/low_level_hash.h"
16
17
#include "absl/base/internal/unaligned_access.h"
18
#include "absl/base/prefetch.h"
19
#include "absl/numeric/int128.h"
20
21
namespace absl {
22
ABSL_NAMESPACE_BEGIN
23
namespace hash_internal {
24
25
202
static uint64_t Mix(uint64_t v0, uint64_t v1) {
26
202
  absl::uint128 p = v0;
27
202
  p *= v1;
28
202
  return absl::Uint128Low64(p) ^ absl::Uint128High64(p);
29
202
}
30
31
uint64_t LowLevelHash(const void* data, size_t len, uint64_t seed,
32
56
                      const uint64_t salt[5]) {
33
  // Prefetch the cacheline that data resides in.
34
56
  PrefetchToLocalCache(data);
35
56
  const uint8_t* ptr = static_cast<const uint8_t*>(data);
36
56
  uint64_t starting_length = static_cast<uint64_t>(len);
37
56
  uint64_t current_state = seed ^ salt[0];
38
39
56
  if (len > 64) {
40
    // If we have more than 64 bytes, we're going to handle chunks of 64
41
    // bytes at a time. We're going to build up two separate hash states
42
    // which we will then hash together.
43
0
    uint64_t duplicated_state = current_state;
44
45
0
    do {
46
      // Always prefetch the next cacheline.
47
0
      PrefetchToLocalCache(ptr + ABSL_CACHELINE_SIZE);
48
49
0
      uint64_t a = absl::base_internal::UnalignedLoad64(ptr);
50
0
      uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8);
51
0
      uint64_t c = absl::base_internal::UnalignedLoad64(ptr + 16);
52
0
      uint64_t d = absl::base_internal::UnalignedLoad64(ptr + 24);
53
0
      uint64_t e = absl::base_internal::UnalignedLoad64(ptr + 32);
54
0
      uint64_t f = absl::base_internal::UnalignedLoad64(ptr + 40);
55
0
      uint64_t g = absl::base_internal::UnalignedLoad64(ptr + 48);
56
0
      uint64_t h = absl::base_internal::UnalignedLoad64(ptr + 56);
57
58
0
      uint64_t cs0 = Mix(a ^ salt[1], b ^ current_state);
59
0
      uint64_t cs1 = Mix(c ^ salt[2], d ^ current_state);
60
0
      current_state = (cs0 ^ cs1);
61
62
0
      uint64_t ds0 = Mix(e ^ salt[3], f ^ duplicated_state);
63
0
      uint64_t ds1 = Mix(g ^ salt[4], h ^ duplicated_state);
64
0
      duplicated_state = (ds0 ^ ds1);
65
66
0
      ptr += 64;
67
0
      len -= 64;
68
0
    } while (len > 64);
69
70
0
    current_state = current_state ^ duplicated_state;
71
0
  }
72
73
  // We now have a data `ptr` with at most 64 bytes and the current state
74
  // of the hashing state machine stored in current_state.
75
146
  while (len > 16) {
76
90
    uint64_t a = absl::base_internal::UnalignedLoad64(ptr);
77
90
    uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8);
78
79
90
    current_state = Mix(a ^ salt[1], b ^ current_state);
80
81
90
    ptr += 16;
82
90
    len -= 16;
83
90
  }
84
85
  // We now have a data `ptr` with at most 16 bytes.
86
56
  uint64_t a = 0;
87
56
  uint64_t b = 0;
88
56
  if (len > 8) {
89
    // When we have at least 9 and at most 16 bytes, set A to the first 64
90
    // bits of the input and B to the last 64 bits of the input. Yes, they will
91
    // overlap in the middle if we are working with less than the full 16
92
    // bytes.
93
20
    a = absl::base_internal::UnalignedLoad64(ptr);
94
20
    b = absl::base_internal::UnalignedLoad64(ptr + len - 8);
95
36
  } else if (len > 3) {
96
    // If we have at least 4 and at most 8 bytes, set A to the first 32
97
    // bits and B to the last 32 bits.
98
26
    a = absl::base_internal::UnalignedLoad32(ptr);
99
26
    b = absl::base_internal::UnalignedLoad32(ptr + len - 4);
100
26
  } else if (len > 0) {
101
    // If we have at least 1 and at most 3 bytes, read all of the provided
102
    // bits into A, with some adjustments.
103
10
    a = static_cast<uint64_t>((ptr[0] << 16) | (ptr[len >> 1] << 8) |
104
10
                              ptr[len - 1]);
105
10
    b = 0;
106
10
  } else {
107
0
    a = 0;
108
0
    b = 0;
109
0
  }
110
111
56
  uint64_t w = Mix(a ^ salt[1], b ^ current_state);
112
56
  uint64_t z = salt[1] ^ starting_length;
113
56
  return Mix(w, z);
114
56
}
115
116
}  // namespace hash_internal
117
ABSL_NAMESPACE_END
118
}  // namespace absl