1/** 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 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 * http://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 16#include <ctime> 17#include "utils/hash.h" 18 19#include "gtest/gtest.h" 20#include "utils/logger.h" 21#include "mem/mem.h" 22#include "os/mem.h" 23#include "utils/asan_interface.h" 24 25namespace panda { 26 27class HashTest : public testing::Test { 28public: 29 HashTest() 30 { 31#ifdef PANDA_NIGHTLY_TEST_ON 32 seed_ = std::time(NULL); 33#else 34 seed_ = 0xDEADBEEF; 35#endif 36 } 37 ~HashTest() {} 38 39protected: 40 template <class T> 41 void OneObject32bitsHashTest(); 42 template <class T> 43 void OneStringHashTest(); 44 template <class T> 45 void StringMemHashTest(); 46 template <class T> 47 void EndOfPageStringHashTest(); 48 static constexpr size_t KEY40INBYTES = 5; 49 static constexpr size_t KEY32INBYTES = 4; 50 static constexpr size_t KEY8INBYTES = 1; 51 52// Some platforms have this macro so do not redefine it. 53#ifndef PAGE_SIZE 54 static constexpr size_t PAGE_SIZE = SIZE_1K * 4; 55#endif 56 57 unsigned seed_; 58}; 59 60template <class T> 61void HashTest::OneObject32bitsHashTest() 62{ 63 srand(seed_); 64 65 uint32_t object32 = rand(); 66 uint32_t first_hash = T::GetHash32(reinterpret_cast<uint8_t *>(&object32), KEY32INBYTES); 67 uint32_t second_hash = T::GetHash32(reinterpret_cast<uint8_t *>(&object32), KEY32INBYTES); 68 if (first_hash != second_hash) { 69 std::cout << "Failed 32bit key hash on seed = 0x" << std::hex << seed_ << std::endl; 70 } 71 ASSERT_EQ(first_hash, second_hash); 72 73 uint8_t object8 = rand(); 74 first_hash = T::GetHash32(reinterpret_cast<uint8_t *>(&object8), KEY8INBYTES); 75 second_hash = T::GetHash32(reinterpret_cast<uint8_t *>(&object8), KEY8INBYTES); 76 if (first_hash != second_hash) { 77 std::cout << "Failed 32bit key hash on seed = 0x" << std::hex << seed_ << std::endl; 78 } 79 ASSERT_EQ(first_hash, second_hash); 80 81 // Set up 64 bits value and use only 40 bits from it 82 uint64_t object40 = rand(); 83 first_hash = T::GetHash32(reinterpret_cast<uint8_t *>(&object40), KEY40INBYTES); 84 second_hash = T::GetHash32(reinterpret_cast<uint8_t *>(&object40), KEY40INBYTES); 85 if (first_hash != second_hash) { 86 std::cout << "Failed 32bit key hash on seed = 0x" << std::hex << seed_ << std::endl; 87 } 88 ASSERT_EQ(first_hash, second_hash); 89} 90 91template <class T> 92void HashTest::OneStringHashTest() 93{ 94 char string[] = "Over 1000!\0"; 95 // Dummy check. Don't ask me why... 96 if (sizeof(char) != sizeof(uint8_t)) { 97 return; 98 } 99 uint8_t *mutf8_string = reinterpret_cast<uint8_t *>(string); 100 uint32_t first_hash = T::GetHash32String(mutf8_string); 101 uint32_t second_hash = T::GetHash32String(mutf8_string); 102 ASSERT_EQ(first_hash, second_hash); 103} 104 105template <class T> 106void HashTest::StringMemHashTest() 107{ 108 char string[] = "COULD YOU CREATE MORE COMPLEX TESTS,OK?\0"; 109 size_t string_size = strlen(string); 110 uint8_t *mutf8_string = reinterpret_cast<uint8_t *>(string); 111 uint32_t second_hash = T::GetHash32(mutf8_string, string_size); 112 uint32_t first_hash = T::GetHash32String(mutf8_string); 113 ASSERT_EQ(first_hash, second_hash); 114} 115 116template <class T> 117void HashTest::EndOfPageStringHashTest() 118{ 119 constexpr const int64_t immTwo = 2; 120 size_t string_size = 3; 121 constexpr size_t ALLOC_SIZE = PAGE_SIZE * 2; 122 void *mem = panda::os::mem::MapRWAnonymousRaw(ALLOC_SIZE); 123 ASAN_UNPOISON_MEMORY_REGION(mem, ALLOC_SIZE); 124 panda::os::mem::MakeMemProtected(reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(mem) + PAGE_SIZE), PAGE_SIZE); 125 char *string = 126 reinterpret_cast<char *>((reinterpret_cast<uintptr_t>(mem) + PAGE_SIZE) - sizeof(char) * string_size); 127 string[0] = 'O'; 128 string[1] = 'K'; 129 string[immTwo] = '\0'; 130 uint8_t *mutf8_string = reinterpret_cast<uint8_t *>(string); 131 uint32_t second_hash = T::GetHash32(mutf8_string, string_size - 1); 132 uint32_t first_hash = T::GetHash32String(mutf8_string); 133 ASSERT_EQ(first_hash, second_hash); 134 auto res = panda::os::mem::UnmapRaw(mem, ALLOC_SIZE); 135 ASSERT_FALSE(res); 136} 137 138// If we hash an object twice, it must return the same value 139// Do it for 8 bits key, 32 bits and 40 bits key. 140TEST_F(HashTest, OneObjectHashTest) 141{ 142 HashTest::OneObject32bitsHashTest<MurmurHash32<DEFAULT_SEED>>(); 143} 144 145// If we hash a string twice, it must return the same value 146TEST_F(HashTest, OneStringHashTest) 147{ 148 HashTest::OneStringHashTest<MurmurHash32<DEFAULT_SEED>>(); 149} 150 151// If we hash a string with out string method, 152// we should get the same result as we use a pointer to string as a raw memory. 153TEST_F(HashTest, StringMemHashTest) 154{ 155 HashTest::StringMemHashTest<MurmurHash32<DEFAULT_SEED>>(); 156} 157 158// Try to hash the string which located at the end of allocated page. 159// Check that we will not have SEGERROR here. 160TEST_F(HashTest, EndOfPageStringHashTest) 161{ 162 HashTest::EndOfPageStringHashTest<MurmurHash32<DEFAULT_SEED>>(); 163} 164 165} // namespace panda 166