diff --git a/CHANGELOG.md b/CHANGELOG.md index 86c22859cf826535f46302ea52500fcd9205f1d3..bbf0eaa56f1f0be4e133b921253f4b8cf1f3b5ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ SPDX-License-Identifier: LicenseRef-GPL-2.0-only-with-DUNE-exception # Master (will become release 2.10) +- Base the implementation of `VariableBlockVector` on `std::vector` as the storage type. Note that + this prevents from using `bool` as block type that was possible before. + - A method `BCRSMatrix::setIndicesNoSort()` was added. Similar to `BCRSMatrix::setIndices()` this allows to insert all indices of a row at once, but - incontrast to the latter - does not sort them. diff --git a/dune/istl/test/vbvectortest.cc b/dune/istl/test/vbvectortest.cc index 2ecd29cf9b38269e08dbb1f0196404c4f017b7bc..89e563f82617f4de5b010c204b3119d3a5e09665 100644 --- a/dune/istl/test/vbvectortest.cc +++ b/dune/istl/test/vbvectortest.cc @@ -87,5 +87,10 @@ int main() testVectorSpaceOperations(v5); testScalarProduct(v5); + // check move construction + auto v6 = std::move(v4); + suite.check(v6.size()==20, "Check size of moved-constructed object"); + suite.check(v6[0].size() == 8, "Check if blocksize of move constructed vector survived"); + return suite.exit(); } diff --git a/dune/istl/vbvector.hh b/dune/istl/vbvector.hh index 681df8c85e6ed24ce8a58dff2919edda5cb85678..8ca5bcae75ffc17a6cace8a5695792d3b444fedb 100644 --- a/dune/istl/vbvector.hh +++ b/dune/istl/vbvector.hh @@ -12,6 +12,7 @@ #include <memory> #include <dune/common/ftraits.hh> +#include <dune/common/indexediterator.hh> #include <dune/common/iteratorfacades.hh> #include "istlexception.hh" #include "bvector.hh" @@ -45,8 +46,16 @@ namespace Dune { // on the large array. However, access operators have to be // overwritten. { + using Base = Imp::block_vector_unmanaged<B,typename A::size_type>; + // just a shorthand - typedef Imp::BlockVectorWindow<B,A> window_type; + using window_type = Imp::BlockVectorWindow<B,A>; + + // data-structure holding the windows (but not the actual data) + using VectorWindows = std::vector<window_type, typename std::allocator_traits<A>::template rebind_alloc<window_type>>; + + // block type bool is not supported since std::vector<bool> is used for storage + static_assert(not std::is_same_v<B,bool>, "Block type 'bool' not supported by VariableBlockVector."); public: @@ -56,338 +65,166 @@ namespace Dune { using field_type = typename Imp::BlockTraits<B>::field_type; //! export the allocator type - typedef A allocator_type; + using allocator_type = A; /** \brief Export type used for references to container entries * * \note This is not B&, but an internal proxy class! */ - typedef window_type& reference; + using reference = window_type&; /** \brief Export type used for const references to container entries * * \note This is not B&, but an internal proxy class! */ - typedef const window_type& const_reference; + using const_reference = const window_type&; //! The size type for the index access - typedef typename A::size_type size_type; + using size_type = typename A::size_type; /** \brief Type of the elements of the outer vector, i.e., dynamic vectors of B * * Note that this is *not* the type referred to by the iterators and random access operators, * which return proxy objects. */ - typedef BlockVector<B,A> value_type; + using value_type = BlockVector<B,A>; /** \brief Same as value_type, here for historical reasons */ - typedef BlockVector<B,A> block_type; + using block_type = BlockVector<B,A>; //===== constructors and such - /** constructor without arguments makes empty vector, - object cannot be used yet + /** + * \brief Constructor without arguments makes an empty vector. + * \note object cannot be used yet. The size and block sizes need to be initialized. */ - VariableBlockVector () : Imp::block_vector_unmanaged<B,size_type>() - { - // nothing is known ... - nblocks = 0; - block = nullptr; - initialized = false; - } - - /** make vector with given number of blocks, but size of each block is not yet known, - object cannot be used yet + VariableBlockVector () : + Base() + {} + + /** + * \brief Construct a vector with given number of blocks, but size of each + * block is not yet known. + * \note Object cannot be used yet. Needs to be initialized using the + * `createbegin()` and `createend()` create-iterators to fill the block sizes. */ - explicit VariableBlockVector (size_type _nblocks) : Imp::block_vector_unmanaged<B,size_type>() - { - // we can allocate the windows now - nblocks = _nblocks; - if (nblocks>0) - { - block = windowAllocator_.allocate(nblocks); - new (block) window_type[nblocks]; - } - else - { - nblocks = 0; - block = nullptr; - } - - // Note: memory in base class still not allocated - // the vector not usable - initialized = false; - } - - /** make vector with given number of blocks each having a constant size, - object is fully usable then. - - \param _nblocks Number of blocks - \param m Number of elements in each block + explicit VariableBlockVector (size_type numBlocks) : + Base(), + block(numBlocks) + {} + + /** + * \brief Construct a vector with given number of blocks each having a + * constant size. + * \note Object is fully usable after construction. + * + * \param numBlocks Number of blocks + * \param blockSize Number of elements in each block */ - VariableBlockVector (size_type _nblocks, size_type m) : Imp::block_vector_unmanaged<B,size_type>() + VariableBlockVector (size_type numBlocks, size_type blockSize) : + Base(), + block(numBlocks), + storage_(numBlocks*blockSize) { // and we can allocate the big array in the base class - this->n = _nblocks*m; - if (this->n>0) - { - this->p = allocator_.allocate(this->n); - new (this->p)B[this->n]; - } - else - { - this->n = 0; - this->p = nullptr; - } + syncBaseArray(); - // we can allocate the windows now - nblocks = _nblocks; - if (nblocks>0) - { - // allocate and construct the windows - block = windowAllocator_.allocate(nblocks); - new (block) window_type[nblocks]; - - // set the windows into the big array - for (size_type i=0; i<nblocks; ++i) - block[i].set(m,this->p+(i*m)); - } - else - { - nblocks = 0; - block = nullptr; - } + // set the windows into the big array + for (size_type i=0; i<numBlocks; ++i) + block[i].set(blockSize,this->p+(i*blockSize)); // and the vector is usable initialized = true; } - //! copy constructor, has copy semantics - VariableBlockVector (const VariableBlockVector& a) + //! Copy constructor, has copy semantics + VariableBlockVector (const VariableBlockVector& a) : + Base(static_cast<const Base&>(a)), + block(a.block), + storage_(a.storage_) { - // allocate the big array in the base class - this->n = a.n; - if (this->n>0) - { - // allocate and construct objects - this->p = allocator_.allocate(this->n); - new (this->p)B[this->n]; + syncBaseArray(); - // copy data - for (size_type i=0; i<this->n; i++) this->p[i]=a.p[i]; - } - else - { - this->n = 0; - this->p = nullptr; - } - - // we can allocate the windows now - nblocks = a.nblocks; - if (nblocks>0) - { - // alloc - block = windowAllocator_.allocate(nblocks); - new (block) window_type[nblocks]; - - // and we must set the windows - block[0].set(a.block[0].getsize(),this->p); // first block - for (size_type i=1; i<nblocks; ++i) // and the rest - block[i].set(a.block[i].getsize(),block[i-1].getptr()+block[i-1].getsize()); - } - else - { - nblocks = 0; - block = nullptr; + // and we must set the windows + if (block.size()>0) { + block[0].set(block[0].getsize(),this->p); // first block + for (size_type i=1; i<block.size(); ++i) // and the rest + block[i].set(block[i].getsize(),block[i-1].getptr()+block[i-1].getsize()); } // and we have a usable vector - initialized = true; + initialized = a.initialized; } - //! free dynamic memory - ~VariableBlockVector () + //! Move constructor: + VariableBlockVector (VariableBlockVector&& tmp) : + Base() { - if (this->n>0) { - size_type i=this->n; - while (i) - this->p[--i].~B(); - allocator_.deallocate(this->p,this->n); - } - if (nblocks>0) { - size_type i=nblocks; - while (i) - block[--i].~window_type(); - windowAllocator_.deallocate(block,nblocks); - } + tmp.swap(*this); + } + + ~VariableBlockVector () = default; + + //! Copy and move assignment + VariableBlockVector& operator= (VariableBlockVector tmp) + { + tmp.swap(*this); + return *this; + } + + //! Exchange the storage and internal state with `other`. + void swap (VariableBlockVector& other) noexcept + { + using std::swap; + swap(storage_, other.storage_); + swap(block, other.block); + swap(initialized, other.initialized); + + other.syncBaseArray(); + syncBaseArray(); } + //! Free function to swap the storage and internal state of `lhs` with `rhs`. + friend void swap (VariableBlockVector& lhs, VariableBlockVector& rhs) noexcept + { + lhs.swap(rhs); + } //! same effect as constructor with same argument - void resize (size_type _nblocks) + void resize (size_type numBlocks) { - // deconstruct objects and deallocate memory if necessary - if (this->n>0) { - size_type i=this->n; - while (i) - this->p[--i].~B(); - allocator_.deallocate(this->p,this->n); - } - if (nblocks>0) { - size_type i=nblocks; - while (i) - block[--i].~window_type(); - windowAllocator_.deallocate(block,nblocks); - } - this->n = 0; - this->p = nullptr; + storage_.clear(); + + syncBaseArray(); // we can allocate the windows now - nblocks = _nblocks; - if (nblocks>0) - { - block = windowAllocator_.allocate(nblocks); - new (block) window_type[nblocks]; - } - else - { - nblocks = 0; - block = nullptr; - } + block.resize(numBlocks); // and the vector not fully usable initialized = false; } //! same effect as constructor with same argument - void resize (size_type _nblocks, size_type m) + void resize (size_type numBlocks, size_type blockSize) { - // deconstruct objects and deallocate memory if necessary - if (this->n>0) { - size_type i=this->n; - while (i) - this->p[--i].~B(); - allocator_.deallocate(this->p,this->n); - } - if (nblocks>0) { - size_type i=nblocks; - while (i) - block[--i].~window_type(); - windowAllocator_.deallocate(block,nblocks); - } - // and we can allocate the big array in the base class - this->n = _nblocks*m; - if (this->n>0) - { - this->p = allocator_.allocate(this->n); - new (this->p)B[this->n]; - } - else - { - this->n = 0; - this->p = nullptr; - } - - // we can allocate the windows now - nblocks = _nblocks; - if (nblocks>0) - { - // allocate and construct objects - block = windowAllocator_.allocate(nblocks); - new (block) window_type[nblocks]; + storage_.resize(numBlocks*blockSize); + block.resize(numBlocks); + syncBaseArray(); - // set the windows into the big array - for (size_type i=0; i<nblocks; ++i) - block[i].set(m,this->p+(i*m)); - } - else - { - nblocks = 0; - block = nullptr; - } + // set the windows into the big array + for (size_type i=0; i<block.size(); ++i) + block[i].set(blockSize,this->p+(i*blockSize)); // and the vector is usable initialized = true; } - //! assignment - VariableBlockVector& operator= (const VariableBlockVector& a) - { - if (&a!=this) // check if this and a are different objects - { - // reallocate arrays if necessary - // Note: still the block sizes may vary ! - if (this->n!=a.n || nblocks!=a.nblocks) - { - // deconstruct objects and deallocate memory if necessary - if (this->n>0) { - size_type i=this->n; - while (i) - this->p[--i].~B(); - allocator_.deallocate(this->p,this->n); - } - if (nblocks>0) { - size_type i=nblocks; - while (i) - block[--i].~window_type(); - windowAllocator_.deallocate(block,nblocks); - } - - // allocate the big array in the base class - this->n = a.n; - if (this->n>0) - { - // allocate and construct objects - this->p = allocator_.allocate(this->n); - new (this->p)B[this->n]; - } - else - { - this->n = 0; - this->p = nullptr; - } - - // we can allocate the windows now - nblocks = a.nblocks; - if (nblocks>0) - { - // alloc - block = windowAllocator_.allocate(nblocks); - new (block) window_type[nblocks]; - } - else - { - nblocks = 0; - block = nullptr; - } - } - - // copy block structure, might be different although - // sizes are the same ! - if (nblocks>0) - { - block[0].set(a.block[0].getsize(),this->p); // first block - for (size_type i=1; i<nblocks; ++i) // and the rest - block[i].set(a.block[i].getsize(),block[i-1].getptr()+block[i-1].getsize()); - } - - // and copy the data - for (size_type i=0; i<this->n; i++) this->p[i]=a.p[i]; - } - - // and we have a usable vector - initialized = true; - - return *this; // Return reference to make constructions like a=b=c; work. - } - - //===== assignment from scalar - //! assign from scalar + //! Set all entries to the given scalar `k`. VariableBlockVector& operator= (const field_type& k) { (static_cast<Imp::block_vector_unmanaged<B,size_type>&>(*this)) = k; @@ -409,12 +246,12 @@ namespace Dune { struct SizeProxy { - operator size_type() const + operator size_type () const { return target->getsize(); } - SizeProxy& operator=(size_type size) + SizeProxy& operator= (size_type size) { target->setsize(size); return *this; @@ -424,8 +261,8 @@ namespace Dune { friend class CreateIterator; - SizeProxy(window_type& t) - : target(&t) + SizeProxy (window_type& t) : + target(&t) {} window_type* target; @@ -461,15 +298,17 @@ namespace Dune { CreateIterator (VariableBlockVector& _v, int _i, bool _isEnd) : v(_v), i(_i), - isEnd(_isEnd) {} + isEnd(_isEnd) + {} - ~CreateIterator() { + ~CreateIterator () + { // When the iterator gets destructed, we allocate the memory // for the VariableBlockVector if // 1. the current iterator was not created as enditerator // 2. we're at the last block // 3. the vector hasn't been initialized earlier - if (not isEnd && i==v.nblocks && not v.initialized) + if (not isEnd && i==v.block.size() && not v.initialized) v.allocate(); } @@ -540,13 +379,13 @@ namespace Dune { #ifdef DUNE_ISTL_WITH_CHECKING if (initialized) DUNE_THROW(ISTLError,"no CreateIterator in initialized state"); #endif - return CreateIterator(*this,0, false); + return CreateIterator(*this, 0, false); } //! get create iterator pointing to one after the last block CreateIterator createend () { - return CreateIterator(*this,nblocks, true); + return CreateIterator(*this, block.size(), true); } @@ -558,7 +397,7 @@ namespace Dune { window_type& operator[] (size_type i) { #ifdef DUNE_ISTL_WITH_CHECKING - if (i>=nblocks) DUNE_THROW(ISTLError,"index out of range"); + if (i>=block.size()) DUNE_THROW(ISTLError,"index out of range"); #endif return block[i]; } @@ -567,113 +406,44 @@ namespace Dune { const window_type& operator[] (size_type i) const { #ifdef DUNE_ISTL_WITH_CHECKING - if (i<0 || i>=nblocks) DUNE_THROW(ISTLError,"index out of range"); + if (i<0 || i>=block.size()) DUNE_THROW(ISTLError,"index out of range"); #endif return block[i]; } - //! Iterator class for sequential access - template <class T, class R> - class RealIterator - : public RandomAccessIteratorFacade<RealIterator<T,R>, T, R> - { - public: - //! constructor, no arguments - RealIterator () - { - p = nullptr; - i = 0; - } - - //! constructor - RealIterator (window_type* _p, size_type _i) - : p(_p), i(_i) - {} - - //! prefix increment - void increment() - { - ++i; - } - - //! prefix decrement - void decrement() - { - --i; - } - - //! equality - bool equals (const RealIterator& it) const - { - return (p+i)==(it.p+it.i); - } - - //! dereferencing - window_type& dereference () const - { - return p[i]; - } - - void advance(std::ptrdiff_t d) - { - i+=d; - } - - std::ptrdiff_t distanceTo(const RealIterator& o) const - { - return o.i-i; - } - - // Needed for operator[] of the iterator - window_type& elementAt (std::ptrdiff_t offset) const - { - return p[i+offset]; - } - - /** \brief Return the index of the entry this iterator is pointing to */ - size_type index() const - { - return i; - } - - private: - window_type* p; - size_type i; - }; - - using Iterator = RealIterator<value_type,window_type&>; + using Iterator = IndexedIterator<typename VectorWindows::iterator>; //! begin Iterator Iterator begin () { - return Iterator(block,0); + return Iterator{block.begin()}; } //! end Iterator Iterator end () { - return Iterator(block,nblocks); + return Iterator{block.end()}; } //! @returns an iterator that is positioned before //! the end iterator of the vector, i.e. at the last entry. Iterator beforeEnd () { - return Iterator(block,nblocks-1); + return Iterator{--block.end()}; } //! @returns an iterator that is positioned before //! the first entry of the vector. - Iterator beforeBegin () const + Iterator beforeBegin () { - return Iterator(block,-1); + return Iterator{--block.begin()}; } /** \brief Export the iterator type using std naming rules */ using iterator = Iterator; /** \brief Const iterator */ - using ConstIterator = RealIterator<const value_type, const window_type&>; + using ConstIterator = IndexedIterator<typename VectorWindows::const_iterator>; /** \brief Export the const iterator type using std naming rules */ using const_iterator = ConstIterator; @@ -681,98 +451,102 @@ namespace Dune { //! begin ConstIterator ConstIterator begin () const { - return ConstIterator(block,0); + return ConstIterator{block.begin()}; } //! end ConstIterator ConstIterator end () const { - return ConstIterator(block,nblocks); + return ConstIterator{block.end()}; } //! @returns an iterator that is positioned before //! the end iterator of the vector. i.e. at the last element. - ConstIterator beforeEnd() const + ConstIterator beforeEnd () const { - return ConstIterator(block,nblocks-1); + return ConstIterator{--block.end()}; + } + + //! @returns an iterator that is positioned before + //! the first entry of the vector. + ConstIterator beforeBegin () const + { + return ConstIterator{--block.begin()}; } //! end ConstIterator ConstIterator rend () const { - return ConstIterator(block,-1); + return ConstIterator{block.rend()}; } //! random access returning iterator (end if not contained) Iterator find (size_type i) { - return Iterator(block,std::min(i,nblocks)); + Iterator tmp = block.begin(); + tmp+=std::min(i, block.size()); + return tmp; } //! random access returning iterator (end if not contained) ConstIterator find (size_type i) const { - return ConstIterator(block,std::min(i,nblocks)); + ConstIterator tmp = block.begin(); + tmp+=std::min(i, block.size()); + return tmp; } //===== sizes //! number of blocks in the vector (are of variable size here) - size_type N () const + size_type N () const noexcept { - return nblocks; + return block.size(); } /** Number of blocks in the vector * * Returns the same value as method N(), because the vector is dense */ - size_type size () const + size_type size () const noexcept { - return nblocks; + return block.size(); } private: - void allocate() { + void allocate () + { if (this->initialized) DUNE_THROW(ISTLError, "Attempt to re-allocate already initialized VariableBlockVector"); // calculate space needed: - this->n=0; - for(size_type i = 0; i < nblocks; i++) { - this->n += block[i].size(); - } + size_type storageNeeded = 0; + for(size_type i = 0; i < block.size(); i++) + storageNeeded += block[i].size(); - // now we can allocate the big array in the base class of v - if (this->n>0) - { - // allocate and construct objects - this->p = allocator_.allocate(this->n); - new (this->p)B[this->n]; - } - else - { - this->p = nullptr; - } + storage_.resize(storageNeeded); + syncBaseArray(); // and we set the window pointers - this->block[0].setptr(this->p); // pointer to first block - for (size_type j=1; j<nblocks; ++j) // and the rest + block[0].setptr(this->p); // pointer to first block + for (size_type j=1; j<block.size(); ++j) // and the rest block[j].setptr(block[j-1].getptr()+block[j-1].getsize()); // and the vector is ready this->initialized = true; } - size_type nblocks; // number of blocks in vector - window_type* block; // array of blocks pointing to the array in the base class - bool initialized; // true if vector has been initialized - - A allocator_; + void syncBaseArray () noexcept + { + this->p = storage_.data(); + this->n = storage_.size(); + } - typename std::allocator_traits<A>::template rebind_alloc<window_type> windowAllocator_; + VectorWindows block = {}; // vector of blocks pointing to the array in the base class + std::vector<B, A> storage_ = {}; + bool initialized = false; // true if vector has been initialized }; /** @addtogroup DenseMatVec