4#include <initializer_list>
22 Backing = (T*)std::malloc(count *
sizeof(T));
40 operator bool()const noexcept {
46 return Backing[index];
85 static constexpr size_type growth_factor_numerator = 3;
86 static constexpr size_type growth_factor_denominator = 2;
88 void deallocate() noexcept {
95 T* allocate_memory(
size_type count)
noexcept {
96 if (count == 0)
return nullptr;
104 if (count > SIZE_MAX /
sizeof(T)) {
108 void* ptr = std::malloc(count *
sizeof(T));
109 return static_cast<T*
>(ptr);
112 void destroy_range(T* first, T* last)
noexcept {
113 for (; first != last; ++first) {
118 void destroy_all() noexcept {
120 destroy_range(m_data, m_data + m_size);
126 const size_type old_capacity = m_capacity;
129 if (min_capacity > max_cap) {
133 if (old_capacity > max_cap - old_capacity / 2) {
137 const size_type new_capacity = old_capacity + old_capacity / 2;
138 return (new_capacity < min_capacity) ? min_capacity : new_capacity;
143 if (new_capacity == 0) {
152 T* new_data = allocate_memory(new_capacity);
159 bool move_success =
true;
161 if constexpr (std::is_nothrow_move_constructible_v<T>) {
162 move_success = uninitialized_move(m_data, m_data + m_size, new_data);
165 move_success = uninitialized_copy(m_data, m_data + m_size, new_data);
178 m_capacity = new_capacity;
183 template<
typename InputIt>
184 bool uninitialized_copy(InputIt first, InputIt last, T* dest)
noexcept {
186 for (; first != last; ++first, ++current) {
187 if constexpr (std::is_nothrow_copy_constructible_v<T>) {
188 new (current) T(*first);
193 new (current) T(*first);
199 template<
typename InputIt>
200 bool uninitialized_move(InputIt first, InputIt last, T* dest)
noexcept {
202 for (; first != last; ++first, ++current) {
203 new (current) T(std::move(*first));
208 template<
typename... Args>
209 bool construct_at(T* ptr, Args&&... args)
noexcept {
210 if constexpr (std::is_nothrow_constructible_v<T, Args...>) {
211 new (ptr) T(std::forward<Args>(args)...);
216 new (ptr) T(std::forward<Args>(args)...);
222 void make_invalid() noexcept {
233 List() noexcept : m_data(
nullptr), m_size(0), m_capacity(0), m_valid(true) {}
235 explicit List(
size_type count) noexcept : m_data(
nullptr), m_size(0), m_capacity(0), m_valid(
true) {
237 m_data = allocate_memory(count);
245 if (!construct_at(m_data + i)) {
246 destroy_range(m_data, m_data + i);
255 List(
size_type count,
const T& value) noexcept : m_data(
nullptr), m_size(0), m_capacity(0), m_valid(
true) {
257 m_data = allocate_memory(count);
265 if (!construct_at(m_data + i, value)) {
266 destroy_range(m_data, m_data + i);
275 template<
typename InputIt>
276 List(InputIt first, InputIt last) noexcept : m_data(
nullptr), m_size(0), m_capacity(0), m_valid(
true) {
277 if constexpr (std::is_same_v<typename std::iterator_traits<InputIt>::iterator_category,
278 std::random_access_iterator_tag>) {
279 const size_type count = std::distance(first, last);
281 m_data = allocate_memory(count);
287 if (!uninitialized_copy(first, last, m_data)) {
296 for (; first != last; ++first) {
305 List(std::initializer_list<T> init) noexcept :
List(init.begin(), init.end()) {}
308 List(
const List& other) noexcept : m_data(
nullptr), m_size(0), m_capacity(0), m_valid(
true) {
309 if (!other.m_valid) {
314 if (other.m_size > 0) {
315 m_data = allocate_memory(other.m_size);
320 m_capacity = other.m_size;
321 if (!uninitialized_copy(other.m_data, other.m_data + other.m_size, m_data)) {
325 m_size = other.m_size;
331 : m_data(other.m_data), m_size(other.m_size), m_capacity(other.m_capacity), m_valid(other.m_valid) {
332 other.m_data =
nullptr;
334 other.m_capacity = 0;
335 other.m_valid =
true;
351 if (
this != &other) {
352 if (!other.m_valid) {
357 if (other.m_size <= m_capacity) {
359 size_type common_size = std::min(m_size, other.m_size);
362 for (
size_type i = 0; i < common_size; ++i) {
363 m_data[i] = other.m_data[i];
367 for (
size_type i = common_size; i < other.m_size; ++i) {
368 if (!construct_at(m_data + i, other.m_data[i])) {
369 destroy_range(m_data + common_size, m_data + i);
370 m_size = common_size;
377 destroy_range(m_data + other.m_size, m_data + m_size);
379 m_size = other.m_size;
395 if (
this != &other) {
399 m_data = other.m_data;
400 m_size = other.m_size;
401 m_capacity = other.m_capacity;
402 m_valid = other.m_valid;
404 other.m_data =
nullptr;
406 other.m_capacity = 0;
407 other.m_valid =
true;
413 assign(init.begin(), init.end());
421 if (count <= m_capacity) {
422 size_type common_size = std::min(m_size, count);
424 for (
size_type i = 0; i < common_size; ++i) {
428 for (
size_type i = common_size; i < count; ++i) {
429 if (!construct_at(m_data + i, value)) {
430 destroy_range(m_data + common_size, m_data + i);
431 m_size = common_size;
436 destroy_range(m_data + count, m_data + m_size);
441 List temp(count, value);
450 template<
typename InputIt>
454 List temp(first, last);
463 return assign(init.begin(), init.end());
492 if (!m_valid || pos >= m_size)
return nullptr;
497 if (!m_valid || pos >= m_size)
return nullptr;
510 return m_data[m_size - 1];
514 return m_data[m_size - 1];
521 const T*
data() const noexcept {
552 return std::numeric_limits<size_type>::max() /
sizeof(T);
557 if (new_capacity > m_capacity) {
558 return reallocate(new_capacity);
569 if (m_size < m_capacity) {
570 return reallocate(m_size);
586 if (m_size == m_capacity) {
587 size_type new_capacity = calculate_growth(m_size + 1);
590 Result result = reallocate(new_capacity);
594 if (!construct_at(m_data + m_size, value)) {
604 if (m_size == m_capacity) {
605 size_type new_capacity = calculate_growth(m_size + 1);
608 Result result = reallocate(new_capacity);
612 if (!construct_at(m_data + m_size, std::move(value))) {
619 template<
typename... Args>
623 if (m_size == m_capacity) {
624 size_type new_capacity = calculate_growth(m_size + 1);
627 Result result = reallocate(new_capacity);
631 if (!construct_at(m_data + m_size, std::forward<Args>(args)...)) {
650 if (count < m_size) {
651 destroy_range(m_data + count, m_data + m_size);
654 else if (count > m_size) {
655 if (count > m_capacity) {
656 Result result = reallocate(count);
660 for (
size_type i = m_size; i < count; ++i) {
661 if (!construct_at(m_data + i)) {
662 destroy_range(m_data + m_size, m_data + i);
674 if (count < m_size) {
675 destroy_range(m_data + count, m_data + m_size);
678 else if (count > m_size) {
679 if (count > m_capacity) {
680 Result result = reallocate(count);
684 for (
size_type i = m_size; i < count; ++i) {
685 if (!construct_at(m_data + i, value)) {
686 destroy_range(m_data + m_size, m_data + i);
696 std::swap(m_data, other.m_data);
697 std::swap(m_size, other.m_size);
698 std::swap(m_capacity, other.m_capacity);
699 std::swap(m_valid, other.m_valid);
707 if (m_size == m_capacity) {
708 size_type new_capacity = calculate_growth(m_size + 1);
711 Result result = reallocate(new_capacity);
716 for (
size_type i = m_size; i > pos; --i) {
718 if (!construct_at(m_data + i, std::move(m_data[i - 1]))) {
723 m_data[i] = std::move(m_data[i - 1]);
732 if (!construct_at(m_data + pos, value)) {
746 for (
size_type i = pos; i < m_size - 1; ++i) {
747 m_data[i] = std::move(m_data[i + 1]);
760 if (!lhs.is_valid() || !rhs.is_valid())
return false;
762 if (lhs.size() != rhs.size())
return false;
764 for (
size_t i = 0; i < lhs.size(); ++i) {
765 if (lhs[i] != rhs[i])
return false;
772 return !(lhs == rhs);
A generic dynamic array container that manages a sequence of elements of type T, providing memory man...
List & operator=(List &&other) noexcept
List(size_type count, const T &value) noexcept
const_reverse_iterator crbegin() const noexcept
const T * data() const noexcept
List(const List &other) noexcept
List(List &&other) noexcept
const T & const_reference
List(size_type count) noexcept
Result assign(size_type count, const T &value) noexcept
void swap(List &other) noexcept
Result at(size_type pos, const_reference &out) const noexcept
reference back() noexcept
bool empty() const noexcept
const_iterator cbegin() const noexcept
std::ptrdiff_t difference_type
Result pop_back() noexcept
Result at(size_type pos, reference &out) noexcept
Result resize(size_type count) noexcept
iterator begin() noexcept
Result resize(size_type count, const T &value) noexcept
const_iterator end() const noexcept
const_iterator begin() const noexcept
Result push_back(const T &value) noexcept
List(std::initializer_list< T > init) noexcept
Result push_back(T &&value) noexcept
Result shrink_to_fit() noexcept
reverse_iterator rbegin() noexcept
reference front() noexcept
Result assign(std::initializer_list< T > init) noexcept
List(InputIt first, InputIt last) noexcept
const_reverse_iterator rbegin() const noexcept
size_type max_size() const noexcept
Result insert(size_type pos, const T &value) noexcept
const T * get(size_type pos) const noexcept
const_reverse_iterator rend() const noexcept
const_reference front() const noexcept
Result erase(size_type pos) noexcept
bool is_valid() const noexcept
const_reference operator[](size_type pos) const noexcept
const_reference back() const noexcept
List & operator=(const List &other) noexcept
size_type capacity() const noexcept
Result reserve(size_type new_capacity) noexcept
const_reverse_iterator crend() const noexcept
Result assign(InputIt first, InputIt last) noexcept
reference operator[](size_type pos) noexcept
std::reverse_iterator< iterator > reverse_iterator
size_type size() const noexcept
const_iterator cend() const noexcept
std::reverse_iterator< const_iterator > const_reverse_iterator
List & operator=(std::initializer_list< T > init) noexcept
Result emplace_back(Args &&... args) noexcept
T * get(size_type pos) noexcept
reverse_iterator rend() noexcept
The Hubris Engine main namespace.
bool is_failure(List< int >::Result result) noexcept
bool is_success(List< int >::Result result) noexcept
void swap(List< T > &lhs, List< T > &rhs) noexcept
bool operator==(const List< T > &lhs, const List< T > &rhs) noexcept
bool operator!=(const List< T > &lhs, const List< T > &rhs) noexcept
void deallocate() noexcept
T & operator[](size_t index) const