/* * Copyright (C) 2018 The Android Open Source Project * * 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 * * http://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. */ #ifndef ART_LIBARTBASE_BASE_DATA_HASH_H_ #define ART_LIBARTBASE_BASE_DATA_HASH_H_ #include #include "base/globals.h" #include "base/macros.h" namespace art { // Note: Touching this file or any #included file causes too many files to be rebuilt, so // we want to avoid #including any files that are not necessary. Therefore we use templates // (and std::enable_if<>) to avoid `#including headers for `ArrayRef<>` or `BitMemoryRegion`. class BitMemoryRegion; class DataHash { private: static constexpr bool kUseMurmur3Hash = true; public: template >> size_t operator()(const Container& array) const { // Containers that provide the data() function use contiguous storage. const uint8_t* data = reinterpret_cast(array.data()); uint32_t length_in_bytes = sizeof(typename Container::value_type) * array.size(); if (kUseMurmur3Hash) { uint32_t hash = Murmur3Start(); const size_t nblocks = length_in_bytes / 4; using unaligned_uint32_t __attribute__((__aligned__(1))) = uint32_t; const unaligned_uint32_t* blocks = reinterpret_cast(data); for (size_t i = 0; i != nblocks; ++i) { hash = Murmur3Update(hash, blocks[i]); } const uint8_t* tail = reinterpret_cast(data + nblocks * 4); uint32_t last_block = 0; switch (length_in_bytes & 3) { case 3: last_block ^= tail[2] << 16; FALLTHROUGH_INTENDED; case 2: last_block ^= tail[1] << 8; FALLTHROUGH_INTENDED; case 1: last_block ^= tail[0]; hash = Murmur3UpdatePartial(hash, last_block); } hash = Murmur3Finish(hash, length_in_bytes); return hash; } else { return HashBytes(data, length_in_bytes); } } // Hash bytes using a relatively fast hash. static inline size_t HashBytes(const uint8_t* data, size_t length_in_bytes) { size_t hash = HashBytesStart(); for (uint32_t i = 0; i != length_in_bytes; ++i) { hash = HashBytesUpdate(hash, data[i]); } return HashBytesFinish(hash); } template >> size_t operator()(BMR region) const { if (kUseMurmur3Hash) { size_t num_full_blocks = region.size_in_bits() / kMurmur3BlockBits; size_t num_end_bits = region.size_in_bits() % kMurmur3BlockBits; size_t hash = Murmur3Start(); for (uint32_t i = 0; i != num_full_blocks; ++i) { uint32_t block = region.LoadBits(i * kMurmur3BlockBits, kMurmur3BlockBits); hash = Murmur3Update(hash, block); } if (num_end_bits != 0u) { uint32_t end_bits = region.LoadBits(num_full_blocks * kMurmur3BlockBits, num_end_bits); hash = Murmur3UpdatePartial(hash, end_bits); } return HashBytesFinish(hash); } else { size_t num_full_bytes = region.size_in_bits() / kBitsPerByte; size_t num_end_bits = region.size_in_bits() % kBitsPerByte; size_t hash = HashBytesStart(); for (uint32_t i = 0; i != num_full_bytes; ++i) { uint8_t byte = region.LoadBits(i * kBitsPerByte, kBitsPerByte); hash = HashBytesUpdate(hash, byte); } if (num_end_bits != 0u) { uint32_t end_bits = region.LoadBits(num_full_bytes * kBitsPerByte, num_end_bits); hash = HashBytesUpdate(hash, end_bits); } return HashBytesFinish(hash); } } private: ALWAYS_INLINE static constexpr size_t HashBytesStart() { return 0x811c9dc5; } ALWAYS_INLINE static constexpr size_t HashBytesUpdate(size_t hash, uint8_t value) { return (hash * 16777619) ^ value; } ALWAYS_INLINE static constexpr size_t HashBytesFinish(size_t hash) { hash += hash << 13; hash ^= hash >> 7; hash += hash << 3; hash ^= hash >> 17; hash += hash << 5; return hash; } static constexpr uint32_t kMurmur3Seed = 0u; static constexpr uint32_t kMurmur3BlockBits = 32u; static constexpr uint32_t kMurmur3C1 = 0xcc9e2d51; static constexpr uint32_t kMurmur3C2 = 0x1b873593; static constexpr uint32_t kMurmur3R1 = 15; static constexpr uint32_t kMurmur3R2 = 13; static constexpr uint32_t kMurmur3M = 5; static constexpr uint32_t kMurmur3N = 0xe6546b64; ALWAYS_INLINE static constexpr uint32_t Murmur3Start() { return kMurmur3Seed; } ALWAYS_INLINE static constexpr uint32_t Murmur3Update(uint32_t hash, uint32_t block) { uint32_t k = block; k *= kMurmur3C1; k = (k << kMurmur3R1) | (k >> (32 - kMurmur3R1)); k *= kMurmur3C2; hash ^= k; hash = ((hash << kMurmur3R2) | (hash >> (32 - kMurmur3R2))) * kMurmur3M + kMurmur3N; return hash; } ALWAYS_INLINE static constexpr uint32_t Murmur3UpdatePartial(uint32_t hash, uint32_t block) { uint32_t k = block; k *= kMurmur3C1; k = (k << kMurmur3R1) | (k >> (32 - kMurmur3R1)); k *= kMurmur3C2; hash ^= k; // Note: Unlike full block, the partial block does not have `hash = hash * M + N`. return hash; } ALWAYS_INLINE static constexpr uint32_t Murmur3Finish(uint32_t hash, uint32_t length_in_bytes) { hash ^= length_in_bytes; hash ^= (hash >> 16); hash *= 0x85ebca6b; hash ^= (hash >> 13); hash *= 0xc2b2ae35; hash ^= (hash >> 16); return hash; } }; } // namespace art #endif // ART_LIBARTBASE_BASE_DATA_HASH_H_