EHS/include/ehs/Vector.h

639 lines
14 KiB
C
Raw Normal View History

2024-02-05 22:25:30 -08:00
#pragma once
#include "BaseObj.h"
#include "Types.h"
#include "Util.h"
#include <initializer_list>
#include <utility>
namespace ehs
{
/// An array with extra memory pre-allocated for fast pushes.
/// @tparam T Array data type to use.
/// @tparam N Number data type to use.
/// @note If extra memory is set to five then each time that memory is filled it will add five extra.
template<typename T, typename N = UInt_64>
class Vector : public BaseObj
{
protected:
N rawSize;
N size;
N stride;
T* data;
public:
/// Frees any data created on the heap.
~Vector() override
{
delete[] data;
}
/// Default members initialization.
Vector()
: rawSize(0), size(0), stride(5), data(nullptr)
{
AddType("Vector");
}
/// Initializes members for pre-allocated memory to write to later.
/// @param [in] size The size of memory to pre-allocate.
/// @param [in] stride The stride size of memory to pre-allocate.
Vector(const N size, const N stride)
: rawSize(size + stride), size(size), stride(stride), data(new T[rawSize])
{
AddType("Vector");
}
/// Initializes this vector with an initializer list object.
/// @param [in] list The given initializer list.
/// @param [in] stride The extra amount of memory to allocate.
Vector(std::initializer_list<T> list, const N stride = 5)
: rawSize(0), size(list.size()), stride(stride), data(nullptr)
{
AddType("Vector");
if (stride)
{
rawSize = list.size() / stride * stride;
if (list.size() % stride)
rawSize += stride;
}
else
{
rawSize = list.size();
}
data = new T[rawSize];
N i = 0;
for (auto v = list.begin(); v != list.end(); ++v)
data[i++] = std::move(*v);
}
/// Initializes members with given C-style array.
/// @param [in] data The C-style array.
/// @param [in] size The size of the given C-style array.
/// @param [in] stride The size of the extra memory allocated.
Vector(const T* data, const N size, const N stride)
: rawSize(0), size(size), stride(stride), data(nullptr)
{
AddType("Vector");
if (stride)
{
rawSize = size / stride * stride;
if (size % stride)
rawSize += stride;
}
else
{
rawSize = size;
}
data = new T[rawSize];
for (N i = 0; i < size; ++i)
this->data[i] = data[i];
}
/// Copies all members from the given vector object.
/// @param [in] vec The vector object to copy from.
Vector(const Vector& vec)
: BaseObj(vec), rawSize(vec.rawSize), size(vec.size), stride(vec.stride), data(new T[rawSize])
{
for (N i = 0; i < size; ++i)
data[i] = vec.data[i];
}
Vector(Vector&& vec) noexcept
: BaseObj((BaseObj&&)vec), rawSize(vec.rawSize), size(vec.size), stride(vec.stride), data(vec.data)
{
vec.rawSize = 0;
vec.size = 0;
vec.stride = 0;
vec.data = nullptr;
}
/// Copies all members from the given vector object.
/// @param [in] vec The vector object to copy from.
/// @returns The vector that has been assigned to.
Vector& operator=(const Vector& vec)
{
if (this == &vec)
return *this;
BaseObj::operator=(vec);
rawSize = vec.rawSize;
size = vec.size;
stride = vec.stride;
delete[] data;
data = new T[rawSize];
for (N i = 0; i < size; ++i)
data[i] = vec.data[i];
return *this;
}
Vector& operator=(Vector&& vec) noexcept
{
if (this == &vec)
return *this;
BaseObj::operator=((BaseObj&&)vec);
rawSize = vec.rawSize;
size = vec.size;
stride = vec.stride;
delete[] data;
data = vec.data;
vec.rawSize = 0;
vec.size = 0;
vec.stride = 0;
vec.data = nullptr;
return *this;
}
bool operator==(const Vector& in) const
{
if (size != in.size)
return false;
return Util::Compare(data, in.data, size);
}
bool operator!=(const Vector& in) const
{
if (size != in.size)
return true;
return !Util::Compare(data, in.data, size);
}
/// Adds a given initializer list at the end of the vector.
/// @param [in] value The given initializer list to push to the end of the vector.
Vector& operator+=(std::initializer_list<T> value)
{
if (size + value.size() >= rawSize)
{
if (stride)
{
rawSize = (size + value.size()) / stride * stride;
if ((size + value.size()) % stride)
rawSize += stride;
}
else
{
rawSize = size + value.size();
}
T* result = new T[rawSize];
for (N i = 0; i < size; ++i)
result[i] = std::move(std::move(data[i]));
delete[] data;
data = result;
}
for (auto v = value.begin(); v != value.end(); ++v)
data[size++] = std::move(*v);
return *this;
}
/// Adds a given value at the end of the vector.
/// @param [in] value The given value to push to the end of the vector.
Vector& operator+=(const T value)
{
if (size + 1 >= rawSize)
{
if (stride)
rawSize = size + stride;
else
rawSize = size + 1;
T* result = new T[rawSize];
for (N i = 0; i < size; ++i)
result[i] = std::move(data[i]);
delete[] data;
data = result;
}
data[size++] = std::move(value);
return *this;
}
/// Retrieves the raw C-style array from casting an array object.
operator T* () const
{
return data;
}
/// Retrieves the size of the vector object including the extra memory allocated.
/// @returns The raw size.
N RawSize() const
{
return rawSize;
}
/// Retrieves the size of the array not including the extra memory allocated.
/// @returns The size.
N Size() const
{
return size;
}
/// Retrieves the size of the extra memory allocated.
/// @returns The extra size.
N Stride() const
{
return stride;
}
/// Retrieves the index at the end of the array.
/// @returns The index.
N End() const
{
return size ? size - 1 : size;
}
/// Copies a vector object with offsets.
/// @param [in] dstOffset The offset index to copy the given vector object to.
/// @param [in] src The given vector object.
/// @param [in] srcOffset The offset index from the given vector object to copy from.
void Copy(const N dstOffset, Vector<T, N> src, const N srcOffset = 0)
{
for (N i = 0; i < src.Size() - srcOffset; ++i)
data[i + dstOffset] = std::move(src[i + srcOffset]);
}
/// Copies a C-style array with offsets.
/// @param [in] dstOffset The offset index to copy the given C-style array to.
/// @param [in] src The given C-style array.
/// @param [in] size The size from the given C-style array to copy.
void Copy(const N dstOffset, const T* src, const N inSize)
{
if (dstOffset + inSize > size)
return;
for (N i = 0; i < inSize; ++i)
data[i + dstOffset] = src[i];
}
/// Swaps two values in the vector.
/// @param [in] a The first index to swap with.
/// @param [in] b The second index to swap with.
void Swap(N a, N b)
{
T tmp = std::move(data[a]);
data[a] = std::move(data[b]);
data[b] = std::move(tmp);
}
/// Inserts a value at a specified index that is available.
/// @param [in] index The index to insert the value at.
/// @param [in] value The given value to insert.
void Insert(const N index, const T value)
{
N newSize = 0;
if (index > size - 1)
newSize = size + ((index + 1) - size);
else
newSize = size + 1;
if (newSize >= rawSize)
{
if (stride)
rawSize += newSize + stride;
else
rawSize = newSize;
T* result = new T[rawSize];
for (N i = 0; i < index; ++i)
result[i] = std::move(data[i]);
result[index] = std::move(value);
for (N i = index; i < size; ++i)
result[i + 1] = std::move(data[i]);
delete[] data;
data = result;
}
else
{
for (N i = index; i < size; ++i)
data[i + 1] = std::move(data[i]);
data[index] = std::move(value);
}
size = newSize;
}
/// Removes a value at a specified index.
/// @param [in] index The index to remove the value at.
/// @returns The removed data.
T Remove(const N index)
{
T popped = {};
if (!size || index >= size)
return popped;
popped = std::move(data[index]);
if (!stride)
{
rawSize = size - 1;
T* result = new T[rawSize];
for (N i = 0; i < index; ++i)
result[i] = std::move(data[i]);
for (N i = index + 1; i < size; ++i)
result[i - 1] = std::move(data[i]);
delete[] data;
data = result;
}
else if (rawSize - stride && size - 1 <= rawSize - stride)
{
rawSize -= stride;
T* result = new T[rawSize];
for (N i = 0; i < index; ++i)
result[i] = std::move(data[i]);
for (N i = index + 1; i < size; ++i)
result[i - 1] = std::move(data[i]);
delete[] data;
data = result;
}
else
{
for (N i = index + 1; i < size; ++i)
data[i - 1] = std::move(data[i]);
}
--size;
return popped;
}
/// Adds a given C-style array at the end of the vector.
/// @param [in] value The given C-style array to push to the end of the vector.
/// @param [in] size The size of the given C-style array.
void Push(const T* const value, const N size)
{
if (this->size + size >= rawSize)
{
if (stride)
{
rawSize = (this->size + size()) / stride * stride;
if ((this->size + size) % stride)
rawSize += stride;
}
else
{
rawSize = this->size + size;
}
T* result = new T[rawSize];
for (N i = 0; i < size; ++i)
result[i] = std::move(data[i]);
delete[] data;
data = result;
}
for (N i = 0; i < size; ++i)
data[this->size + i] = value[i];
this->size += size;
}
/// Adds a given vector object at the end of the vector.
/// @param [in] value The given vector object to push to the end of the vector.
void Push(Vector<T> value)
{
if (size + value.size >= rawSize)
{
if (stride)
{
rawSize = (size + value.size) / stride * stride;
if ((size + value.size) % stride)
rawSize += stride;
}
else
{
rawSize = size + value.size;
}
T* result = new T[rawSize];
for (N i = 0; i < size; ++i)
result[i] = std::move(data[i]);
delete[] data;
data = result;
}
for (N i = 0; i < value.size; ++i)
data[size + i] = std::move(value.data[i]);
size += value.size;
}
/// Adds a given initializer at the end of the vector.
/// @param [in] value The given initializer list to push to the end of the vector.
void Push(std::initializer_list<T> value)
{
if (size + value.size() >= rawSize)
{
if (stride)
{
rawSize = (size + value.size()) / stride * stride;
if ((size + value.size()) % stride)
rawSize += stride;
}
else
{
rawSize = size + value.size();
}
T* result = new T[rawSize];
for (N i = 0; i < size; ++i)
result[i] = std::move(data[i]);
delete[] data;
data = result;
}
for (auto v = value.begin(); v != value.end(); ++v)
data[size++] = std::move(*v);
}
/// Adds a given value at the end of the vector.
/// @param [in] value The given value to push to the end of the vector.
void Push(T value)
{
if (size + 1 >= rawSize)
{
if (stride)
rawSize += stride;
else
rawSize = size + 1;
T* result = new T[rawSize];
for (N i = 0; i < size; ++i)
result[i] = std::move(data[i]);
delete[] data;
data = result;
}
data[size++] = std::move(value);
}
/// Much like the stack it pops a value at the end of the vector.
/// @returns The removed value.
T Pop()
{
T popped = {};
if (!size)
return popped;
popped = std::move(data[--size]);
if (!stride)
{
rawSize = size;
T* result = new T[rawSize];
for (N i = 0; i < size; ++i)
result[i] = std::move(data[i]);
delete[] data;
data = result;
}
else if (rawSize - stride && size < rawSize - stride)
{
rawSize -= stride;
T* result = new T[rawSize];
for (N i = 0; i < size; ++i)
result[i] = std::move(data[i]);
delete[] data;
data = result;
}
return popped;
}
/// Will swap the value at the given index with the value at the end of the vector and pops it.
/// @param [in] index The index of the value to swap with.
/// @returns The removed value.
T Pop(const N index)
{
if (!size)
return {};
N lastIndex = size - 1;
if (index < lastIndex)
Swap(index, lastIndex);
return Pop();
}
/// Resizes the vector while keeping its alignment.
/// @param [in] newSize The size to change to.
void Resize(const N newSize)
{
if (newSize == size)
return;
if (stride)
{
rawSize = newSize / stride * stride;
if (newSize % stride)
rawSize += stride;
}
else
{
rawSize = newSize;
}
T* result = new T[rawSize];
for (N i = 0; i < size && i < newSize; ++i)
result[i] = std::move(data[i]);
delete[] data;
data = result;
size = newSize;
}
/// Removes any extra allocated memory.
void ExactSize()
{
if (!stride)
return;
stride = 0;
if (size)
{
rawSize = size;
T* result = new T[rawSize];
for (N i = 0; i < size; ++i)
result[i] = std::move(data[i]);
delete[] data;
data = result;
}
else
{
rawSize = 0;
delete[] data;
data = nullptr;
}
}
/// Releases the resources of the vector.
void Clear()
{
if (!size)
return;
rawSize = stride;
size = 0;
delete[] data;
if (rawSize)
data = new T[rawSize];
else
data = nullptr;
}
};
}