#pragma once
#include <cstring>
#include "alloc.hpp"
#include "base.hpp"
#ifdef _DEBUG
#include "Exception.hpp"
#endif
NAMESPACE_SOUP
{
template <typename T>
class IntVector
{
public:
T* m_data = nullptr;
size_t m_capacity = 0;
size_t m_size = 0;
explicit constexpr IntVector() noexcept = default;
explicit IntVector(const IntVector<T>& b) SOUP_EXCAL
: m_capacity(b.m_capacity), m_size(b.m_size)
{
if (m_capacity != 0)
{
m_data = (T*)soup::malloc(m_capacity * sizeof(T));
memcpy(m_data, b.m_data, m_size * sizeof(T));
}
}
explicit IntVector(IntVector<T>&& b) noexcept
: m_data(b.m_data), m_capacity(b.m_capacity), m_size(b.m_size)
{
b.m_data = nullptr;
b.m_capacity = 0;
b.m_size = 0;
}
~IntVector() noexcept
{
free();
}
void operator=(const IntVector<T>& b) SOUP_EXCAL
{
if (m_capacity < b.m_size)
{
free();
m_capacity = b.m_size;
m_data = (T*)soup::malloc(m_capacity * sizeof(T));
}
m_size = b.m_size;
memcpy(m_data, b.m_data, m_size * sizeof(T));
}
void operator=(IntVector<T>&& b) noexcept
{
free();
m_data = b.m_data;
m_size = b.m_size;
m_capacity = b.m_capacity;
b.m_data = nullptr;
b.m_capacity = 0;
b.m_size = 0;
}
[[nodiscard]] constexpr size_t size() const noexcept
{
return m_size;
}
[[nodiscard]] constexpr size_t capacity() const noexcept
{
return m_capacity;
}
[[nodiscard]] SOUP_FORCEINLINE T& operator[](size_t idx) noexcept
{
return m_data[idx];
}
[[nodiscard]] SOUP_FORCEINLINE const T& operator[](size_t idx) const noexcept
{
return m_data[idx];
}
[[nodiscard]] T& at(size_t idx)
#ifndef _DEBUG
noexcept
#endif
{
#ifdef _DEBUG
SOUP_IF_UNLIKELY (idx >= size())
{
SOUP_THROW(Exception("Out of range"));
}
#endif
return m_data[idx];
}
[[nodiscard]] const T& at(size_t idx) const
#ifndef _DEBUG
noexcept
#endif
{
#ifdef _DEBUG
SOUP_IF_UNLIKELY (idx >= size())
{
SOUP_THROW(Exception("Out of range"));
}
#endif
return m_data[idx];
}
void emplaceZeroesFront(size_t elms) noexcept
{
#if true
SOUP_IF_UNLIKELY (m_size + elms > m_capacity)
{
m_capacity = m_size + elms;
auto data = reinterpret_cast<T*>(soup::malloc(m_capacity * sizeof(T)));
memcpy(&data[elms], &m_data[0], m_size * sizeof(T));
memset(&data[0], 0, elms * sizeof(T));
m_data = data;
m_size = m_capacity;
return;
}
#else
while (m_size + elms > m_capacity)
{
makeSpaceForMoreElements();
}
#endif
memmove(&m_data[elms], &m_data[0], m_size * sizeof(T));
memset(&m_data[0], 0, elms * sizeof(T));
m_size += elms;
}
void emplace_back(T val) SOUP_EXCAL
{
SOUP_IF_UNLIKELY (m_size == m_capacity)
{
makeSpaceForMoreElements();
}
m_data[m_size] = val;
++m_size;
}
void erase(size_t first, size_t last) noexcept
{
const auto count = (last - first);
m_size -= count;
if (m_size != first)
{
memcpy(&m_data[first], &m_data[last], (m_size - first) * sizeof(T));
}
}
void eraseFirst(size_t num) noexcept
{
m_size -= num;
memcpy(&m_data[0], &m_data[num], m_size * sizeof(T));
}
void preallocate() SOUP_EXCAL
{
if (m_capacity == 0)
{
m_capacity = (0x1000 / sizeof(T));
m_data = reinterpret_cast<T*>(soup::malloc(m_capacity * sizeof(T)));
}
}
protected:
void makeSpaceForMoreElements() SOUP_EXCAL
{
m_capacity += (0x1000 / sizeof(T));
m_data = reinterpret_cast<T*>(soup::realloc(m_data, m_capacity * sizeof(T)));
}
public:
void clear() noexcept
{
m_size = 0;
}
protected:
void free() noexcept
{
if (m_capacity != 0)
{
soup::free(m_data);
}
}
};
}