SHADE_Y3/SHADE_Engine/src/Resource/SparseSet.hpp

146 lines
5.2 KiB
C++

/************************************************************************************//*!
\file SparseSet.h
\author Tng Kah Wei, kahwei.tng, 390009620
\par email: kahwei.tng\@digipen.edu
\date Jul 30, 2021
\brief Contains the declaration and template implementation of a generic
SparseSet.
Copyright (C) 2021 DigiPen Institute of Technology.
Reproduction or disclosure of this file or its contents without the prior written consent
of DigiPen Institute of Technology is prohibited.
*//*************************************************************************************/
#pragma once
// Primary Header
#include "SparseSet.h"
namespace SHADE
{
/*---------------------------------------------------------------------------------*/
/* SparseSet: Constructor */
/*---------------------------------------------------------------------------------*/
template<typename T>
SparseSet<T>::SparseSet()
{
static_assert(std::is_move_assignable_v<T>, "Objects stored by the SparseSet must be move-assignable.");
static_assert(std::is_move_constructible_v<T>, "Objects stored by the SparseSet must be move-constructible.");
}
/*---------------------------------------------------------------------------------*/
/* SparseSet: Usage Functions */
/*---------------------------------------------------------------------------------*/
template<typename T>
T& SparseSet<T>::get(index_type idx)
{
return at(idx);
}
template<typename T>
const T& SparseSet<T>::get(index_type idx) const
{
return at(idx);
}
template<typename T>
bool SparseSet<T>::contains(index_type idx) const
{
if (idx >= sparseArray.size())
return false;
return sparseArray[idx] != INVALID;
}
template<typename T>
void SparseSet<T>::erase(index_type idx)
{
if (idx >= sparseArray.size() || !contains(idx))
throw std::invalid_argument("An element at this index does not exist!");
// Swap with the last element
const int BACK_IDX = denseArray.size() - 1;
std::swap(denseArray[sparseArray[idx]], denseArray.back());
denseArray.pop_back();
// Update the sparse set by swapping the indices
sparseArray[inverseSparseArray[BACK_IDX]] = sparseArray[idx];
inverseSparseArray[sparseArray[idx]] = inverseSparseArray[BACK_IDX];
// Mark the "removed" values as invalid
sparseArray[idx] = INVALID;
inverseSparseArray[BACK_IDX] = INVALID;
// Reset the dense array value
}
template<typename T>
SparseSet<T>::reference SparseSet<T>::at(index_type idx)
{
return const_cast<reference>(static_cast<const SparseSet<T>&>(*this).at(idx));
}
template<typename T>
SparseSet<T>::const_reference SparseSet<T>::at(index_type idx) const
{
// Range Check
if (idx >= sparseArray.size() || !contains(idx))
throw std::out_of_range("An element at this index does not exist!");
return denseArray[sparseArray[idx]];
}
template<typename T>
SparseSet<T>::size_type SparseSet<T>::size() const
{
return denseArray.size();
}
template<typename T>
bool SparseSet<T>::empty() const
{
return denseArray.empty();
}
template<typename T>
void SparseSet<T>::clear()
{
// Default initialize
denseArray.clear();
// Invalidate the sparse array
std::fill(sparseArray.begin(), sparseArray.end(), INVALID);
std::fill(inverseSparseArray.begin(), inverseSparseArray.end(), INVALID);
}
template<typename T>
template<typename ...Args>
SparseSet<T>::reference SparseSet<T>::insert(index_type idx, Args && ...args)
{
// We need to resize the array
if (idx >= sparseArray.size())
{
const int NEW_SIZE = idx + 1;
sparseArray.resize(NEW_SIZE, INVALID);
inverseSparseArray.resize(NEW_SIZE, INVALID);
}
else if (contains(idx))
{
throw std::invalid_argument("An element at this index already exists!");
}
// Insert to the back
auto& insertedElem = denseArray.emplace_back(std::forward<Args>(args) ...);
// Update sparse and inverse sparse arrays
const index_type DENSE_IDX = denseArray.size() - 1;
sparseArray[idx] = DENSE_IDX;
inverseSparseArray[DENSE_IDX] = idx;
return insertedElem;
}
/*---------------------------------------------------------------------------------*/
/* Convenience Operator Functions */
/*---------------------------------------------------------------------------------*/
template<typename T>
inline typename SparseSet<T>::reference SparseSet<T>::operator[](index_type idx)
{
return at(idx);
}
template<typename T>
inline typename SparseSet<T>::const_reference SparseSet<T>::operator[](index_type idx) const
{
return at(idx);
}
}