Files
2025-05-18 13:04:45 +08:00

203 lines
5.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#if (defined(__AUTORTFM) && __AUTORTFM)
#include "StlAllocator.h"
#include <functional>
#include <type_traits>
#include <unordered_map>
namespace AutoRTFM
{
// A simple structure holding a templated key and value pair.
template<typename KeyType, typename ValueType>
struct TKeyAndValue
{
KeyType Key;
ValueType Value;
};
// HashMap is an unordered hashmap.
// TODO(SOL-7652): This currently wraps a std::unordered_map. Reimplement to
// improve performance and to avoid platform-specific variations in behavior.
template<typename KeyType, typename ValueType, typename Hash = std::hash<KeyType>, typename Equal = std::equal_to<KeyType>>
class THashMap
{
using FInnerMap = std::unordered_map<KeyType, ValueType, Hash, Equal, StlAllocator<std::pair<const KeyType, ValueType>>>;
template<bool bConst>
class TIterator
{
using FInnerIterator = std::conditional_t<bConst, typename FInnerMap::const_iterator, typename FInnerMap::iterator>;
using FKeyAndValue = std::conditional_t<bConst, const TKeyAndValue<const KeyType, ValueType>, TKeyAndValue<const KeyType, ValueType>>;
public:
// Constructor wrapping inner-iterator
TIterator(FInnerIterator InnerIterator) : InnerIterator{InnerIterator} {}
// Copy constructor
TIterator(const TIterator&) = default;
// Move constructor
TIterator(TIterator&&) = default;
// Copy assignment operator
TIterator& operator = (const TIterator& Other) = default;
// Move assignment operator
TIterator& operator = (TIterator&& Other) = default;
FKeyAndValue& operator*() const
{
// Super hacky: Reinterpret a std::pair to a TKeyAndValue so
// that THashMap's iterator has the interface of a TMap instead of a
// std::unordered_map.
// TODO(SOL-7652): Remove this filth when THashMap is reimplemented.
using FPair = std::conditional_t<bConst,
const std::pair<const KeyType, ValueType>,
std::pair<const KeyType, ValueType>>;
FPair* Pair = &*InnerIterator;
FKeyAndValue* KeyAndValue = reinterpret_cast<FKeyAndValue*>(Pair);
static_assert(std::is_same_v<decltype(Pair->first), decltype(KeyAndValue->Key)>);
static_assert(std::is_same_v<decltype(Pair->second), decltype(KeyAndValue->Value)>);
static_assert(offsetof(FPair, first) == offsetof(FKeyAndValue, Key));
static_assert(offsetof(FPair, second) == offsetof(FKeyAndValue, Value));
static_assert(sizeof(FPair) == sizeof(FKeyAndValue));
static_assert(alignof(FPair) == alignof(FKeyAndValue));
return *KeyAndValue;
}
TIterator& operator++()
{
++InnerIterator;
return *this;
}
bool operator == (const TIterator& Other) const
{
return InnerIterator == Other.InnerIterator;
}
bool operator != (const TIterator& Other) const
{
return InnerIterator != Other.InnerIterator;
}
private:
FInnerIterator InnerIterator;
};
public:
using Key = KeyType;
using Value = ValueType;
using Iterator = TIterator</* bConst */ false>;
using ConstIterator = TIterator</* bConst */ true>;
// Constructor
THashMap() = default;
// Destructor
~THashMap() = default;
// Copy constructor
THashMap(const THashMap& Other) = default;
// Move constructor
THashMap(THashMap&& Other) : Map{std::move(Other.Map)}
{
if (this != &Other)
{
Other.Map.clear(); // Ensure Other is cleared.
}
}
// Copy assignment operator
THashMap& operator = (const THashMap& Other) = default;
// Move assignment operator
THashMap& operator = (THashMap&& Other)
{
if (this != &Other)
{
Map = std::move(Other.Map);
Other.Map.clear(); // Ensure Other is cleared.
}
return *this;
}
// Set the value associated with a key, replacing any existing entry with
// the given key.
template<typename K, typename V>
void Add(K&& Key, V&& Value)
{
Map[std::forward<K>(Key)] = std::forward<V>(Value);
}
// Looks up the value with the given key, returning a pointer to the value
// if found or nullptr if not found.
// Warning: The returned pointer will become invalid if the hash map is
// modified.
template<typename K>
ValueType* Find(K&& Key)
{
typename FInnerMap::iterator It = Map.find(std::forward<K>(Key));
return It == Map.end() ? nullptr : &It->second;
}
// Looks up the value with the given key, returning a reference to the
// existing value if found or a reference to a newly added, zero-initialized
// value if not found.
// Warning: The returned reference will become invalid if the hash map is
// modified.
template<typename K>
ValueType& FindOrAdd(K&& Key)
{
return Map[std::forward<K>(Key)];
}
// Removes the entry with the given key. This is a no-op if the hash map
// does not contain an entry with the given key.
void Remove(const KeyType& Key)
{
Map.erase(Key);
}
// Returns true if the hash map contains an entry with the given key.
bool Contains(const KeyType& Key)
{
return Map.count(Key) != 0;
}
// Removes all entries from the hash map, freeing all allocations.
void Empty()
{
Map.clear();
}
// Removes all entries from the hash map.
// TODO(SOL-7652): Preserve any heap allocations made by the hash map.
void Reset()
{
Map.clear();
}
// Returns the number of entries held by the hash map.
size_t Num() const
{
return Map.size();
}
// Returns true if the hash map holds no entries.
bool IsEmpty() const
{
return Map.empty();
}
// Iterator methods
ConstIterator begin() const { return ConstIterator{Map.begin()}; }
ConstIterator end() const { return ConstIterator{Map.end()}; }
Iterator begin() { return Iterator{Map.begin()}; }
Iterator end() { return Iterator{Map.end()}; }
private:
FInnerMap Map;
};
} // AutoRTFM
#endif // (defined(__AUTORTFM) && __AUTORTFM)