klayout/src/tl/tlReuseVector.h

1006 lines
20 KiB
C++

/*
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 <stdlib.h>
#include <iterator>
#include <vector>
#include <cstring>
#include "tlAssert.h"
#include "tlTypeTraits.h"
namespace tl
{
template <class Value> class reuse_vector;
template <class Value> class reuse_vector_const_iterator;
/**
* @brief The iterator for a reuse_vector
*/
template <class Value>
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<Value> *v, size_type n)
: mp_v (v), m_n (n)
{ }
/**
* @brief Equality with const iterator
*/
bool operator== (const reuse_vector_const_iterator<Value> &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<Value> &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<Value> *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 <class V> friend class reuse_vector_const_iterator;
reuse_vector<Value> *mp_v;
size_type m_n;
};
/**
* @brief The const_iterator for a reuse_vector
*/
template <class Value>
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<Value> *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<Value> &d)
: mp_v (d.mp_v), m_n (d.m_n)
{ }
/**
* @brief cast to non-const iterator
*/
reuse_vector_iterator<Value> to_non_const () const
{
return reuse_vector_iterator<Value> (const_cast<reuse_vector<Value> *> (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<Value> *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 <class V> friend class reuse_vector_iterator;
const reuse_vector<Value> *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<bool> 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 Value>
class reuse_vector
{
public:
typedef Value value_type;
typedef size_t size_type;
typedef reuse_vector_iterator<value_type> iterator;
typedef reuse_vector_const_iterator<value_type> const_iterator;
/**
* @brief Default constructor
*/
reuse_vector ()
{
init ();
}
/**
* @brief Assignment constructor
*
* Assign the sequence of [from,to) to the vector.
*/
template <class Iter>
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 <class Iter>
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 <class V>
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 <class Iter>
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<Value>::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<class V> friend class reuse_vector_iterator;
template<class V> 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