334 lines
6.4 KiB
C++
334 lines
6.4 KiB
C++
#pragma once
|
|
|
|
|
|
#include "engine/allocator.h"
|
|
#include "engine/crt.h"
|
|
#include "engine/metaprogramming.h"
|
|
|
|
|
|
namespace Lumix
|
|
{
|
|
template <typename Key, typename Value>
|
|
class AssociativeArray
|
|
{
|
|
public:
|
|
explicit AssociativeArray(IAllocator& allocator)
|
|
: m_allocator(allocator)
|
|
, m_size(0)
|
|
, m_capacity(0)
|
|
, m_keys(nullptr)
|
|
, m_values(nullptr)
|
|
{}
|
|
|
|
|
|
~AssociativeArray()
|
|
{
|
|
callDestructors(m_keys, m_size);
|
|
callDestructors(m_values, m_size);
|
|
m_allocator.deallocate(m_keys);
|
|
}
|
|
|
|
|
|
Value& insert(const Key& key)
|
|
{
|
|
if (m_capacity == m_size) reserve(m_capacity < 4 ? 4 : m_capacity * 2);
|
|
|
|
int i = index(key);
|
|
ASSERT(i >= 0 && ((i < m_size && m_keys[i] != key) || i == m_size));
|
|
memmove(m_keys + i + 1, m_keys + i, sizeof(Key) * (m_size - i));
|
|
memmove(m_values + i + 1, m_values + i, sizeof(Value) * (m_size - i));
|
|
new (NewPlaceholder(), &m_values[i]) Value();
|
|
new (NewPlaceholder(), &m_keys[i]) Key(key);
|
|
++m_size;
|
|
return m_values[i];
|
|
}
|
|
|
|
|
|
template <class _Ty> struct remove_reference
|
|
{
|
|
typedef _Ty type;
|
|
};
|
|
|
|
|
|
template <class _Ty> struct remove_reference<_Ty&>
|
|
{
|
|
typedef _Ty type;
|
|
};
|
|
|
|
|
|
template <class _Ty> struct remove_reference<_Ty&&>
|
|
{
|
|
typedef _Ty type;
|
|
};
|
|
|
|
|
|
template <class _Ty> _Ty&& myforward(typename remove_reference<_Ty>::type& _Arg)
|
|
{
|
|
return (static_cast<_Ty&&>(_Arg));
|
|
}
|
|
|
|
|
|
template <typename... Params> Value& emplace(const Key& key, Params&&... params)
|
|
{
|
|
if (m_capacity == m_size) reserve(m_capacity < 4 ? 4 : m_capacity * 2);
|
|
|
|
int i = index(key);
|
|
ASSERT(i >= 0 && ((i < m_size && m_keys[i] != key) || i == m_size));
|
|
|
|
memmove(m_keys + i + 1, m_keys + i, sizeof(Key) * (m_size - i));
|
|
memmove(m_values + i + 1, m_values + i, sizeof(Value) * (m_size - i));
|
|
new (NewPlaceholder(), &m_values[i]) Value(myforward<Params>(params)...);
|
|
new (NewPlaceholder(), &m_keys[i]) Key(key);
|
|
++m_size;
|
|
|
|
return m_values[i];
|
|
}
|
|
|
|
|
|
int insert(const Key& key, Value&& value)
|
|
{
|
|
if (m_capacity == m_size) reserve(m_capacity < 4 ? 4 : m_capacity * 2);
|
|
|
|
int i = index(key);
|
|
if (i >= 0 && ((i < m_size && m_keys[i] != key) || i == m_size))
|
|
{
|
|
memmove(m_keys + i + 1, m_keys + i, sizeof(Key) * (m_size - i));
|
|
memmove(m_values + i + 1, m_values + i, sizeof(Value) * (m_size - i));
|
|
new (NewPlaceholder(), &m_values[i]) Value(Move(value));
|
|
new (NewPlaceholder(), &m_keys[i]) Key(key);
|
|
++m_size;
|
|
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
int insert(const Key& key, const Value& value)
|
|
{
|
|
if (m_capacity == m_size) reserve(m_capacity < 4 ? 4 : m_capacity * 2);
|
|
|
|
int i = index(key);
|
|
if (i >= 0 && ((i < m_size && m_keys[i] != key) || i == m_size))
|
|
{
|
|
memmove(m_keys + i + 1, m_keys + i, sizeof(Key) * (m_size - i));
|
|
memmove(m_values + i + 1, m_values + i, sizeof(Value) * (m_size - i));
|
|
new (NewPlaceholder(), &m_values[i]) Value(value);
|
|
new (NewPlaceholder(), &m_keys[i]) Key(key);
|
|
++m_size;
|
|
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
bool find(const Key& key, Value& value) const
|
|
{
|
|
int i = find(key);
|
|
if (i < 0)
|
|
{
|
|
return false;
|
|
}
|
|
value = m_values[i];
|
|
return true;
|
|
}
|
|
|
|
|
|
int find(const Key& key) const
|
|
{
|
|
int l = 0;
|
|
int h = m_size - 1;
|
|
while (l < h)
|
|
{
|
|
int mid = (l + h) >> 1;
|
|
if (m_keys[mid] < key)
|
|
{
|
|
l = mid + 1;
|
|
}
|
|
else
|
|
{
|
|
h = mid;
|
|
}
|
|
}
|
|
if (l == h && m_keys[l] == key)
|
|
{
|
|
return l;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
const Value& operator [](const Key& key) const
|
|
{
|
|
int index = find(key);
|
|
if (index >= 0)
|
|
{
|
|
return m_values[index];
|
|
}
|
|
else
|
|
{
|
|
ASSERT(false);
|
|
return m_values[0];
|
|
}
|
|
}
|
|
|
|
|
|
Value& operator [](const Key& key)
|
|
{
|
|
int index = find(key);
|
|
if (index >= 0)
|
|
{
|
|
return m_values[index];
|
|
}
|
|
else
|
|
{
|
|
return m_values[insert(key, Value())];
|
|
}
|
|
}
|
|
|
|
|
|
int size() const
|
|
{
|
|
return m_size;
|
|
}
|
|
|
|
|
|
Value& get(const Key& key)
|
|
{
|
|
int index = find(key);
|
|
ASSERT(index >= 0);
|
|
return m_values[index];
|
|
}
|
|
|
|
const Value& get(const Key& key) const
|
|
{
|
|
int index = find(key);
|
|
ASSERT(index >= 0);
|
|
return m_values[index];
|
|
}
|
|
|
|
Value* begin() { return m_values; }
|
|
Value* end() { return m_values + m_size; }
|
|
const Value* begin() const { return m_values; }
|
|
const Value* end() const { return m_values + m_size; }
|
|
|
|
|
|
Value& at(int index)
|
|
{
|
|
return m_values[index];
|
|
}
|
|
|
|
|
|
const Value& at(int index) const
|
|
{
|
|
return m_values[index];
|
|
}
|
|
|
|
|
|
void clear()
|
|
{
|
|
callDestructors(m_keys, m_size);
|
|
callDestructors(m_values, m_size);
|
|
m_size = 0;
|
|
}
|
|
|
|
|
|
void reserve(int new_capacity)
|
|
{
|
|
if (m_capacity >= new_capacity) return;
|
|
|
|
u8* new_data = (u8*)m_allocator.allocate(new_capacity * (sizeof(Key) + sizeof(Value)));
|
|
|
|
memcpy(new_data, m_keys, sizeof(Key) * m_size);
|
|
memcpy(new_data + sizeof(Key) * new_capacity, m_values, sizeof(Value) * m_size);
|
|
|
|
m_allocator.deallocate(m_keys);
|
|
m_keys = (Key*)new_data;
|
|
m_values = (Value*)(new_data + sizeof(Key) * new_capacity);
|
|
|
|
m_capacity = new_capacity;
|
|
}
|
|
|
|
|
|
const Key& getKey(int index) const
|
|
{
|
|
return m_keys[index];
|
|
}
|
|
|
|
|
|
void eraseAt(int index)
|
|
{
|
|
if (index >= 0 && index < m_size)
|
|
{
|
|
m_values[index].~Value();
|
|
m_keys[index].~Key();
|
|
if (index < m_size - 1)
|
|
{
|
|
memmove(m_keys + index, m_keys + index + 1, sizeof(Key) * (m_size - index - 1));
|
|
memmove(m_values + index, m_values + index + 1, sizeof(Value) * (m_size - index - 1));
|
|
}
|
|
--m_size;
|
|
}
|
|
}
|
|
|
|
|
|
void erase(const Key& key)
|
|
{
|
|
int i = find(key);
|
|
if (i >= 0)
|
|
{
|
|
eraseAt(i);
|
|
}
|
|
}
|
|
|
|
Span<Key> keys() const {
|
|
Span<Key> res;
|
|
res.m_begin = m_keys;
|
|
res.m_end = m_keys + m_size;
|
|
return res;
|
|
}
|
|
|
|
private:
|
|
template <typename T> void callDestructors(T* ptr, int count)
|
|
{
|
|
for (int i = 0; i < count; ++i)
|
|
{
|
|
ptr[i].~T();
|
|
}
|
|
}
|
|
|
|
|
|
int index(const Key& key) const
|
|
{
|
|
int l = 0;
|
|
int h = m_size - 1;
|
|
while (l < h)
|
|
{
|
|
int mid = (l + h) >> 1;
|
|
if (m_keys[mid] < key)
|
|
{
|
|
l = mid + 1;
|
|
}
|
|
else
|
|
{
|
|
h = mid;
|
|
}
|
|
}
|
|
if (l + 1 == m_size && m_keys[l] < key)
|
|
{
|
|
return l + 1;
|
|
}
|
|
return l;
|
|
}
|
|
|
|
private:
|
|
IAllocator& m_allocator;
|
|
Key* m_keys;
|
|
Value* m_values;
|
|
int m_size;
|
|
int m_capacity;
|
|
};
|
|
|
|
|
|
} // namespace Lumix
|