mirror of https://github.com/CGAL/cgal
404 lines
10 KiB
C++
404 lines
10 KiB
C++
// ============================================================================
|
|
//
|
|
// Copyright (c) 2001 The CGAL Consortium
|
|
//
|
|
// This software and related documentation is part of an INTERNAL release
|
|
// of the Computational Geometry Algorithms Library (CGAL). It is not
|
|
// intended for general use.
|
|
//
|
|
// ----------------------------------------------------------------------------
|
|
//
|
|
// release :
|
|
// release_date :
|
|
//
|
|
// file : include/CGAL/DS_Container.h
|
|
// revision : $Revision$
|
|
// revision_date : $Date$
|
|
// author(s) : Sylvain Pion <Sylvain.Pion@sophia.inria.fr>
|
|
//
|
|
// coordinator : INRIA Sophia Antipolis (<Mariette.Yvinec@sophia.inria.fr>)
|
|
//
|
|
// ============================================================================
|
|
|
|
#ifndef CGAL_DS_CONTAINER_H
|
|
#define CGAL_DS_CONTAINER_H
|
|
|
|
#include <CGAL/basic.h>
|
|
#include <vector>
|
|
#include <memory>
|
|
|
|
// What we want is a kind of STL like container/allocator with the following
|
|
// properties, by decreasing priority :
|
|
// - a fast get_new_element() and release_element().
|
|
// - a compact representation, which should be N*sizeof(Elt) + o(N) (modulo
|
|
// alignment requirements).
|
|
// - a bidirectional iterator over the (non-free = used) elements.
|
|
// - size() to know how many (non-free) elements we have.
|
|
// - find() if an element is stored in the container or not. This doesn't
|
|
// really need to be fast, but if we can... (used by is_cell()...).
|
|
// - would be nice to have a temporary_free_list (still active elements, but
|
|
// which are going to be freed soon). Probably it prevents compactness.
|
|
// - all this is expected especially when there are not so much free objects
|
|
// compared to the allocated elements.
|
|
// - eventually something to copy this data structure, providing a way to
|
|
// update the pointers (give access to a hash_map, at least a function that
|
|
// converts an old pointer to the new one ?)
|
|
|
|
// Internal representation :
|
|
// - Elements are allocated by arrays of DS_Container_allocation_size, and
|
|
// pointers to these arrays are stored in a vector<>.
|
|
// - Representation of freeness :
|
|
// - The iterator needs to be able to test if a given element is free or not.
|
|
// (running over the free list each time could kill performance, but maybe
|
|
// we can only do that after a first check that there's a NULL
|
|
// somewhere else... or maybe not NULL, but a pointer that is definitely
|
|
// not possible, like 1, or 1 + &this_container ?). Maybe we should store
|
|
// union {
|
|
// ds_cell;
|
|
// struct { void *list_ptr; int magic_key;}
|
|
// } Elt;
|
|
// in which case we _must_ use the STL allocators...
|
|
// I wonder if it's still valid C++, otherwise which destructor is run.. ?
|
|
// - We need to have fast access to a free element.
|
|
|
|
// So, 2 possibilities in practice :
|
|
// - Elt must be able to store a list pointer, and answer if it's free or not.
|
|
// Given some constraints in practive, this can be achieved by :
|
|
// - squatting a particular zone of the Elt which is not NULL when used to be
|
|
// able to tell if it's free. And squating another (or the same) to store
|
|
// the list pointer.
|
|
|
|
// So either Elt has an additional pointer field, or it has some way to store
|
|
// the necessary information. Both things should be provided by an
|
|
// object passed to DS_Container giving access to the list pointer, and if
|
|
// NULL, then it means the Elt is free.
|
|
|
|
// TODO :
|
|
// - Add a const_iterator
|
|
// - .merge(DS_container &) that merges two containers into one.
|
|
// (useful for divide and conquer and co ?)
|
|
// - The magic_key is not clean, we should preferably require
|
|
// some member functions from Elt to store the necessary information
|
|
// (a bit like In_place_list does).
|
|
|
|
CGAL_BEGIN_NAMESPACE
|
|
|
|
// Global instead of static const member, because of VC++ bug.
|
|
const unsigned int Free_elt_magic0 = 0xc9a1c9a1;
|
|
|
|
class Free_elt {
|
|
unsigned key;
|
|
Free_elt * ptr;
|
|
public:
|
|
|
|
Free_elt * next() const {
|
|
return ptr;
|
|
}
|
|
|
|
void set_next(Free_elt *p) {
|
|
ptr = p;
|
|
}
|
|
|
|
void mark_free() {
|
|
key = Free_elt_magic0;
|
|
}
|
|
|
|
void unmark_free() {
|
|
key = 0;
|
|
}
|
|
|
|
bool seems_free() const {
|
|
return key == Free_elt_magic0;
|
|
}
|
|
};
|
|
|
|
const int DS_Container_allocation_size = 1024;
|
|
// const int DS_Container_allocation_size = 8192;
|
|
|
|
template < class DSC > class DS_Container_iterator;
|
|
|
|
template < class Elt, class Alloc = CGAL_ALLOCATOR(Elt) > //, size_t=1024> ???
|
|
class DS_Container
|
|
{
|
|
typedef DS_Container<Elt, Alloc> Self;
|
|
public:
|
|
|
|
typedef Elt value_type;
|
|
typedef Elt& reference;
|
|
typedef const Elt & const_reference;
|
|
typedef DS_Container_iterator<Self> iterator;
|
|
typedef std::size_t size_type;
|
|
|
|
friend class DS_Container_iterator<Self>;
|
|
|
|
DS_Container()
|
|
{
|
|
CGAL_assertion(sizeof(Free_elt) <= sizeof(Elt));
|
|
init_free_list();
|
|
size_ = 0;
|
|
}
|
|
|
|
// The copy constructor and assignment operators preserve
|
|
// the iterator order.
|
|
DS_Container(const DS_Container &c)
|
|
{
|
|
std::copy(c.begin(), c.end(), std::back_inserter(*this));
|
|
}
|
|
|
|
DS_Container & operator=(const DS_Container &c)
|
|
{
|
|
if (&c != this) {
|
|
clear();
|
|
std::copy(c.begin(), c.end(), std::back_inserter(*this));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void push_back(const Elt &e)
|
|
{
|
|
*get_new_element() = e;
|
|
}
|
|
|
|
// Compared to push_back(), we avoid a copy, and get the pointer...
|
|
Elt * get_new_element()
|
|
{
|
|
if (is_free_list_empty()) {
|
|
array_vect.push_back(alloc.allocate(DS_Container_allocation_size));
|
|
for (int i=DS_Container_allocation_size-1; i>=0; --i)
|
|
put_on_free_list((Free_elt *) &array_vect.back()[i]);
|
|
}
|
|
|
|
Free_elt * ret = free_list.next();
|
|
free_list.set_next(ret->next());
|
|
ret->unmark_free();
|
|
// alloc.construct((Elt *) ret, Elt()); // Creates/copies temporary :(
|
|
new ((void *) ret) Elt();
|
|
++size_;
|
|
return (Elt *) ret;
|
|
}
|
|
|
|
void release_element(Elt *x) // erase() is the std naming ?
|
|
{
|
|
CGAL_assertion(!is_free(x));
|
|
alloc.destroy(x);
|
|
put_on_free_list((Free_elt *)x);
|
|
--size_;
|
|
}
|
|
|
|
bool is_element(Elt *x) const
|
|
{
|
|
// Compare address with the beginning of each array.
|
|
for (typename Array_vect::const_iterator it = array_vect.begin();
|
|
it != array_vect.end(); ++it)
|
|
if (x >= *it && (x - *it) < DS_Container_allocation_size)
|
|
return !is_free(x);
|
|
return false;
|
|
}
|
|
|
|
iterator begin() const
|
|
{
|
|
return iterator(*this);
|
|
}
|
|
|
|
iterator end() const
|
|
{
|
|
return iterator(*this, 0);
|
|
}
|
|
|
|
~DS_Container()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
void swap(Self &c)
|
|
{
|
|
std::swap(alloc, c.alloc);
|
|
std::swap(array_vect, c.array_vect);
|
|
std::swap(free_list, c.free_list);
|
|
std::swap(size_, c.size_);
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
for (iterator it = begin(); it != end(); ++it)
|
|
alloc.destroy(&*it);
|
|
for (typename Array_vect::iterator ait = array_vect.begin();
|
|
ait != array_vect.end(); ++ait)
|
|
alloc.deallocate(*ait, DS_Container_allocation_size);
|
|
array_vect.clear();
|
|
init_free_list();
|
|
size_ = 0;
|
|
}
|
|
|
|
size_type size() const
|
|
{
|
|
return size_;
|
|
}
|
|
|
|
private:
|
|
|
|
void put_on_free_list(Free_elt *x)
|
|
{
|
|
x->mark_free();
|
|
x->set_next(free_list.next());
|
|
free_list.set_next(x);
|
|
}
|
|
|
|
bool is_in_free_list(const Free_elt *x) const
|
|
{
|
|
for (Free_elt *p=free_list.next(); p!=NULL; p=p->next())
|
|
if (p == x)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool is_free(const Elt *x) const
|
|
{
|
|
// seems_free() may answer true when it's not, for unlucky people.
|
|
// Therefore we can add this _really_ expensive assertion.
|
|
// CGAL_expensive_assertion(!((const Free_elt *) x)->seems_free()
|
|
// || is_in_free_list((const Free_elt *) x));
|
|
return ((const Free_elt *) x)->seems_free();
|
|
}
|
|
|
|
bool is_free_list_empty() const
|
|
{
|
|
return free_list.next() == NULL;
|
|
}
|
|
|
|
void init_free_list()
|
|
{
|
|
free_list.unmark_free();
|
|
free_list.set_next(NULL);
|
|
}
|
|
|
|
typedef std::vector<Elt *> Array_vect;
|
|
|
|
const Array_vect & array() const
|
|
{
|
|
return array_vect;
|
|
}
|
|
|
|
Alloc alloc;
|
|
Array_vect array_vect;
|
|
Free_elt free_list;
|
|
size_type size_;
|
|
};
|
|
|
|
template < class DSC >
|
|
class DS_Container_iterator
|
|
{
|
|
typedef DS_Container_iterator<DSC> Self;
|
|
typedef typename DSC::value_type Elt;
|
|
|
|
public:
|
|
typedef typename DSC::value_type value_type;
|
|
typedef value_type * pointer;
|
|
typedef value_type & reference;
|
|
typedef std::size_t size_type;
|
|
typedef std::ptrdiff_t difference_type;
|
|
typedef std::bidirectional_iterator_tag iterator_category;
|
|
|
|
DS_Container_iterator()
|
|
{}
|
|
|
|
DS_Container_iterator(const DSC &d)
|
|
: cont(&d), vect(0), index(0)
|
|
{
|
|
if (cont->array().size()>0 && is_free())
|
|
operator++();
|
|
}
|
|
|
|
DS_Container_iterator(const DSC &d, int)
|
|
: cont(&d), vect(cont->array().size()), index(0)
|
|
{}
|
|
|
|
bool operator==(const Self & it) const
|
|
{
|
|
return (it.vect == vect) && (index == it.index) && (it.cont == cont);
|
|
}
|
|
|
|
bool operator!=(const Self & it) const
|
|
{
|
|
return !(*this == it);
|
|
}
|
|
|
|
Self & operator++()
|
|
{
|
|
CGAL_assertion(vect <= cont->array().size());
|
|
|
|
if (vect == cont->array().size()) {
|
|
CGAL_assertion(index == 0);
|
|
return *this;
|
|
}
|
|
|
|
do {
|
|
if (index < DS_Container_allocation_size-1)
|
|
++index;
|
|
else {
|
|
index = 0;
|
|
++vect;
|
|
if (vect == cont->array().size())
|
|
break;
|
|
}
|
|
} while (is_free()); // Skip the free Elts.
|
|
return *this;
|
|
}
|
|
|
|
Self & operator--()
|
|
{
|
|
do {
|
|
if (index > 0)
|
|
--index;
|
|
else {
|
|
if (vect == 0)
|
|
break;
|
|
index = DS_Container_allocation_size-1;
|
|
--vect;
|
|
}
|
|
} while (is_free()); // Skip the free Elts.
|
|
return *this;
|
|
}
|
|
|
|
Self operator++(int)
|
|
{
|
|
Self tmp(*this);
|
|
++(*this);
|
|
return tmp;
|
|
}
|
|
|
|
Self operator--(int)
|
|
{
|
|
Self tmp(*this);
|
|
--(*this);
|
|
return tmp;
|
|
}
|
|
|
|
Elt & operator*() const
|
|
{
|
|
CGAL_assertion(!is_free());
|
|
return cont->array()[vect][index];
|
|
}
|
|
|
|
Elt * operator->() const
|
|
{
|
|
CGAL_assertion(!is_free());
|
|
return &(cont->array()[vect][index]);
|
|
}
|
|
|
|
private:
|
|
|
|
bool is_free() const
|
|
{
|
|
return cont->is_free(&(cont->array()[vect][index]));
|
|
}
|
|
|
|
const DSC * cont; // The container
|
|
typename DSC::Array_vect::size_type vect; // index of the current array.
|
|
unsigned int index; // index in the current array.
|
|
};
|
|
|
|
CGAL_END_NAMESPACE
|
|
|
|
#endif // CGAL_DS_CONTAINER_H
|