/* KLayout Layout Viewer Copyright (C) 2006-2017 Matthias Koefferlein This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef HDR_tlReuseVector #define HDR_tlReuseVector #include #include #include #include #include "tlAssert.h" #include "tlTypeTraits.h" namespace tl { template class reuse_vector; template class reuse_vector_const_iterator; /** * @brief The iterator for a reuse_vector */ template class reuse_vector_iterator { public: typedef size_t size_type; typedef Value value_type; typedef value_type *pointer; typedef value_type &reference; // operator* returns a value typedef std::forward_iterator_tag iterator_category; typedef size_type difference_type; /** * @brief The default constructor */ reuse_vector_iterator () : mp_v (0), m_n (0) { } /** * @brief The constructor */ reuse_vector_iterator (reuse_vector *v, size_type n) : mp_v (v), m_n (n) { } /** * @brief Equality with const iterator */ bool operator== (const reuse_vector_const_iterator &d) const { return mp_v == d.mp_v && m_n == d.m_n; } /** * @brief Inequality with const iterator */ bool operator!= (const reuse_vector_const_iterator &d) const { return ! operator== (d); } /** * @brief Equality */ bool operator== (const reuse_vector_iterator &d) const { return mp_v == d.mp_v && m_n == d.m_n; } /** * @brief Inequality */ bool operator!= (const reuse_vector_iterator &d) const { return ! operator== (d); } /** * @brief Less operator */ bool operator< (const reuse_vector_iterator &d) const { if (mp_v != d.mp_v) { return mp_v < d.mp_v; } return m_n < d.m_n; } /** * @brief Dereference operator */ value_type &operator* () const { tl_assert (mp_v->is_used (m_n)); return mp_v->item (m_n); } /** * @brief Access operator */ value_type *operator-> () const { tl_assert (mp_v->is_used (m_n)); return &mp_v->item (m_n); } /** * @brief Validity * * An iterator is valid if the object is still available * This does not take care about the container, but just about elements inside it. * If the container is deleted, the iterators become invalid without further notice. */ bool is_valid () const { return mp_v->is_used (m_n); } /** * @brief Increment operator */ reuse_vector_iterator &operator++ () { do { ++m_n; } while (! at_end () && ! mp_v->is_used (m_n)); return *this; } /** * @brief "at end" predicate */ bool at_end () const { return index () >= mp_v->last (); } /** * @brief The index of the element pointed to */ size_t index () const { return m_n; } /** * @brief The pointer to the vector that this iterator points into */ reuse_vector *vector () const { return mp_v; } /** * @brief A distance between two vectors */ difference_type operator- (reuse_vector_iterator d) const { // KLUDGE: this is slow and can be optimized if the vector does not have deleted items difference_type n = 0; while (d != *this) { ++d; ++n; } return n; } private: template friend class reuse_vector_const_iterator; reuse_vector *mp_v; size_type m_n; }; /** * @brief The const_iterator for a reuse_vector */ template class reuse_vector_const_iterator { public: typedef size_t size_type; typedef Value value_type; typedef const value_type *pointer; typedef const value_type &reference; // operator* returns a value typedef std::forward_iterator_tag iterator_category; typedef size_type difference_type; /** * @brief The default constructor */ reuse_vector_const_iterator () : mp_v (0), m_n (0) { } /** * @brief The constructor */ reuse_vector_const_iterator (const reuse_vector *v, size_type n) : mp_v (v), m_n (n) { } /** * @brief The conversion of a non-const iterator to a const iterator */ reuse_vector_const_iterator (const reuse_vector_iterator &d) : mp_v (d.mp_v), m_n (d.m_n) { } /** * @brief cast to non-const iterator */ reuse_vector_iterator to_non_const () const { return reuse_vector_iterator (const_cast *> (mp_v), m_n); } /** * @brief Equality */ bool operator== (const reuse_vector_const_iterator &d) const { return mp_v == d.mp_v && m_n == d.m_n; } /** * @brief Inequality */ bool operator!= (const reuse_vector_const_iterator &d) const { return ! operator== (d); } /** * @brief Less operator */ bool operator< (const reuse_vector_const_iterator &d) const { if (mp_v != d.mp_v) { return mp_v < d.mp_v; } return m_n < d.m_n; } /** * @brief Dereference operator */ const value_type &operator* () const { tl_assert (mp_v->is_used (m_n)); return mp_v->item (m_n); } /** * @brief Access operator */ const value_type *operator-> () const { tl_assert (mp_v->is_used (m_n)); return &mp_v->item (m_n); } /** * @brief Unsafe access to the target address * * This method is intended for special use cases such as the cached box picker in * the box tree */ const value_type *unsafe_target_addr () const { return &mp_v->item (m_n); } /** * @brief Validity * * An iterator is valid if the object is still available * This does not take care about the container, but just about elements inside it. * If the container is deleted, the iterators become invalid without further notice. */ bool is_valid () const { return mp_v->is_used (m_n); } /** * @brief Increment operator */ reuse_vector_const_iterator &operator++ () { do { ++m_n; } while (! at_end () && ! mp_v->is_used (m_n)); return *this; } /** * @brief "at end" predicate */ bool at_end () const { return index () >= mp_v->last (); } /** * @brief The index of the element pointed to */ size_t index () const { return m_n; } /** * @brief The pointer to the vector that this iterator points into */ const reuse_vector *vector () const { return mp_v; } /** * @brief A distance between two vectors */ difference_type operator- (reuse_vector_const_iterator d) const { // KLUDGE: this is slow and can be optimized if the vector does not have deleted items difference_type n = 0; while (d != *this) { ++d; ++n; } return n; } private: template friend class reuse_vector_iterator; const reuse_vector *mp_v; size_type m_n; }; /** * @brief A helper class describing the "unused" entries of a reuse_vector */ class ReuseData { public: typedef size_t size_type; ReuseData () : m_first_used (0), m_last_used (0), m_next_free (0), m_size (0) { } ReuseData (size_type n) : m_first_used (0), m_last_used (n), m_next_free (n), m_size (n) { m_used.resize (n, true); } size_type first () const { return m_first_used; } size_type last () const { return m_last_used; } size_type size () const { return m_size; } size_type allocate () { tl_assert (can_allocate ()); size_type r = m_next_free; m_used [r] = true; if (r >= m_last_used) { m_last_used = r + 1; } if (r < m_first_used) { m_first_used = r; } while (m_next_free != m_used.size () && m_used [m_next_free]) { ++m_next_free; } ++m_size; return r; } bool can_allocate () const { return (m_next_free < m_used.size ()); } void deallocate (size_type n) { m_used [n] = false; if (n == m_first_used) { while (m_first_used < m_last_used && ! m_used [m_first_used]) { ++m_first_used; } } if (n == m_last_used - 1) { while (m_last_used > m_first_used && ! m_used [m_last_used - 1]) { --m_last_used; } } if (n < m_next_free) { m_next_free = n; } --m_size; } void reserve (size_type n) { m_used.reserve (n); } bool is_used (size_type n) const { return m_used [n]; } size_t mem_reqd () const { return (m_used.size () + 7) / 8 + sizeof (*this); } size_t mem_used () const { return m_used.capacity () / 8 + sizeof (*this); } private: std::vector m_used; size_type m_first_used, m_last_used, m_next_free, m_size; }; /** * @brief A vector that maintains the order of elements but allows to reference elements in a stable way * * This container allows to insert and delete elements while references to them (through iterators) * remain stable. The insert does not necessarily happen at a certain position. Instead, the vector * keeps a reuseable member list (hence reuse_vector). In addition, the iterators deliver stable references * by using indices and a container pointer. This way, the iterators point to the same element * even after delete and insert (and potentially reallocation) actions. * * The memory requirements of this container are the same than that of a std::vector. * * One requirement is that sizeof(C) >= sizeof(void *). */ template class reuse_vector { public: typedef Value value_type; typedef size_t size_type; typedef reuse_vector_iterator iterator; typedef reuse_vector_const_iterator const_iterator; /** * @brief Default constructor */ reuse_vector () { init (); } /** * @brief Assignment constructor * * Assign the sequence of [from,to) to the vector. */ template reuse_vector (Iter from, Iter to) { init (); reserve (std::distance (from, to)); insert (from, to); } /** * @brief Copy constructor * * See operator= for a description of the copy operation. */ reuse_vector (const reuse_vector &d) { init (); reserve (d.size ()); for (const_iterator i = d.begin (); i != d.end (); ++i) { insert (*i); } } /** * @brief Destructor */ ~reuse_vector () { release (); } /** * @brief Assignment * * The assignment will not only copy the items but also compact the vector, i.e. * create a linear chain of elements without holes for unused ones and a capacity * exactly matching the required count. */ reuse_vector &operator= (const reuse_vector &d) { if (&d != this) { release (); reserve (d.size ()); for (const_iterator i = d.begin (); i != d.end (); ++i) { insert (*i); } } return *this; } /** * @brief Assignment * * Assign the sequence of [from,to) to the vector. * See operator= for a description of the assignment semantics. */ template void assign (Iter from, Iter to) { release (); reserve (std::distance (from, to)); insert (from, to); } /** * @brief equality operator */ bool operator== (const reuse_vector &d) const { if (size () != d.size ()) { return false; } const_iterator i = begin (); const_iterator ii = d.begin (); while (i != end ()) { if (*i != *ii) { return false; } ++i; ++ii; } return true; } /** * @brief Inequality */ bool operator!= (const reuse_vector &d) const { return ! operator== (d); } /** * @brief less operator */ bool operator< (const reuse_vector &d) const { if (size () != d.size ()) { return size () < d.size (); } const_iterator i = begin (); const_iterator ii = d.begin (); while (i != end ()) { if (*i != *ii) { return *i < *ii; } ++i; ++ii; } return false; } /** * @brief For a given pointer, tell if the element is a member of this container */ template bool is_member_of (const V *ptr) const { return (ptr >= mp_start && ptr < mp_finish); } /** * @brief Deliver the iterator for a certain element (given by pointer) */ iterator iterator_from_pointer (Value *ptr) { return iterator (this, ptr - mp_start); } /** * @brief "begin" iterator */ iterator begin () { return iterator (this, first ()); } /** * @brief "end" iterator */ iterator end () { return iterator (this, last ()); } /** * @brief Deliver the iterator for a certain element (given by pointer) */ const_iterator iterator_from_pointer (const Value *ptr) const { return const_iterator (this, ptr - mp_start); } /** * @brief "begin" const iterator */ const_iterator begin () const { return const_iterator (this, first ()); } /** * @brief "end" const iterator */ const_iterator end () const { return const_iterator (this, last ()); } /** * @brief Access by the basic index * * The basic index is the internal index reported from the iterator's index() method */ value_type &item (size_type n) { return mp_start [n]; } /** * @brief Access by the basic index * * The basic index is the internal index reported from the iterator's index() method */ const value_type &item (size_type n) const { return mp_start [n]; } /** * @brief Insert one element into the container */ iterator insert (const value_type &item) { size_type n = 0; if (mp_rdata) { n = mp_rdata->allocate (); // when the last unused member is allocated, we can remove the // ReuseData pointer and add to the end if (! mp_rdata->can_allocate ()) { delete mp_rdata; mp_rdata = 0; } } else { if (mp_finish == mp_capacity) { // Special case: we are inserting an element from our own space - since reserve will first // release the element we have to create a copy before we insert it. if (&item >= mp_start && &item < mp_finish) { value_type copy (item); return insert (copy); } reserve (size () == 0 ? 4 : size () * 2); } n = size_t (mp_finish - mp_start); ++mp_finish; } new (mp_start + n) value_type (item); return iterator (this, n); } /** * @brief Insert a sequence of elements [from,to) into the container */ template void insert (const Iter &from, const Iter &to) { if (! (from == to)) { // don't reservce if the first element of the sequence comes from ourself. // This implies that we try to duplicate our own elements which would invalidate // the source upon reservation. if (! (&*from >= mp_start && &*from < mp_finish)) { reserve (size () + std::distance (from, to)); } for (Iter i = from; i != to; ++i) { insert (*i); } } } /** * @brief Erase the given element from the container * * If the element was erased already, nothing will happen. */ void erase (const iterator &i) { if (! mp_rdata) { mp_rdata = new ReuseData (size ()); } if (mp_rdata->is_used (i.index ())) { item (i.index ()).~value_type (); mp_rdata->deallocate (i.index ()); } } /** * @brief Erase the given sequence from the container */ void erase (const iterator &from, const iterator &to) { // trivial shortcut if (from == to) { return; } if (! mp_rdata) { mp_rdata = new ReuseData (size ()); } for (size_type i = from.index (); i != to.index (); ++i) { if (mp_rdata->is_used (i)) { item (i).~value_type (); mp_rdata->deallocate (i); } } } /** * @brief Clear the container * * This does not release the memory allocated for this container, * similar to that what std::vector does. */ void clear () { if (mp_start) { // call destructor for (size_type i = first (); i < last (); ++i) { if (is_used (i)) { item (i).~value_type (); } } } if (mp_rdata) { delete mp_rdata; mp_rdata = 0; } mp_finish = mp_start; } /** * @brief Return the size * * The size is the number of elements that are actually stored. * The size is computed in linear time. */ size_type size () const { if (mp_rdata) { return mp_rdata->size (); } else { return size_type (mp_finish - mp_start); } } /** * @brief Empty predicate */ bool empty () const { return size () == 0; } /** * @brief Return the capacity * * The capacity is the number of elements that can be stored without reallocation to happen. */ size_type capacity () const { return size_type (mp_capacity - mp_start); } /** * @brief Reserve space for a given number of elements * * This guarantees that for the next n-size() inserts no reallocation will occure * No resizing will happen, if n is less than the current capacity. */ void reserve (size_type n) { typename tl::type_traits::relocate_requirements relocate_requirements_tag; internal_reserve (n, relocate_requirements_tag); } /** * @brief Release the memory allocated and clear the container */ void release () { if (mp_start) { // call destructor for (size_type i = first (); i < last (); ++i) { if (is_used (i)) { item (i).~value_type (); } } delete [] ((char *) mp_start); } if (mp_rdata) { delete mp_rdata; mp_rdata = 0; } init (); } size_t mem_reqd () const { return (mp_capacity - mp_start) * sizeof (Value) + (mp_rdata != 0 ? mp_rdata->mem_reqd () : 0); } size_t mem_used () const { return (mp_finish - mp_start) * sizeof (Value) + (mp_rdata != 0 ? mp_rdata->mem_used () : 0); } private: value_type *mp_start, *mp_finish, *mp_capacity; ReuseData *mp_rdata; template friend class reuse_vector_iterator; template friend class reuse_vector_const_iterator; void init () { mp_start = mp_finish = mp_capacity = 0; mp_rdata = 0; } bool is_used (size_type n) const { if (n >= first () && n < last ()) { if (mp_rdata) { return mp_rdata->is_used (n); } else { return true; } } return false; } size_type first () const { if (mp_rdata) { return mp_rdata->first (); } else { return 0; } } size_type last () const { if (mp_rdata) { return mp_rdata->last (); } else { return size_type (mp_finish - mp_start); } } void internal_reserve (size_type n, tl::complex_relocate_required) { if (n > capacity ()) { value_type *new_start = (value_type *) (new char [sizeof (value_type) * n]); size_type l = last (); for (size_type i = first (); i < l; ++i) { if (is_used (i)) { new (new_start + i) value_type (item (i)); item (i).~value_type (); } } size_type e = size_type (mp_finish - mp_start); if (mp_rdata) { mp_rdata->reserve (n); } if (mp_start) { delete [] ((char *) mp_start); } mp_start = new_start; mp_finish = mp_start + e; mp_capacity = mp_start + n; } } void internal_reserve (size_type n, tl::trivial_relocate_required) { if (n > capacity ()) { value_type *new_start = (value_type *) (new char [sizeof (value_type) * n]); size_type l = last (); size_type i = first (); memcpy ((void *)(new_start + i), (void *)(mp_start + i), (l - i) * sizeof (Value)); size_type e = size_type (mp_finish - mp_start); if (mp_rdata) { mp_rdata->reserve (n); } if (mp_start) { delete [] ((char *) mp_start); } mp_start = new_start; mp_finish = mp_start + e; mp_capacity = mp_start + n; } } }; } #endif