diff --git a/Sources/ComputeCxx/Comparison/Compare.h b/Sources/ComputeCxx/Comparison/Compare.h index cef8810..69465f1 100644 --- a/Sources/ComputeCxx/Comparison/Compare.h +++ b/Sources/ComputeCxx/Comparison/Compare.h @@ -40,8 +40,11 @@ class Compare { Enum(const Enum &) = delete; Enum &operator=(const Enum &) = delete; - // Non-moveable - Enum(Enum &&) = delete; + // Move constructible so that vector can relocate it, the moved-from enum no longer owns anything + Enum(Enum &&other) + : type(std::exchange(other.type, nullptr)), enum_tag(other.enum_tag), lhs(other.lhs), rhs(other.rhs), + lhs_copy(other.lhs_copy), rhs_copy(other.rhs_copy), offset(other.offset), mode(other.mode), + owns_copies(std::exchange(other.owns_copies, false)) {}; Enum &operator=(Enum &&) = delete; }; diff --git a/Sources/ComputeCxx/Vector/Vector.h b/Sources/ComputeCxx/Vector/Vector.h index 4e8bf27..de8b47c 100644 --- a/Sources/ComputeCxx/Vector/Vector.h +++ b/Sources/ComputeCxx/Vector/Vector.h @@ -4,8 +4,11 @@ #include #include #include +#include #include +#include #include +#include #include #include @@ -17,6 +20,18 @@ IAG_ASSUME_NONNULL_BEGIN namespace IAG { +namespace details { + +template +struct vector_storage { + alignas(T) char _bytes[sizeof(T) * capacity]; + + T *_Nonnull data() { return reinterpret_cast(_bytes); }; + const T *_Nonnull data() const { return reinterpret_cast(_bytes); }; +}; + +} // namespace details + template requires std::unsigned_integral<_size_type> class vector { @@ -33,9 +48,7 @@ class vector { private: // Elements are constructed and destroyed explicitly. After relocation to // the heap, the inline storage must not destroy the old element copies. - union { - T _inline_buffer[_inline_capacity]; - }; + details::vector_storage _inline_buffer; T *_Nullable _buffer = nullptr; size_type _size = 0; size_type _capacity = _inline_capacity; @@ -61,13 +74,13 @@ class vector { reference operator[](size_type pos) { return data()[pos]; }; const_reference operator[](size_type pos) const { return data()[pos]; }; - reference front() { return &data()[0]; }; - const_reference front() const { return *&data()[0]; }; - reference back() { return *&data()[_size - 1]; }; - const_reference back() const { return *&data()[_size - 1]; }; + reference front() { return data()[0]; }; + const_reference front() const { return data()[0]; }; + reference back() { return data()[_size - 1]; }; + const_reference back() const { return data()[_size - 1]; }; - T *_Nonnull data() { return _buffer != nullptr ? _buffer : _inline_buffer; }; - const T *_Nonnull data() const { return _buffer != nullptr ? _buffer : _inline_buffer; }; + T *_Nonnull data() { return _buffer != nullptr ? _buffer : _inline_buffer.data(); }; + const T *_Nonnull data() const { return _buffer != nullptr ? _buffer : _inline_buffer.data(); }; // Iterators @@ -119,6 +132,7 @@ static_assert(std::contiguous_iterator::const_iterator>); namespace details { +// Relocates elements with memcpy, so only valid for trivially copyable elements. template requires std::unsigned_integral void *_Nullable realloc_vector(void *_Nullable buffer, void *_Nonnull _inline_buffer, size_type inline_capacity, @@ -154,14 +168,37 @@ void *_Nullable realloc_vector(void *_Nullable buffer, void *_Nonnull _inline_bu return new_buffer; } +// Relocates elements with realloc, so only valid for trivially copyable elements, or when buffer is null. +template + requires std::unsigned_integral +void *_Nullable realloc_vector(void *_Nullable buffer, size_type *_Nonnull size, size_type preferred_new_size) { + if (preferred_new_size == 0) { + *size = 0; + free(buffer); + return nullptr; + } + + size_t new_size_bytes = malloc_good_size(preferred_new_size * element_size); + size_type new_size = (size_type)(new_size_bytes / element_size); + if (new_size == *size) { + // nothing to do + return buffer; + } + + void *new_buffer = realloc(buffer, new_size_bytes); + if (!new_buffer) { + precondition_failure("allocation failure"); + } + *size = new_size; + return new_buffer; +} + } // namespace details template requires std::unsigned_integral vector::~vector() { - for (auto i = 0; i < _size; i++) { - data()[i].~T(); - } + std::destroy(begin(), end()); if (_buffer) { free((void *)_buffer); } @@ -169,54 +206,65 @@ vector::~vector() { template requires std::unsigned_integral -vector::vector(vector &&other) noexcept - : _size(std::exchange(other._size, 0)), _capacity(std::exchange(other._capacity, _inline_capacity)) { +vector::vector(vector &&other) noexcept { if (other._buffer) { _buffer = std::exchange(other._buffer, nullptr); + _capacity = std::exchange(other._capacity, _inline_capacity); } else { - for (auto i = 0; i < _size; ++i) { - new (&_inline_buffer[i]) T(std::move(other._inline_buffer[i])); - other._inline_buffer[i].~T(); - } - _buffer = nullptr; + std::uninitialized_move(other.begin(), other.end(), _inline_buffer.data()); + std::destroy(other.begin(), other.end()); } + _size = std::exchange(other._size, 0); } template requires std::unsigned_integral vector &vector::operator=(vector &&other) noexcept { - if (this != &other) { - for (auto i = 0; i < _size; i++) { - data()[i].~T(); - } + if (this == &other) { + return *this; + } + + std::destroy(begin(), end()); + if (other._buffer) { + // steal the heap buffer if (_buffer) { - free(_buffer); + free((void *)_buffer); } - if (other._buffer) { - _buffer = std::exchange(other._buffer, nullptr); - _size = other._size; - _capacity = other._capacity; - } else { - for (auto i = 0; i < other._size; ++i) { - new (&_inline_buffer[i]) T(std::move(other._inline_buffer[i])); - other._inline_buffer[i].~T(); - } - _buffer = nullptr; - _size = other._size; - _capacity = other._capacity; - } - other._size = 0; - other._capacity = _inline_capacity; + _buffer = std::exchange(other._buffer, nullptr); + _capacity = std::exchange(other._capacity, _inline_capacity); + } else { + // inline elements always fit in our current storage + std::uninitialized_move(other.begin(), other.end(), begin()); + std::destroy(other.begin(), other.end()); } + _size = std::exchange(other._size, 0); return *this; } template requires std::unsigned_integral void vector::reserve_slow(size_type new_cap) { - size_type effective_new_cap = std::max(capacity() * 1.5, new_cap * 1.0); - _buffer = reinterpret_cast(details::realloc_vector( - (void *)_buffer, (void *)_inline_buffer, _inline_capacity, &_capacity, effective_new_cap)); + constexpr size_t max_capacity = std::numeric_limits::max(); + if (_capacity == max_capacity) { + precondition_failure("vector capacity overflow"); + } + size_type effective_new_cap = (size_type)std::clamp(2 * size_t(_capacity) + 1, size_t(new_cap), max_capacity); + + if constexpr (std::is_trivially_copyable_v) { + _buffer = reinterpret_cast(details::realloc_vector( + (void *)_buffer, (void *)_inline_buffer.data(), _inline_capacity, &_capacity, effective_new_cap)); + } else { + size_type new_capacity = 0; + T *new_buffer = reinterpret_cast( + details::realloc_vector(nullptr, &new_capacity, effective_new_cap)); + std::uninitialized_move(begin(), end(), new_buffer); + std::destroy(begin(), end()); + if (_buffer) { + free((void *)_buffer); + } + _buffer = new_buffer; + _capacity = new_capacity; + } } template @@ -231,18 +279,33 @@ void vector::reserve(size_type new_cap) { template requires std::unsigned_integral void vector::shrink_to_fit() { - if (capacity() > _size) { + if (!_buffer || capacity() == _size) { + return; + } + + if constexpr (std::is_trivially_copyable_v) { _buffer = reinterpret_cast(details::realloc_vector( - _buffer, _inline_buffer, _inline_capacity, &_capacity, _size)); + _buffer, _inline_buffer.data(), _inline_capacity, &_capacity, _size)); + } else { + T *new_buffer = nullptr; + size_type new_capacity = _inline_capacity; + if (_size > _inline_capacity) { + new_capacity = 0; + new_buffer = + reinterpret_cast(details::realloc_vector(nullptr, &new_capacity, _size)); + } + std::uninitialized_move(begin(), end(), new_buffer ? new_buffer : _inline_buffer.data()); + std::destroy(begin(), end()); + free((void *)_buffer); + _buffer = new_buffer; + _capacity = new_capacity; } } template requires std::unsigned_integral void vector::clear() { - for (auto i = 0; i < _size; i++) { - data()[i].~T(); - } + std::destroy(begin(), end()); _size = 0; } @@ -250,12 +313,33 @@ template requires std::unsigned_integral vector::iterator vector::insert(const_iterator pos, const T &value) { + if (pos == end()) { + push_back(value); + return end() - 1; + } + + // value may refer to an element of this vector auto offset = pos - begin(); - reserve(_size + 1); + const T *value_ptr = &value; + if (_size >= _capacity) { + if (!std::less<>()(value_ptr, begin()) && std::less<>()(value_ptr, end())) { + auto value_offset = value_ptr - begin(); + reserve(_size + 1); + value_ptr = begin() + value_offset; + } else { + reserve(_size + 1); + } + } iterator mutable_pos = begin() + offset; - std::move_backward(mutable_pos, end(), end() + 1); - new (mutable_pos) value_type(value); + + new (end()) value_type(std::move(back())); + std::move_backward(mutable_pos, end() - 1, end()); _size += 1; + + if (!std::less<>()(value_ptr, mutable_pos) && std::less<>()(value_ptr, end())) { + ++value_ptr; + } + *mutable_pos = *value_ptr; return mutable_pos; } @@ -263,12 +347,33 @@ template requires std::unsigned_integral vector::iterator vector::insert(const_iterator pos, T &&value) { + if (pos == end()) { + push_back(std::move(value)); + return end() - 1; + } + + // value may refer to an element of this vector auto offset = pos - begin(); - reserve(_size + 1); + T *value_ptr = &value; + if (_size >= _capacity) { + if (!std::less<>()(value_ptr, begin()) && std::less<>()(value_ptr, end())) { + auto value_offset = value_ptr - begin(); + reserve(_size + 1); + value_ptr = begin() + value_offset; + } else { + reserve(_size + 1); + } + } iterator mutable_pos = begin() + offset; - std::move_backward(mutable_pos, end(), end() + 1); - new (mutable_pos) value_type(std::move(value)); + + new (end()) value_type(std::move(back())); + std::move_backward(mutable_pos, end() - 1, end()); _size += 1; + + if (!std::less<>()(value_ptr, mutable_pos) && std::less<>()(value_ptr, end())) { + ++value_ptr; + } + *mutable_pos = std::move(*value_ptr); return mutable_pos; } @@ -281,37 +386,53 @@ vector::iterator vector requires std::unsigned_integral vector::iterator vector::erase(iterator first, iterator last) { - auto count = last - first; - if (count == 0) { + if (first == last) { return last; } - for (auto iter = first; iter != last; iter++) { - iter->~T(); - } - for (auto iter = last, old_end = end(); iter != old_end; iter++) { - std::swap(*(iter - count), *iter); - } - _size -= count; + iterator new_end = std::move(last, end(), first); + std::destroy(new_end, end()); + _size = (size_type)(new_end - begin()); return end(); } template requires std::unsigned_integral void vector::push_back(const T &value) { - reserve(_size + 1); - new (&data()[_size]) value_type(value); + // value may refer to an element of this vector + const T *value_ptr = &value; + if (_size >= _capacity) { + if (!std::less<>()(value_ptr, begin()) && std::less<>()(value_ptr, end())) { + auto value_offset = value_ptr - begin(); + reserve(_size + 1); + value_ptr = begin() + value_offset; + } else { + reserve(_size + 1); + } + } + new (end()) value_type(*value_ptr); _size += 1; } template requires std::unsigned_integral void vector::push_back(T &&value) { - reserve(_size + 1); - new (&data()[_size]) value_type(std::move(value)); + // value may refer to an element of this vector + T *value_ptr = &value; + if (_size >= _capacity) { + if (!std::less<>()(value_ptr, begin()) && std::less<>()(value_ptr, end())) { + auto value_offset = value_ptr - begin(); + reserve(_size + 1); + value_ptr = begin() + value_offset; + } else { + reserve(_size + 1); + } + } + new (end()) value_type(std::move(*value_ptr)); _size += 1; } @@ -319,32 +440,34 @@ template requires std::unsigned_integral template vector::reference vector::emplace_back(Args &&...args) { - reserve(_size + 1); - new (&data()[_size]) value_type(std::forward(args)...); + if (_size >= _capacity) { + // construct before growing in case args refer to an element of this vector + value_type value(std::forward(args)...); + reserve(_size + 1); + new (end()) value_type(std::move(value)); + } else { + new (end()) value_type(std::forward(args)...); + } _size += 1; - return data()[_size - 1]; + return back(); } template requires std::unsigned_integral void vector::pop_back() { assert(size() > 0); - data()[_size - 1].~T(); _size -= 1; + std::destroy_at(end()); } template requires std::unsigned_integral void vector::resize(size_type count) { - reserve(count); if (count < _size) { - for (auto i = count; i < _size; i++) { - data()[i].~T(); - } + std::destroy(begin() + count, end()); } else if (count > _size) { - for (auto i = _size; i < count; i++) { - new (&data()[i]) value_type(); - } + reserve(count); + std::uninitialized_value_construct(end(), begin() + count); } _size = count; } @@ -352,15 +475,21 @@ void vector::resize(size_type count) { template requires std::unsigned_integral void vector::resize(size_type count, const value_type &value) { - reserve(count); if (count < _size) { - for (auto i = count; i < _size; i++) { - data()[i].~T(); - } + std::destroy(begin() + count, end()); } else if (count > _size) { - for (auto i = _size; i < count; i++) { - new (&data()[i]) value_type(value); + // value may refer to an element of this vector + const T *value_ptr = &value; + if (count > _capacity) { + if (!std::less<>()(value_ptr, begin()) && std::less<>()(value_ptr, end())) { + auto value_offset = value_ptr - begin(); + reserve(count); + value_ptr = begin() + value_offset; + } else { + reserve(count); + } } + std::uninitialized_fill(end(), begin() + count, *value_ptr); } _size = count; } @@ -406,10 +535,10 @@ class vector { reference operator[](size_type pos) { return data()[pos]; }; const_reference operator[](size_type pos) const { return data()[pos]; }; - reference front() { return &data()[0]; }; - const_reference front() const { return *&data()[0]; }; - reference back() { return *&data()[_size - 1]; }; - const_reference back() const { return *&data()[_size - 1]; }; + reference front() { return data()[0]; }; + const_reference front() const { return data()[0]; }; + reference back() { return data()[_size - 1]; }; + const_reference back() const { return data()[_size - 1]; }; T *_Nonnull data() { return _buffer; }; const T *_Nonnull data() const { return _buffer; }; @@ -456,79 +585,62 @@ class vector { void resize(size_type count, const value_type &value); }; -namespace details { - -template - requires std::unsigned_integral -void *_Nullable realloc_vector(void *_Nullable buffer, size_type *_Nonnull size, size_type preferred_new_size) { - if (preferred_new_size == 0) { - *size = 0; - free(buffer); - return nullptr; - } - - size_t new_size_bytes = malloc_good_size(preferred_new_size * element_size); - size_type new_size = (size_type)(new_size_bytes / element_size); - if (new_size == *size) { - // nothing to do - return buffer; - } - - void *new_buffer = realloc(buffer, new_size_bytes); - if (!new_buffer) { - precondition_failure("allocation failure"); - } - *size = new_size; - return new_buffer; -} - -} // namespace details - template requires std::unsigned_integral vector::~vector() { - for (auto i = 0; i < _size; i++) { - _buffer[i].~T(); - } + std::destroy(begin(), end()); if (_buffer) { - free(_buffer); + free((void *)_buffer); } } template requires std::unsigned_integral vector::vector(vector &&other) noexcept - : _buffer(std::exchange(other._buffer, nullptr)), _size(other._size), _capacity(other._capacity) { - other._size = 0; - other._capacity = 0; -} + : _buffer(std::exchange(other._buffer, nullptr)), _size(std::exchange(other._size, 0)), + _capacity(std::exchange(other._capacity, 0)) {} template requires std::unsigned_integral vector &vector::operator=(vector &&other) noexcept { - if (this != &other) { - for (auto i = 0; i < _size; i++) { - _buffer[i].~T(); - } - if (_buffer) { - free(_buffer); - } - _buffer = other._buffer; - _size = other._size; - _capacity = other._capacity; - other._buffer = nullptr; - other._size = 0; - other._capacity = 0; + if (this == &other) { + return *this; } + + std::destroy(begin(), end()); + if (_buffer) { + free((void *)_buffer); + } + _buffer = std::exchange(other._buffer, nullptr); + _size = std::exchange(other._size, 0); + _capacity = std::exchange(other._capacity, 0); return *this; } template requires std::unsigned_integral void vector::reserve_slow(size_type new_cap) { - size_type effective_new_cap = std::max(capacity() * 1.5, new_cap * 1.0); - _buffer = - reinterpret_cast(details::realloc_vector(_buffer, &_capacity, effective_new_cap)); + constexpr size_t max_capacity = std::numeric_limits::max(); + if (_capacity == max_capacity) { + precondition_failure("vector capacity overflow"); + } + size_type effective_new_cap = (size_type)std::clamp(2 * size_t(_capacity) + 1, size_t(new_cap), max_capacity); + + if constexpr (std::is_trivially_copyable_v) { + _buffer = reinterpret_cast( + details::realloc_vector((void *)_buffer, &_capacity, effective_new_cap)); + } else { + size_type new_capacity = 0; + T *new_buffer = reinterpret_cast( + details::realloc_vector(nullptr, &new_capacity, effective_new_cap)); + std::uninitialized_move(begin(), end(), new_buffer); + std::destroy(begin(), end()); + if (_buffer) { + free((void *)_buffer); + } + _buffer = new_buffer; + _capacity = new_capacity; + } } template @@ -543,17 +655,30 @@ void vector::reserve(size_type new_cap) { template requires std::unsigned_integral void vector::shrink_to_fit() { - if (capacity() > size()) { - _buffer = reinterpret_cast(details::realloc_vector(_buffer, &_capacity, 0)); + if (capacity() == size()) { + return; + } + + if constexpr (std::is_trivially_copyable_v) { + _buffer = reinterpret_cast(details::realloc_vector(_buffer, &_capacity, _size)); + } else { + size_type new_capacity = 0; + T *new_buffer = + reinterpret_cast(details::realloc_vector(nullptr, &new_capacity, _size)); + if (new_buffer) { + std::uninitialized_move(begin(), end(), new_buffer); + } + std::destroy(begin(), end()); + free((void *)_buffer); + _buffer = new_buffer; + _capacity = new_capacity; } } template requires std::unsigned_integral void vector::clear() { - for (auto i = 0; i < _size; i++) { - data()[i].~T(); - } + std::destroy(begin(), end()); _size = 0; } @@ -564,12 +689,29 @@ vector::iterator vector::insert(const_iterator push_back(value); return end() - 1; } + + // value may refer to an element of this vector auto offset = pos - begin(); - reserve(_size + 1); + const T *value_ptr = &value; + if (_size >= _capacity) { + if (!std::less<>()(value_ptr, begin()) && std::less<>()(value_ptr, end())) { + auto value_offset = value_ptr - begin(); + reserve(_size + 1); + value_ptr = begin() + value_offset; + } else { + reserve(_size + 1); + } + } iterator mutable_pos = begin() + offset; - std::move_backward(mutable_pos, end(), end() + 1); - new (mutable_pos) value_type(value); + + new (end()) value_type(std::move(back())); + std::move_backward(mutable_pos, end() - 1, end()); _size += 1; + + if (!std::less<>()(value_ptr, mutable_pos) && std::less<>()(value_ptr, end())) { + ++value_ptr; + } + *mutable_pos = *value_ptr; return mutable_pos; } @@ -580,12 +722,29 @@ vector::iterator vector::insert(const_iterator push_back(std::move(value)); return end() - 1; } + + // value may refer to an element of this vector auto offset = pos - begin(); - reserve(_size + 1); + T *value_ptr = &value; + if (_size >= _capacity) { + if (!std::less<>()(value_ptr, begin()) && std::less<>()(value_ptr, end())) { + auto value_offset = value_ptr - begin(); + reserve(_size + 1); + value_ptr = begin() + value_offset; + } else { + reserve(_size + 1); + } + } iterator mutable_pos = begin() + offset; - std::move_backward(mutable_pos, end(), end() + 1); - new (mutable_pos) value_type(std::move(value)); + + new (end()) value_type(std::move(back())); + std::move_backward(mutable_pos, end() - 1, end()); _size += 1; + + if (!std::less<>()(value_ptr, mutable_pos) && std::less<>()(value_ptr, end())) { + ++value_ptr; + } + *mutable_pos = std::move(*value_ptr); return mutable_pos; } @@ -598,36 +757,52 @@ vector::iterator vector::erase(iterator pos) { return erase(pos, pos + 1); } +// Unlike std::vector, returns the new end() rather than the iterator following the erased elements. template requires std::unsigned_integral vector::iterator vector::erase(iterator first, iterator last) { - auto count = last - first; - if (count == 0) { + if (first == last) { return last; } - for (auto iter = first; iter != last; iter++) { - iter->~T(); - } - for (auto iter = last, old_end = end(); iter != old_end; iter++) { - std::swap(*(iter - count), *iter); - } - _size -= count; + iterator new_end = std::move(last, end(), first); + std::destroy(new_end, end()); + _size = (size_type)(new_end - begin()); return end(); } template requires std::unsigned_integral void vector::push_back(const T &value) { - reserve(_size + 1); - new (&_buffer[_size]) value_type(value); + // value may refer to an element of this vector + const T *value_ptr = &value; + if (_size >= _capacity) { + if (!std::less<>()(value_ptr, begin()) && std::less<>()(value_ptr, end())) { + auto value_offset = value_ptr - begin(); + reserve(_size + 1); + value_ptr = begin() + value_offset; + } else { + reserve(_size + 1); + } + } + new (end()) value_type(*value_ptr); _size += 1; } template requires std::unsigned_integral void vector::push_back(T &&value) { - reserve(_size + 1); - new (&_buffer[_size]) value_type(std::move(value)); + // value may refer to an element of this vector + T *value_ptr = &value; + if (_size >= _capacity) { + if (!std::less<>()(value_ptr, begin()) && std::less<>()(value_ptr, end())) { + auto value_offset = value_ptr - begin(); + reserve(_size + 1); + value_ptr = begin() + value_offset; + } else { + reserve(_size + 1); + } + } + new (end()) value_type(std::move(*value_ptr)); _size += 1; } @@ -635,22 +810,18 @@ template requires std::unsigned_integral void vector::pop_back() { assert(size() > 0); - data()[_size - 1].~T(); _size -= 1; + std::destroy_at(end()); } template requires std::unsigned_integral void vector::resize(size_type count) { - reserve(count); if (count < _size) { - for (auto i = count; i < _size; i++) { - data()[i].~T(); - } + std::destroy(begin() + count, end()); } else if (count > _size) { - for (auto i = _size; i < count; i++) { - new (&data()[i]) value_type(); - } + reserve(count); + std::uninitialized_value_construct(end(), begin() + count); } _size = count; } @@ -658,15 +829,21 @@ void vector::resize(size_type count) { template requires std::unsigned_integral void vector::resize(size_type count, const value_type &value) { - reserve(count); if (count < _size) { - for (auto i = count; i < _size; i++) { - data()[i].~T(); - } + std::destroy(begin() + count, end()); } else if (count > _size) { - for (auto i = _size; i < count; i++) { - new (&data()[i]) value_type(value); + // value may refer to an element of this vector + const T *value_ptr = &value; + if (count > _capacity) { + if (!std::less<>()(value_ptr, begin()) && std::less<>()(value_ptr, end())) { + auto value_offset = value_ptr - begin(); + reserve(count); + value_ptr = begin() + value_offset; + } else { + reserve(count); + } } + std::uninitialized_fill(end(), begin() + count, *value_ptr); } _size = count; } @@ -712,10 +889,10 @@ class vector, 0, _size_type> { reference operator[](size_type pos) { return data()[pos]; }; const_reference operator[](size_type pos) const { return data()[pos]; }; - reference front() { return &data()[0]; }; - const_reference front() const { return *&data()[0]; }; - reference back() { return *&data()[_size - 1]; }; - const_reference back() const { return *&data()[_size - 1]; }; + reference front() { return data()[0]; }; + const_reference front() const { return data()[0]; }; + reference back() { return data()[_size - 1]; }; + const_reference back() const { return data()[_size - 1]; }; std::unique_ptr *_Nonnull data() { return _buffer; }; const std::unique_ptr *_Nonnull data() const { return _buffer; }; @@ -748,7 +925,7 @@ class vector, 0, _size_type> { void clear(); - iterator insert(const_iterator pos, const std::unique_ptr &value); + iterator insert(const_iterator pos, const std::unique_ptr &value) = delete; iterator insert(const_iterator pos, std::unique_ptr &&value); iterator erase(iterator pos); @@ -759,56 +936,62 @@ class vector, 0, _size_type> { void pop_back(); void resize(size_type count); - void resize(size_type count, const value_type &value); + void resize(size_type count, const value_type &value) = delete; }; template requires std::unsigned_integral vector, 0, size_type>::~vector() { - for (auto i = 0; i < _size; i++) { - _buffer[i].reset(); - } + std::destroy(begin(), end()); if (_buffer) { - free(_buffer); + free((void *)_buffer); } } template requires std::unsigned_integral vector, 0, size_type>::vector(vector &&other) noexcept - : _buffer(std::exchange(other._buffer, nullptr)), _size(other._size), _capacity(other._capacity) { - other._size = 0; - other._capacity = 0; -} + : _buffer(std::exchange(other._buffer, nullptr)), _size(std::exchange(other._size, 0)), + _capacity(std::exchange(other._capacity, 0)) {} template requires std::unsigned_integral vector, 0, size_type> & vector, 0, size_type>::operator=(vector &&other) noexcept { - if (this != &other) { - for (auto i = 0; i < _size; i++) { - _buffer[i].~T(); - } - if (_buffer) { - free(_buffer); - } - _buffer = other._buffer; - _size = other._size; - _capacity = other._capacity; - other._buffer = nullptr; - other._size = 0; - other._capacity = 0; + if (this == &other) { + return *this; + } + + std::destroy(begin(), end()); + if (_buffer) { + free((void *)_buffer); } + _buffer = std::exchange(other._buffer, nullptr); + _size = std::exchange(other._size, 0); + _capacity = std::exchange(other._capacity, 0); return *this; } template requires std::unsigned_integral void vector, 0, size_type>::reserve_slow(size_type new_cap) { - size_type effective_new_cap = std::max(capacity() * 1.5, new_cap * 1.0); - _buffer = reinterpret_cast *>( - details::realloc_vector)>(_buffer, &_capacity, + constexpr size_t max_capacity = std::numeric_limits::max(); + if (_capacity == max_capacity) { + precondition_failure("vector capacity overflow"); + } + size_type effective_new_cap = (size_type)std::clamp(2 * size_t(_capacity) + 1, size_t(new_cap), max_capacity); + + size_type new_capacity = 0; + auto new_buffer = reinterpret_cast *>( + details::realloc_vector)>(nullptr, &new_capacity, effective_new_cap)); + std::uninitialized_move(begin(), end(), new_buffer); + std::destroy(begin(), end()); + if (_buffer) { + free((void *)_buffer); + } + _buffer = new_buffer; + _capacity = new_capacity; } template @@ -820,6 +1003,67 @@ void vector, 0, size_type>::reserve(size_type n reserve_slow(new_cap); } +template + requires std::unsigned_integral +void vector, 0, size_type>::shrink_to_fit() { + if (capacity() == size()) { + return; + } + + size_type new_capacity = 0; + auto new_buffer = reinterpret_cast *>( + details::realloc_vector)>(nullptr, &new_capacity, _size)); + if (new_buffer) { + std::uninitialized_move(begin(), end(), new_buffer); + } + std::destroy(begin(), end()); + free((void *)_buffer); + _buffer = new_buffer; + _capacity = new_capacity; +} + +template + requires std::unsigned_integral +void vector, 0, size_type>::clear() { + std::destroy(begin(), end()); + _size = 0; +} + +template + requires std::unsigned_integral +vector, 0, size_type>::iterator +vector, 0, size_type>::insert(const_iterator pos, + std::unique_ptr &&value) { + if (pos == end()) { + push_back(std::move(value)); + return end() - 1; + } + + // value may refer to an element of this vector + auto offset = pos - begin(); + auto value_ptr = &value; + if (_size >= _capacity) { + if (!std::less<>()(value_ptr, begin()) && std::less<>()(value_ptr, end())) { + auto value_offset = value_ptr - begin(); + reserve(_size + 1); + value_ptr = begin() + value_offset; + } else { + reserve(_size + 1); + } + } + iterator mutable_pos = begin() + offset; + + new (end()) value_type(std::move(back())); + std::move_backward(mutable_pos, end() - 1, end()); + _size += 1; + + if (!std::less<>()(value_ptr, mutable_pos) && std::less<>()(value_ptr, end())) { + ++value_ptr; + } + *mutable_pos = std::move(*value_ptr); + return mutable_pos; +} + template requires std::unsigned_integral vector, 0, size_type>::iterator @@ -830,32 +1074,58 @@ vector, 0, size_type>::erase(iterator pos) { return erase(pos, pos + 1); } +// Unlike std::vector, returns the new end() rather than the iterator following the erased elements. template requires std::unsigned_integral vector, 0, size_type>::iterator vector, 0, size_type>::erase(iterator first, iterator last) { - auto count = last - first; - if (count == 0) { + if (first == last) { return last; } - for (auto iter = first; iter != last; iter++) { - iter->reset(); - } - for (auto iter = last, old_end = end(); iter != old_end; iter++) { - std::swap(*(iter - count), *iter); - } - _size -= count; + iterator new_end = std::move(last, end(), first); + std::destroy(new_end, end()); + _size = (size_type)(new_end - begin()); return end(); } template requires std::unsigned_integral void vector, 0, size_type>::push_back(std::unique_ptr &&value) { - reserve(_size + 1); - new (&_buffer[_size]) value_type(std::move(value)); + // value may refer to an element of this vector + auto value_ptr = &value; + if (_size >= _capacity) { + if (!std::less<>()(value_ptr, begin()) && std::less<>()(value_ptr, end())) { + auto value_offset = value_ptr - begin(); + reserve(_size + 1); + value_ptr = begin() + value_offset; + } else { + reserve(_size + 1); + } + } + new (end()) value_type(std::move(*value_ptr)); _size += 1; } +template + requires std::unsigned_integral +void vector, 0, size_type>::pop_back() { + assert(size() > 0); + _size -= 1; + std::destroy_at(end()); +} + +template + requires std::unsigned_integral +void vector, 0, size_type>::resize(size_type count) { + if (count < _size) { + std::destroy(begin() + count, end()); + } else if (count > _size) { + reserve(count); + std::uninitialized_value_construct(end(), begin() + count); + } + _size = count; +} + } // namespace IAG IAG_ASSUME_NONNULL_END