////////////////////////////////////////////////////////////////////////////////////////// // A multi-platform support c++11 library with focus on asynchronous socket I/O for any // client application. ////////////////////////////////////////////////////////////////////////////////////////// /* The MIT License (MIT) Copyright (c) 2012-2023 HALX99 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Version: 4.1.1 The pod_vector aka array_buffer concepts: a. The memory model is similar to to std::vector, but only accept trivially_copyable(no destructor & no custom copy constructor) types b. The resize behavior differrent stl, always allocate exactly c. By default resize without fill (uninitialized and for overwrite), use insert/append insetad if you want fill memory inside container d. Support release internal buffer ownership with `release_pointer` e. Transparent iterator f. expand/append/insert/push_back will trigger memory allocate growth strategy MSVC g. resize_and_overwrite (c++23) */ #ifndef YASIO__POD_VECTOR_HPP #define YASIO__POD_VECTOR_HPP #include #include #include #include #include #include "yasio/buffer_alloc.hpp" #include "yasio/compiler/feature_test.hpp" namespace yasio { template > class pod_vector { public: using pointer = _Ty*; using const_pointer = const _Ty*; using reference = _Ty&; using const_reference = const _Ty&; using _Alloc_traits = buffer_allocator_traits<_Alloc>; using size_type = typename _Alloc_traits::size_type; using value_type = _Ty; using iterator = _Ty*; // transparent iterator using const_iterator = const _Ty*; using allocator_type = _Alloc; pod_vector() {} explicit pod_vector(size_type count) { resize(static_cast(count)); } pod_vector(size_type count, const_reference val) { resize(static_cast(count), val); } template ::value, int> = 0> pod_vector(_Iter first, _Iter last) { assign(first, last); } pod_vector(const pod_vector& rhs) { assign(rhs); }; pod_vector(pod_vector&& rhs) YASIO__NOEXCEPT { assign(std::move(rhs)); } /*pod_vector(std::initializer_list rhs) { _Assign_range(rhs.begin(), rhs.end()); }*/ ~pod_vector() { _Tidy(); } pod_vector& operator=(const pod_vector& rhs) { assign(rhs); return *this; } pod_vector& operator=(pod_vector&& rhs) YASIO__NOEXCEPT { this->swap(rhs); return *this; } template pod_vector& operator+=(const _Cont& rhs) { return this->append(std::begin(rhs), std::end(rhs)); } pod_vector& operator+=(const_reference rhs) { this->push_back(rhs); return *this; } template ::value, int> = 0> void assign(_Iter first, _Iter last) { _Assign_range(first, last); } void assign(const pod_vector& rhs) { _Assign_range(rhs.begin(), rhs.end()); } void assign(pod_vector&& rhs) { _Assign_rv(std::move(rhs)); } void swap(pod_vector& rhs) YASIO__NOEXCEPT { std::swap(_Myfirst, rhs._Myfirst); std::swap(_Mysize, rhs._Mysize); std::swap(_Myres, rhs._Myres); } template ::value, int> = 0> iterator insert(iterator pos, _Iter first, _Iter last) { auto mlast = _Myfirst + _Mysize; _YASIO_VERIFY_RANGE(pos >= _Myfirst && pos <= mlast && first <= last, "pod_vector: out of range!"); if (first != last) { auto insertion_off = static_cast(std::distance(_Myfirst, pos)); if (pos == mlast) append(first, last); else { auto ifirst = std::addressof(*first); static_assert(sizeof(*ifirst) == sizeof(value_type), "pod_vector: iterator type incompatible!"); auto count = static_cast(std::distance(first, last)); if (insertion_off >= 0) { expand(count); pos = _Myfirst + insertion_off; mlast = _Myfirst + _Mysize; auto move_to = pos + count; std::copy_n(pos, mlast - move_to, move_to); std::copy_n((iterator)ifirst, count, pos); } } return _Myfirst + insertion_off; } return pos; } iterator insert(iterator pos, size_type count, const_reference val) { auto mlast = _Myfirst + _Mysize; _YASIO_VERIFY_RANGE(pos >= _Myfirst && pos <= mlast, "pod_vector: out of range!"); if (count) { auto insertion_off = std::distance(_Myfirst, pos); if (pos == mlast) append(count, val); else { if (insertion_off >= 0) { const auto old_size = _Mysize; expand(count); pos = _Myfirst + insertion_off; mlast = _Myfirst + _Mysize; auto move_to = pos + count; std::copy_n(pos, mlast - move_to, move_to); std::fill_n(pos, count, val); } } return _Myfirst + insertion_off; } return pos; } iterator insert(iterator pos, const value_type& val) { // insert val at pos return emplace(pos, val); } iterator insert(iterator pos, value_type&& val) { // insert by moving val at pos return emplace(pos, std::move(val)); } template iterator emplace(iterator pos, _Valty&&... val) { auto insertion_off = std::distance(_Myfirst, pos); _YASIO_VERIFY_RANGE(insertion_off <= _Mysize, "pod_vector: out of range!"); #if YASIO__HAS_CXX20 emplace_back(std::forward<_Valty>(val)...); std::rotate(begin() + insertion_off, end() - 1, end()); return (begin() + insertion_off); #else auto mlast = _Myfirst + _Mysize; if (pos == mlast) emplace_back(std::forward<_Valty>(val)...); else { if (insertion_off >= 0) { expand(1); pos = _Myfirst + insertion_off; mlast = _Myfirst + _Mysize; auto move_to = pos + 1; std::copy_n(pos, mlast - move_to, move_to); ::yasio::construct_at(pos, std::forward<_Valty>(val)...); } } return _Myfirst + insertion_off; #endif } template ::value, int> = 0> pod_vector& append(_Iter first, const _Iter last) { if (first != last) { auto ifirst = std::addressof(*first); static_assert(sizeof(*ifirst) == sizeof(value_type), "pod_vector: iterator type incompatible!"); auto count = static_cast(std::distance(first, last)); if (count > 1) { const auto old_size = _Mysize; expand(count); std::copy_n((iterator)ifirst, count, _Myfirst + old_size); } else if (count == 1) push_back(static_cast(*(iterator)ifirst)); } return *this; } pod_vector& append(size_type count, const_reference val) { expand(count, val); return *this; } void push_back(value_type&& val) { push_back(val); } void push_back(const value_type& val) { emplace_back(val); } template inline value_type& emplace_back(_Valty&&... val) { if (_Mysize < _Myres) return *::yasio::construct_at(_Myfirst + _Mysize++, std::forward<_Valty>(val)...); return *_Emplace_back_reallocate(std::forward<_Valty>(val)...); } iterator erase(iterator pos) { const auto mlast = _Myfirst + _Mysize; _YASIO_VERIFY_RANGE(pos >= _Myfirst && pos < mlast, "pod_vector: out of range!"); _Mysize = static_cast(std::move(pos + 1, mlast, pos) - _Myfirst); return pos; } iterator erase(iterator first, iterator last) { const auto mlast = _Myfirst + _Mysize; _YASIO_VERIFY_RANGE((first <= last) && first >= _Myfirst && last <= mlast, "pod_vector: out of range!"); _Mysize = static_cast(std::move(last, mlast, first) - _Myfirst); return first; } value_type& front() { _YASIO_VERIFY_RANGE(!empty(), "pod_vector: out of range!"); return *_Myfirst; } value_type& back() { _YASIO_VERIFY_RANGE(!empty(), "pod_vector: out of range!"); return _Myfirst[_Mysize - 1]; } static YASIO__CONSTEXPR size_type max_size() YASIO__NOEXCEPT { return _Alloc_traits::max_size(); } iterator begin() YASIO__NOEXCEPT { return _Myfirst; } iterator end() YASIO__NOEXCEPT { return _Myfirst + _Mysize; } const_iterator begin() const YASIO__NOEXCEPT { return _Myfirst; } const_iterator end() const YASIO__NOEXCEPT { return _Myfirst + _Mysize; } pointer data() YASIO__NOEXCEPT { return _Myfirst; } const_pointer data() const YASIO__NOEXCEPT { return _Myfirst; } size_type capacity() const YASIO__NOEXCEPT { return _Myres; } size_type size() const YASIO__NOEXCEPT { return _Mysize; } size_type length() const YASIO__NOEXCEPT { return _Mysize; } void clear() YASIO__NOEXCEPT { _Mysize = 0; } bool empty() const YASIO__NOEXCEPT { return _Mysize == 0; } const_reference operator[](size_type index) const { return this->at(index); } reference operator[](size_type index) { return this->at(index); } const_reference at(size_type index) const { _YASIO_VERIFY_RANGE(index < this->size(), "pod_vector: out of range!"); return _Myfirst[index]; } reference at(size_type index) { _YASIO_VERIFY_RANGE(index < this->size(), "pod_vector: out of range!"); return _Myfirst[index]; } #pragma region modify size and capacity void resize(size_type new_size) { if (this->capacity() < new_size) _Resize_reallocate<_Reallocation_policy::_Exactly>(new_size); else _Eos(new_size); } void expand(size_type count) { const auto new_size = this->size() + count; if (this->capacity() < new_size) _Resize_reallocate<_Reallocation_policy::_At_least>(new_size); else _Eos(new_size); } void shrink_to_fit() { // reduce capacity to size, provide strong guarantee if (_Mysize != _Myres) { // something to do if (!_Mysize) _Tidy(); else _Reallocate<_Reallocation_policy::_Exactly>(_Mysize); } } void reserve(size_type new_cap) { if (this->capacity() < new_cap) _Reallocate<_Reallocation_policy::_Exactly>(new_cap); } template void resize_and_overwrite(const size_type new_size, _Operation op) { _Reallocate<_Reallocation_policy::_Exactly>(new_size); _Eos(std::move(op)(_Myfirst, new_size)); } #pragma endregion void resize(size_type new_size, const_reference val) { auto old_size = this->size(); if (old_size != new_size) { resize(new_size); if (old_size < new_size) std::fill_n(_Myfirst + old_size, new_size - old_size, val); } } void expand(size_type count, const_reference val) { if (count) { auto old_size = this->size(); expand(count); if (count) std::fill_n(_Myfirst + old_size, count, val); } } ptrdiff_t index_of(const_reference val) const YASIO__NOEXCEPT { auto it = std::find(begin(), end(), val); if (it != this->end()) return std::distance(begin(), it); return -1; } void reset(size_type new_size) { resize(new_size); memset(_Myfirst, 0x0, size_bytes()); } size_t size_bytes() const YASIO__NOEXCEPT { return this->size() * sizeof(value_type); } template pointer detach_abi(_Intty& len) YASIO__NOEXCEPT { len = static_cast<_Intty>(this->size()); auto ptr = _Myfirst; _Myfirst = nullptr; _Mysize = _Myres = 0; return ptr; } pointer detach_abi() YASIO__NOEXCEPT { size_type ignored_len; return this->detach_abi(ignored_len); } void attach_abi(pointer ptr, size_type len) { _Tidy(); _Myfirst = ptr; _Mysize = _Myres = len; } pointer release_pointer() YASIO__NOEXCEPT { return detach_abi(); } private: void _Eos(size_type size) YASIO__NOEXCEPT { _Mysize = size; } template pointer _Emplace_back_reallocate(_Valty&&... val) { const auto _Oldsize = _Mysize; if (_Oldsize == max_size()) throw std::length_error("pod_vector too long"); const size_type _Newsize = _Oldsize + 1; _Resize_reallocate<_Reallocation_policy::_At_least>(_Newsize); const pointer _Newptr = ::yasio::construct_at(_Myfirst + _Oldsize, std::forward<_Valty>(val)...); return _Newptr; } template ::value, int> = 0> void _Assign_range(_Iter first, _Iter last) { auto ifirst = std::addressof(*first); static_assert(sizeof(*ifirst) == sizeof(value_type), "pod_vector: iterator type incompatible!"); if (ifirst != _Myfirst) { _Mysize = 0; if (last > first) { const auto count = static_cast(std::distance(first, last)); resize(count); std::copy_n((iterator)ifirst, count, _Myfirst); } } } void _Assign_rv(pod_vector&& rhs) { memcpy(this, &rhs, sizeof(rhs)); memset(&rhs, 0, sizeof(rhs)); } enum class _Reallocation_policy { _At_least, _Exactly }; template <_Reallocation_policy _Policy> void _Resize_reallocate(size_type size) { _Reallocate<_Policy>(size); _Eos(size); } template <_Reallocation_policy _Policy> void _Reallocate(size_type size) { size_type new_cap; if YASIO__CONSTEXPR (_Policy == _Reallocation_policy::_Exactly) new_cap = size; else new_cap = _Calculate_growth(size); auto _Newvec = _Alloc::reallocate(_Myfirst, _Myres, new_cap); if (_Newvec) { _Myfirst = _Newvec; _Myres = new_cap; } else throw std::bad_alloc{}; } size_type _Calculate_growth(const size_type new_size) const { // given _Oldcapacity and _Newsize, calculate geometric growth const size_type old_cap = capacity(); YASIO__CONSTEXPR auto max_cap = max_size(); if (old_cap > max_cap - old_cap / 2) return max_cap; // geometric growth would overflow const size_type geometric = old_cap + (old_cap >> 1); if (geometric < new_size) return new_size; // geometric growth would be insufficient return geometric; // geometric growth is sufficient } void _Tidy() YASIO__NOEXCEPT { // free all storage if (_Myfirst) { _Alloc::deallocate(_Myfirst, _Myres); _Myfirst = nullptr; _Mysize = _Myres = 0; } } pointer _Myfirst = nullptr; size_type _Mysize = 0; size_type _Myres = 0; }; #pragma region c++20 like std::erase template void erase(pod_vector<_Ty, _Alloc>& cont, const _Ty& val) { cont.erase(std::remove(cont.begin(), cont.end(), val), cont.end()); } template void erase_if(pod_vector<_Ty, _Alloc>& cont, _Pr pred) { cont.erase(std::remove_if(cont.begin(), cont.end(), pred), cont.end()); } #pragma endregion template inline typename _Cont::iterator insert_sorted(_Cont& vec, typename _Cont::value_type const& val) { return vec.insert(std::upper_bound(vec.begin(), vec.end(), val), val); } template inline typename _Cont::iterator insert_sorted(_Cont& vec, typename _Cont::value_type const& val, _Pred pred) { return vec.insert(std::upper_bound(vec.begin(), vec.end(), val, pred), val); } // alias: array_buffer template > using array_buffer = pod_vector<_Ty, _Alloc>; } // namespace yasio #endif