1333fe21d0
clang/clangd complain about the new HashSet type: hash_map.cc:98:20: warning: braces around scalar initializer [-Wbraced-scalar-init] set.insert({10}); ^~~~ The argument to HashSet<int>::insert is just an int, so we don't need braces. Only an actual HashMap would need braces to construct a HashItem object.
204 lines
5.4 KiB
C++
204 lines
5.4 KiB
C++
#include "hash_map.hh"
|
|
|
|
#include "clock.hh"
|
|
#include "string.hh"
|
|
#include "buffer_utils.hh"
|
|
#include "unit_tests.hh"
|
|
|
|
#include <random>
|
|
#include <unordered_map>
|
|
|
|
namespace Kakoune
|
|
{
|
|
|
|
UnitTest test_hash_map{[] {
|
|
// Basic usage
|
|
{
|
|
HashMap<int, int> map;
|
|
map.insert({10, 1});
|
|
map.insert({20, 2});
|
|
kak_assert(map.find_index(0) == -1);
|
|
kak_assert(map.find_index(10) == 0);
|
|
kak_assert(map.find_index(20) == 1);
|
|
kak_assert(map[10] == 1);
|
|
kak_assert(map[20] == 2);
|
|
kak_assert(map[30] == 0);
|
|
map[30] = 3;
|
|
kak_assert(map.find_index(30) == 2);
|
|
map.remove(20);
|
|
kak_assert(map.find_index(30) == 1);
|
|
kak_assert(map.size() == 2);
|
|
}
|
|
|
|
// Replace Multiple entries with the same key
|
|
{
|
|
HashMap<int, int> map;
|
|
map.insert({10, 1});
|
|
map.insert({10, 2});
|
|
kak_assert(map.find_index(10) == 0);
|
|
kak_assert(map.size() == 1);
|
|
kak_assert(map[10] == 2);
|
|
map.remove(10);
|
|
kak_assert(map.find_index(10) == -1);
|
|
}
|
|
|
|
// Multiple entries with the same key
|
|
{
|
|
MultiHashMap<int, int> map;
|
|
map.insert({10, 1});
|
|
map.insert({10, 2});
|
|
kak_assert(map.find_index(10) == 0);
|
|
map.remove(10);
|
|
kak_assert(map.find_index(10) == 0);
|
|
map.remove(10);
|
|
kak_assert(map.find_index(10) == -1);
|
|
map.insert({20, 1});
|
|
map.insert({20, 2});
|
|
map.remove_all(20);
|
|
kak_assert(map.find_index(20) == -1);
|
|
}
|
|
|
|
// Check hash compatible support
|
|
{
|
|
HashMap<String, int> map;
|
|
map.insert({"test", 10});
|
|
kak_assert(map["test"_sv] == 10);
|
|
map.remove("test"_sv);
|
|
}
|
|
|
|
// make sure we get what we expect from the hash map
|
|
{
|
|
std::random_device dev;
|
|
std::default_random_engine re{dev()};
|
|
std::uniform_int_distribution<int> dist;
|
|
|
|
HashMap<int, int> map;
|
|
Vector<std::pair<int, int>> ref;
|
|
|
|
for (int i = 0; i < 100; ++i)
|
|
{
|
|
auto key = dist(re), value = dist(re);
|
|
ref.push_back({key, value});
|
|
map.insert({key, value});
|
|
|
|
std::shuffle(ref.begin(), ref.end(), re);
|
|
for (auto& elem : ref)
|
|
{
|
|
auto it = map.find(elem.first);
|
|
kak_assert(it != map.end() and it->value == elem.second);
|
|
}
|
|
}
|
|
}
|
|
}};
|
|
|
|
UnitTest test_hash_set{[] {
|
|
// Basic usage
|
|
{
|
|
HashSet<int> set;
|
|
set.insert(10);
|
|
set.insert(20);
|
|
kak_assert(set.find_index(0) == -1);
|
|
kak_assert(set.find_index(10) == 0);
|
|
kak_assert(set.find_index(20) == 1);
|
|
kak_assert(set[10] == 10);
|
|
kak_assert(set[20] == 20);
|
|
kak_assert(set[30] == 30);
|
|
set[30];
|
|
kak_assert(set.find_index(30) == 2);
|
|
set.remove(20);
|
|
kak_assert(set.find_index(30) == 1);
|
|
kak_assert(set.size() == 2);
|
|
}
|
|
|
|
// Replace Multiple entries with the same key
|
|
{
|
|
HashSet<int> set;
|
|
set.insert(10);
|
|
set.insert(10);
|
|
kak_assert(set.find_index(10) == 0);
|
|
kak_assert(set.size() == 1);
|
|
set.remove(10);
|
|
kak_assert(set.find_index(10) == -1);
|
|
}
|
|
|
|
// Multiple entries with the same key
|
|
{
|
|
MultiHashSet<int> set;
|
|
set.insert(10);
|
|
set.insert(10);
|
|
kak_assert(set.find_index(10) == 0);
|
|
set.remove(10);
|
|
kak_assert(set.find_index(10) == 0);
|
|
set.remove(10);
|
|
kak_assert(set.find_index(10) == -1);
|
|
set.insert(20);
|
|
set.insert(20);
|
|
set.remove_all(20);
|
|
kak_assert(set.find_index(20) == -1);
|
|
}
|
|
|
|
// Check hash compatible support
|
|
{
|
|
HashSet<String> set;
|
|
set.insert("test");
|
|
kak_assert(set["test"_sv] == "test");
|
|
set.remove("test"_sv);
|
|
}
|
|
}};
|
|
|
|
template<typename Map>
|
|
void do_profile(size_t count, StringView type)
|
|
{
|
|
std::random_device dev;
|
|
std::default_random_engine re{dev()};
|
|
std::uniform_int_distribution<size_t> dist{0, count};
|
|
|
|
Vector<size_t> vec;
|
|
for (size_t i = 0; i < count; ++i)
|
|
vec.push_back(i);
|
|
std::shuffle(vec.begin(), vec.end(), re);
|
|
|
|
Map map;
|
|
auto start = Clock::now();
|
|
|
|
for (auto v : vec)
|
|
map.insert({v, dist(re)});
|
|
auto after_insert = Clock::now();
|
|
|
|
for (size_t i = 0; i < count; ++i)
|
|
++map[dist(re)];
|
|
auto after_read = Clock::now();
|
|
|
|
for (size_t i = 0; i < count; ++i)
|
|
map.erase(dist(re));
|
|
auto after_remove = Clock::now();
|
|
|
|
int c = 0;
|
|
for (auto v : vec)
|
|
{
|
|
auto it = map.find(v);
|
|
if (it != map.end())
|
|
++c;
|
|
}
|
|
auto after_find = Clock::now();
|
|
|
|
using namespace std::chrono;
|
|
write_to_debug_buffer(format("{} ({}) -- inserts: {}us, reads: {}us, remove: {}us, find: {}us ({})", type, count,
|
|
duration_cast<microseconds>(after_insert - start).count(),
|
|
duration_cast<microseconds>(after_read - after_insert).count(),
|
|
duration_cast<microseconds>(after_remove - after_read).count(),
|
|
duration_cast<microseconds>(after_find - after_remove).count(),
|
|
c));
|
|
}
|
|
|
|
void profile_hash_maps()
|
|
{
|
|
for (auto i : { 1000, 10000, 100000, 1000000, 10000000 })
|
|
{
|
|
do_profile<std::unordered_map<size_t, size_t>>(i, "UnorderedMap");
|
|
do_profile<HashMap<size_t, size_t>>(i, " HashMap");
|
|
}
|
|
}
|
|
|
|
}
|