Files
unity/crnlib/crn_sparse_array.h
T

346 lines
7.6 KiB
C++

// File: crn_sparse_array.h
// See Copyright Notice and license at the end of inc/crnlib.h
#pragma once
namespace crnlib {
template <typename T, uint Log2N>
class sparse_array_traits {
public:
static inline void* alloc_space(uint size) {
return crnlib_malloc(size);
}
static inline void free_space(void* p) {
crnlib_free(p);
}
static inline void construct_group(T* p) {
scalar_type<T>::construct_array(p, 1U << Log2N);
}
static inline void destruct_group(T* p) {
scalar_type<T>::destruct_array(p, 1U << Log2N);
}
static inline void construct_element(T* p) {
scalar_type<T>::construct(p);
}
static inline void destruct_element(T* p) {
scalar_type<T>::destruct(p);
}
static inline void copy_group(T* pDst, const T* pSrc) {
for (uint j = 0; j < (1U << Log2N); j++)
pDst[j] = pSrc[j];
}
};
template <typename T, uint Log2N = 5, template <typename, uint> class Traits = sparse_array_traits>
class sparse_array : public Traits<T, Log2N> {
public:
enum { N = 1U << Log2N };
inline sparse_array()
: m_size(0), m_num_active_groups(0) {
init_default();
}
inline sparse_array(uint size)
: m_size(0), m_num_active_groups(0) {
init_default();
resize(size);
}
inline sparse_array(const sparse_array& other)
: m_size(0), m_num_active_groups(0) {
init_default();
*this = other;
}
inline ~sparse_array() {
for (uint i = 0; (i < m_groups.size()) && m_num_active_groups; i++)
free_group(m_groups[i]);
deinit_default();
}
bool assign(const sparse_array& other) {
if (this == &other)
return true;
if (!try_resize(other.size()))
return false;
for (uint i = 0; i < other.m_groups.size(); i++) {
const T* p = other.m_groups[i];
T* q = m_groups[i];
if (p) {
if (!q) {
q = alloc_group(true);
if (!q)
return false;
m_groups[i] = q;
}
copy_group(q, p);
} else if (q) {
free_group(q);
m_groups[i] = NULL;
}
}
return true;
}
sparse_array& operator=(const sparse_array& other) {
if (!assign(other)) {
CRNLIB_FAIL("Out of memory");
}
return *this;
}
bool operator==(const sparse_array& other) const {
if (m_size != other.m_size)
return false;
for (uint i = 0; i < m_size; i++)
if (!((*this)[i] == other[i]))
return false;
return true;
}
bool operator<(const sparse_array& rhs) const {
const uint min_size = math::minimum(m_size, rhs.m_size);
uint i;
for (i = 0; i < min_size; i++)
if (!((*this)[i] == rhs[i]))
break;
if (i < min_size)
return (*this)[i] < rhs[i];
return m_size < rhs.m_size;
}
void clear() {
if (m_groups.size()) {
for (uint i = 0; (i < m_groups.size()) && m_num_active_groups; i++)
free_group(m_groups[i]);
m_groups.clear();
}
m_size = 0;
CRNLIB_ASSERT(!m_num_active_groups);
}
bool try_resize(uint size) {
if (m_size == size)
return true;
const uint new_num_groups = (size + N - 1) >> Log2N;
if (new_num_groups != m_groups.size()) {
for (uint i = new_num_groups; i < m_groups.size(); i++)
free_group(m_groups[i]);
if (!m_groups.try_resize(new_num_groups))
return false;
}
m_size = size;
return true;
}
void resize(uint size) {
if (!try_resize(size)) {
CRNLIB_FAIL("Out of memory");
}
}
inline uint size() const { return m_size; }
inline bool empty() const { return 0 == m_size; }
inline uint capacity() const { return m_groups.size(); }
inline const T& operator[](uint i) const {
CRNLIB_ASSERT(i < m_size);
const T* p = m_groups[i >> Log2N];
const void* t = m_default;
return p ? p[i & (N - 1)] : *reinterpret_cast<const T*>(t);
}
inline const T* get(uint i) const {
CRNLIB_ASSERT(i < m_size);
const T* p = m_groups[i >> Log2N];
return p ? &p[i & (N - 1)] : NULL;
}
inline T* get(uint i) {
CRNLIB_ASSERT(i < m_size);
T* p = m_groups[i >> Log2N];
return p ? &p[i & (N - 1)] : NULL;
}
inline bool is_present(uint i) const {
CRNLIB_ASSERT(i < m_size);
return m_groups[i >> Log2N] != NULL;
}
inline uint get_num_groups() const { return m_groups.size(); }
inline const T* get_group(uint group_index) const {
return m_groups[group_index];
}
inline T* get_group(uint group_index) {
return m_groups[group_index];
}
inline uint get_group_size() const {
return N;
}
inline T* ensure_valid(uint index) {
CRNLIB_ASSERT(index <= m_size);
const uint group_index = index >> Log2N;
if (group_index >= m_groups.size()) {
T* p = alloc_group(true);
if (!p)
return NULL;
if (!m_groups.try_push_back(p)) {
free_group(p);
return NULL;
}
}
T* p = m_groups[group_index];
if (!p) {
p = alloc_group(true);
if (!p)
return NULL;
m_groups[group_index] = p;
}
m_size = math::maximum(index + 1, m_size);
return p + (index & (N - 1));
}
inline bool set(uint index, const T& obj) {
T* p = ensure_valid(index);
if (!p)
return false;
*p = obj;
return true;
}
inline void push_back(const T& obj) {
if (!set(m_size, obj)) {
CRNLIB_FAIL("Out of memory");
}
}
inline bool try_push_back(const T& obj) {
return set(m_size, obj);
}
inline void pop_back() {
CRNLIB_ASSERT(m_size);
if (m_size)
resize(m_size - 1);
}
inline void unset_range(uint start, uint num) {
if (!num)
return;
CRNLIB_ASSERT((start + num) <= capacity());
const uint num_to_skip = math::minimum(math::get_align_up_value_delta(start, N), num);
num -= num_to_skip;
const uint first_group = (start + num_to_skip) >> Log2N;
const uint num_groups = num >> Log2N;
for (uint i = 0; i < num_groups; i++) {
T* p = m_groups[first_group + i];
if (p) {
free_group(p);
m_groups[i] = NULL;
}
}
}
inline void unset_all() {
unset_range(0, m_groups.size() << Log2N);
}
inline void swap(sparse_array& other) {
utils::swap(m_size, other.m_size);
m_groups.swap(other.m_groups);
utils::swap(m_num_active_groups, other.m_num_active_groups);
}
private:
uint m_size;
uint m_num_active_groups;
crnlib::vector<T*> m_groups;
uint64 m_default[(sizeof(T) + sizeof(uint64) - 1) / sizeof(uint64)];
inline T* alloc_group(bool nofail = false) {
T* p = static_cast<T*>(sparse_array_traits<T, Log2N>::alloc_space(N * sizeof(T)));
if (!p) {
if (nofail)
return NULL;
CRNLIB_FAIL("Out of memory");
}
sparse_array_traits<T, Log2N>::construct_group(p);
m_num_active_groups++;
return p;
}
inline void free_group(T* p) {
if (p) {
CRNLIB_ASSERT(m_num_active_groups);
m_num_active_groups--;
sparse_array_traits<T, Log2N>::destruct_group(p);
sparse_array_traits<T, Log2N>::free_space(p);
}
}
inline void init_default() {
sparse_array_traits<T, Log2N>::construct_element(reinterpret_cast<T*>(m_default));
}
inline void deinit_default() {
sparse_array_traits<T, Log2N>::destruct_element(reinterpret_cast<T*>(m_default));
}
};
} // namespace crnlib