diff --git a/aocommon/uvector_03.h b/aocommon/uvector_03.h deleted file mode 100644 index b6a9f70..0000000 --- a/aocommon/uvector_03.h +++ /dev/null @@ -1,1013 +0,0 @@ -#ifndef AO_UVECTOR_H -#define AO_UVECTOR_H - -#include -#include -#include -#include -#include -#include - -/** - * @file uvector.h - * Header file for uvector and its relational and swap functions. - * @author André Offringa - * @copyright André Offringa, 2013, distributed under the GPL license version 3. - */ - -namespace ao { - -/** - * @defgroup uvector Class uvector and related functions. - * @{ - */ - -/** - * @brief A container similar to std::vector, but one that allows construction - * without initializing its elements. - * @details This container is similar to a std::vector, except that it can be - * constructor without initializing its elements. This saves the overhead of - * initialization, hence the constructor @ref uvector(size_t) is significantly - * faster than the corresponding std::vector constructor, and has no overhead to - * a manually allocated array. - * - * Probably its greatest strength lies in the construction of containers with a - * number of elements that is runtime defined, but that will be initialized - * later. For example: - * - * @code - * // Open a file - * ifstream file("myfile.bin"); - * - * // Construct a buffer for this file - * uvector buffer(buffer_size); - * - * // Read some data into the buffer - * file.read(&buffer[0], buffer_size); - * @endcode - * - * However, it has a few more use-cases with improved performance over - * std::vector. This is true because of more strengent requirements on the - * element's type. - * - * The container will behave correctly with any trivial type, but will not work - * for almost all non-trivial types. - * - * The element type must be trivial. Because of the use of @c memcpy and @c - * memmove, the @ref push_back() and @ref insert() methods are a bit faster than - * the std::vector counterparts, at least on gcc 4.7. - * - * The methods with different semantics compared to std::vector are: - * * @ref uvector(size_t n) - * * @ref resize(size_t n) - * - * Also the following new members are introduced: - * * @ref insert_uninitialized(const_iterator position, size_t n) - * * @ref push_back(InputIterator first, InputIterator last) - * * @ref push_back(size_t n, const Tp& val) - * * @ref push_back(std::initializer_list initlist) - * * @ref push_back_uninitialized(size_t n) - * - * All other members work exactly like std::vector's members, although some are - * slightly faster because of the stricter requirements on the element type. - * - * @tparam Tp Container's element type - * @tparam Alloc Allocator type. Default is to use the std::allocator. - * - * @author André Offringa - * @copyright André Offringa, 2013, distributed under the GPL license version 3. - */ -template > -class uvector : private Alloc { - public: - /// Element type - typedef Tp value_type; - /// Type of allocator used to allocate and deallocate space - typedef Alloc allocator_type; - /// Reference to element type - typedef Tp& reference; - /// Constant reference to element type - typedef const Tp& const_reference; - /// Pointer to element type - typedef Tp* pointer; - /// Pointer to constant element type - typedef const Tp* const_pointer; - /// Iterator type - typedef Tp* iterator; - /// Iterator type of constant elements - typedef const Tp* const_iterator; - /// Reverse iterator type - typedef std::reverse_iterator reverse_iterator; - /// Reverse iterator of constant elements - typedef std::reverse_iterator const_reverse_iterator; - /// Difference between to iterators - typedef std::ptrdiff_t difference_type; - /// Type used for indexing elements - typedef std::size_t size_t; - /// Type used for indexing elements - typedef std::size_t size_type; - - private: - pointer _begin, _end, _endOfStorage; - - public: - /** @brief Construct an empty uvector. - * @param allocator Allocator used for allocating and deallocating memory. - */ - explicit uvector(const allocator_type& allocator = Alloc()) - : Alloc(allocator), _begin(0), _end(0), _endOfStorage(0) {} - - /** @brief Construct a vector with given amount of elements, without - * initializing these. - * @details This constructor deviates from std::vector's behaviour, because it - * will not value construct its elements. It is therefore faster than the - * corresponding constructor of std::vector. - * @param n Number of elements that the uvector will be initialized with. - */ - explicit uvector(size_t n) - : _begin(allocate(n)), _end(_begin + n), _endOfStorage(_end) {} - - /** @brief Construct a vector with given amount of elements and set these to a - * specific value. - * @details This constructor will initialize its members with the given value. - * @param n Number of elements that the uvector will be initialized with. - * @param val Value to initialize all elements with - * @param allocator Allocator used for allocating and deallocating memory. - */ - uvector(size_t n, const value_type& val, - const allocator_type& allocator = Alloc()) - : Alloc(allocator), - _begin(allocate(n)), - _end(_begin + n), - _endOfStorage(_end) { - std::uninitialized_fill_n(_begin, n, val); - } - - /** @brief Construct a vector by copying elements from a range. - * @param first Iterator to range start - * @param last Iterator to range end - * @param allocator Allocator used for allocating and deallocating memory. - */ - template - uvector(InputIterator first, InputIterator last, - const allocator_type& allocator = Alloc()) - : Alloc(allocator) { - construct_from_range(first, last, - std::is_integral()); - } - - /** @brief Copy construct a uvector. - * @details The allocator of the new uvector will be initialized from - * @c - * std::allocator_traits::select_on_container_copy_construction(other). - * @param other Source uvector to be copied from. - */ - uvector(const uvector& other) - : Alloc(static_cast(other)), - _begin(allocate(other.size())), - _end(_begin + other.size()), - _endOfStorage(_end) { - memcpy(_begin, other._begin, other.size() * sizeof(Tp)); - } - - /** @brief Copy construct a uvector with custom allocator. - * @param other Source uvector to be copied from. - * @param allocator Allocator used for allocating and deallocating memory. - */ - uvector(const uvector& other, const allocator_type& allocator) - : Alloc(allocator), - _begin(allocate(other.size())), - _end(_begin + other.size()), - _endOfStorage(_end) { - memcpy(_begin, other._begin, other.size() * sizeof(Tp)); - } - - /** @brief Destructor. */ - ~uvector() { deallocate(); } - - /** @brief Assign another uvector to this uvector. - * @details The allocator of the uvector will be assigned to @p other when - * std::allocator_traits::propagate_on_container_copy_assignment() is - * of true_type. - */ - uvector& operator=(const uvector& other) { - return assign_copy_from(other, std::false_type()); - } - - /** @brief Assign another uvector to this uvector. - * @details The allocator of the uvector will be assigned to @p other when - * std::allocator_traits::propagate_on_container_move_assignment() is - * of true_type. - */ - uvector& operator=(uvector&& other) { - return assign_move_from(std::move(other), std::false_type()); - } - - /** @brief Get iterator to first element. */ - iterator begin() { return _begin; } - - /** @brief Get constant iterator to first element. */ - const_iterator begin() const { return _begin; } - - /** @brief Get iterator to element past last element. */ - iterator end() { return _end; } - - /** @brief Get constant iterator to element past last element. */ - const_iterator end() const { return _end; } - - /** @brief Get reverse iterator to last element. */ - reverse_iterator rbegin() { return reverse_iterator(end()); } - - /** @brief Get constant reverse iterator to last element. */ - const_reverse_iterator rbegin() const { - return const_reverse_iterator(end()); - } - - /** @brief Get reverse iterator to element before first element. */ - reverse_iterator rend() { return reverse_iterator(begin()); } - - /** @brief Get constant reverse iterator to element before first element. */ - const_reverse_iterator rend() const { - return const_reverse_iterator(begin()); - } - - /** @brief Get constant iterator to first element. */ - const_iterator cbegin() const { return _begin; } - - /** @brief Get constant iterator to element past last element. */ - const_iterator cend() const { return _end; } - - /** @brief Get constant reverse iterator to last element. */ - const_reverse_iterator crbegin() const { - return const_reverse_iterator(end()); - } - - /** @brief Get constant reverse iterator to element before first element. */ - const_reverse_iterator crend() const { - return const_reverse_iterator(begin()); - } - - /** @brief Get number of elements in container. */ - size_t size() const { return _end - _begin; } - - /** @brief Get maximum number of elements that this container can hold. */ - size_t max_size() const { return Alloc::max_size(); } - - /** @brief Change the number of elements in the container. - * @details If the new size is larger than the current size, new values will - * be left uninitialized. Therefore, it is more efficient than @c - * resize(size_t) in - * @c std::vector, as well as @ref resize(size_t, const Tp&). - * If the new size is smaller than the current size, the container will be - * truncated and elements past the new size will be removed. No destructor of - * the removed elements will be called. - * @param n The new size of the container. - */ - void resize(size_t n) { - if (capacity() < n) { - size_t newSize = enlarge_size(n); - pointer newStorage = allocate(newSize); - memcpy(newStorage, _begin, size() * sizeof(Tp)); - deallocate(); - _begin = newStorage; - _endOfStorage = _begin + newSize; - } - _end = _begin + n; - } - - /** @brief Change the number of elements in the container. - * @details If the new size is larger than the current size, new values will - * be initialized by the given value. If the new size is smaller than the - * current size, the container will be truncated and elements past the new - * size will be removed. No destructor of the removed elements will be called. - * @param n The new size of the container. - * @param val New value of elements that get added to the container. - */ - void resize(size_t n, const Tp& val) { - size_t oldSize = size(); - if (capacity() < n) { - pointer newStorage = allocate(n); - memcpy(newStorage, _begin, size() * sizeof(Tp)); - deallocate(); - _begin = newStorage; - _endOfStorage = _begin + n; - } - _end = _begin + n; - if (oldSize < n) - std::uninitialized_fill(_begin + oldSize, _end, val); - } - - /** @brief Get the number of elements the container can currently hold without - * reallocating storage. */ - size_t capacity() const { return _endOfStorage - _begin; } - - /** @brief Determine if the container is currently empty. - * @returns @c true if @ref size() == 0. */ - bool empty() const { return _begin == _end; } - - /** @brief Reserve space for a number of elements, to prevent the overhead of - * extra reallocations. - * @details This has no effect on the working of the uvector, except that it - * might change the current capacity. This can enhance performance when a - * large number of elements are added, and an approximate size is known a - * priori. - * - * This method might cause a reallocation, causing iterators to be - * invalidated. - * @param n Number of elements to reserve space for. - */ - void reserve(size_t n) { - if (capacity() < n) { - const size_t curSize = size(); - pointer newStorage = allocate(n); - memcpy(newStorage, _begin, curSize * sizeof(Tp)); - deallocate(); - _begin = newStorage; - _end = newStorage + curSize; - _endOfStorage = _begin + n; - } - } - - /** @brief Change the capacity of the container such that no extra space is - * hold. - * @details This has no effect on the working of the uvector, except that it - * might change the current capacity. This can reduce the current memory usage - * of the container. - * - * This method might cause a reallocation, causing iterators to be - * invalidated. - */ - void shrink_to_fit() { - const size_t curSize = size(); - if (curSize == 0) { - deallocate(); - _begin = 0; - _end = 0; - _endOfStorage = 0; - } else { - pointer newStorage = allocate(curSize); - memcpy(newStorage, _begin, curSize * sizeof(Tp)); - deallocate(); - _begin = newStorage; - _end = newStorage + curSize; - _endOfStorage = _begin + curSize; - } - } - - /** @brief Get a reference to the element at the given index. */ - Tp& operator[](size_t index) { return _begin[index]; } - - /** @brief Get a constant reference to the element at the given index. */ - const Tp& operator[](size_t index) const { return _begin[index]; } - - /** @brief Get a reference to the element at the given index with bounds - * checking. - * @throws std::out_of_range when given index is past the last element. - */ - Tp& at(size_t index) { - check_bounds(index); - return _begin[index]; - } - - /** @brief Get a constant reference to the element at the given index with - * bounds checking. - * @throws std::out_of_range when given index is past the last element. - */ - const Tp& at(size_t index) const { - check_bounds(index); - return _begin[index]; - } - - /** @brief Get reference to first element in container. */ - Tp& front() { return *_begin; } - - /** @brief Get constant reference to first element in container. */ - const Tp& front() const { return *_begin; } - - /** @brief Get reference to last element in container. */ - Tp& back() { return *(_end - 1); } - - /** @brief Get constant reference to last element in container. */ - const Tp& back() const { return *(_end - 1); } - - /** @brief Get pointer to internal storage. */ - Tp* data() { return _begin; } - - /** @brief Get constant pointer to internal storage. */ - const Tp* data() const { return _begin; } - - /** @brief Assign this container to be equal to the given range. - * @details The container will be resized to fit the length of the given - * range. Iterators are invalidated. - * @param first Iterator to the beginning of the range. - * @param last Iterator past the end of the range. - */ - template - void assign(InputIterator first, InputIterator last) { - assign_from_range(first, last, - std::is_integral()); - } - - /** @brief Resize the container and assign the given value to all elements. - * @details Iterators are invalidated. - * @param n New size of container - * @param val Value to be assigned to all elements. - */ - void assign(size_t n, const Tp& val) { - if (n > capacity()) { - iterator newStorage = allocate(n); - deallocate(); - _begin = newStorage; - _endOfStorage = _begin + n; - } - _end = _begin + n; - std::uninitialized_fill_n(_begin, n, val); - } - - /** @brief Add the given value to the end of the container. - * @details Iterators are invalidated. - * @param item Value of new element. - */ - void push_back(const Tp& item) { - if (_end == _endOfStorage) enlarge(enlarge_size(1)); - *_end = item; - ++_end; - } - - /** @brief Add the given value to the end of the container by moving it in. - * @details Iterators are invalidated. - * - * Note that this container can only hold simple types that do not perform - * allocations. Therefore, there is probably no benefit in moving the new item - * in over copying it in with @ref push_back(const Tp&). - * @param item Value of new element. - */ - void push_back(Tp&& item) { - if (_end == _endOfStorage) enlarge(enlarge_size(1)); - *_end = std::move(item); - ++_end; - } - - /** @brief Remove the last element from the container. */ - void pop_back() { --_end; } - - /** @brief Insert an element at a given position. - * @details All iterators will be invalidated. This operation needs to move - * all elements after the new element, and can therefore be expensive. - * @param position Position of the new element. The new element will be added - * before the old element at that position. - * @param item Value of the new item. - * @return Position of the new element. - */ - iterator insert(const_iterator position, const Tp& item) { - if (_end == _endOfStorage) { - size_t index = position - _begin; - enlarge_for_insert(enlarge_size(1), index, 1); - position = _begin + index; - } else { - memmove(const_cast(position) + 1, position, - (_end - position) * sizeof(Tp)); - ++_end; - } - *const_cast(position) = item; - return const_cast(position); - } - - /** @brief Insert elements at a given position and initialize them with a - * value. - * @details All iterators will be invalidated. This operation needs to move - * all elements after the new element, and can therefore be expensive. - * @param position Position of the new elements. The new elements will be - * added before the old element at that position. - * @param n Number of elements to add. - * @param val Value of the new item. - * @return Position of the first new element. - */ - iterator insert(const_iterator position, size_t n, const Tp& val) { - if (capacity() < size() + n) { - size_t index = position - _begin; - enlarge_for_insert(enlarge_size(n), index, n); - position = _begin + index; - } else { - memmove(const_cast(position) + n, position, - (_end - position) * sizeof(Tp)); - _end += n; - } - std::uninitialized_fill_n(const_cast(position), n, - val); - return const_cast(position); - } - - /** @brief Insert elements at a given position and initialize them from a - * range. - * @details All iterators will be invalidated. This operation needs to move - * all elements after the new element, and can therefore be expensive. - * @param position Position of the new elements. The new elements will be - * added before the old element at that position. - * @param first Iterator to the beginning of the range. - * @param last Iterator past the end of the range. - * @return Position of the first new element. - */ - template - iterator insert(const_iterator position, InputIterator first, - InputIterator last) { - return insert_from_range(position, first, last, - std::is_integral()); - } - - /** @brief Delete an element from the container. - * @details This operation moves all elements past the removed element, and - * can therefore be expensive. - * @param position Position of element to be removed. - * @return Iterator pointing to the first element past the delete element. - */ - iterator erase(const_iterator position) { - --_end; - memmove(const_cast(position), position + 1, - (_end - position) * sizeof(Tp)); - return const_cast(position); - } - - /** @brief Delete a range of elements from the container. - * @details This operation moves all elements past the removed elements, and - * can therefore be expensive. - * @param first Position of first element to be removed. - * @param last Position past last element to be removed. - * @return Iterator pointing to the first element past the delete element. - */ - iterator erase(const_iterator first, const_iterator last) { - size_t n = last - first; - _end -= n; - memmove(const_cast(first), first + n, - (_end - first) * sizeof(Tp)); - return const_cast(first); - } - - /** @brief Swap the contents of this uvector with the given uvector. - * @details Iterators to both vectors will remain valid and will point into - * to the swapped container afterwards. This function will never reallocate - * space. - * - * The allocator will be swapped when the @c propagate_on_container_swap - * of the respective @c allocator_trait is @c true_type. - * Its behaviour is undefined when the allocators do not compare equal and - * @c propagate_on_container_swap is false. - * @param other Other uvector whose contents it to be swapped with this. - */ - void swap(uvector& other) { swap(other, std::false_type()); } - - /** @brief Remove all elements from the container. */ - void clear() { _end = _begin; } - - /** @brief Get a copy of the allocator. */ - allocator_type get_allocator() const { return *this; } - - // --- NON STANDARD METHODS --- - - /** @brief Insert elements at a given position without initializing them. - * @details All iterators will be invalidated. This operation needs to move - * all elements after the new element, and can therefore be expensive. It will - * not initialize the new elements, and is therefore faster than @ref - * insert(const_iterator, size_t, const Tp&). - * - * This method is non-standard: it is not present in std::vector. - * @param position Position of the new elements. The new elements will be - * added before the old element at that position. - * @param n Number of elements to add. - */ - iterator insert_uninitialized(const_iterator position, size_t n) { - if (capacity() < size() + n) { - size_t index = position - _begin; - enlarge_for_insert(enlarge_size(n), index, n); - position = _begin + index; - } else { - memmove(const_cast(position) + n, position, - (_end - position) * sizeof(Tp)); - _end += n; - } - return const_cast(position); - } - - /** @brief Add a range of items to the end of the container. - * @details All iterators will be invalidated. - * - * This method is non-standard: it is not present in std::vector. - * @param first Iterator to the beginning of the range. - * @param last Iterator past the end of the range. - */ - template - void push_back(InputIterator first, InputIterator last) { - push_back_range(first, last, - std::is_integral()); - } - - /** @brief Add elements at the end and initialize them with a value. - * @details All iterators will be invalidated. - * - * This method is non-standard: it is not present in std::vector. - * @param n Number of elements to add. - * @param val Value of the new items. - */ - void push_back(size_t n, const Tp& val) { - if (capacity() - size() < n) { - enlarge(enlarge_size(n)); - } - std::uninitialized_fill_n(_end, n, val); - _end += n; - } - - /** @brief Add elements at the end without initializing them. - * @details All iterators will be invalidated. - * - * This method is non-standard: it is not present in std::vector. - * @param n Number of elements to add. - */ - void push_back_uninitialized(size_t n) { resize(size() + n); } - - private: - pointer allocate(size_t n) { return Alloc::allocate(n); } - - void deallocate() { deallocate(_begin, capacity()); } - - void deallocate(pointer begin, size_t n) { - if (begin != 0) Alloc::deallocate(begin, n); - } - - template - void construct_from_range(InputIterator first, InputIterator last, - std::false_type) { - construct_from_range( - first, last, - typename std::iterator_traits::iterator_category()); - } - - template - void construct_from_range(Integral n, Integral val, std::true_type) { - _begin = allocate(n); - _end = _begin + n; - _endOfStorage = _end; - std::uninitialized_fill_n(_begin, n, val); - } - - template - void construct_from_range(InputIterator first, InputIterator last, - std::forward_iterator_tag) { - size_t n = std::distance(first, last); - _begin = allocate(n); - _end = _begin + n; - _endOfStorage = _begin + n; - Tp* destIter = _begin; - while (first != last) { - *destIter = *first; - ++destIter; - ++first; - } - } - - template - void assign_from_range(InputIterator first, InputIterator last, - std::false_type) { - assign_from_range( - first, last, - typename std::iterator_traits::iterator_category()); - } - - // This function is called from assign(iter,iter) when Tp is an integral. In - // that case, the user tried to call assign(n, &val), but it got caught by the - // wrong overload. - template - void assign_from_range(Integral n, Integral val, std::true_type) { - if (size_t(n) > capacity()) { - iterator newStorage = allocate(n); - deallocate(); - _begin = newStorage; - _endOfStorage = _begin + n; - } - _end = _begin + n; - std::uninitialized_fill_n(_begin, n, val); - } - - template - void assign_from_range(InputIterator first, InputIterator last, - std::forward_iterator_tag) { - size_t n = std::distance(first, last); - if (n > capacity()) { - iterator newStorage = allocate(n); - deallocate(); - _begin = newStorage; - _endOfStorage = _begin + n; - } - _end = _begin + n; - Tp* destIter = _begin; - while (first != last) { - *destIter = *first; - ++destIter; - ++first; - } - } - - template - iterator insert_from_range(const_iterator position, InputIterator first, - InputIterator last, std::false_type) { - return insert_from_range( - position, first, last, - typename std::iterator_traits::iterator_category()); - } - - template - iterator insert_from_range(const_iterator position, Integral n, Integral val, - std::true_type) { - if (capacity() < size() + n) { - size_t index = position - _begin; - enlarge_for_insert(enlarge_size(n), index, n); - position = _begin + index; - } else { - memmove(const_cast(position) + n, position, - (_end - position) * sizeof(Tp)); - _end += n; - } - std::uninitialized_fill_n(const_cast(position), n, - val); - return const_cast(position); - } - - template - iterator insert_from_range(const_iterator position, InputIterator first, - InputIterator last, std::forward_iterator_tag) { - size_t n = std::distance(first, last); - if (capacity() < size() + n) { - size_t index = position - _begin; - enlarge_for_insert(enlarge_size(n), index, n); - position = _begin + index; - } else { - memmove(const_cast(position) + n, position, - (_end - position) * sizeof(Tp)); - _end += n; - } - Tp* destIter = const_cast(position); - while (first != last) { - *destIter = *first; - ++destIter; - ++first; - } - return const_cast(position); - } - - void check_bounds(size_t index) const { - if (index >= size()) - throw std::out_of_range("Access to element in uvector past end"); - } - - size_t enlarge_size(size_t extra_space_needed) const { - return size() + std::max(size(), extra_space_needed); - } - - void enlarge(size_t newSize) { - pointer newStorage = allocate(newSize); - memcpy(newStorage, _begin, size() * sizeof(Tp)); - deallocate(); - _end = newStorage + size(); - _begin = newStorage; - _endOfStorage = _begin + newSize; - } - - void enlarge_for_insert(size_t newSize, size_t insert_position, - size_t insert_count) { - pointer newStorage = allocate(newSize); - memcpy(newStorage, _begin, insert_position * sizeof(Tp)); - memcpy(newStorage + insert_position + insert_count, - _begin + insert_position, (size() - insert_position) * sizeof(Tp)); - deallocate(); - _end = newStorage + size() + insert_count; - _begin = newStorage; - _endOfStorage = _begin + newSize; - } - - // implementation of operator=() without - // propagate_on_container_copy_assignment - uvector& assign_copy_from(const uvector& other, std::false_type) { - const size_t n = other.size(); - if (n > capacity()) { - iterator newStorage = allocate(n); - deallocate(); - _begin = newStorage; - _end = _begin + n; - _endOfStorage = _end; - } - memcpy(_begin, other._begin, n * sizeof(Tp)); - return *this; - } - - // implementation of operator=() with propagate_on_container_copy_assignment - uvector& assign_copy_from(const uvector& other, std::true_type) { - if (static_cast(other) == static_cast(*this)) { - assign_copy_from(other, std::false_type()); - } else { - const size_t n = other.size(); - iterator newStorage = static_cast(other).allocate(n); - deallocate(); - _begin = newStorage; - _end = _begin + n; - _endOfStorage = _end; - memcpy(_begin, other._begin, n * sizeof(Tp)); - Alloc::operator=(static_cast(other)); - } - return *this; - } - - // implementation of operator=() without - // propagate_on_container_move_assignment - uvector& assign_move_from(uvector&& other, std::false_type) { - if (static_cast(other) == static_cast(*this)) { - deallocate(); - _begin = other._begin; - _end = other._end; - _endOfStorage = other._endOfStorage; - other._begin = 0; - other._end = 0; - other._endOfStorage = 0; - } else { - // We should not propagate the allocator and the allocators are different. - // This means we can not swap the allocated space, since then we would - // deallocate the space with a different allocator type. Therefore, we - // need to copy: - assign_copy_from(other, std::false_type()); - } - return *this; - } - - // implementation of operator=() with propagate_on_container_move_assignment - uvector& assign_move_from(uvector&& other, std::true_type) { - deallocate(); - Alloc::operator=(std::move(static_cast(other))); - _begin = other._begin; - _end = other._end; - _endOfStorage = other._endOfStorage; - other._begin = 0; - other._end = 0; - other._endOfStorage = 0; - return *this; - } - - // implementation of swap with propagate_on_container_swap - void swap(uvector& other, std::true_type) { - std::swap(_begin, other._begin); - std::swap(_end, other._end); - std::swap(_endOfStorage, other._endOfStorage); - std::swap(static_cast(other), static_cast(*this)); - } - - // implementation of swap without propagate_on_container_swap - void swap(uvector& other, std::false_type) { - std::swap(_begin, other._begin); - std::swap(_end, other._end); - std::swap(_endOfStorage, other._endOfStorage); - /** - * We have two choices here: - * - Do not swap the allocators. For stateful allocators, we would need to - * reallocate memory, and iterators would not be valid UNLESS - * they were stored as indices. However, containers with stateful - * allocators are not allowed to be swapped unless the allocators are equal, - * in which case swapping is not necessary. - * - Swap the allocators. This would not reallocate memory and - * iterators remain valid, but the trait ignores - * propagate_on_container_swap. - * - * The standard says: - * "Allocator replacement is performed by copy assignment, move assignment, - * or swapping of the allocator only if allocator_traits:: - * propagate_on_container_copy_assignment::value, - * allocator_traits::propagate_on_container_move_assignment::value, - * or allocator_traits::propagate_on_container_swap::value is - * true within the implementation of the corresponding container operation. - * The behavior of a call to a container’s swap function is undefined unless - * the objects being swapped have allocators that compare equal or - * allocator_traits::propagate_on_container_swap::value is - * true." - */ - } - - template - void push_back_range(InputIterator first, InputIterator last, - std::false_type) { - push_back_range( - first, last, - typename std::iterator_traits::iterator_category()); - } - - // This function is called from push_back(iter,iter) when Tp is an integral. - // In that case, the user tried to call push_back(n, &val), but it got caught - // by the wrong overload. - template - void push_back_range(Integral n, Integral val, std::true_type) { - if (capacity() - size() < size_t(n)) { - enlarge(enlarge_size(n)); - } - std::uninitialized_fill_n(_end, n, val); - _end += n; - } - - template - void push_back_range(InputIterator first, InputIterator last, - std::forward_iterator_tag) { - size_t n = std::distance(first, last); - if (n > capacity() - size()) { - enlarge(enlarge_size(n)); - } - while (first != last) { - *_end = *first; - ++_end; - ++first; - } - } -}; - -/** @brief Compare two uvectors for equality. */ -template -inline bool operator==(const uvector& lhs, - const uvector& rhs) { - return lhs.size() == rhs.size() && - std::equal(lhs.begin(), lhs.end(), rhs.begin()); -} - -/** @brief Compare two uvectors for inequality. */ -template -inline bool operator!=(const uvector& lhs, - const uvector& rhs) { - return !(lhs == rhs); -} - -/** @brief Compare two uvectors for smaller than. - * @details If two uvectors compare equal up to the length of one, the uvector - * with the smallest size is consider to be smaller. - */ -template -inline bool operator<(const uvector& lhs, - const uvector& rhs) { - const size_t minSize = std::min(lhs.size(), rhs.size()); - for (size_t i = 0; i != minSize; ++i) { - if (lhs[i] < rhs[i]) - return true; - else if (lhs[i] > rhs[i]) - return false; - } - return lhs.size() < rhs.size(); -} - -/** @brief Compare two uvectors for smaller than or equal. - * @details If two uvectors compare equal up to the length of one, the uvector - * with the smallest size is consider to be smaller. - */ -template -inline bool operator<=(const uvector& lhs, - const uvector& rhs) { - const size_t minSize = std::min(lhs.size(), rhs.size()); - for (size_t i = 0; i != minSize; ++i) { - if (lhs[i] < rhs[i]) - return true; - else if (lhs[i] > rhs[i]) - return false; - } - return lhs.size() <= rhs.size(); -} - -/** @brief Compare two uvectors for larger than. - * @details If two uvectors compare equal up to the length of one, the uvector - * with the smallest size is consider to be smaller. - */ -template -inline bool operator>(const uvector& lhs, - const uvector& rhs) { - return rhs < lhs; -} - -/** @brief Compare two uvectors for larger than or equal. - * @details If two uvectors compare equal up to the length of one, the uvector - * with the smallest size is consider to be smaller. - */ -template -inline bool operator>=(const uvector& lhs, - const uvector& rhs) { - return rhs <= lhs; -} - -/** @brief Swap the contents of the two uvectors. - * @details Iterators to both vectors will remain valid and will point into - * to the swapped container afterwards. This function will never reallocate - * space. - * - * The allocator will be swapped when the @c propagate_on_container_swap - * of the respective @c allocator_trait is @c true_type. - * Its behaviour is undefined when the allocators do not compare equal and - * @c propagate_on_container_swap is false. - */ -template -inline void swap(uvector& x, uvector& y) { - x.swap(y); -} - -/** @} */ - -} // end of namespace ao - -#endif // AO_UVECTOR_H diff --git a/aocommon/uvector_11.h b/aocommon/uvector_11.h deleted file mode 100644 index 72c9fd6..0000000 --- a/aocommon/uvector_11.h +++ /dev/null @@ -1,1207 +0,0 @@ -#ifndef AO_UVECTOR_H -#define AO_UVECTOR_H - -#include -#include -#include -#include -#include -#include - -/** - * @file uvector.h - * Header file for uvector and its relational and swap functions. - * @author André Offringa - * @copyright André Offringa, 2013, distributed under the GPL license version 3. - */ - -namespace ao { - -/** - * @defgroup uvector Class uvector and related functions. - * @{ - */ - -/** - * @brief A container similar to std::vector, but one that allows construction - * without initializing its elements. - * @details This container is similar to a std::vector, except that it can be - * constructed without initializing its elements. This saves the overhead of - * initialization, hence the constructor @ref uvector(size_t) is significantly - * faster than the corresponding std::vector constructor, and has no overhead - * compared to a manually allocated array. - * - * Probably its greatest strength lies in the construction of containers with a - * number of elements that is runtime defined, but that will be initialized - * later. For example: - * - * @code - * // Open a file - * ifstream file("myfile.bin"); - * - * // Construct a buffer for this file - * uvector buffer(buffer_size); - * - * // Read some data into the buffer - * file.read(&buffer[0], buffer_size); - * @endcode - * - * However, it has a few more use-cases with improved performance over - * std::vector. This is possible because of more strengent requirements on the - * element's type. - * - * The container will behave correctly with any trivial type, but will not work - * for almost all non-trivial types. - * - * The methods with different semantics compared to std::vector are: - * * @ref uvector(size_t n) - * * @ref resize(size_t n) - * - * Also the following new members are introduced: - * * @ref insert_uninitialized(const_iterator position, size_t n) - * * @ref push_back(InputIterator first, InputIterator last) - * * @ref push_back(size_t n, const Tp& val) - * * @ref push_back(std::initializer_list initlist) - * * @ref push_back_uninitialized(size_t n) - * - * All other members work exactly like std::vector's members, although some are - * slightly faster because of the stricter requirements on the element type. - * - * @tparam Tp Container's element type - * @tparam Alloc Allocator type. Default is to use the std::allocator. - * - * @author André Offringa - * @copyright André Offringa, 2013, distributed under the GPL license version 3. - */ -template > -class uvector : private Alloc { - static_assert(std::is_standard_layout(), - "A uvector can only hold classes with standard layout"); - - private: -#if __cplusplus > 201402L - typedef std::allocator_traits::is_always_equal - allocator_is_always_equal; -#else - typedef std::false_type allocator_is_always_equal; -#endif - public: - /// Element type - typedef Tp value_type; - /// Type of allocator used to allocate and deallocate space - typedef Alloc allocator_type; - /// Reference to element type - typedef Tp& reference; - /// Constant reference to element type - typedef const Tp& const_reference; - /// Pointer to element type - typedef Tp* pointer; - /// Pointer to constant element type - typedef const Tp* const_pointer; - /// Iterator type - typedef Tp* iterator; - /// Iterator type of constant elements - typedef const Tp* const_iterator; - /// Reverse iterator type - typedef std::reverse_iterator reverse_iterator; - /// Reverse iterator of constant elements - typedef std::reverse_iterator const_reverse_iterator; - /// Difference between to iterators - typedef std::ptrdiff_t difference_type; - /// Type used for indexing elements - typedef std::size_t size_t; - /// Type used for indexing elements - typedef std::size_t size_type; - - private: - pointer _begin, _end, _endOfStorage; - - public: - /** @brief Construct an empty uvector. - * @param allocator Allocator used for allocating and deallocating memory. - */ - explicit uvector(const allocator_type& allocator = Alloc()) noexcept - : Alloc(allocator), - _begin(nullptr), - _end(nullptr), - _endOfStorage(nullptr) {} - - /** @brief Construct a vector with given amount of elements, without - * initializing these. - * @details This constructor deviates from std::vector's behaviour, because it - * will not value construct its elements. It is therefore faster than the - * corresponding constructor of std::vector. - * @param n Number of elements that the uvector will be initialized with. - */ - explicit uvector(size_t n) - : _begin(allocate(n)), _end(_begin + n), _endOfStorage(_end) {} - - /** @brief Construct a vector with given amount of elements and set these to a - * specific value. - * @details This constructor will initialize its members with the given value. - * @param n Number of elements that the uvector will be initialized with. - * @param val Value to initialize all elements with - * @param allocator Allocator used for allocating and deallocating memory. - */ - uvector(size_t n, const value_type& val, - const allocator_type& allocator = Alloc()) - : Alloc(allocator), - _begin(allocate(n)), - _end(_begin + n), - _endOfStorage(_end) { - std::uninitialized_fill_n(_begin, n, val); - } - - /** @brief Construct a vector by copying elements from a range. - * @param first Iterator to range start - * @param last Iterator to range end - * @param allocator Allocator used for allocating and deallocating memory. - */ - template - uvector(InputIterator first, InputIterator last, - const allocator_type& allocator = Alloc()) - : Alloc(allocator) { - construct_from_range(first, last, - std::is_integral()); - } - - /** @brief Copy construct a uvector. - * @details The allocator of the new uvector will be initialized from - * @c - * std::allocator_traits::select_on_container_copy_construction(other). - * @param other Source uvector to be copied from. - */ - uvector(const uvector& other) - : Alloc( - std::allocator_traits::select_on_container_copy_construction( - static_cast(other))), - _begin(allocate(other.size())), - _end(_begin + other.size()), - _endOfStorage(_end) { - std::copy(other._begin, other._end, _begin); - } - - /** @brief Copy construct a uvector with custom allocator. - * @param other Source uvector to be copied from. - * @param allocator Allocator used for allocating and deallocating memory. - */ - uvector(const uvector& other, const allocator_type& allocator) - : Alloc(allocator), - _begin(allocate(other.size())), - _end(_begin + other.size()), - _endOfStorage(_end) { - std::copy(other._begin, other._end, _begin); - } - - /** @brief Move construct a uvector. - * @param other Source uvector to be moved from. - */ - uvector(uvector&& other) noexcept - : Alloc(std::move(other)), - _begin(other._begin), - _end(other._end), - _endOfStorage(other._endOfStorage) { - other._begin = nullptr; - other._end = nullptr; - other._endOfStorage = nullptr; - } - - /** @brief Move construct a uvector with custom allocator. - * @param other Source uvector to be moved from. - * @param allocator Allocator used for allocating and deallocating memory. - */ - uvector(uvector&& other, const allocator_type& allocator) noexcept - : Alloc(allocator), - _begin(other._begin), - _end(other._end), - _endOfStorage(other._endOfStorage) { - other._begin = nullptr; - other._end = nullptr; - other._endOfStorage = nullptr; - } - - /** @brief Construct a uvector from a initializer list. - * @param initlist Initializer list used for initializing the new uvector. - * @param allocator Allocator used for allocating and deallocating memory. - */ - uvector(std::initializer_list initlist, - const allocator_type& allocator = Alloc()) - : Alloc(allocator), - _begin(allocate(initlist.size())), - _end(_begin + initlist.size()), - _endOfStorage(_end) { - iterator destIter = _begin; - for (typename std::initializer_list::const_iterator i = - initlist.begin(); - i != initlist.end(); ++i) { - *destIter = *i; - ++destIter; - } - } - - /** @brief Destructor. */ - ~uvector() noexcept { deallocate(); } - - /** @brief Assign another uvector to this uvector. - * @details The allocator of the uvector will be assigned to @p other when - * std::allocator_traits::propagate_on_container_copy_assignment() is - * of true_type. - */ - uvector& operator=(const uvector& other) { - return assign_copy_from( - other, typename std::allocator_traits< - Alloc>::propagate_on_container_copy_assignment()); - } - - /** @brief Assign another uvector to this uvector. - * @details The allocator of the uvector will be assigned to @p other when - * std::allocator_traits::propagate_on_container_move_assignment() is - * of true_type. - */ - uvector& operator=(uvector&& other) noexcept( - std::allocator_traits< - Alloc>::propagate_on_container_move_assignment::value || - allocator_is_always_equal::value) { - return assign_move_from( - std::move(other), typename std::allocator_traits< - Alloc>::propagate_on_container_move_assignment()); - } - - /** @brief Get iterator to first element. */ - iterator begin() noexcept { return _begin; } - - /** @brief Get constant iterator to first element. */ - const_iterator begin() const noexcept { return _begin; } - - /** @brief Get iterator to element past last element. */ - iterator end() noexcept { return _end; } - - /** @brief Get constant iterator to element past last element. */ - const_iterator end() const noexcept { return _end; } - - /** @brief Get reverse iterator to last element. */ - reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - - /** @brief Get constant reverse iterator to last element. */ - const_reverse_iterator rbegin() const noexcept { - return const_reverse_iterator(end()); - } - - /** @brief Get reverse iterator to element before first element. */ - reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - - /** @brief Get constant reverse iterator to element before first element. */ - const_reverse_iterator rend() const noexcept { - return const_reverse_iterator(begin()); - } - - /** @brief Get constant iterator to first element. */ - const_iterator cbegin() const noexcept { return _begin; } - - /** @brief Get constant iterator to element past last element. */ - const_iterator cend() const noexcept { return _end; } - - /** @brief Get constant reverse iterator to last element. */ - const_reverse_iterator crbegin() const noexcept { - return const_reverse_iterator(end()); - } - - /** @brief Get constant reverse iterator to element before first element. */ - const_reverse_iterator crend() const noexcept { - return const_reverse_iterator(begin()); - } - - /** @brief Get number of elements in container. */ - size_t size() const noexcept { return _end - _begin; } - - /** @brief Get maximum number of elements that this container can hold. */ - size_t max_size() const noexcept { return Alloc::max_size(); } - - /** @brief Change the number of elements in the container. - * @details If the new size is larger than the current size, new values will - * be left uninitialized. Therefore, it is more efficient than @c - * resize(size_t) in - * @c std::vector, as well as @ref resize(size_t, const Tp&). - * If the new size is smaller than the current size, the container will be - * truncated and elements past the new size will be removed. No destructor of - * the removed elements will be called. - * @param n The new size of the container. - */ - void resize(size_t n) { - if (capacity() < n) { - size_t newSize = enlarge_size(n); - pointer newStorage = allocate(newSize); - std::move(_begin, _end, newStorage); - deallocate(); - _begin = newStorage; - _endOfStorage = _begin + newSize; - } - _end = _begin + n; - } - - /** @brief Change the number of elements in the container. - * @details If the new size is larger than the current size, new values will - * be initialized by the given value. If the new size is smaller than the - * current size, the container will be truncated and elements past the new - * size will be removed. No destructor of the removed elements will be called. - * @param n The new size of the container. - * @param val New value of elements that get added to the container. - */ - void resize(size_t n, const Tp& val) { - size_t oldSize = size(); - if (capacity() < n) { - pointer newStorage = allocate(n); - std::move(_begin, _end, newStorage); - deallocate(); - _begin = newStorage; - _endOfStorage = _begin + n; - } - _end = _begin + n; - if (oldSize < n) - std::uninitialized_fill(_begin + oldSize, _end, val); - } - - /** @brief Get the number of elements the container can currently hold without - * reallocating storage. */ - size_t capacity() const noexcept { return _endOfStorage - _begin; } - - /** @brief Determine if the container is currently empty. - * @returns @c true if @ref size() == 0. */ - bool empty() const noexcept { return _begin == _end; } - - /** @brief Reserve space for a number of elements, to prevent the overhead of - * extra reallocations. - * @details This has no effect on the working of the uvector, except that it - * might change the current capacity. This can enhance performance when a - * large number of elements are added, and an approximate size is known a - * priori. - * - * This method might cause a reallocation, causing iterators to be - * invalidated. - * @param n Number of elements to reserve space for. - */ - void reserve(size_t n) { - if (capacity() < n) { - const size_t curSize = size(); - pointer newStorage = allocate(n); - std::move(_begin, _begin + curSize, newStorage); - deallocate(); - _begin = newStorage; - _end = newStorage + curSize; - _endOfStorage = _begin + n; - } - } - - /** @brief Change the capacity of the container such that no extra space is - * hold. - * @details This has no effect on the working of the uvector, except that it - * might change the current capacity. This can reduce the current memory usage - * of the container. - * - * This method might cause a reallocation, causing iterators to be - * invalidated. - */ - void shrink_to_fit() { - const size_t curSize = size(); - if (curSize == 0) { - deallocate(); - _begin = nullptr; - _end = nullptr; - _endOfStorage = nullptr; - } else if (curSize < capacity()) { - pointer newStorage = allocate(curSize); - std::move(_begin, _begin + curSize, newStorage); - deallocate(); - _begin = newStorage; - _end = newStorage + curSize; - _endOfStorage = _begin + curSize; - } - } - - /** @brief Get a reference to the element at the given index. */ - Tp& operator[](size_t index) noexcept { return _begin[index]; } - - /** @brief Get a constant reference to the element at the given index. */ - const Tp& operator[](size_t index) const noexcept { return _begin[index]; } - - /** @brief Get a reference to the element at the given index with bounds - * checking. - * @throws std::out_of_range when given index is past the last element. - */ - Tp& at(size_t index) { - check_bounds(index); - return _begin[index]; - } - - /** @brief Get a constant reference to the element at the given index with - * bounds checking. - * @throws std::out_of_range when given index is past the last element. - */ - const Tp& at(size_t index) const { - check_bounds(index); - return _begin[index]; - } - - /** @brief Get reference to first element in container. */ - Tp& front() noexcept { return *_begin; } - - /** @brief Get constant reference to first element in container. */ - const Tp& front() const noexcept { return *_begin; } - - /** @brief Get reference to last element in container. */ - Tp& back() noexcept { return *(_end - 1); } - - /** @brief Get constant reference to last element in container. */ - const Tp& back() const noexcept { return *(_end - 1); } - - /** @brief Get pointer to internal storage. */ - Tp* data() noexcept { return _begin; } - - /** @brief Get constant pointer to internal storage. */ - const Tp* data() const noexcept { return _begin; } - - /** @brief Assign this container to be equal to the given range. - * @details The container will be resized to fit the length of the given - * range. Iterators are invalidated. - * @param first Iterator to the beginning of the range. - * @param last Iterator past the end of the range. - */ - template - void assign(InputIterator first, InputIterator last) { - assign_from_range(first, last, - std::is_integral()); - } - - /** @brief Resize the container and assign the given value to all elements. - * @details Iterators are invalidated. - * @param n New size of container - * @param val Value to be assigned to all elements. - */ - void assign(size_t n, const Tp& val) { - if (n > capacity()) { - iterator newStorage = allocate(n); - deallocate(); - _begin = newStorage; - _endOfStorage = _begin + n; - } - _end = _begin + n; - std::uninitialized_fill_n(_begin, n, val); - } - - /** @brief Assign this container to an initializer list. - * @details The container will be resized to fit the length of the given - * initializer list. Iterators are invalidated. - * @param initlist List of values to assign to the container. - */ - void assign(std::initializer_list initlist) { - if (initlist.size() > capacity()) { - iterator newStorage = allocate(initlist.size()); - deallocate(); - _begin = newStorage; - _endOfStorage = _begin + initlist.size(); - } - _end = _begin + initlist.size(); - iterator destIter = _begin; - for (typename std::initializer_list::const_iterator i = - initlist.begin(); - i != initlist.end(); ++i) { - *destIter = *i; - ++destIter; - } - } - - /** @brief Add the given value to the end of the container. - * @details Iterators are invalidated. - * @param item Value of new element. - */ - void push_back(const Tp& item) { - if (_end == _endOfStorage) enlarge(enlarge_size(1)); - *_end = item; - ++_end; - } - - /** @brief Add the given value to the end of the container by moving it in. - * @details Iterators are invalidated. - * - * Note that this container can only hold simple types that do not perform - * allocations. Therefore, there is probably no benefit in moving the new item - * in over copying it in with @ref push_back(const Tp&). - * @param item Value of new element. - */ - void push_back(Tp&& item) { - if (_end == _endOfStorage) enlarge(enlarge_size(1)); - *_end = std::move(item); - ++_end; - } - - /** @brief Remove the last element from the container. */ - void pop_back() { --_end; } - - /** @brief Insert an element at a given position. - * @details All iterators will be invalidated. This operation needs to move - * all elements after the new element, and can therefore be expensive. - * @param position Position of the new element. The new element will be added - * before the old element at that position. - * @param item Value of the new item. - * @return Position of the new element. - */ - iterator insert(const_iterator position, const Tp& item) { - if (_end == _endOfStorage) { - size_t index = position - _begin; - enlarge_for_insert(enlarge_size(1), index, 1); - position = _begin + index; - } else { - std::move_backward(const_cast(position), _end, _end + 1); - ++_end; - } - *const_cast(position) = item; - return const_cast(position); - } - - /** @brief Insert elements at a given position and initialize them with a - * value. - * @details All iterators will be invalidated. This operation needs to move - * all elements after the new element, and can therefore be expensive. - * @param position Position of the new elements. The new elements will be - * added before the old element at that position. - * @param n Number of elements to add. - * @param val Value of the new item. - * @return Position of the first new element. - */ - iterator insert(const_iterator position, size_t n, const Tp& val) { - if (capacity() < size() + n) { - size_t index = position - _begin; - enlarge_for_insert(enlarge_size(n), index, n); - position = _begin + index; - } else { - std::move_backward(const_cast(position), _end, _end + n); - _end += n; - } - std::uninitialized_fill_n(const_cast(position), n, - val); - return const_cast(position); - } - - /** @brief Insert elements at a given position and initialize them from a - * range. - * @details All iterators will be invalidated. This operation needs to move - * all elements after the new element, and can therefore be expensive. - * @param position Position of the new elements. The new elements will be - * added before the old element at that position. - * @param first Iterator to the beginning of the range. - * @param last Iterator past the end of the range. - * @return Position of the first new element. - */ - template - iterator insert(const_iterator position, InputIterator first, - InputIterator last) { - return insert_from_range(position, first, last, - std::is_integral()); - } - - /** @brief Insert an element at a given position by moving it in. - * @details All iterators will be invalidated. This operation needs to move - * all elements after the new element, and can therefore be expensive. - * - * Note that this container can only hold simple types that do not perform - * allocations. Therefore, there is probably no benefit in moving the new item - * in over copying it in with - * @ref insert(const_iterator, const Tp&). - * @param position Position of the new element. The new element will be added - * before the old element at that position. - * @param item Value of the new item. - * @return Position of the new element. - */ - iterator insert(const_iterator position, Tp&& item) { - if (_end == _endOfStorage) { - size_t index = position - _begin; - enlarge_for_insert(enlarge_size(1), index, 1); - position = _begin + index; - } else { - std::move_backward(const_cast(position), _end, _end + 1); - ++_end; - } - *const_cast(position) = std::move(item); - return const_cast(position); - } - - /** @brief Insert elements at a given position and initialize them from a - * initializer list. - * @details All iterators will be invalidated. This operation needs to move - * all elements after the new element, and can therefore be expensive. - * @param position Position of the new elements. The new elements will be - * added before the old element at that position. - * @param initlist List of items to insert. - * @return Position of the first new element. - */ - iterator insert(const_iterator position, std::initializer_list initlist) { - if (capacity() < size() + initlist.size()) { - size_t index = position - _begin; - enlarge_for_insert(enlarge_size(initlist.size()), index, initlist.size()); - position = _begin + index; - } else { - std::move_backward(const_cast(position), _end, - _end + initlist.size()); - _end += initlist.size(); - } - iterator destIter = const_cast(position); - for (typename std::initializer_list::const_iterator i = - initlist.begin(); - i != initlist.end(); ++i) { - *destIter = *i; - ++destIter; - } - return const_cast(position); - } - - /** @brief Delete an element from the container. - * @details This operation moves all elements past the removed element, and - * can therefore be expensive. - * @param position Position of element to be removed. - * @return Iterator pointing to the first element past the delete element. - */ - iterator erase(const_iterator position) { - std::move(const_cast(position) + 1, _end, - const_cast(position)); - --_end; - return const_cast(position); - } - - /** @brief Delete a range of elements from the container. - * @details This operation moves all elements past the removed elements, and - * can therefore be expensive. - * @param first Position of first element to be removed. - * @param last Position past last element to be removed. - * @return Iterator pointing to the first element past the delete element. - */ - iterator erase(const_iterator first, const_iterator last) { - std::move(const_cast(last), _end, const_cast(first)); - _end -= last - first; - return const_cast(first); - } - - /** @brief Swap the contents of this uvector with the given uvector. - * @details Iterators to both vectors will remain valid and will point into - * to the swapped container afterwards. This function will never reallocate - * space. - * - * The allocator will be swapped when the @c propagate_on_container_swap - * of the respective @c allocator_trait is @c true_type. - * Its behaviour is undefined when the allocators do not compare equal and - * @c propagate_on_container_swap is false. - * @param other Other uvector whose contents it to be swapped with this. - */ - void swap(uvector& other) noexcept { - swap(other, - typename std::allocator_traits::propagate_on_container_swap()); - } - - /** @brief Remove all elements from the container. */ - void clear() { _end = _begin; } - - /** @brief Insert an element at a given position by constructing it in place. - * @details All iterators will be invalidated. This operation needs to move - * all elements after the new element, and can therefore be expensive. - * @param position Position of the new element. The new element will be added - * before the old element at that position. - * @param args List of arguments to be forwarded to construct the new element. - * @return Position of the new element. - */ - template - iterator emplace(const_iterator position, Args&&... args) { - if (_end == _endOfStorage) { - size_t index = position - _begin; - enlarge_for_insert(enlarge_size(1), index, 1); - position = _begin + index; - } else { - std::move_backward(const_cast(position), _end, _end + 1); - ++_end; - } - *const_cast(position) = Tp(std::forward(args)...); - return const_cast(position); - } - - /** @brief Add the given value to the end of the container by constructing it - * in place. - * @details Iterators are invalidated. - * @param args List of arguments to be forwarded to construct the new element. - */ - template - void emplace_back(Args&&... args) { - if (_end == _endOfStorage) enlarge(enlarge_size(1)); - *_end = Tp(std::forward(args)...); - ++_end; - } - - /** @brief Get a copy of the allocator. */ - allocator_type get_allocator() const noexcept { return *this; } - - // --- NON STANDARD METHODS --- - - /** @brief Insert elements at a given position without initializing them. - * @details All iterators will be invalidated. This operation needs to move - * all elements after the new element, and can therefore be expensive. It will - * not initialize the new elements, and is therefore faster than @ref - * insert(const_iterator, size_t, const Tp&). - * - * This method is non-standard: it is not present in std::vector. - * @param position Position of the new elements. The new elements will be - * added before the old element at that position. - * @param n Number of elements to add. - */ - iterator insert_uninitialized(const_iterator position, size_t n) { - if (capacity() < size() + n) { - size_t index = position - _begin; - enlarge_for_insert(enlarge_size(n), index, n); - position = _begin + index; - } else { - std::move_backward(const_cast(position), _end, _end + n); - _end += n; - } - return const_cast(position); - } - - /** @brief Add a range of items to the end of the container. - * @details All iterators will be invalidated. - * - * This method is non-standard: it is not present in std::vector. - * @param first Iterator to the beginning of the range. - * @param last Iterator past the end of the range. - */ - template - void push_back(InputIterator first, InputIterator last) { - push_back_range(first, last, - std::is_integral()); - } - - /** @brief Add elements at the end and initialize them with a value. - * @details All iterators will be invalidated. - * - * This method is non-standard: it is not present in std::vector. - * @param n Number of elements to add. - * @param val Value of the new items. - */ - void push_back(size_t n, const Tp& val) { - if (capacity() - size() < n) { - enlarge(enlarge_size(n)); - } - std::uninitialized_fill_n(_end, n, val); - _end += n; - } - - /** @brief Add elements from an initializer list to the end of the container. - * @details All iterators will be invalidated. - * - * This method is non-standard: it is not present in std::vector. - * @param initlist The list with values to add. - */ - void push_back(std::initializer_list initlist) { - if (capacity() - size() < initlist.size()) { - enlarge(enlarge_size(initlist.size())); - } - for (typename std::initializer_list::iterator i = initlist.begin(); - i != initlist.end(); ++i) { - *_end = *i; - ++_end; - } - } - - /** @brief Add elements at the end without initializing them. - * @details All iterators will be invalidated. - * - * This method is non-standard: it is not present in std::vector. - * @param n Number of elements to add. - */ - void push_back_uninitialized(size_t n) { resize(size() + n); } - - private: - pointer allocate(size_t n) { return Alloc::allocate(n); } - - void deallocate() noexcept { deallocate(_begin, capacity()); } - - void deallocate(pointer begin, size_t n) noexcept { - if (begin != nullptr) Alloc::deallocate(begin, n); - } - - template - void construct_from_range(InputIterator first, InputIterator last, - std::false_type) { - construct_from_range( - first, last, - typename std::iterator_traits::iterator_category()); - } - - template - void construct_from_range(Integral n, Integral val, std::true_type) { - _begin = allocate(n); - _end = _begin + n; - _endOfStorage = _end; - std::uninitialized_fill_n(_begin, n, val); - } - - template - void construct_from_range(InputIterator first, InputIterator last, - std::forward_iterator_tag) { - size_t n = std::distance(first, last); - _begin = allocate(n); - _end = _begin + n; - _endOfStorage = _begin + n; - Tp* destIter = _begin; - while (first != last) { - *destIter = *first; - ++destIter; - ++first; - } - } - - template - void assign_from_range(InputIterator first, InputIterator last, - std::false_type) { - assign_from_range( - first, last, - typename std::iterator_traits::iterator_category()); - } - - // This function is called from assign(iter,iter) when Tp is an integral. In - // that case, the user tried to call assign(n, &val), but it got caught by the - // wrong overload. - template - void assign_from_range(Integral n, Integral val, std::true_type) { - if (size_t(n) > capacity()) { - iterator newStorage = allocate(n); - deallocate(); - _begin = newStorage; - _endOfStorage = _begin + n; - } - _end = _begin + n; - std::uninitialized_fill_n(_begin, n, val); - } - - template - void assign_from_range(InputIterator first, InputIterator last, - std::forward_iterator_tag) { - size_t n = std::distance(first, last); - if (n > capacity()) { - iterator newStorage = allocate(n); - deallocate(); - _begin = newStorage; - _endOfStorage = _begin + n; - } - _end = _begin + n; - Tp* destIter = _begin; - while (first != last) { - *destIter = *first; - ++destIter; - ++first; - } - } - - template - iterator insert_from_range(const_iterator position, InputIterator first, - InputIterator last, std::false_type) { - return insert_from_range( - position, first, last, - typename std::iterator_traits::iterator_category()); - } - - template - iterator insert_from_range(const_iterator position, Integral n, Integral val, - std::true_type) { - if (capacity() < size() + n) { - size_t index = position - _begin; - enlarge_for_insert(enlarge_size(n), index, n); - position = _begin + index; - } else { - std::move_backward(const_cast(position), _end, _end + n); - _end += n; - } - std::uninitialized_fill_n(const_cast(position), n, - val); - return const_cast(position); - } - - template - iterator insert_from_range(const_iterator position, InputIterator first, - InputIterator last, std::forward_iterator_tag) { - size_t n = std::distance(first, last); - if (capacity() < size() + n) { - size_t index = position - _begin; - enlarge_for_insert(enlarge_size(n), index, n); - position = _begin + index; - } else { - std::move_backward(const_cast(position), _end, _end + n); - _end += n; - } - Tp* destIter = const_cast(position); - while (first != last) { - *destIter = *first; - ++destIter; - ++first; - } - return const_cast(position); - } - - void check_bounds(size_t index) const { - if (index >= size()) - throw std::out_of_range("Access to element in uvector past end"); - } - - size_t enlarge_size(size_t extra_space_needed) const noexcept { - return size() + std::max(size(), extra_space_needed); - } - - void enlarge(size_t newSize) { - pointer newStorage = allocate(newSize); - std::copy(_begin, _end, newStorage); - deallocate(); - _end = newStorage + size(); - _begin = newStorage; - _endOfStorage = _begin + newSize; - } - - void enlarge_for_insert(size_t newSize, size_t insert_position, - size_t insert_count) { - pointer newStorage = allocate(newSize); - std::copy(_begin, _begin + insert_position, newStorage); - std::copy(_begin + insert_position, _end, - newStorage + insert_position + insert_count); - deallocate(); - _end = newStorage + size() + insert_count; - _begin = newStorage; - _endOfStorage = _begin + newSize; - } - - // implementation of operator=(const&) without - // propagate_on_container_copy_assignment - uvector& assign_copy_from(const uvector& other, std::false_type) { - const size_t n = other.size(); - if (n > capacity()) { - iterator newStorage = allocate(n); - deallocate(); - _begin = newStorage; - _end = _begin + n; - _endOfStorage = _end; - } - std::copy(other._begin, other._begin + n, _begin); - return *this; - } - - // implementation of operator=(const&) with - // propagate_on_container_copy_assignment - uvector& assign_copy_from(const uvector& other, std::true_type) { - if (allocator_is_always_equal() || - static_cast(other) == static_cast(*this)) { - assign_copy_from(other, std::false_type()); - } else { - const size_t n = other.size(); - iterator newStorage = static_cast(other).allocate(n); - deallocate(); - _begin = newStorage; - _end = _begin + n; - _endOfStorage = _end; - std::copy(other._begin, other._begin + n, _begin); - Alloc::operator=(static_cast(other)); - } - return *this; - } - - // implementation of operator=() without - // propagate_on_container_move_assignment - uvector& assign_move_from( - uvector&& other, - std::false_type) noexcept(allocator_is_always_equal::value) { - if (allocator_is_always_equal::value || - static_cast(other) == static_cast(*this)) { - deallocate(); - _begin = other._begin; - _end = other._end; - _endOfStorage = other._endOfStorage; - other._begin = nullptr; - other._end = nullptr; - other._endOfStorage = nullptr; - } else { - // We should not propagate the allocator and the allocators are different. - // This means we can not swap the allocated space, since then we would - // deallocate the space with a different allocator type. Therefore, we - // need to copy: - assign_copy_from(other, std::false_type()); - } - return *this; - } - - // implementation of operator=() with propagate_on_container_move_assignment - uvector& assign_move_from(uvector&& other, - std::true_type) noexcept { - deallocate(); - Alloc::operator=(std::move(static_cast(other))); - _begin = other._begin; - _end = other._end; - _endOfStorage = other._endOfStorage; - other._begin = nullptr; - other._end = nullptr; - other._endOfStorage = nullptr; - return *this; - } - - // implementation of swap with propagate_on_container_swap - void swap(uvector& other, std::true_type) noexcept { - std::swap(_begin, other._begin); - std::swap(_end, other._end); - std::swap(_endOfStorage, other._endOfStorage); - std::swap(static_cast(other), static_cast(*this)); - } - - // implementation of swap without propagate_on_container_swap - void swap(uvector& other, std::false_type) noexcept { - std::swap(_begin, other._begin); - std::swap(_end, other._end); - std::swap(_endOfStorage, other._endOfStorage); - /** - * We have two choices here: - * - Do not swap the allocators. For stateful allocators, we would need to - * reallocate memory, and iterators would not be valid UNLESS - * they were stored as indices. However, containers with stateful - * allocators are not allowed to be swapped unless the allocators are equal, - * in which case swapping is not necessary. - * - Swap the allocators. This would not reallocate memory and - * iterators remain valid, but the trait ignores - * propagate_on_container_swap. - * - * The standard says: - * "Allocator replacement is performed by copy assignment, move assignment, - * or swapping of the allocator only if allocator_traits:: - * propagate_on_container_copy_assignment::value, - * allocator_traits::propagate_on_container_move_assignment::value, - * or allocator_traits::propagate_on_container_swap::value is - * true within the implementation of the corresponding container operation. - * The behavior of a call to a container’s swap function is undefined unless - * the objects being swapped have allocators that compare equal or - * allocator_traits::propagate_on_container_swap::value is - * true." - */ - } - - template - void push_back_range(InputIterator first, InputIterator last, - std::false_type) { - push_back_range( - first, last, - typename std::iterator_traits::iterator_category()); - } - - // This function is called from push_back(iter,iter) when Tp is an integral. - // In that case, the user tried to call push_back(n, &val), but it got caught - // by the wrong overload. - template - void push_back_range(Integral n, Integral val, std::true_type) { - if (capacity() - size() < size_t(n)) { - enlarge(enlarge_size(n)); - } - std::uninitialized_fill_n(_end, n, val); - _end += n; - } - - template - void push_back_range(InputIterator first, InputIterator last, - std::forward_iterator_tag) { - size_t n = std::distance(first, last); - if (n > capacity() - size()) { - enlarge(enlarge_size(n)); - } - while (first != last) { - *_end = *first; - ++_end; - ++first; - } - } -}; - -/** @brief Compare two uvectors for equality. */ -template -inline bool operator==(const uvector& lhs, - const uvector& rhs) noexcept { - return lhs.size() == rhs.size() && - std::equal(lhs.begin(), lhs.end(), rhs.begin()); -} - -/** @brief Compare two uvectors for inequality. */ -template -inline bool operator!=(const uvector& lhs, - const uvector& rhs) noexcept { - return !(lhs == rhs); -} - -/** @brief Compare two uvectors for smaller than. - * @details If two uvectors compare equal up to the length of one, the uvector - * with the smallest size is consider to be smaller. - */ -template -inline bool operator<(const uvector& lhs, - const uvector& rhs) noexcept { - const size_t minSize = std::min(lhs.size(), rhs.size()); - for (size_t i = 0; i != minSize; ++i) { - if (lhs[i] < rhs[i]) - return true; - else if (lhs[i] > rhs[i]) - return false; - } - return lhs.size() < rhs.size(); -} - -/** @brief Compare two uvectors for smaller than or equal. - * @details If two uvectors compare equal up to the length of one, the uvector - * with the smallest size is consider to be smaller. - */ -template -inline bool operator<=(const uvector& lhs, - const uvector& rhs) noexcept { - const size_t minSize = std::min(lhs.size(), rhs.size()); - for (size_t i = 0; i != minSize; ++i) { - if (lhs[i] < rhs[i]) - return true; - else if (lhs[i] > rhs[i]) - return false; - } - return lhs.size() <= rhs.size(); -} - -/** @brief Compare two uvectors for larger than. - * @details If two uvectors compare equal up to the length of one, the uvector - * with the smallest size is consider to be smaller. - */ -template -inline bool operator>(const uvector& lhs, - const uvector& rhs) noexcept { - return rhs < lhs; -} - -/** @brief Compare two uvectors for larger than or equal. - * @details If two uvectors compare equal up to the length of one, the uvector - * with the smallest size is consider to be smaller. - */ -template -inline bool operator>=(const uvector& lhs, - const uvector& rhs) noexcept { - return rhs <= lhs; -} - -/** @brief Swap the contents of the two uvectors. - * @details Iterators to both vectors will remain valid and will point into - * to the swapped container afterwards. This function will never reallocate - * space. - * - * The allocator will be swapped when the @c propagate_on_container_swap - * of the respective @c allocator_trait is @c true_type. - * Its behaviour is undefined when the allocators do not compare equal and - * @c propagate_on_container_swap is false. - */ -template -inline void swap(uvector& x, uvector& y) { - x.swap(y); -} - -/** @} */ - -} // end of namespace ao - -#endif // AO_UVECTOR_H diff --git a/dyscostman.cc b/dyscostman.cc index a4783b7..080223b 100644 --- a/dyscostman.cc +++ b/dyscostman.cc @@ -164,7 +164,7 @@ casacore::Bool DyscoStMan::flush(casacore::AipsIO &, return false; } -void DyscoStMan::create(casacore::uInt nRow) { +void DyscoStMan::create64(casacore::rownr_t nRow) { _nRow = nRow; _fStream.reset(new std::fstream( fileName().c_str(), @@ -281,7 +281,7 @@ void DyscoStMan::initializeRowsPerBlock(size_t rowsPerBlock, if (writeToHeader) writeHeader(); } -void DyscoStMan::open(casacore::uInt nRow, casacore::AipsIO &) { +casacore::rownr_t DyscoStMan::open64(casacore::rownr_t nRow, casacore::AipsIO &) { _nRow = nRow; _fStream.reset(new std::fstream(fileName().c_str(), std::ios_base::in | std::ios_base::out)); @@ -302,6 +302,7 @@ void DyscoStMan::open(casacore::uInt nRow, casacore::AipsIO &) { _nBlocksInFile = (size_t(size) - _headerSize) / _blockSize; else _nBlocksInFile = 0; + return nRow; } casacore::DataManagerColumn *DyscoStMan::makeScalarColumn( @@ -344,7 +345,10 @@ casacore::DataManagerColumn *DyscoStMan::makeIndArrColumn( "column desc constructor"); } -void DyscoStMan::resync(casacore::uInt /*nRow*/) {} +casacore::rownr_t DyscoStMan::resync64(casacore::rownr_t nRow) +{ + return nRow; +} void DyscoStMan::deleteManager() { unlink(fileName().c_str()); } @@ -377,9 +381,9 @@ void DyscoStMan::prepare() { void DyscoStMan::reopenRW() {} -void DyscoStMan::addRow(casacore::uInt nrrow) { _nRow += nrrow; } +void DyscoStMan::addRow64(casacore::rownr_t nrrow) { _nRow += nrrow; } -void DyscoStMan::removeRow(casacore::uInt rowNr) { +void DyscoStMan::removeRow64(casacore::rownr_t rowNr) { if (rowNr != _nRow - 1) throw DyscoStManError( "Trying to remove a row in the middle of the file: " diff --git a/dyscostman.h b/dyscostman.h index 7375e2f..329f0ef 100644 --- a/dyscostman.h +++ b/dyscostman.h @@ -51,7 +51,7 @@ class DyscoStMan : public casacore::DataManager { * initialized to AF normalization with a truncated Gaussian distribution for * the quantization, and a truncation of sigma = 2.5. To change the settings, * use one of the Set...Distribution() methods and SetNormalization(). - * @param databitRate The number of bits per float used for visibilities. + * @param dataBitRate The number of bits per float used for visibilities. * @param weightBitRate The number of bits per float used for the weight * column. * @param name Storage manager name. @@ -299,6 +299,7 @@ class DyscoStMan : public casacore::DataManager { * To be called by a column once it determines rowsPerBlock and antennaCount. * @param rowsPerBlock Number of measurement set rows in one time block. * @param antennaCount Highest antenna index+1 used in a time block. + * @param writeToHeader Write the header? */ void initializeRowsPerBlock(size_t rowsPerBlock, size_t antennaCount, bool writeToHeader); @@ -334,11 +335,11 @@ class DyscoStMan : public casacore::DataManager { // Let the storage manager create files as needed for a new table. // This allows a column with an indirect array to create its file. - virtual void create(casacore::uInt nRow) final override; + virtual void create64(casacore::rownr_t nRow) final override; // Open the storage manager file for an existing table. // Return the number of rows in the data file. - virtual void open(casacore::uInt nRow, casacore::AipsIO &) final override; + virtual casacore::rownr_t open64(casacore::rownr_t nRow, casacore::AipsIO &) final override; // Create a column in the storage manager on behalf of a table column. // The caller will NOT delete the newly created object. @@ -357,7 +358,7 @@ class DyscoStMan : public casacore::DataManager { const casacore::String &name, int dataType, const casacore::String &dataTypeID) final override; - virtual void resync(casacore::uInt nRow) final override; + virtual casacore::rownr_t resync64(casacore::rownr_t nRow) final override; virtual void deleteManager() final override; @@ -371,10 +372,10 @@ class DyscoStMan : public casacore::DataManager { virtual void reopenRW() final override; // Add rows to the storage manager. - virtual void addRow(casacore::uInt nrrow) final override; + virtual void addRow64(casacore::rownr_t nrrow) final override; // Delete a row from all columns. - virtual void removeRow(casacore::uInt rowNr) final override; + virtual void removeRow64(casacore::rownr_t rowNr) final override; // Do the final addition of a column. virtual void addColumn(casacore::DataManagerColumn *) final override; diff --git a/dyscostmancol.h b/dyscostmancol.h index 8ff422e..a197e4f 100644 --- a/dyscostmancol.h +++ b/dyscostmancol.h @@ -4,7 +4,7 @@ #include "dyscodistribution.h" #include "dysconormalization.h" -#include +#include #include @@ -19,7 +19,7 @@ class DyscoStMan; * Base class for columns of the DyscoStMan. * @author André Dysco */ -class DyscoStManColumn : public casacore::StManColumn { +class DyscoStManColumn : public casacore::StManColumnBase { public: /** * Constructor, to be overloaded by subclass. @@ -27,7 +27,7 @@ class DyscoStManColumn : public casacore::StManColumn { * @param dtype The column's type as defined by Casacore. */ explicit DyscoStManColumn(DyscoStMan *parent, int dtype) - : casacore::StManColumn(dtype), + : casacore::StManColumnBase(dtype), _offsetInBlock(0), _storageManager(parent) {} @@ -75,15 +75,17 @@ class DyscoStManColumn : public casacore::StManColumn { /** * Read a row of compressed data from the stman file. - * @param rowIndex The index of the row to read. + * @param blockIndex The block index of the row to read. * @param dest The destination buffer, should be at least of size Stride(). + * @param size The nr of bytes to be read. */ void readCompressedData(size_t blockIndex, unsigned char *dest, size_t size); /** * Write a row of compressed data to the stman file. - * @param rowIndex The index of the row to write. + * @param blockIndex The block index of the row to write. * @param data The data buffer containing Stride() bytes. + * @param size The nr of bytes to be written. */ void writeCompressedData(size_t blockIndex, const unsigned char *data, size_t size); diff --git a/stochasticencoder.h b/stochasticencoder.h index 4dc70cc..8893e99 100644 --- a/stochasticencoder.h +++ b/stochasticencoder.h @@ -98,6 +98,7 @@ class StochasticEncoder { * Use Decode() on the returned symbol to get * the decoded value. * @param value Floating point value to be encoded. + * @param ditherValue The dithering value to apply. */ symbol_t EncodeWithDithering(ValueType value, unsigned ditherValue) const { if (std::isfinite(value)) { diff --git a/threadeddyscocolumn.cc b/threadeddyscocolumn.cc index 1e05a76..07be50c 100644 --- a/threadeddyscocolumn.cc +++ b/threadeddyscocolumn.cc @@ -105,21 +105,20 @@ void ThreadedDyscoColumn::loadBlock(size_t blockIndex) { template void ThreadedDyscoColumn::getValues( - casacore::uInt rowNr, casacore::Array *dataPtr) { + casacore::rownr_t rowNr, casacore::Array *dataArr) { + // Make sure array storage is contiguous. + casacore::Bool deleteIt; + DataType* dataPtr = dataArr->getStorage (deleteIt); if (!areOffsetsInitialized()) { // Trying to read before first block was written -- return zero // TODO if a few rows were written of the first block, those are // incorrectly returned. This is a rare case but can be fixed. - for (typename casacore::Array::contiter i = dataPtr->cbegin(); - i != dataPtr->cend(); ++i) - *i = DataType(); + *dataPtr = DataType(); } else { size_t blockIndex = getBlockIndex(rowNr); if (blockIndex >= nBlocksInFile()) { // Trying to read a row that was not stored yet -- return zero - for (typename casacore::Array::contiter i = dataPtr->cbegin(); - i != dataPtr->cend(); ++i) - *i = DataType(); + *dataPtr = DataType(); } else { std::unique_lock lock(_mutex); // Wait until the block to be read is not in the write cache @@ -137,9 +136,10 @@ void ThreadedDyscoColumn::getValues( // The time block encoder is now initialized and contains the unpacked // block. - _timeBlockBuffer->GetData(getRowWithinBlock(rowNr), dataPtr->data()); + _timeBlockBuffer->GetData(getRowWithinBlock(rowNr), dataPtr); } } + dataArr->putStorage (dataPtr, deleteIt); } template @@ -168,7 +168,10 @@ void ThreadedDyscoColumn::storeBlock() { template void ThreadedDyscoColumn::putValues( - casacore::uInt rowNr, const casacore::Array *dataPtr) { + casacore::rownr_t rowNr, const casacore::Array *dataArr) { + // Make sure array storage is contiguous. + casacore::Bool deleteIt; + const DataType* dataPtr = dataArr->getStorage (deleteIt); if (!areOffsetsInitialized()) { // If the manager did not initialize its offsets yet, then it is determined // from the first "time block" (a block with the same time, field and spw) @@ -207,11 +210,12 @@ void ThreadedDyscoColumn::putValues( // Load new block loadBlock(blockIndex); } - _timeBlockBuffer->SetData(blockRow, ant1, ant2, dataPtr->data()); + _timeBlockBuffer->SetData(blockRow, ant1, ant2, dataPtr); } else { - _timeBlockBuffer->SetData(rowNr, ant1, ant2, dataPtr->data()); + _timeBlockBuffer->SetData(rowNr, ant1, ant2, dataPtr); } _isCurrentBlockChanged = true; + dataArr->freeStorage (dataPtr, deleteIt); } template diff --git a/threadeddyscocolumn.h b/threadeddyscocolumn.h index f5519f9..377ca6d 100644 --- a/threadeddyscocolumn.h +++ b/threadeddyscocolumn.h @@ -24,7 +24,7 @@ namespace dyscostman { class DyscoStMan; /** - * A column for storing compressed values in a threaded way, tailered for the + * A column for storing compressed values in a threaded way, tailored for the * data and weight columns that use a threaded approach for encoding. * @author André Offringa */ @@ -50,8 +50,8 @@ class ThreadedDyscoColumn : public DyscoStManColumn { virtual void setShapeColumn(const casacore::IPosition &shape) override; /** Get the dimensions of the values in a particular row. - * @param rownr The row to get the shape for. */ - virtual casacore::IPosition shape(casacore::uInt /*rownr*/) override { + * The rownr parameter is not used as the shape is the same for all rows. */ + virtual casacore::IPosition shape(casacore::rownr_t /*rownr*/) override { return _shape; } @@ -61,18 +61,10 @@ class ThreadedDyscoColumn : public DyscoStManColumn { * @param rowNr The row number to get the values for. * @param dataPtr The array of values, which should be a contiguous array. */ - virtual void getArrayComplexV( - casacore::uInt rowNr, - casacore::Array *dataPtr) override { - // Note that this method is specialized for std::complex -- the - // generic method won't do anything - return DyscoStManColumn::getArrayComplexV(rowNr, dataPtr); - } - virtual void getArrayfloatV(casacore::uInt rowNr, - casacore::Array *dataPtr) override { - // Note that this method is specialized for float -- the generic method - // won't do anything - return DyscoStManColumn::getArrayfloatV(rowNr, dataPtr); + virtual void getArrayV( + casacore::rownr_t rowNr, + casacore::ArrayBase &dataPtr) override { + return DyscoStManColumn::getArrayV(rowNr, dataPtr); } /** @@ -82,18 +74,10 @@ class ThreadedDyscoColumn : public DyscoStManColumn { * @param rowNr The row number to write the values to. * @param dataPtr The data pointer, which should be a contiguous array. */ - virtual void putArrayComplexV( - casacore::uInt rowNr, - const casacore::Array *dataPtr) override { - // Note that this method is specialized for std::complex -- the - // generic method won't do anything - return DyscoStManColumn::putArrayComplexV(rowNr, dataPtr); - } - virtual void putArrayfloatV(casacore::uInt rowNr, - const casacore::Array *dataPtr) override { - // Note that this method is specialized for float -- the generic method - // won't do anything - return DyscoStManColumn::putArrayfloatV(rowNr, dataPtr); + virtual void putArrayV( + casacore::rownr_t rowNr, + const casacore::ArrayBase &dataPtr) override { + return DyscoStManColumn::putArrayV(rowNr, dataPtr); } virtual void Prepare(DyscoDistribution distribution, @@ -190,8 +174,8 @@ class ThreadedDyscoColumn : public DyscoStManColumn { typedef std::map cache_t; - void getValues(casacore::uInt rowNr, casacore::Array *dataPtr); - void putValues(casacore::uInt rowNr, const casacore::Array *dataPtr); + void getValues(casacore::rownr_t rowNr, casacore::Array *dataPtr); + void putValues(casacore::rownr_t rowNr, const casacore::Array *dataPtr); void stopThreads(); void encodeAndWrite(size_t blockIndex, const CacheItem &item, @@ -228,24 +212,24 @@ class ThreadedDyscoColumn : public DyscoStManColumn { }; template <> -inline void ThreadedDyscoColumn>::getArrayComplexV( - casacore::uInt rowNr, casacore::Array *dataPtr) { - getValues(rowNr, dataPtr); +inline void ThreadedDyscoColumn>::getArrayV( + casacore::rownr_t rowNr, casacore::ArrayBase &dataPtr) { + getValues(rowNr, static_cast>*>(&dataPtr)); } template <> -inline void ThreadedDyscoColumn>::putArrayComplexV( - casacore::uInt rowNr, const casacore::Array *dataPtr) { - putValues(rowNr, dataPtr); +inline void ThreadedDyscoColumn>::putArrayV( + casacore::rownr_t rowNr, const casacore::ArrayBase &dataPtr) { + putValues(rowNr, static_cast>*>(&dataPtr)); } template <> -inline void ThreadedDyscoColumn::getArrayfloatV( - casacore::uInt rowNr, casacore::Array *dataPtr) { - getValues(rowNr, dataPtr); +inline void ThreadedDyscoColumn::getArrayV( + casacore::rownr_t rowNr, casacore::ArrayBase &dataPtr) { + getValues(rowNr, static_cast*>(&dataPtr)); } template <> -inline void ThreadedDyscoColumn::putArrayfloatV( - casacore::uInt rowNr, const casacore::Array *dataPtr) { - putValues(rowNr, dataPtr); +inline void ThreadedDyscoColumn::putArrayV( + casacore::rownr_t rowNr, const casacore::ArrayBase &dataPtr) { + putValues(rowNr, static_cast*>(&dataPtr)); } extern template class ThreadedDyscoColumn>; diff --git a/uvector.h b/uvector.h index bb2c335..72c9fd6 100644 --- a/uvector.h +++ b/uvector.h @@ -1 +1,1207 @@ -#include "aocommon/uvector_11.h" +#ifndef AO_UVECTOR_H +#define AO_UVECTOR_H + +#include +#include +#include +#include +#include +#include + +/** + * @file uvector.h + * Header file for uvector and its relational and swap functions. + * @author André Offringa + * @copyright André Offringa, 2013, distributed under the GPL license version 3. + */ + +namespace ao { + +/** + * @defgroup uvector Class uvector and related functions. + * @{ + */ + +/** + * @brief A container similar to std::vector, but one that allows construction + * without initializing its elements. + * @details This container is similar to a std::vector, except that it can be + * constructed without initializing its elements. This saves the overhead of + * initialization, hence the constructor @ref uvector(size_t) is significantly + * faster than the corresponding std::vector constructor, and has no overhead + * compared to a manually allocated array. + * + * Probably its greatest strength lies in the construction of containers with a + * number of elements that is runtime defined, but that will be initialized + * later. For example: + * + * @code + * // Open a file + * ifstream file("myfile.bin"); + * + * // Construct a buffer for this file + * uvector buffer(buffer_size); + * + * // Read some data into the buffer + * file.read(&buffer[0], buffer_size); + * @endcode + * + * However, it has a few more use-cases with improved performance over + * std::vector. This is possible because of more strengent requirements on the + * element's type. + * + * The container will behave correctly with any trivial type, but will not work + * for almost all non-trivial types. + * + * The methods with different semantics compared to std::vector are: + * * @ref uvector(size_t n) + * * @ref resize(size_t n) + * + * Also the following new members are introduced: + * * @ref insert_uninitialized(const_iterator position, size_t n) + * * @ref push_back(InputIterator first, InputIterator last) + * * @ref push_back(size_t n, const Tp& val) + * * @ref push_back(std::initializer_list initlist) + * * @ref push_back_uninitialized(size_t n) + * + * All other members work exactly like std::vector's members, although some are + * slightly faster because of the stricter requirements on the element type. + * + * @tparam Tp Container's element type + * @tparam Alloc Allocator type. Default is to use the std::allocator. + * + * @author André Offringa + * @copyright André Offringa, 2013, distributed under the GPL license version 3. + */ +template > +class uvector : private Alloc { + static_assert(std::is_standard_layout(), + "A uvector can only hold classes with standard layout"); + + private: +#if __cplusplus > 201402L + typedef std::allocator_traits::is_always_equal + allocator_is_always_equal; +#else + typedef std::false_type allocator_is_always_equal; +#endif + public: + /// Element type + typedef Tp value_type; + /// Type of allocator used to allocate and deallocate space + typedef Alloc allocator_type; + /// Reference to element type + typedef Tp& reference; + /// Constant reference to element type + typedef const Tp& const_reference; + /// Pointer to element type + typedef Tp* pointer; + /// Pointer to constant element type + typedef const Tp* const_pointer; + /// Iterator type + typedef Tp* iterator; + /// Iterator type of constant elements + typedef const Tp* const_iterator; + /// Reverse iterator type + typedef std::reverse_iterator reverse_iterator; + /// Reverse iterator of constant elements + typedef std::reverse_iterator const_reverse_iterator; + /// Difference between to iterators + typedef std::ptrdiff_t difference_type; + /// Type used for indexing elements + typedef std::size_t size_t; + /// Type used for indexing elements + typedef std::size_t size_type; + + private: + pointer _begin, _end, _endOfStorage; + + public: + /** @brief Construct an empty uvector. + * @param allocator Allocator used for allocating and deallocating memory. + */ + explicit uvector(const allocator_type& allocator = Alloc()) noexcept + : Alloc(allocator), + _begin(nullptr), + _end(nullptr), + _endOfStorage(nullptr) {} + + /** @brief Construct a vector with given amount of elements, without + * initializing these. + * @details This constructor deviates from std::vector's behaviour, because it + * will not value construct its elements. It is therefore faster than the + * corresponding constructor of std::vector. + * @param n Number of elements that the uvector will be initialized with. + */ + explicit uvector(size_t n) + : _begin(allocate(n)), _end(_begin + n), _endOfStorage(_end) {} + + /** @brief Construct a vector with given amount of elements and set these to a + * specific value. + * @details This constructor will initialize its members with the given value. + * @param n Number of elements that the uvector will be initialized with. + * @param val Value to initialize all elements with + * @param allocator Allocator used for allocating and deallocating memory. + */ + uvector(size_t n, const value_type& val, + const allocator_type& allocator = Alloc()) + : Alloc(allocator), + _begin(allocate(n)), + _end(_begin + n), + _endOfStorage(_end) { + std::uninitialized_fill_n(_begin, n, val); + } + + /** @brief Construct a vector by copying elements from a range. + * @param first Iterator to range start + * @param last Iterator to range end + * @param allocator Allocator used for allocating and deallocating memory. + */ + template + uvector(InputIterator first, InputIterator last, + const allocator_type& allocator = Alloc()) + : Alloc(allocator) { + construct_from_range(first, last, + std::is_integral()); + } + + /** @brief Copy construct a uvector. + * @details The allocator of the new uvector will be initialized from + * @c + * std::allocator_traits::select_on_container_copy_construction(other). + * @param other Source uvector to be copied from. + */ + uvector(const uvector& other) + : Alloc( + std::allocator_traits::select_on_container_copy_construction( + static_cast(other))), + _begin(allocate(other.size())), + _end(_begin + other.size()), + _endOfStorage(_end) { + std::copy(other._begin, other._end, _begin); + } + + /** @brief Copy construct a uvector with custom allocator. + * @param other Source uvector to be copied from. + * @param allocator Allocator used for allocating and deallocating memory. + */ + uvector(const uvector& other, const allocator_type& allocator) + : Alloc(allocator), + _begin(allocate(other.size())), + _end(_begin + other.size()), + _endOfStorage(_end) { + std::copy(other._begin, other._end, _begin); + } + + /** @brief Move construct a uvector. + * @param other Source uvector to be moved from. + */ + uvector(uvector&& other) noexcept + : Alloc(std::move(other)), + _begin(other._begin), + _end(other._end), + _endOfStorage(other._endOfStorage) { + other._begin = nullptr; + other._end = nullptr; + other._endOfStorage = nullptr; + } + + /** @brief Move construct a uvector with custom allocator. + * @param other Source uvector to be moved from. + * @param allocator Allocator used for allocating and deallocating memory. + */ + uvector(uvector&& other, const allocator_type& allocator) noexcept + : Alloc(allocator), + _begin(other._begin), + _end(other._end), + _endOfStorage(other._endOfStorage) { + other._begin = nullptr; + other._end = nullptr; + other._endOfStorage = nullptr; + } + + /** @brief Construct a uvector from a initializer list. + * @param initlist Initializer list used for initializing the new uvector. + * @param allocator Allocator used for allocating and deallocating memory. + */ + uvector(std::initializer_list initlist, + const allocator_type& allocator = Alloc()) + : Alloc(allocator), + _begin(allocate(initlist.size())), + _end(_begin + initlist.size()), + _endOfStorage(_end) { + iterator destIter = _begin; + for (typename std::initializer_list::const_iterator i = + initlist.begin(); + i != initlist.end(); ++i) { + *destIter = *i; + ++destIter; + } + } + + /** @brief Destructor. */ + ~uvector() noexcept { deallocate(); } + + /** @brief Assign another uvector to this uvector. + * @details The allocator of the uvector will be assigned to @p other when + * std::allocator_traits::propagate_on_container_copy_assignment() is + * of true_type. + */ + uvector& operator=(const uvector& other) { + return assign_copy_from( + other, typename std::allocator_traits< + Alloc>::propagate_on_container_copy_assignment()); + } + + /** @brief Assign another uvector to this uvector. + * @details The allocator of the uvector will be assigned to @p other when + * std::allocator_traits::propagate_on_container_move_assignment() is + * of true_type. + */ + uvector& operator=(uvector&& other) noexcept( + std::allocator_traits< + Alloc>::propagate_on_container_move_assignment::value || + allocator_is_always_equal::value) { + return assign_move_from( + std::move(other), typename std::allocator_traits< + Alloc>::propagate_on_container_move_assignment()); + } + + /** @brief Get iterator to first element. */ + iterator begin() noexcept { return _begin; } + + /** @brief Get constant iterator to first element. */ + const_iterator begin() const noexcept { return _begin; } + + /** @brief Get iterator to element past last element. */ + iterator end() noexcept { return _end; } + + /** @brief Get constant iterator to element past last element. */ + const_iterator end() const noexcept { return _end; } + + /** @brief Get reverse iterator to last element. */ + reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } + + /** @brief Get constant reverse iterator to last element. */ + const_reverse_iterator rbegin() const noexcept { + return const_reverse_iterator(end()); + } + + /** @brief Get reverse iterator to element before first element. */ + reverse_iterator rend() noexcept { return reverse_iterator(begin()); } + + /** @brief Get constant reverse iterator to element before first element. */ + const_reverse_iterator rend() const noexcept { + return const_reverse_iterator(begin()); + } + + /** @brief Get constant iterator to first element. */ + const_iterator cbegin() const noexcept { return _begin; } + + /** @brief Get constant iterator to element past last element. */ + const_iterator cend() const noexcept { return _end; } + + /** @brief Get constant reverse iterator to last element. */ + const_reverse_iterator crbegin() const noexcept { + return const_reverse_iterator(end()); + } + + /** @brief Get constant reverse iterator to element before first element. */ + const_reverse_iterator crend() const noexcept { + return const_reverse_iterator(begin()); + } + + /** @brief Get number of elements in container. */ + size_t size() const noexcept { return _end - _begin; } + + /** @brief Get maximum number of elements that this container can hold. */ + size_t max_size() const noexcept { return Alloc::max_size(); } + + /** @brief Change the number of elements in the container. + * @details If the new size is larger than the current size, new values will + * be left uninitialized. Therefore, it is more efficient than @c + * resize(size_t) in + * @c std::vector, as well as @ref resize(size_t, const Tp&). + * If the new size is smaller than the current size, the container will be + * truncated and elements past the new size will be removed. No destructor of + * the removed elements will be called. + * @param n The new size of the container. + */ + void resize(size_t n) { + if (capacity() < n) { + size_t newSize = enlarge_size(n); + pointer newStorage = allocate(newSize); + std::move(_begin, _end, newStorage); + deallocate(); + _begin = newStorage; + _endOfStorage = _begin + newSize; + } + _end = _begin + n; + } + + /** @brief Change the number of elements in the container. + * @details If the new size is larger than the current size, new values will + * be initialized by the given value. If the new size is smaller than the + * current size, the container will be truncated and elements past the new + * size will be removed. No destructor of the removed elements will be called. + * @param n The new size of the container. + * @param val New value of elements that get added to the container. + */ + void resize(size_t n, const Tp& val) { + size_t oldSize = size(); + if (capacity() < n) { + pointer newStorage = allocate(n); + std::move(_begin, _end, newStorage); + deallocate(); + _begin = newStorage; + _endOfStorage = _begin + n; + } + _end = _begin + n; + if (oldSize < n) + std::uninitialized_fill(_begin + oldSize, _end, val); + } + + /** @brief Get the number of elements the container can currently hold without + * reallocating storage. */ + size_t capacity() const noexcept { return _endOfStorage - _begin; } + + /** @brief Determine if the container is currently empty. + * @returns @c true if @ref size() == 0. */ + bool empty() const noexcept { return _begin == _end; } + + /** @brief Reserve space for a number of elements, to prevent the overhead of + * extra reallocations. + * @details This has no effect on the working of the uvector, except that it + * might change the current capacity. This can enhance performance when a + * large number of elements are added, and an approximate size is known a + * priori. + * + * This method might cause a reallocation, causing iterators to be + * invalidated. + * @param n Number of elements to reserve space for. + */ + void reserve(size_t n) { + if (capacity() < n) { + const size_t curSize = size(); + pointer newStorage = allocate(n); + std::move(_begin, _begin + curSize, newStorage); + deallocate(); + _begin = newStorage; + _end = newStorage + curSize; + _endOfStorage = _begin + n; + } + } + + /** @brief Change the capacity of the container such that no extra space is + * hold. + * @details This has no effect on the working of the uvector, except that it + * might change the current capacity. This can reduce the current memory usage + * of the container. + * + * This method might cause a reallocation, causing iterators to be + * invalidated. + */ + void shrink_to_fit() { + const size_t curSize = size(); + if (curSize == 0) { + deallocate(); + _begin = nullptr; + _end = nullptr; + _endOfStorage = nullptr; + } else if (curSize < capacity()) { + pointer newStorage = allocate(curSize); + std::move(_begin, _begin + curSize, newStorage); + deallocate(); + _begin = newStorage; + _end = newStorage + curSize; + _endOfStorage = _begin + curSize; + } + } + + /** @brief Get a reference to the element at the given index. */ + Tp& operator[](size_t index) noexcept { return _begin[index]; } + + /** @brief Get a constant reference to the element at the given index. */ + const Tp& operator[](size_t index) const noexcept { return _begin[index]; } + + /** @brief Get a reference to the element at the given index with bounds + * checking. + * @throws std::out_of_range when given index is past the last element. + */ + Tp& at(size_t index) { + check_bounds(index); + return _begin[index]; + } + + /** @brief Get a constant reference to the element at the given index with + * bounds checking. + * @throws std::out_of_range when given index is past the last element. + */ + const Tp& at(size_t index) const { + check_bounds(index); + return _begin[index]; + } + + /** @brief Get reference to first element in container. */ + Tp& front() noexcept { return *_begin; } + + /** @brief Get constant reference to first element in container. */ + const Tp& front() const noexcept { return *_begin; } + + /** @brief Get reference to last element in container. */ + Tp& back() noexcept { return *(_end - 1); } + + /** @brief Get constant reference to last element in container. */ + const Tp& back() const noexcept { return *(_end - 1); } + + /** @brief Get pointer to internal storage. */ + Tp* data() noexcept { return _begin; } + + /** @brief Get constant pointer to internal storage. */ + const Tp* data() const noexcept { return _begin; } + + /** @brief Assign this container to be equal to the given range. + * @details The container will be resized to fit the length of the given + * range. Iterators are invalidated. + * @param first Iterator to the beginning of the range. + * @param last Iterator past the end of the range. + */ + template + void assign(InputIterator first, InputIterator last) { + assign_from_range(first, last, + std::is_integral()); + } + + /** @brief Resize the container and assign the given value to all elements. + * @details Iterators are invalidated. + * @param n New size of container + * @param val Value to be assigned to all elements. + */ + void assign(size_t n, const Tp& val) { + if (n > capacity()) { + iterator newStorage = allocate(n); + deallocate(); + _begin = newStorage; + _endOfStorage = _begin + n; + } + _end = _begin + n; + std::uninitialized_fill_n(_begin, n, val); + } + + /** @brief Assign this container to an initializer list. + * @details The container will be resized to fit the length of the given + * initializer list. Iterators are invalidated. + * @param initlist List of values to assign to the container. + */ + void assign(std::initializer_list initlist) { + if (initlist.size() > capacity()) { + iterator newStorage = allocate(initlist.size()); + deallocate(); + _begin = newStorage; + _endOfStorage = _begin + initlist.size(); + } + _end = _begin + initlist.size(); + iterator destIter = _begin; + for (typename std::initializer_list::const_iterator i = + initlist.begin(); + i != initlist.end(); ++i) { + *destIter = *i; + ++destIter; + } + } + + /** @brief Add the given value to the end of the container. + * @details Iterators are invalidated. + * @param item Value of new element. + */ + void push_back(const Tp& item) { + if (_end == _endOfStorage) enlarge(enlarge_size(1)); + *_end = item; + ++_end; + } + + /** @brief Add the given value to the end of the container by moving it in. + * @details Iterators are invalidated. + * + * Note that this container can only hold simple types that do not perform + * allocations. Therefore, there is probably no benefit in moving the new item + * in over copying it in with @ref push_back(const Tp&). + * @param item Value of new element. + */ + void push_back(Tp&& item) { + if (_end == _endOfStorage) enlarge(enlarge_size(1)); + *_end = std::move(item); + ++_end; + } + + /** @brief Remove the last element from the container. */ + void pop_back() { --_end; } + + /** @brief Insert an element at a given position. + * @details All iterators will be invalidated. This operation needs to move + * all elements after the new element, and can therefore be expensive. + * @param position Position of the new element. The new element will be added + * before the old element at that position. + * @param item Value of the new item. + * @return Position of the new element. + */ + iterator insert(const_iterator position, const Tp& item) { + if (_end == _endOfStorage) { + size_t index = position - _begin; + enlarge_for_insert(enlarge_size(1), index, 1); + position = _begin + index; + } else { + std::move_backward(const_cast(position), _end, _end + 1); + ++_end; + } + *const_cast(position) = item; + return const_cast(position); + } + + /** @brief Insert elements at a given position and initialize them with a + * value. + * @details All iterators will be invalidated. This operation needs to move + * all elements after the new element, and can therefore be expensive. + * @param position Position of the new elements. The new elements will be + * added before the old element at that position. + * @param n Number of elements to add. + * @param val Value of the new item. + * @return Position of the first new element. + */ + iterator insert(const_iterator position, size_t n, const Tp& val) { + if (capacity() < size() + n) { + size_t index = position - _begin; + enlarge_for_insert(enlarge_size(n), index, n); + position = _begin + index; + } else { + std::move_backward(const_cast(position), _end, _end + n); + _end += n; + } + std::uninitialized_fill_n(const_cast(position), n, + val); + return const_cast(position); + } + + /** @brief Insert elements at a given position and initialize them from a + * range. + * @details All iterators will be invalidated. This operation needs to move + * all elements after the new element, and can therefore be expensive. + * @param position Position of the new elements. The new elements will be + * added before the old element at that position. + * @param first Iterator to the beginning of the range. + * @param last Iterator past the end of the range. + * @return Position of the first new element. + */ + template + iterator insert(const_iterator position, InputIterator first, + InputIterator last) { + return insert_from_range(position, first, last, + std::is_integral()); + } + + /** @brief Insert an element at a given position by moving it in. + * @details All iterators will be invalidated. This operation needs to move + * all elements after the new element, and can therefore be expensive. + * + * Note that this container can only hold simple types that do not perform + * allocations. Therefore, there is probably no benefit in moving the new item + * in over copying it in with + * @ref insert(const_iterator, const Tp&). + * @param position Position of the new element. The new element will be added + * before the old element at that position. + * @param item Value of the new item. + * @return Position of the new element. + */ + iterator insert(const_iterator position, Tp&& item) { + if (_end == _endOfStorage) { + size_t index = position - _begin; + enlarge_for_insert(enlarge_size(1), index, 1); + position = _begin + index; + } else { + std::move_backward(const_cast(position), _end, _end + 1); + ++_end; + } + *const_cast(position) = std::move(item); + return const_cast(position); + } + + /** @brief Insert elements at a given position and initialize them from a + * initializer list. + * @details All iterators will be invalidated. This operation needs to move + * all elements after the new element, and can therefore be expensive. + * @param position Position of the new elements. The new elements will be + * added before the old element at that position. + * @param initlist List of items to insert. + * @return Position of the first new element. + */ + iterator insert(const_iterator position, std::initializer_list initlist) { + if (capacity() < size() + initlist.size()) { + size_t index = position - _begin; + enlarge_for_insert(enlarge_size(initlist.size()), index, initlist.size()); + position = _begin + index; + } else { + std::move_backward(const_cast(position), _end, + _end + initlist.size()); + _end += initlist.size(); + } + iterator destIter = const_cast(position); + for (typename std::initializer_list::const_iterator i = + initlist.begin(); + i != initlist.end(); ++i) { + *destIter = *i; + ++destIter; + } + return const_cast(position); + } + + /** @brief Delete an element from the container. + * @details This operation moves all elements past the removed element, and + * can therefore be expensive. + * @param position Position of element to be removed. + * @return Iterator pointing to the first element past the delete element. + */ + iterator erase(const_iterator position) { + std::move(const_cast(position) + 1, _end, + const_cast(position)); + --_end; + return const_cast(position); + } + + /** @brief Delete a range of elements from the container. + * @details This operation moves all elements past the removed elements, and + * can therefore be expensive. + * @param first Position of first element to be removed. + * @param last Position past last element to be removed. + * @return Iterator pointing to the first element past the delete element. + */ + iterator erase(const_iterator first, const_iterator last) { + std::move(const_cast(last), _end, const_cast(first)); + _end -= last - first; + return const_cast(first); + } + + /** @brief Swap the contents of this uvector with the given uvector. + * @details Iterators to both vectors will remain valid and will point into + * to the swapped container afterwards. This function will never reallocate + * space. + * + * The allocator will be swapped when the @c propagate_on_container_swap + * of the respective @c allocator_trait is @c true_type. + * Its behaviour is undefined when the allocators do not compare equal and + * @c propagate_on_container_swap is false. + * @param other Other uvector whose contents it to be swapped with this. + */ + void swap(uvector& other) noexcept { + swap(other, + typename std::allocator_traits::propagate_on_container_swap()); + } + + /** @brief Remove all elements from the container. */ + void clear() { _end = _begin; } + + /** @brief Insert an element at a given position by constructing it in place. + * @details All iterators will be invalidated. This operation needs to move + * all elements after the new element, and can therefore be expensive. + * @param position Position of the new element. The new element will be added + * before the old element at that position. + * @param args List of arguments to be forwarded to construct the new element. + * @return Position of the new element. + */ + template + iterator emplace(const_iterator position, Args&&... args) { + if (_end == _endOfStorage) { + size_t index = position - _begin; + enlarge_for_insert(enlarge_size(1), index, 1); + position = _begin + index; + } else { + std::move_backward(const_cast(position), _end, _end + 1); + ++_end; + } + *const_cast(position) = Tp(std::forward(args)...); + return const_cast(position); + } + + /** @brief Add the given value to the end of the container by constructing it + * in place. + * @details Iterators are invalidated. + * @param args List of arguments to be forwarded to construct the new element. + */ + template + void emplace_back(Args&&... args) { + if (_end == _endOfStorage) enlarge(enlarge_size(1)); + *_end = Tp(std::forward(args)...); + ++_end; + } + + /** @brief Get a copy of the allocator. */ + allocator_type get_allocator() const noexcept { return *this; } + + // --- NON STANDARD METHODS --- + + /** @brief Insert elements at a given position without initializing them. + * @details All iterators will be invalidated. This operation needs to move + * all elements after the new element, and can therefore be expensive. It will + * not initialize the new elements, and is therefore faster than @ref + * insert(const_iterator, size_t, const Tp&). + * + * This method is non-standard: it is not present in std::vector. + * @param position Position of the new elements. The new elements will be + * added before the old element at that position. + * @param n Number of elements to add. + */ + iterator insert_uninitialized(const_iterator position, size_t n) { + if (capacity() < size() + n) { + size_t index = position - _begin; + enlarge_for_insert(enlarge_size(n), index, n); + position = _begin + index; + } else { + std::move_backward(const_cast(position), _end, _end + n); + _end += n; + } + return const_cast(position); + } + + /** @brief Add a range of items to the end of the container. + * @details All iterators will be invalidated. + * + * This method is non-standard: it is not present in std::vector. + * @param first Iterator to the beginning of the range. + * @param last Iterator past the end of the range. + */ + template + void push_back(InputIterator first, InputIterator last) { + push_back_range(first, last, + std::is_integral()); + } + + /** @brief Add elements at the end and initialize them with a value. + * @details All iterators will be invalidated. + * + * This method is non-standard: it is not present in std::vector. + * @param n Number of elements to add. + * @param val Value of the new items. + */ + void push_back(size_t n, const Tp& val) { + if (capacity() - size() < n) { + enlarge(enlarge_size(n)); + } + std::uninitialized_fill_n(_end, n, val); + _end += n; + } + + /** @brief Add elements from an initializer list to the end of the container. + * @details All iterators will be invalidated. + * + * This method is non-standard: it is not present in std::vector. + * @param initlist The list with values to add. + */ + void push_back(std::initializer_list initlist) { + if (capacity() - size() < initlist.size()) { + enlarge(enlarge_size(initlist.size())); + } + for (typename std::initializer_list::iterator i = initlist.begin(); + i != initlist.end(); ++i) { + *_end = *i; + ++_end; + } + } + + /** @brief Add elements at the end without initializing them. + * @details All iterators will be invalidated. + * + * This method is non-standard: it is not present in std::vector. + * @param n Number of elements to add. + */ + void push_back_uninitialized(size_t n) { resize(size() + n); } + + private: + pointer allocate(size_t n) { return Alloc::allocate(n); } + + void deallocate() noexcept { deallocate(_begin, capacity()); } + + void deallocate(pointer begin, size_t n) noexcept { + if (begin != nullptr) Alloc::deallocate(begin, n); + } + + template + void construct_from_range(InputIterator first, InputIterator last, + std::false_type) { + construct_from_range( + first, last, + typename std::iterator_traits::iterator_category()); + } + + template + void construct_from_range(Integral n, Integral val, std::true_type) { + _begin = allocate(n); + _end = _begin + n; + _endOfStorage = _end; + std::uninitialized_fill_n(_begin, n, val); + } + + template + void construct_from_range(InputIterator first, InputIterator last, + std::forward_iterator_tag) { + size_t n = std::distance(first, last); + _begin = allocate(n); + _end = _begin + n; + _endOfStorage = _begin + n; + Tp* destIter = _begin; + while (first != last) { + *destIter = *first; + ++destIter; + ++first; + } + } + + template + void assign_from_range(InputIterator first, InputIterator last, + std::false_type) { + assign_from_range( + first, last, + typename std::iterator_traits::iterator_category()); + } + + // This function is called from assign(iter,iter) when Tp is an integral. In + // that case, the user tried to call assign(n, &val), but it got caught by the + // wrong overload. + template + void assign_from_range(Integral n, Integral val, std::true_type) { + if (size_t(n) > capacity()) { + iterator newStorage = allocate(n); + deallocate(); + _begin = newStorage; + _endOfStorage = _begin + n; + } + _end = _begin + n; + std::uninitialized_fill_n(_begin, n, val); + } + + template + void assign_from_range(InputIterator first, InputIterator last, + std::forward_iterator_tag) { + size_t n = std::distance(first, last); + if (n > capacity()) { + iterator newStorage = allocate(n); + deallocate(); + _begin = newStorage; + _endOfStorage = _begin + n; + } + _end = _begin + n; + Tp* destIter = _begin; + while (first != last) { + *destIter = *first; + ++destIter; + ++first; + } + } + + template + iterator insert_from_range(const_iterator position, InputIterator first, + InputIterator last, std::false_type) { + return insert_from_range( + position, first, last, + typename std::iterator_traits::iterator_category()); + } + + template + iterator insert_from_range(const_iterator position, Integral n, Integral val, + std::true_type) { + if (capacity() < size() + n) { + size_t index = position - _begin; + enlarge_for_insert(enlarge_size(n), index, n); + position = _begin + index; + } else { + std::move_backward(const_cast(position), _end, _end + n); + _end += n; + } + std::uninitialized_fill_n(const_cast(position), n, + val); + return const_cast(position); + } + + template + iterator insert_from_range(const_iterator position, InputIterator first, + InputIterator last, std::forward_iterator_tag) { + size_t n = std::distance(first, last); + if (capacity() < size() + n) { + size_t index = position - _begin; + enlarge_for_insert(enlarge_size(n), index, n); + position = _begin + index; + } else { + std::move_backward(const_cast(position), _end, _end + n); + _end += n; + } + Tp* destIter = const_cast(position); + while (first != last) { + *destIter = *first; + ++destIter; + ++first; + } + return const_cast(position); + } + + void check_bounds(size_t index) const { + if (index >= size()) + throw std::out_of_range("Access to element in uvector past end"); + } + + size_t enlarge_size(size_t extra_space_needed) const noexcept { + return size() + std::max(size(), extra_space_needed); + } + + void enlarge(size_t newSize) { + pointer newStorage = allocate(newSize); + std::copy(_begin, _end, newStorage); + deallocate(); + _end = newStorage + size(); + _begin = newStorage; + _endOfStorage = _begin + newSize; + } + + void enlarge_for_insert(size_t newSize, size_t insert_position, + size_t insert_count) { + pointer newStorage = allocate(newSize); + std::copy(_begin, _begin + insert_position, newStorage); + std::copy(_begin + insert_position, _end, + newStorage + insert_position + insert_count); + deallocate(); + _end = newStorage + size() + insert_count; + _begin = newStorage; + _endOfStorage = _begin + newSize; + } + + // implementation of operator=(const&) without + // propagate_on_container_copy_assignment + uvector& assign_copy_from(const uvector& other, std::false_type) { + const size_t n = other.size(); + if (n > capacity()) { + iterator newStorage = allocate(n); + deallocate(); + _begin = newStorage; + _end = _begin + n; + _endOfStorage = _end; + } + std::copy(other._begin, other._begin + n, _begin); + return *this; + } + + // implementation of operator=(const&) with + // propagate_on_container_copy_assignment + uvector& assign_copy_from(const uvector& other, std::true_type) { + if (allocator_is_always_equal() || + static_cast(other) == static_cast(*this)) { + assign_copy_from(other, std::false_type()); + } else { + const size_t n = other.size(); + iterator newStorage = static_cast(other).allocate(n); + deallocate(); + _begin = newStorage; + _end = _begin + n; + _endOfStorage = _end; + std::copy(other._begin, other._begin + n, _begin); + Alloc::operator=(static_cast(other)); + } + return *this; + } + + // implementation of operator=() without + // propagate_on_container_move_assignment + uvector& assign_move_from( + uvector&& other, + std::false_type) noexcept(allocator_is_always_equal::value) { + if (allocator_is_always_equal::value || + static_cast(other) == static_cast(*this)) { + deallocate(); + _begin = other._begin; + _end = other._end; + _endOfStorage = other._endOfStorage; + other._begin = nullptr; + other._end = nullptr; + other._endOfStorage = nullptr; + } else { + // We should not propagate the allocator and the allocators are different. + // This means we can not swap the allocated space, since then we would + // deallocate the space with a different allocator type. Therefore, we + // need to copy: + assign_copy_from(other, std::false_type()); + } + return *this; + } + + // implementation of operator=() with propagate_on_container_move_assignment + uvector& assign_move_from(uvector&& other, + std::true_type) noexcept { + deallocate(); + Alloc::operator=(std::move(static_cast(other))); + _begin = other._begin; + _end = other._end; + _endOfStorage = other._endOfStorage; + other._begin = nullptr; + other._end = nullptr; + other._endOfStorage = nullptr; + return *this; + } + + // implementation of swap with propagate_on_container_swap + void swap(uvector& other, std::true_type) noexcept { + std::swap(_begin, other._begin); + std::swap(_end, other._end); + std::swap(_endOfStorage, other._endOfStorage); + std::swap(static_cast(other), static_cast(*this)); + } + + // implementation of swap without propagate_on_container_swap + void swap(uvector& other, std::false_type) noexcept { + std::swap(_begin, other._begin); + std::swap(_end, other._end); + std::swap(_endOfStorage, other._endOfStorage); + /** + * We have two choices here: + * - Do not swap the allocators. For stateful allocators, we would need to + * reallocate memory, and iterators would not be valid UNLESS + * they were stored as indices. However, containers with stateful + * allocators are not allowed to be swapped unless the allocators are equal, + * in which case swapping is not necessary. + * - Swap the allocators. This would not reallocate memory and + * iterators remain valid, but the trait ignores + * propagate_on_container_swap. + * + * The standard says: + * "Allocator replacement is performed by copy assignment, move assignment, + * or swapping of the allocator only if allocator_traits:: + * propagate_on_container_copy_assignment::value, + * allocator_traits::propagate_on_container_move_assignment::value, + * or allocator_traits::propagate_on_container_swap::value is + * true within the implementation of the corresponding container operation. + * The behavior of a call to a container’s swap function is undefined unless + * the objects being swapped have allocators that compare equal or + * allocator_traits::propagate_on_container_swap::value is + * true." + */ + } + + template + void push_back_range(InputIterator first, InputIterator last, + std::false_type) { + push_back_range( + first, last, + typename std::iterator_traits::iterator_category()); + } + + // This function is called from push_back(iter,iter) when Tp is an integral. + // In that case, the user tried to call push_back(n, &val), but it got caught + // by the wrong overload. + template + void push_back_range(Integral n, Integral val, std::true_type) { + if (capacity() - size() < size_t(n)) { + enlarge(enlarge_size(n)); + } + std::uninitialized_fill_n(_end, n, val); + _end += n; + } + + template + void push_back_range(InputIterator first, InputIterator last, + std::forward_iterator_tag) { + size_t n = std::distance(first, last); + if (n > capacity() - size()) { + enlarge(enlarge_size(n)); + } + while (first != last) { + *_end = *first; + ++_end; + ++first; + } + } +}; + +/** @brief Compare two uvectors for equality. */ +template +inline bool operator==(const uvector& lhs, + const uvector& rhs) noexcept { + return lhs.size() == rhs.size() && + std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +/** @brief Compare two uvectors for inequality. */ +template +inline bool operator!=(const uvector& lhs, + const uvector& rhs) noexcept { + return !(lhs == rhs); +} + +/** @brief Compare two uvectors for smaller than. + * @details If two uvectors compare equal up to the length of one, the uvector + * with the smallest size is consider to be smaller. + */ +template +inline bool operator<(const uvector& lhs, + const uvector& rhs) noexcept { + const size_t minSize = std::min(lhs.size(), rhs.size()); + for (size_t i = 0; i != minSize; ++i) { + if (lhs[i] < rhs[i]) + return true; + else if (lhs[i] > rhs[i]) + return false; + } + return lhs.size() < rhs.size(); +} + +/** @brief Compare two uvectors for smaller than or equal. + * @details If two uvectors compare equal up to the length of one, the uvector + * with the smallest size is consider to be smaller. + */ +template +inline bool operator<=(const uvector& lhs, + const uvector& rhs) noexcept { + const size_t minSize = std::min(lhs.size(), rhs.size()); + for (size_t i = 0; i != minSize; ++i) { + if (lhs[i] < rhs[i]) + return true; + else if (lhs[i] > rhs[i]) + return false; + } + return lhs.size() <= rhs.size(); +} + +/** @brief Compare two uvectors for larger than. + * @details If two uvectors compare equal up to the length of one, the uvector + * with the smallest size is consider to be smaller. + */ +template +inline bool operator>(const uvector& lhs, + const uvector& rhs) noexcept { + return rhs < lhs; +} + +/** @brief Compare two uvectors for larger than or equal. + * @details If two uvectors compare equal up to the length of one, the uvector + * with the smallest size is consider to be smaller. + */ +template +inline bool operator>=(const uvector& lhs, + const uvector& rhs) noexcept { + return rhs <= lhs; +} + +/** @brief Swap the contents of the two uvectors. + * @details Iterators to both vectors will remain valid and will point into + * to the swapped container afterwards. This function will never reallocate + * space. + * + * The allocator will be swapped when the @c propagate_on_container_swap + * of the respective @c allocator_trait is @c true_type. + * Its behaviour is undefined when the allocators do not compare equal and + * @c propagate_on_container_swap is false. + */ +template +inline void swap(uvector& x, uvector& y) { + x.swap(y); +} + +/** @} */ + +} // end of namespace ao + +#endif // AO_UVECTOR_H