mirror of
https://github.com/grumpycoders/pcsx-redux.git
synced 2025-04-02 10:41:54 -04:00
204 lines
6.2 KiB
C++
204 lines
6.2 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2020 PCSX-Redux authors *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
|
|
***************************************************************************/
|
|
|
|
#include "support/hashtable.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
struct HashElement;
|
|
typedef PCSX::Intrusive::HashTable<int, HashElement> HashTableType;
|
|
struct HashElement : public HashTableType::Node {
|
|
HashElement(int tag = 0) : m_tag(tag) {}
|
|
int m_tag = 0;
|
|
};
|
|
|
|
TEST(BasicHashTable, EmptyHashTable) {
|
|
HashTableType hashtab;
|
|
EXPECT_TRUE(hashtab.empty());
|
|
}
|
|
|
|
TEST(BasicHashTable, InsertOne) {
|
|
HashTableType hashtab;
|
|
hashtab.insert(42, new HashElement(42));
|
|
EXPECT_FALSE(hashtab.empty());
|
|
auto p = hashtab.find(42);
|
|
EXPECT_FALSE(p == hashtab.end());
|
|
HashElement& n = *p;
|
|
EXPECT_EQ(n.getKey(), 42);
|
|
EXPECT_EQ(n.m_tag, 42);
|
|
hashtab.destroyAll();
|
|
EXPECT_TRUE(hashtab.empty());
|
|
}
|
|
|
|
TEST(BasicHashTable, InsertMany) {
|
|
HashTableType hashtab;
|
|
for (unsigned i = 0; i < 42; i++) {
|
|
hashtab.insert(i, new HashElement(i));
|
|
}
|
|
EXPECT_EQ(hashtab.size(), 42);
|
|
for (unsigned i = 0; i < 42; i++) {
|
|
auto p = hashtab.find(i);
|
|
EXPECT_FALSE(p == hashtab.end());
|
|
HashElement& n = *p;
|
|
EXPECT_EQ(n.getKey(), i);
|
|
EXPECT_EQ(n.m_tag, i);
|
|
}
|
|
|
|
hashtab.destroyAll();
|
|
EXPECT_TRUE(hashtab.empty());
|
|
}
|
|
|
|
TEST(BasicHashTable, DeleteMany) {
|
|
HashTableType hashtab;
|
|
for (unsigned i = 0; i < 256; i++) {
|
|
hashtab.insert(i, new HashElement(i));
|
|
}
|
|
EXPECT_EQ(hashtab.size(), 256);
|
|
for (unsigned i = 256; i != 0; i--) {
|
|
auto p = hashtab.find(i - 1);
|
|
EXPECT_FALSE(p == hashtab.end());
|
|
HashElement& n = *p;
|
|
EXPECT_EQ(n.getKey(), i - 1);
|
|
EXPECT_EQ(n.m_tag, i - 1);
|
|
|
|
delete &n;
|
|
}
|
|
|
|
EXPECT_TRUE(hashtab.empty());
|
|
|
|
for (unsigned i = 0; i < 42; i++) {
|
|
hashtab.insert(i, new HashElement(i));
|
|
}
|
|
EXPECT_EQ(hashtab.size(), 42);
|
|
for (unsigned i = 0; i < 42; i++) {
|
|
auto p = hashtab.find(i);
|
|
EXPECT_FALSE(p == hashtab.end());
|
|
HashElement& n = *p;
|
|
EXPECT_EQ(n.getKey(), i);
|
|
EXPECT_EQ(n.m_tag, i);
|
|
}
|
|
|
|
hashtab.destroyAll();
|
|
EXPECT_TRUE(hashtab.empty());
|
|
}
|
|
|
|
TEST(BasicHashTable, UseAfterDestroy) {
|
|
HashTableType hashtab;
|
|
for (unsigned i = 0; i < 42; i++) {
|
|
hashtab.insert(i, new HashElement(i));
|
|
}
|
|
hashtab.destroyAll();
|
|
for (unsigned i = 0; i < 42; i++) {
|
|
hashtab.insert(100 + i, new HashElement(100 + i));
|
|
}
|
|
EXPECT_EQ(hashtab.size(), 42);
|
|
for (unsigned i = 0; i < 42; i++) {
|
|
auto p = hashtab.find(100 + i);
|
|
EXPECT_FALSE(p == hashtab.end());
|
|
HashElement& n = *p;
|
|
EXPECT_EQ(n.getKey(), 100 + i);
|
|
EXPECT_EQ(n.m_tag, 100 + i);
|
|
}
|
|
|
|
hashtab.destroyAll();
|
|
EXPECT_TRUE(hashtab.empty());
|
|
}
|
|
TEST(AdvancedHashTable, Eviction) {
|
|
HashTableType hashtab;
|
|
HashElement* e1 = new HashElement(1);
|
|
HashElement* e2 = new HashElement(2);
|
|
|
|
hashtab.insert(42, e1);
|
|
hashtab.insert(42, e2);
|
|
|
|
EXPECT_EQ(hashtab.size(), 1);
|
|
EXPECT_FALSE(hashtab.contains(e1));
|
|
EXPECT_TRUE(hashtab.contains(e2));
|
|
EXPECT_FALSE(e1->isLinked());
|
|
EXPECT_TRUE(e2->isLinked());
|
|
|
|
auto i = hashtab.find(42);
|
|
EXPECT_EQ(i->m_tag, 2);
|
|
hashtab.destroyAll();
|
|
delete e1;
|
|
}
|
|
TEST(AdvancedHashTable, Iterator) {
|
|
HashTableType hashtab;
|
|
for (unsigned i = 0; i < 64; i++) {
|
|
hashtab.insert(i, new HashElement(i));
|
|
}
|
|
EXPECT_EQ(hashtab.size(), 64);
|
|
uint64_t seen = 0;
|
|
|
|
for (auto& i : hashtab) {
|
|
int tag = i.m_tag;
|
|
EXPECT_FALSE(seen & (1ULL << tag));
|
|
seen |= (1ULL << tag);
|
|
}
|
|
|
|
EXPECT_EQ(seen, 0xffffffffffffffffULL);
|
|
|
|
hashtab.destroyAll();
|
|
}
|
|
|
|
struct FlawedHash {
|
|
static constexpr uint32_t hash(const int& key) { return 42; }
|
|
static constexpr bool isEqual(const int& lhs, const int& rhs) { return lhs == rhs; }
|
|
};
|
|
struct FlawedHashElement;
|
|
typedef PCSX::Intrusive::HashTable<int, FlawedHashElement, FlawedHash> FlawedHashTableType;
|
|
struct FlawedHashElement : public FlawedHashTableType::Node {
|
|
FlawedHashElement(int tag = 0) : m_tag(tag) {}
|
|
int m_tag = 0;
|
|
};
|
|
TEST(FlawedHashTable, DeleteMany) {
|
|
FlawedHashTableType hashtab;
|
|
for (unsigned i = 0; i < 256; i++) {
|
|
hashtab.insert(i, new FlawedHashElement(i));
|
|
}
|
|
EXPECT_EQ(hashtab.size(), 256);
|
|
for (unsigned i = 0; i < 256; i++) {
|
|
auto p = hashtab.find(i);
|
|
EXPECT_FALSE(p == hashtab.end());
|
|
FlawedHashElement& n = *p;
|
|
EXPECT_EQ(n.getKey(), i);
|
|
EXPECT_EQ(n.m_tag, i);
|
|
|
|
delete &n;
|
|
}
|
|
|
|
EXPECT_TRUE(hashtab.empty());
|
|
|
|
for (unsigned i = 0; i < 42; i++) {
|
|
hashtab.insert(i, new FlawedHashElement(i));
|
|
}
|
|
EXPECT_EQ(hashtab.size(), 42);
|
|
for (unsigned i = 0; i < 42; i++) {
|
|
auto p = hashtab.find(i);
|
|
EXPECT_FALSE(p == hashtab.end());
|
|
FlawedHashElement& n = *p;
|
|
EXPECT_EQ(n.getKey(), i);
|
|
EXPECT_EQ(n.m_tag, i);
|
|
}
|
|
|
|
hashtab.destroyAll();
|
|
EXPECT_TRUE(hashtab.empty());
|
|
}
|