Skip to content

Instantly share code, notes, and snippets.

@Pikachuxxxx
Created September 22, 2025 16:30
Show Gist options
  • Save Pikachuxxxx/97528ea701d38c7d20afb1f736c0ebfe to your computer and use it in GitHub Desktop.
Save Pikachuxxxx/97528ea701d38c7d20afb1f736c0ebfe to your computer and use it in GitHub Desktop.
Custom Arrays for Razix Engine
#pragma once
namespace Razix {
// Forward declarations
template<typename T, size_t N>
class FixedArray;
template<typename T>
class DynamicArray;
template<typename T, size_t SmallSize = 16>
class SmallArray;
//=============================================================================
// FixedArray - Stack allocated, compile-time size
//=============================================================================
template<typename T, size_t N>
class FixedArray
{
public:
using value_type = T;
using size_type = size_t;
using reference = T&;
using const_reference = const T&;
using pointer = T*;
using const_pointer = const T*;
using iterator = T*;
using const_iterator = const T*;
private:
alignas(T) char m_data[sizeof(T) * N];
size_type m_size = 0;
public:
// Constructors
FixedArray() = default;
FixedArray(std::initializer_list<T> init)
{
for (const auto& item: init) {
if (m_size < N) {
push_back(item);
}
}
}
explicit FixedArray(size_type count, const T& value = T{})
{
resize(count, value);
}
// Destructor
~FixedArray()
{
clear();
}
// Copy constructor
FixedArray(const FixedArray& other)
: m_size(other.m_size)
{
for (size_type i = 0; i < m_size; ++i) {
new (data() + i) T(other[i]);
}
}
// Move constructor
FixedArray(FixedArray&& other) noexcept
: m_size(other.m_size)
{
for (size_type i = 0; i < m_size; ++i) {
new (data() + i) T(std::move(other[i]));
}
other.clear();
}
// Assignment operators
FixedArray& operator=(const FixedArray& other)
{
if (this != &other) {
clear();
m_size = other.m_size;
for (size_type i = 0; i < m_size; ++i) {
new (data() + i) T(other[i]);
}
}
return *this;
}
FixedArray& operator=(FixedArray&& other) noexcept
{
if (this != &other) {
clear();
m_size = other.m_size;
for (size_type i = 0; i < m_size; ++i) {
new (data() + i) T(std::move(other[i]));
}
other.clear();
}
return *this;
}
// Element access
reference operator[](size_type index)
{
return data()[index];
}
const_reference operator[](size_type index) const
{
return data()[index];
}
reference at(size_type index)
{
if (index >= m_size) {
// Handle bounds error - could throw or assert
return data()[0]; // Fallback
}
return data()[index];
}
const_reference at(size_type index) const
{
if (index >= m_size) {
return data()[0]; // Fallback
}
return data()[index];
}
reference front() { return data()[0]; }
const_reference front() const { return data()[0]; }
reference back() { return data()[m_size - 1]; }
const_reference back() const { return data()[m_size - 1]; }
// Iterators
iterator begin() { return data(); }
const_iterator begin() const { return data(); }
const_iterator cbegin() const { return data(); }
iterator end() { return data() + m_size; }
const_iterator end() const { return data() + m_size; }
const_iterator cend() const { return data() + m_size; }
// Capacity
bool empty() const { return m_size == 0; }
size_type size() const { return m_size; }
constexpr size_type max_size() const { return N; }
constexpr size_type capacity() const { return N; }
// Modifiers
void clear()
{
for (size_type i = 0; i < m_size; ++i) {
data()[i].~T();
}
m_size = 0;
}
void push_back(const T& value)
{
if (m_size < N) {
new (data() + m_size) T(value);
++m_size;
}
}
void push_back(T&& value)
{
if (m_size < N) {
new (data() + m_size) T(std::move(value));
++m_size;
}
}
template<typename... Args>
reference emplace_back(Args&&... args)
{
if (m_size < N) {
T* ptr = new (data() + m_size) T(std::forward<Args>(args)...);
++m_size;
return *ptr;
}
return back(); // Fallback
}
void pop_back()
{
if (m_size > 0) {
--m_size;
data()[m_size].~T();
}
}
void resize(size_type count, const T& value = T{})
{
if (count > N) count = N;
if (count > m_size) {
for (size_type i = m_size; i < count; ++i) {
new (data() + i) T(value);
}
} else if (count < m_size) {
for (size_type i = count; i < m_size; ++i) {
data()[i].~T();
}
}
m_size = count;
}
// Data access
pointer data() { return reinterpret_cast<T*>(m_data); }
const_pointer data() const { return reinterpret_cast<const T*>(m_data); }
};
//=============================================================================
// DynamicArray - Heap allocated, runtime resizable
//=============================================================================
template<typename T>
class DynamicArray
{
public:
using value_type = T;
using size_type = size_t;
using reference = T&;
using const_reference = const T&;
using pointer = T*;
using const_pointer = const T*;
using iterator = T*;
using const_iterator = const T*;
private:
T* m_data = nullptr;
size_type m_size = 0;
size_type m_capacity = 0;
static constexpr size_type initial_capacity = 4;
static constexpr double growth_factor = 1.5;
void reallocate(size_type new_capacity)
{
T* new_data = static_cast<T*>(operator new(new_capacity * sizeof(T)));
// Move existing elements
for (size_type i = 0; i < m_size; ++i) {
new (new_data + i) T(std::move(m_data[i]));
m_data[i].~T();
}
operator delete(m_data);
m_data = new_data;
m_capacity = new_capacity;
}
public:
// Constructors
DynamicArray() = default;
explicit DynamicArray(size_type count, const T& value = T{})
{
resize(count, value);
}
DynamicArray(std::initializer_list<T> init)
{
reserve(init.size());
for (const auto& item: init) {
push_back(item);
}
}
// Destructor
~DynamicArray()
{
clear();
operator delete(m_data);
}
// Copy constructor
DynamicArray(const DynamicArray& other)
{
reserve(other.m_size);
for (size_type i = 0; i < other.m_size; ++i) {
push_back(other[i]);
}
}
// Move constructor
DynamicArray(DynamicArray&& other) noexcept
: m_data(other.m_data), m_size(other.m_size), m_capacity(other.m_capacity)
{
other.m_data = nullptr;
other.m_size = 0;
other.m_capacity = 0;
}
// Assignment operators
DynamicArray& operator=(const DynamicArray& other)
{
if (this != &other) {
clear();
reserve(other.m_size);
for (size_type i = 0; i < other.m_size; ++i) {
push_back(other[i]);
}
}
return *this;
}
DynamicArray& operator=(DynamicArray&& other) noexcept
{
if (this != &other) {
clear();
operator delete(m_data);
m_data = other.m_data;
m_size = other.m_size;
m_capacity = other.m_capacity;
other.m_data = nullptr;
other.m_size = 0;
other.m_capacity = 0;
}
return *this;
}
// Element access (same as FixedArray)
reference operator[](size_type index) { return m_data[index]; }
const_reference operator[](size_type index) const { return m_data[index]; }
reference at(size_type index)
{
if (index >= m_size) {
return m_data[0]; // Fallback
}
return m_data[index];
}
const_reference at(size_type index) const
{
if (index >= m_size) {
return m_data[0]; // Fallback
}
return m_data[index];
}
reference front() { return m_data[0]; }
const_reference front() const { return m_data[0]; }
reference back() { return m_data[m_size - 1]; }
const_reference back() const { return m_data[m_size - 1]; }
// Iterators
iterator begin() { return m_data; }
const_iterator begin() const { return m_data; }
const_iterator cbegin() const { return m_data; }
iterator end() { return m_data + m_size; }
const_iterator end() const { return m_data + m_size; }
const_iterator cend() const { return m_data + m_size; }
// Capacity
bool empty() const { return m_size == 0; }
size_type size() const { return m_size; }
size_type capacity() const { return m_capacity; }
void reserve(size_type new_capacity)
{
if (new_capacity > m_capacity) {
reallocate(new_capacity);
}
}
void shrink_to_fit()
{
if (m_size < m_capacity) {
reallocate(m_size);
}
}
// Modifiers
void clear()
{
for (size_type i = 0; i < m_size; ++i) {
m_data[i].~T();
}
m_size = 0;
}
void push_back(const T& value)
{
if (m_size >= m_capacity) {
size_type new_capacity = m_capacity == 0 ? initial_capacity
: static_cast<size_type>(m_capacity * growth_factor);
reallocate(new_capacity);
}
new (m_data + m_size) T(value);
++m_size;
}
void push_back(T&& value)
{
if (m_size >= m_capacity) {
size_type new_capacity = m_capacity == 0 ? initial_capacity
: static_cast<size_type>(m_capacity * growth_factor);
reallocate(new_capacity);
}
new (m_data + m_size) T(std::move(value));
++m_size;
}
template<typename... Args>
reference emplace_back(Args&&... args)
{
if (m_size >= m_capacity) {
size_type new_capacity = m_capacity == 0 ? initial_capacity
: static_cast<size_type>(m_capacity * growth_factor);
reallocate(new_capacity);
}
T* ptr = new (m_data + m_size) T(std::forward<Args>(args)...);
++m_size;
return *ptr;
}
void pop_back()
{
if (m_size > 0) {
--m_size;
m_data[m_size].~T();
}
}
void resize(size_type count, const T& value = T{})
{
if (count > m_capacity) {
reserve(count);
}
if (count > m_size) {
for (size_type i = m_size; i < count; ++i) {
new (m_data + i) T(value);
}
} else if (count < m_size) {
for (size_type i = count; i < m_size; ++i) {
m_data[i].~T();
}
}
m_size = count;
}
// Data access
pointer data() { return m_data; }
const_pointer data() const { return m_data; }
};
//=============================================================================
// SmallArray - Small buffer optimization
//=============================================================================
template<typename T, size_t SmallSize>
class SmallArray
{
public:
using value_type = T;
using size_type = size_t;
using reference = T&;
using const_reference = const T&;
using pointer = T*;
using const_pointer = const T*;
using iterator = T*;
using const_iterator = const T*;
private:
union
{
alignas(T) char m_small_data[sizeof(T) * SmallSize];
T* m_large_data;
};
size_type m_size = 0;
size_type m_capacity = SmallSize;
bool m_is_small = true;
static constexpr double growth_factor = 1.5;
void reallocate(size_type new_capacity)
{
T* new_data = static_cast<T*>(operator new(new_capacity * sizeof(T)));
T* old_data = data();
// Move existing elements
for (size_type i = 0; i < m_size; ++i) {
new (new_data + i) T(std::move(old_data[i]));
old_data[i].~T();
}
if (!m_is_small) {
operator delete(m_large_data);
}
m_large_data = new_data;
m_capacity = new_capacity;
m_is_small = false;
}
public:
// Constructors
SmallArray() = default;
explicit SmallArray(size_type count, const T& value = T{})
{
resize(count, value);
}
SmallArray(std::initializer_list<T> init)
{
if (init.size() > SmallSize) {
reserve(init.size());
}
for (const auto& item: init) {
push_back(item);
}
}
// Destructor
~SmallArray()
{
clear();
if (!m_is_small) {
operator delete(m_large_data);
}
}
// Copy constructor
SmallArray(const SmallArray& other)
{
if (other.m_size > SmallSize) {
reserve(other.m_size);
}
for (size_type i = 0; i < other.m_size; ++i) {
push_back(other[i]);
}
}
// Move constructor
SmallArray(SmallArray&& other) noexcept
{
if (other.m_is_small) {
m_size = other.m_size;
m_capacity = SmallSize;
m_is_small = true;
for (size_type i = 0; i < m_size; ++i) {
new (small_data() + i) T(std::move(other.small_data()[i]));
}
} else {
m_large_data = other.m_large_data;
m_size = other.m_size;
m_capacity = other.m_capacity;
m_is_small = false;
other.m_large_data = nullptr;
other.m_size = 0;
other.m_capacity = SmallSize;
other.m_is_small = true;
}
}
// Assignment operators
SmallArray& operator=(const SmallArray& other)
{
if (this != &other) {
clear();
if (!m_is_small) {
operator delete(m_large_data);
m_is_small = true;
m_capacity = SmallSize;
}
if (other.m_size > SmallSize) {
reserve(other.m_size);
}
for (size_type i = 0; i < other.m_size; ++i) {
push_back(other[i]);
}
}
return *this;
}
SmallArray& operator=(SmallArray&& other) noexcept
{
if (this != &other) {
clear();
if (!m_is_small) {
operator delete(m_large_data);
}
if (other.m_is_small) {
m_size = other.m_size;
m_capacity = SmallSize;
m_is_small = true;
for (size_type i = 0; i < m_size; ++i) {
new (small_data() + i) T(std::move(other.small_data()[i]));
}
} else {
m_large_data = other.m_large_data;
m_size = other.m_size;
m_capacity = other.m_capacity;
m_is_small = false;
other.m_large_data = nullptr;
other.m_size = 0;
other.m_capacity = SmallSize;
other.m_is_small = true;
}
}
return *this;
}
// Element access
reference operator[](size_type index) { return data()[index]; }
const_reference operator[](size_type index) const { return data()[index]; }
reference at(size_type index)
{
if (index >= m_size) {
return data()[0]; // Fallback
}
return data()[index];
}
const_reference at(size_type index) const
{
if (index >= m_size) {
return data()[0]; // Fallback
}
return data()[index];
}
reference front() { return data()[0]; }
const_reference front() const { return data()[0]; }
reference back() { return data()[m_size - 1]; }
const_reference back() const { return data()[m_size - 1]; }
// Iterators
iterator begin() { return data(); }
const_iterator begin() const { return data(); }
const_iterator cbegin() const { return data(); }
iterator end() { return data() + m_size; }
const_iterator end() const { return data() + m_size; }
const_iterator cend() const { return data() + m_size; }
// Capacity
bool empty() const { return m_size == 0; }
size_type size() const { return m_size; }
size_type capacity() const { return m_capacity; }
bool is_small() const { return m_is_small; }
void reserve(size_type new_capacity)
{
if (new_capacity > m_capacity) {
reallocate(new_capacity);
}
}
void shrink_to_fit()
{
if (!m_is_small && m_size <= SmallSize) {
T* old_data = m_large_data;
for (size_type i = 0; i < m_size; ++i) {
new (small_data() + i) T(std::move(old_data[i]));
old_data[i].~T();
}
operator delete(old_data);
m_is_small = true;
m_capacity = SmallSize;
} else if (!m_is_small && m_size < m_capacity) {
reallocate(m_size);
}
}
// Modifiers
void clear()
{
T* ptr = data();
for (size_type i = 0; i < m_size; ++i) {
ptr[i].~T();
}
m_size = 0;
}
void push_back(const T& value)
{
if (m_size >= m_capacity) {
size_type new_capacity = static_cast<size_type>(m_capacity * growth_factor);
reallocate(new_capacity);
}
new (data() + m_size) T(value);
++m_size;
}
void push_back(T&& value)
{
if (m_size >= m_capacity) {
size_type new_capacity = static_cast<size_type>(m_capacity * growth_factor);
reallocate(new_capacity);
}
new (data() + m_size) T(std::move(value));
++m_size;
}
template<typename... Args>
reference emplace_back(Args&&... args)
{
if (m_size >= m_capacity) {
size_type new_capacity = static_cast<size_type>(m_capacity * growth_factor);
reallocate(new_capacity);
}
T* ptr = new (data() + m_size) T(std::forward<Args>(args)...);
++m_size;
return *ptr;
}
void pop_back()
{
if (m_size > 0) {
--m_size;
data()[m_size].~T();
}
}
void resize(size_type count, const T& value = T{})
{
if (count > m_capacity) {
reserve(count);
}
if (count > m_size) {
for (size_type i = m_size; i < count; ++i) {
new (data() + i) T(value);
}
} else if (count < m_size) {
for (size_type i = count; i < m_size; ++i) {
data()[i].~T();
}
}
m_size = count;
}
// Data access
pointer data()
{
return m_is_small ? small_data() : m_large_data;
}
const_pointer data() const
{
return m_is_small ? small_data() : m_large_data;
}
private:
T* small_data()
{
return reinterpret_cast<T*>(m_small_data);
}
const T* small_data() const
{
return reinterpret_cast<const T*>(m_small_data);
}
};
} // namespace Razix
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment