| // Copyright 2020 The Abseil Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "absl/hash/internal/low_level_hash.h" |
| |
| #include <cstddef> |
| #include <cstdint> |
| |
| #include "absl/base/internal/unaligned_access.h" |
| #include "absl/base/prefetch.h" |
| #include "absl/numeric/int128.h" |
| |
| namespace absl { |
| ABSL_NAMESPACE_BEGIN |
| namespace hash_internal { |
| |
| static uint64_t Mix(uint64_t v0, uint64_t v1) { |
| absl::uint128 p = v0; |
| p *= v1; |
| return absl::Uint128Low64(p) ^ absl::Uint128High64(p); |
| } |
| |
| uint64_t LowLevelHashLenGt16(const void* data, size_t len, uint64_t seed, |
| const uint64_t salt[5]) { |
| // Prefetch the cacheline that data resides in. |
| PrefetchToLocalCache(data); |
| const uint8_t* ptr = static_cast<const uint8_t*>(data); |
| uint64_t starting_length = static_cast<uint64_t>(len); |
| const uint8_t* last_16_ptr = ptr + starting_length - 16; |
| uint64_t current_state = seed ^ salt[0]; |
| |
| if (len > 64) { |
| // If we have more than 64 bytes, we're going to handle chunks of 64 |
| // bytes at a time. We're going to build up two separate hash states |
| // which we will then hash together. |
| uint64_t duplicated_state0 = current_state; |
| uint64_t duplicated_state1 = current_state; |
| uint64_t duplicated_state2 = current_state; |
| |
| do { |
| // Always prefetch the next cacheline. |
| PrefetchToLocalCache(ptr + ABSL_CACHELINE_SIZE); |
| |
| uint64_t a = absl::base_internal::UnalignedLoad64(ptr); |
| uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8); |
| uint64_t c = absl::base_internal::UnalignedLoad64(ptr + 16); |
| uint64_t d = absl::base_internal::UnalignedLoad64(ptr + 24); |
| uint64_t e = absl::base_internal::UnalignedLoad64(ptr + 32); |
| uint64_t f = absl::base_internal::UnalignedLoad64(ptr + 40); |
| uint64_t g = absl::base_internal::UnalignedLoad64(ptr + 48); |
| uint64_t h = absl::base_internal::UnalignedLoad64(ptr + 56); |
| |
| current_state = Mix(a ^ salt[1], b ^ current_state); |
| duplicated_state0 = Mix(c ^ salt[2], d ^ duplicated_state0); |
| |
| duplicated_state1 = Mix(e ^ salt[3], f ^ duplicated_state1); |
| duplicated_state2 = Mix(g ^ salt[4], h ^ duplicated_state2); |
| |
| ptr += 64; |
| len -= 64; |
| } while (len > 64); |
| |
| current_state = (current_state ^ duplicated_state0) ^ |
| (duplicated_state1 + duplicated_state2); |
| } |
| |
| // We now have a data `ptr` with at most 64 bytes and the current state |
| // of the hashing state machine stored in current_state. |
| if (len > 32) { |
| uint64_t a = absl::base_internal::UnalignedLoad64(ptr); |
| uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8); |
| uint64_t c = absl::base_internal::UnalignedLoad64(ptr + 16); |
| uint64_t d = absl::base_internal::UnalignedLoad64(ptr + 24); |
| |
| uint64_t cs0 = Mix(a ^ salt[1], b ^ current_state); |
| uint64_t cs1 = Mix(c ^ salt[2], d ^ current_state); |
| current_state = cs0 ^ cs1; |
| |
| ptr += 32; |
| len -= 32; |
| } |
| |
| // We now have a data `ptr` with at most 32 bytes and the current state |
| // of the hashing state machine stored in current_state. |
| if (len > 16) { |
| uint64_t a = absl::base_internal::UnalignedLoad64(ptr); |
| uint64_t b = absl::base_internal::UnalignedLoad64(ptr + 8); |
| |
| current_state = Mix(a ^ salt[1], b ^ current_state); |
| } |
| |
| // We now have a data `ptr` with at least 1 and at most 16 bytes. But we can |
| // safely read from `ptr + len - 16`. |
| uint64_t a = absl::base_internal::UnalignedLoad64(last_16_ptr); |
| uint64_t b = absl::base_internal::UnalignedLoad64(last_16_ptr + 8); |
| |
| return Mix(a ^ salt[1] ^ starting_length, b ^ current_state); |
| } |
| |
| uint64_t LowLevelHash(const void* data, size_t len, uint64_t seed, |
| const uint64_t salt[5]) { |
| if (len > 16) return LowLevelHashLenGt16(data, len, seed, salt); |
| |
| // Prefetch the cacheline that data resides in. |
| PrefetchToLocalCache(data); |
| const uint8_t* ptr = static_cast<const uint8_t*>(data); |
| uint64_t starting_length = static_cast<uint64_t>(len); |
| uint64_t current_state = seed ^ salt[0]; |
| if (len == 0) return current_state; |
| |
| uint64_t a = 0; |
| uint64_t b = 0; |
| |
| // We now have a data `ptr` with at least 1 and at most 16 bytes. |
| if (len > 8) { |
| // When we have at least 9 and at most 16 bytes, set A to the first 64 |
| // bits of the input and B to the last 64 bits of the input. Yes, they |
| // will overlap in the middle if we are working with less than the full 16 |
| // bytes. |
| a = absl::base_internal::UnalignedLoad64(ptr); |
| b = absl::base_internal::UnalignedLoad64(ptr + len - 8); |
| } else if (len > 3) { |
| // If we have at least 4 and at most 8 bytes, set A to the first 32 |
| // bits and B to the last 32 bits. |
| a = absl::base_internal::UnalignedLoad32(ptr); |
| b = absl::base_internal::UnalignedLoad32(ptr + len - 4); |
| } else { |
| // If we have at least 1 and at most 3 bytes, read 2 bytes into A and the |
| // other byte into B, with some adjustments. |
| a = static_cast<uint64_t>((ptr[0] << 8) | ptr[len - 1]); |
| b = static_cast<uint64_t>(ptr[len >> 1]); |
| } |
| |
| return Mix(a ^ salt[1] ^ starting_length, b ^ current_state); |
| } |
| |
| } // namespace hash_internal |
| ABSL_NAMESPACE_END |
| } // namespace absl |