From 0053fa20bc99463cc8f0b9cce612bd2a241b68cd Mon Sep 17 00:00:00 2001 From: Guillaume Damiand Date: Thu, 18 Sep 2014 17:03:38 +0200 Subject: [PATCH] Add index containers --- .../CGAL/Compact_container_with_index.h | 923 ++++++++++++++++++ .../CGAL/Compact_container_with_index_2.h | 654 +++++++++++++ 2 files changed, 1577 insertions(+) create mode 100644 Combinatorial_map/include/CGAL/Compact_container_with_index.h create mode 100644 Combinatorial_map/include/CGAL/Compact_container_with_index_2.h diff --git a/Combinatorial_map/include/CGAL/Compact_container_with_index.h b/Combinatorial_map/include/CGAL/Compact_container_with_index.h new file mode 100644 index 00000000000..f9fa3fc869f --- /dev/null +++ b/Combinatorial_map/include/CGAL/Compact_container_with_index.h @@ -0,0 +1,923 @@ +// Copyright (c) 2003,2004,2007-2010 INRIA Sophia-Antipolis (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org); you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 3 of the License, +// or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// +// Author(s) : Sylvain Pion + +#ifndef CGAL_COMPACT_CONTAINER_WITH_INDEX_H +#define CGAL_COMPACT_CONTAINER_WITH_INDEX_H + +#include + +// An STL like container with the following properties : +// - to achieve compactness, it requires access to a pointer stored in T, +// specified by a traits. This pointer is supposed to be 4 bytes aligned +// when the object is alive, otherwise, the container uses the 2 least +// significant bits to store information in the pointer. +// - Ts are allocated in arrays of increasing size, which are linked together +// by their first and last element. +// - the iterator looks at the famous 2 bits to know if it has to deal with +// a free/used/boundary element. + +// TODO : +// - Add .resize() (and proper copy of capacity_). +// - Add preconditions in input that real pointers need to have clean bits. +// Also for the allocated memory alignment, and sizeof(). +// - Do a benchmark before/after. +// - Check the end result with Valgrind. +// - The bit squatting mechanism will be reused for the conflict flag, maybe +// it could be put out of the class. + +// TODO low priority : +// - rebind<> the allocator +// - Exception safety guarantees +// - Thread safety guarantees +// - std requirements on iterators says all defined operations are constant +// time amortized (it's not true here, maybe it could be with some work...) +// - all this is expected especially when there are not so many free objects +// compared to the allocated elements. +// - Should block_size be selectable/hintable by .reserve() ? +// - would be nice to have a temporary_free_list (still active elements, but +// which are going to be freed soon). Probably it prevents compactness. +// - 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 ?). Actually it doesn't have to +// be stuck to a particular DS, because for a list it's useful too... +// - Currently, end() can be invalidated on insert() if a new block is added. +// It would be nice to fix this. We could insert the new block at the +// beginning instead ? That would drop the property that iterator order +// is preserved. Maybe it's not a problem if end() is not preserved, after +// all nothing is going to dereference it, it's just for comparing with +// end() that it can be a problem. +// Another way would be to have end() point to the end of an always +// empty block (containing no usable element), and insert new blocks just +// before this one. +// Instead of having the blocks linked between them, the start/end pointers +// could point back to the container, so that we can do more interesting +// things (e.g. freeing empty blocks automatically) ? + +namespace CGAL { + +template +struct Constant_size_policy_for_cc_with_size +{ + static const unsigned int first_block_size = k; + + template + static void increase_size(Compact_container& /*cc*/) + {} + + template + static void get_index_and_block(typename Compact_container::size_type i, + typename Compact_container::size_type& index, + typename Compact_container::size_type& block) + { + block=i/k; + index=i%k; + } +}; + + +// The traits class describes the way to access the size_type. +// It can be specialized. +template < class T > +struct Compact_container_with_index_traits { + static std::size_t size_t(const T &t) + { return t.for_compact_container_with_index(); } + static std::size_t & size_t(T &t) + { return t.for_compact_container_with_index(); } +}; + +template +class Compact_container_with_index_2; + +namespace internal { + template < class DSC, bool Const> + class CC_iterator_with_index; +} + +template < class T, class Allocator_, class Increment_policy > +class Compact_container_with_index +{ + typedef Allocator_ Al; + typedef Increment_policy Incr_policy; + typedef typename Default::Get< Al, CGAL_ALLOCATOR(T) >::type Allocator; + typedef Compact_container_with_index Self; + typedef Compact_container_with_index_traits Traits; +public: + typedef T value_type; + typedef Allocator allocator_type; + typedef typename Allocator::reference reference; + typedef typename Allocator::const_reference const_reference; + typedef typename Allocator::pointer pointer; + typedef typename Allocator::const_pointer const_pointer; + typedef typename Allocator::size_type size_type; + typedef typename Allocator::difference_type difference_type; + typedef internal::CC_iterator_with_index iterator; + typedef internal::CC_iterator_with_index const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + friend class internal::CC_iterator_with_index; + friend class internal::CC_iterator_with_index; + + template + friend struct Addition_size_policy; + template friend struct Constant_size_policy_for_cc_with_size; + + explicit Compact_container_with_index(const Allocator &a = Allocator()) + : alloc(a) + { + init(); + } + + template < class InputIterator > + Compact_container_with_index(InputIterator first, InputIterator last, + const Allocator & a = Allocator()) + : alloc(a) + { + init(); + std::copy(first, last, CGAL::inserter(*this)); + } + + // The copy constructor and assignment operator preserve the iterator order + Compact_container_with_index(const Compact_container_with_index &c) + : alloc(c.get_allocator()) + { + init(); + block_size = c.block_size; + std::copy(c.begin(), c.end(), CGAL::inserter(*this)); + } + + Compact_container_with_index & + operator=(const Compact_container_with_index &c) + { + if (&c != this) { + Self tmp(c); + swap(tmp); + } + return *this; + } + + ~Compact_container_with_index() + { + clear(); + } + + bool is_used(size_type i) const + { + return (type(i)==USED); + } + + const T& operator[] (size_type i) const + { + typename Self::size_type block_number, index_in_block; + Increment_policy::template get_index_and_block(i, + index_in_block, + block_number); + return all_items[block_number].first[index_in_block]; + } + + T& operator[] (size_type i) + { + typename Self::size_type block_number, index_in_block; + Increment_policy::template get_index_and_block(i, + index_in_block, + block_number); + return all_items[block_number].first[index_in_block]; + } + + void swap(Self &c) + { + std::swap(alloc, c.alloc); + std::swap(capacity_, c.capacity_); + std::swap(size_, c.size_); + std::swap(block_size, c.block_size); + std::swap(free_list, c.free_list); + all_items.swap(c.all_items); + } + + iterator begin() { return iterator(this, 0, 0); } + iterator end() { return iterator(this, capacity_); } + + const_iterator begin() const { return const_iterator(this, 0, 0); } + const_iterator end() const { return const_iterator(this, capacity_); } + + reverse_iterator rbegin() { return reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + + const_reverse_iterator + rbegin() const { return const_reverse_iterator(end()); } + const_reverse_iterator + rend() const { return const_reverse_iterator(begin()); } + + // Compute the index of a given pointer to an element of the compact container. + size_type compute_index(const_pointer value) const + { + size_type res = 0; + for (typename All_items::const_iterator it = all_items.begin(), + itend = all_items.end(); it != itend; ++it) + { + const_pointer p = it->first; + size_type s = it->second; + if (value >= p && value < (p+s)) + { + return res + (value-p); + } + res += s; + } + return 0; + } + + iterator index_to(size_type value) { + return iterator(this, value); + } + const_iterator index_to(size_type value) const { + return const_iterator(this, value); + } + + // Boost.Intrusive interface + iterator iterator_to(reference value) { + return iterator(this, compute_index(&value)); + } + const_iterator iterator_to(const_reference value) const { + return const_iterator(this, compute_index(&value)); + } + + // Special insert methods that construct the objects in place + // (just forward the arguments to the constructor, to optimize a copy). +#ifndef CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES + template < typename... Args > + size_type emplace(const Args&... args) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(args...); + ++size_; + return ret; + } +#else + // inserts a default constructed item. + size_type emplace() + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(); + ++size_; + + return ret; + } + + template < typename T1 > + size_type + emplace(const T1 &t1) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1); + ++size_; + return ret; + } + + template < typename T1, typename T2 > + size_type + emplace(const T1 &t1, const T2 &t2) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1, t2); + ++size_; + return ret; + } + + template < typename T1, typename T2, typename T3 > + size_type + emplace(const T1 &t1, const T2 &t2, const T3 &t3) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1, t2, t3); + ++size_; + return ret; + } + + template < typename T1, typename T2, typename T3, typename T4 > + size_type + emplace(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1, t2, t3, t4); + ++size_; + return ret; + } + + template < typename T1, typename T2, typename T3, typename T4, typename T5 > + size_type + emplace(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, + const T5 &t5) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1, t2, t3, t4, t5); + ++size_; + return ret; + } + + template < typename T1, typename T2, typename T3, typename T4, + typename T5, typename T6 > + size_type + emplace(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, + const T5 &t5, const T6 &t6) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1, t2, t3, t4, t5, t6); + ++size_; + return ret; + } + + template < typename T1, typename T2, typename T3, typename T4, + typename T5, typename T6, typename T7 > + size_type + emplace(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, + const T5 &t5, const T6 &t6, const T7 &t7) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1, t2, t3, t4, t5, t6, t7); + ++size_; + return ret; + } + + template < typename T1, typename T2, typename T3, typename T4, + typename T5, typename T6, typename T7, typename T8 > + size_type + emplace(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, + const T5 &t5, const T6 &t6, const T7 &t7, const T8 &t8) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1, t2, t3, t4, t5, t6, t7, t8); + ++size_; + return ret; + } +#endif // CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES + + size_type insert(const T &t) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + alloc.construct(&e, t); + ++size_; + return ret; + } + + template < class InputIterator > + void insert(InputIterator first, InputIterator last) + { + for (; first != last; ++first) + insert(*first); + } + + template < class InputIterator > + void assign(InputIterator first, InputIterator last) + { + clear(); // erase(begin(), end()); // ? + insert(first, last); + } + + void erase(size_type x) + { + CGAL_precondition(type(x) == USED); + T& e = operator[](x); + alloc.destroy(&e); +#ifndef CGAL_NO_ASSERTIONS + std::memset(&e, 0, sizeof(T)); +#endif + put_on_free_list(x); + --size_; + } + + void erase(iterator first, iterator last) { + while (first != last) + erase(first++); + } + + void clear(); + + // Merge the content of d into *this. d gets cleared. + // The complexity is O(size(free list = capacity-size)). + void merge(Self &d); + + size_type size() const + { + CGAL_expensive_assertion(size_ == + (size_type) std::distance(begin(), end())); + return size_; + } + + size_type max_size() const + { + return alloc.max_size(); + } + + size_type capacity() const + { + return capacity_; + } + + // void resize(size_type sz, T c = T()); // TODO makes sense ??? + + bool empty() const + { + return size_ == 0; + } + + allocator_type get_allocator() const + { + return alloc; + } + + // Returns whether the iterator "cit" is in the range [begin(), end()]. + // Complexity : O(#blocks) = O(sqrt(capacity())). + // This function is mostly useful for purposes of efficient debugging at + // higher levels. + bool owns(const_iterator cit) const + { + // We use the block structure to provide an efficient version : + // we check if the address is in the range of each block, + // and then test whether it is valid (not a free element). + + if (cit == end()) + return true; + + const_pointer c = &*cit; + + for (typename All_items::const_iterator it = all_items.begin(), + itend = all_items.end(); it != itend; ++it) { + const_pointer p = it->first; + size_type s = it->second; + + // Are we in the address range of this block (excluding first and last + // elements) ? + if (c>=p && c<(p+s)) + { + return type(cit) == USED; + } + } + return false; + } + + bool owns_dereferencable(const_iterator cit) const + { + return cit != end() && owns(cit); + } + + /** Reserve method to ensure that the capacity of the Compact_container be + * greater or equal than a given value n. + */ + void reserve(size_type n) + { + if ( capacity_>=n ) return; + + size_type lastblock = all_items.size(); + + while ( capacity_= 0; --i, --index) + put_on_free_list(index); + } + while ( curblock>lastblock ); + } + +private: + + void allocate_new_block(); + + // Definition of the bit squatting : + // ================================= + // e is composed of a size_t and the big 1 bit. + // Here is the meaning of each of the 4 cases. + // + // value of the last bit as "Type" : 0 == reserved element; 1==free element. + // When an element is free, the other bits represent the index of the + // next free element. + + enum Type { USED = 0, FREE = 1 }; + + static const int nbbits_size_type_m1 = sizeof(size_type)*8 - 1; + static const size_type mask_type = ((size_type)-1)-(((size_type)-1)/2); + + // Get the type of the pointee. + static Type static_type(const T& e) + // TODO check if this is ok for little and big endian + { return (Type) ((Traits::size_t(e) & mask_type)>>(nbbits_size_type_m1)); } + + Type type(size_type e) const + { return static_type(operator[](e)); } + + // Sets the pointer part and the type of the pointee. + static void static_set_type(T& e, Type t) + { + // This out of range compare is always true and causes lots of + // unnecessary warnings. + // CGAL_precondition(0 <= t && t < 2); + Traits::size_t(e) &= ( ~mask_type | ( ((size_type)t) <<(nbbits_size_type_m1) ) ); + } + + // get the value of the element (removing the two bits) + static size_type static_get_val(const T& e) + { return (Traits::size_t(e) & ~mask_type); } + + size_type get_val(size_type e) const + { return static_get_val(operator[](e)); } + + // set the value of the element and its type + static void static_set_val(T& e, size_type v, Type t) + { Traits::size_t(e)=v | ( ((size_type)t) <<(nbbits_size_type_m1)); } + + void set_val(size_type e, size_type v, Type t) + { static_set_val(operator[](e), v, t); } + + void put_on_free_list(size_type x) + { + set_val(x, free_list, FREE); + free_list = x; + } + + // We store a vector of pointers to all allocated blocks and their sizes. + // Knowing all pointers, we don't have to walk to the end of a block to reach + // the pointer to the next block. + // Knowing the sizes allows to deallocate() without having to compute the size + // by walking through the block till its end. + // This opens up the possibility for the compiler to optimize the clear() + // function considerably when has_trivial_destructor. + typedef std::vector > All_items; + + void init() + { + block_size = Incr_policy::first_block_size; + capacity_ = 0; + size_ = 0; + free_list = 0; + all_items = All_items(); + + emplace(); + size_=0; + } + + allocator_type alloc; + size_type capacity_; + size_type size_; + size_type block_size; + size_type free_list; + All_items all_items; +}; + +/*template < class T, class Allocator, class Increment_policy > +void Compact_container_with_index::merge(Self &d) +{ + CGAL_precondition(&d != this); + + // Allocators must be "compatible" : + CGAL_precondition(get_allocator() == d.get_allocator()); + + // Concatenate the free_lists. + if (free_list == 0) { + free_list = d.free_list; + } else if (d.free_list != 0) { + size_type e = free_list; + while (get_val(e) != 0) + e = get_val(e); + set_val(e, d.free_list, FREE); + } + // Add the sizes. + size_ += d.size_; + // Add the capacities. + capacity_ += d.capacity_; + // It seems reasonnable to take the max of the block sizes. + block_size = (std::max)(block_size, d.block_size); + // Clear d. + d.init(); +}*/ + +template < class T, class Allocator, class Increment_policy > +void Compact_container_with_index::clear() +{ + for (size_type i=0; ifirst, it->second); + } + + init(); +} + +template < class T, class Allocator, class Increment_policy > +void Compact_container_with_index::allocate_new_block() +{ + pointer new_block = alloc.allocate(block_size); + all_items.push_back(std::make_pair(new_block, block_size)); + // We mark them free in reverse order, so that the insertion order + // will correspond to the iterator order... + for (size_type index = capacity_+block_size-1; index>capacity_; --index) + put_on_free_list(index); + + put_on_free_list(capacity_); + capacity_ += block_size; + // Increase the block_size for the next time. + Increment_policy::increase_size(*this); +} + +namespace internal { + + // ********************************************************************** + // specialization if we use index: in this case, an iterator use one more + // data member of type size_type: memory footprint is more important than + // iterator without index. However such iterator is not supposed to be used + // in function parameters, to store handles through elements... + // We must use indices for that. + template < class DSC, bool Const > + class CC_iterator_with_index + { + typedef typename DSC::iterator iterator; + typedef CC_iterator_with_index Self; + + friend class CC_iterator_with_index; + friend class CC_iterator_with_index; + + public: + typedef typename DSC::value_type value_type; + typedef typename DSC::size_type size_type; + typedef typename DSC::difference_type difference_type; + typedef typename boost::mpl::if_c< Const, const value_type*, + value_type*>::type pointer; + typedef typename boost::mpl::if_c< Const, const value_type&, + value_type&>::type reference; + typedef std::bidirectional_iterator_tag iterator_category; + + typedef typename boost::mpl::if_c< Const, const DSC*, DSC*>::type + cc_pointer; + + // the initialization with NULL is required by our Handle concept. + CC_iterator_with_index() : m_ptr_to_cc(NULL), + m_index(0) + {} + + // Either a harmless copy-ctor, + // or a conversion from iterator to const_iterator. + CC_iterator_with_index (const iterator &it) : m_ptr_to_cc(it.m_ptr_to_cc), + m_index(it.m_index) + {} + + // Same for assignment operator (otherwise MipsPro warns) + CC_iterator_with_index & operator= (const iterator &it) + { + m_ptr_to_cc = it.m_ptr_to_cc; + m_index = it.m_index; + return *this; + } + + // Construction from NULL + CC_iterator_with_index (Nullptr_t CGAL_assertion_code(n)) : + m_ptr_to_cc(NULL), + m_index(0) + { CGAL_assertion (n == NULL); } + + operator size_type() const + { return m_index; } + + size_type get_current() const + { return m_index; } + + protected: + void set_current(size_type dh) + { m_index = dh; } + + protected: + + // Only Compact_container should access these constructors. + //template + friend class Compact_container_with_index; + + //template + friend class Compact_container_with_index_2; + + cc_pointer m_ptr_to_cc; + size_type m_index; + + // For begin() + CC_iterator_with_index(cc_pointer ptr, int, int) : m_ptr_to_cc(ptr), + m_index(0) + { + increment(); // To jump over index=0 which is equivalent to null + } + + // Construction from raw pointer and for end(). + CC_iterator_with_index(cc_pointer ptr, size_type index) : m_ptr_to_cc(ptr), + m_index(index) + {} + + // NB : in case empty container, begin == end == NULL. + void increment() + { + // It's either pointing to end(), or valid. + CGAL_assertion_msg(m_ptr_to_cc != NULL, + "Incrementing a singular iterator or an empty container iterator ?"); + CGAL_assertion_msg(m_index < m_ptr_to_cc->capacity_, + "Incrementing end() ?"); + + // If it's not end(), then it's valid, we can do ++. + do + { + ++m_index; + } + while ( m_index < m_ptr_to_cc->capacity_ && + (m_ptr_to_cc->type(m_index) != DSC::USED) ); + } + + void decrement() + { + // It's either pointing to end(), or valid. + CGAL_assertion_msg(m_ptr_to_cc != NULL, + "Decrementing a singular iterator or an empty container iterator ?"); + CGAL_assertion_msg(m_index>0, "Decrementing begin() ?"); + + CGAL_assertion(m_ptr_to_cc->type(0)==DSC::USED); + + // If it's not begin(), then it's valid, we can do --. + do + { + --m_index; + } + while ( m_ptr_to_cc->type(m_index) != DSC::USED); + } + + public: + + Self & operator++() + { increment(); return *this; } + + Self & operator--() + { decrement(); return *this; } + + Self operator++(int) { Self tmp(*this); ++(*this); return tmp; } + Self operator--(int) { Self tmp(*this); --(*this); return tmp; } + + reference operator*() const { return ((*m_ptr_to_cc)[m_index]); } + + pointer operator->() const { return &((*m_ptr_to_cc)[m_index]); } + + // Can itself be used for bit-squatting. + void * for_compact_container() const { return (m_ptr_to_cc); } + void * & for_compact_container() { return (m_ptr_to_cc); } + + size_type for_compact_container_with_index() const + { return (m_index); } + size_type & for_compact_container_with_index() + { return (m_index); } + + template + friend bool operator==(const CC_iterator_with_index&, + const CC_iterator_with_index&); + + template + friend bool operator!=(const CC_iterator_with_index&, + const CC_iterator_with_index&); + }; + + template < class DSC, bool Const1, bool Const2 > + inline + bool operator==(const CC_iterator_with_index &rhs, + const CC_iterator_with_index &lhs) + { + return rhs.m_ptr_to_cc == lhs.m_ptr_to_cc && + rhs.m_index == lhs.m_index; + } + + template < class DSC, bool Const1, bool Const2 > + inline + bool operator!=(const CC_iterator_with_index &rhs, + const CC_iterator_with_index &lhs) + { + return rhs.m_ptr_to_cc != lhs.m_ptr_to_cc || + rhs.m_index != lhs.m_index; + } + + // Comparisons with NULL are part of CGAL's Handle concept... + /* template < class DSC, bool Const > + inline + bool operator==(const CC_iterator_with_index &rhs, + Nullptr_t CGAL_assertion_code(n)) + { + CGAL_assertion( n == NULL); + return rhs.m_index == 0; + } + + template < class DSC, bool Const > + inline + bool operator!=(const CC_iterator_with_index &rhs, + Nullptr_t CGAL_assertion_code(n)) + { + CGAL_assertion( n == NULL); + return rhs.m_index != 0; + }*/ + +} // namespace internal + +} //namespace CGAL + +#endif // CGAL_COMPACT_CONTAINER_WITH_INDEX_H + diff --git a/Combinatorial_map/include/CGAL/Compact_container_with_index_2.h b/Combinatorial_map/include/CGAL/Compact_container_with_index_2.h new file mode 100644 index 00000000000..d38934e1666 --- /dev/null +++ b/Combinatorial_map/include/CGAL/Compact_container_with_index_2.h @@ -0,0 +1,654 @@ +// Copyright (c) 2003,2004,2007-2010 INRIA Sophia-Antipolis (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org); you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 3 of the License, +// or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// +// Author(s) : Sylvain Pion + +#ifndef CGAL_COMPACT_CONTAINER_WITH_INDEX_2_H +#define CGAL_COMPACT_CONTAINER_WITH_INDEX_2_H + +#include + +// An STL like container with the following properties : +// - to achieve compactness, it requires access to a pointer stored in T, +// specified by a traits. This pointer is supposed to be 4 bytes aligned +// when the object is alive, otherwise, the container uses the 2 least +// significant bits to store information in the pointer. +// - Ts are allocated in arrays of increasing size, which are linked together +// by their first and last element. +// - the iterator looks at the famous 2 bits to know if it has to deal with +// a free/used/boundary element. + +// TODO : +// - Add .resize() (and proper copy of capacity_). +// - Add preconditions in input that real pointers need to have clean bits. +// Also for the allocated memory alignment, and sizeof(). +// - Do a benchmark before/after. +// - Check the end result with Valgrind. +// - The bit squatting mechanism will be reused for the conflict flag, maybe +// it could be put out of the class. + +// TODO low priority : +// - rebind<> the allocator +// - Exception safety guarantees +// - Thread safety guarantees +// - std requirements on iterators says all defined operations are constant +// time amortized (it's not true here, maybe it could be with some work...) +// - all this is expected especially when there are not so many free objects +// compared to the allocated elements. +// - Should block_size be selectable/hintable by .reserve() ? +// - would be nice to have a temporary_free_list (still active elements, but +// which are going to be freed soon). Probably it prevents compactness. +// - 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 ?). Actually it doesn't have to +// be stuck to a particular DS, because for a list it's useful too... +// - Currently, end() can be invalidated on insert() if a new block is added. +// It would be nice to fix this. We could insert the new block at the +// beginning instead ? That would drop the property that iterator order +// is preserved. Maybe it's not a problem if end() is not preserved, after +// all nothing is going to dereference it, it's just for comparing with +// end() that it can be a problem. +// Another way would be to have end() point to the end of an always +// empty block (containing no usable element), and insert new blocks just +// before this one. +// Instead of having the blocks linked between them, the start/end pointers +// could point back to the container, so that we can do more interesting +// things (e.g. freeing empty blocks automatically) ? + +namespace CGAL { + +template < class T, class Allocator_, class Increment_policy > +class Compact_container_with_index_2 +{ + typedef Allocator_ Al; + typedef Increment_policy Incr_policy; + typedef typename Default::Get< Al, CGAL_ALLOCATOR(T) >::type Allocator; + typedef Compact_container_with_index_2 Self; + typedef Compact_container_with_index_traits Traits; +public: + typedef T value_type; + typedef Allocator allocator_type; + typedef typename Allocator::reference reference; + typedef typename Allocator::const_reference const_reference; + typedef typename Allocator::pointer pointer; + typedef typename Allocator::const_pointer const_pointer; + typedef typename Allocator::size_type size_type; + typedef typename Allocator::difference_type difference_type; + typedef internal::CC_iterator_with_index iterator; + typedef internal::CC_iterator_with_index const_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + friend class internal::CC_iterator_with_index; + friend class internal::CC_iterator_with_index; + + template + friend struct Addition_size_policy; + template friend struct Constant_size_policy_for_cc_with_size; + + explicit Compact_container_with_index_2(const Allocator &a = Allocator()) + : alloc(a) + { + init(); + } + + template < class InputIterator > + Compact_container_with_index_2(InputIterator first, InputIterator last, + const Allocator & a = Allocator()) + : alloc(a) + { + init(); + std::copy(first, last, CGAL::inserter(*this)); + } + + // The copy constructor and assignment operator preserve the iterator order + Compact_container_with_index_2(const Compact_container_with_index_2 &c) + : alloc(c.get_allocator()) + { + init(); + block_size = c.block_size; + std::copy(c.begin(), c.end(), CGAL::inserter(*this)); + } + + Compact_container_with_index_2 & + operator=(const Compact_container_with_index_2 &c) + { + if (&c != this) { + Self tmp(c); + swap(tmp); + } + return *this; + } + + ~Compact_container_with_index_2() + { + clear(); + } + + bool is_used(size_type i) const + { + return (type(i)==USED); + } + + const T& operator[] (size_type i) const + { + CGAL_assertion(all_items!=NULL && i=all_items && value < (all_items+capacity_)) + { + return (value-all_items); + } + return 0; + } + + iterator index_to(size_type value) { + return iterator(this, value); + } + const_iterator index_to(size_type value) const { + return const_iterator(this, value); + } + + // Boost.Intrusive interface + iterator iterator_to(reference value) { + return iterator(this, compute_index(&value)); + } + const_iterator iterator_to(const_reference value) const { + return const_iterator(this, compute_index(&value)); + } + + // Special insert methods that construct the objects in place + // (just forward the arguments to the constructor, to optimize a copy). +#ifndef CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES + template < typename... Args > + size_type emplace(const Args&... args) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(args...); + ++size_; + return ret; + } +#else + // inserts a default constructed item. + size_type emplace() + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(); + ++size_; + + return ret; + } + + template < typename T1 > + size_type + emplace(const T1 &t1) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1); + ++size_; + return ret; + } + + template < typename T1, typename T2 > + size_type + emplace(const T1 &t1, const T2 &t2) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1, t2); + ++size_; + return ret; + } + + template < typename T1, typename T2, typename T3 > + size_type + emplace(const T1 &t1, const T2 &t2, const T3 &t3) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1, t2, t3); + ++size_; + return ret; + } + + template < typename T1, typename T2, typename T3, typename T4 > + size_type + emplace(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1, t2, t3, t4); + ++size_; + return ret; + } + + template < typename T1, typename T2, typename T3, typename T4, typename T5 > + size_type + emplace(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, + const T5 &t5) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1, t2, t3, t4, t5); + ++size_; + return ret; + } + + template < typename T1, typename T2, typename T3, typename T4, + typename T5, typename T6 > + size_type + emplace(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, + const T5 &t5, const T6 &t6) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1, t2, t3, t4, t5, t6); + ++size_; + return ret; + } + + template < typename T1, typename T2, typename T3, typename T4, + typename T5, typename T6, typename T7 > + size_type + emplace(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, + const T5 &t5, const T6 &t6, const T7 &t7) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1, t2, t3, t4, t5, t6, t7); + ++size_; + return ret; + } + + template < typename T1, typename T2, typename T3, typename T4, + typename T5, typename T6, typename T7, typename T8 > + size_type + emplace(const T1 &t1, const T2 &t2, const T3 &t3, const T4 &t4, + const T5 &t5, const T6 &t6, const T7 &t7, const T8 &t8) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + new (&e) value_type(t1, t2, t3, t4, t5, t6, t7, t8); + ++size_; + return ret; + } +#endif // CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES + + size_type insert(const T &t) + { + if (free_list == 0) + allocate_new_block(); + + size_type ret = free_list; + T& e = operator[](free_list); + static_set_type(e, USED); + free_list = static_get_val(e); + alloc.construct(&e, t); + ++size_; + return ret; + } + + template < class InputIterator > + void insert(InputIterator first, InputIterator last) + { + for (; first != last; ++first) + insert(*first); + } + + template < class InputIterator > + void assign(InputIterator first, InputIterator last) + { + clear(); // erase(begin(), end()); // ? + insert(first, last); + } + + void erase(size_type x) + { + CGAL_precondition(type(x) == USED); + T& e = operator[](x); + alloc.destroy(&e); +#ifndef CGAL_NO_ASSERTIONS + std::memset(&e, 0, sizeof(T)); +#endif + put_on_free_list(x); + --size_; + } + + void erase(iterator first, iterator last) { + while (first != last) + erase(first++); + } + + void clear(); + + // Merge the content of d into *this. d gets cleared. + // The complexity is O(size(free list = capacity-size)). + void merge(Self &d); + + size_type size() const + { + CGAL_expensive_assertion(size_ == + (size_type) std::distance(begin(), end())); + return size_; + } + + size_type max_size() const + { + return alloc.max_size(); + } + + size_type capacity() const + { + return capacity_; + } + + // void resize(size_type sz, T c = T()); // TODO makes sense ??? + + bool empty() const + { + return size_ == 0; + } + + allocator_type get_allocator() const + { + return alloc; + } + + // Returns whether the iterator "cit" is in the range [begin(), end()]. + // Complexity : O(#blocks) = O(sqrt(capacity())). + // This function is mostly useful for purposes of efficient debugging at + // higher levels. + bool owns(const_iterator cit) const + { + if (cit == end()) + return true; + + const_pointer c = &*cit; + + if (c >=all_items && c < (all_items+capacity_)) + { + return type(cit) == USED; + } + return false; + } + + bool owns_dereferencable(const_iterator cit) const + { + return cit != end() && owns(cit); + } + + /** Reserve method to ensure that the capacity of the Compact_container be + * greater or equal than a given value n. + */ + void reserve(size_type n) + { + if ( capacity_>=n ) return; +// TODO + /* size_type lastblock = all_items.size(); + + while ( capacity_= 0; --i, --index) + put_on_free_list(index); + } + while ( curblock>lastblock );*/ + } + +private: + + void allocate_new_block(); + + // Definition of the bit squatting : + // ================================= + // e is composed of a size_t and the big 1 bit. + // Here is the meaning of each of the 4 cases. + // + // value of the last bit as "Type" : 0 == reserved element; 1==free element. + // When an element is free, the other bits represent the index of the + // next free element. + + enum Type { USED = 0, FREE = 1 }; + + static const int nbbits_size_type_m1 = sizeof(size_type)*8 - 1; + static const size_type mask_type = ((size_type)-1)-(((size_type)-1)/2); + + // Get the type of the pointee. + static Type static_type(const T& e) + // TODO check if this is ok for little and big endian + { return (Type) ((Traits::size_t(e) & mask_type)>>(nbbits_size_type_m1)); } + + Type type(size_type e) const + { return static_type(operator[](e)); } + + // Sets the pointer part and the type of the pointee. + static void static_set_type(T& e, Type t) + { + // This out of range compare is always true and causes lots of + // unnecessary warnings. + // CGAL_precondition(0 <= t && t < 2); + Traits::size_t(e) &= ( ~mask_type | ( ((size_type)t) <<(nbbits_size_type_m1) ) ); + } + + // get the value of the element (removing the two bits) + static size_type static_get_val(const T& e) + { return (Traits::size_t(e) & ~mask_type); } + + size_type get_val(size_type e) const + { return static_get_val(operator[](e)); } + + // set the value of the element and its type + static void static_set_val(T& e, size_type v, Type t) + { Traits::size_t(e)=v | ( ((size_type)t) <<(nbbits_size_type_m1)); } + + void set_val(size_type e, size_type v, Type t) + { static_set_val(operator[](e), v, t); } + + void put_on_free_list(size_type x) + { + set_val(x, free_list, FREE); + free_list = x; + } + + void init() + { + block_size = Incr_policy::first_block_size; + capacity_ = 0; + size_ = 0; + free_list = 0; + all_items = NULL; + + emplace(); + size_=0; + } + + allocator_type alloc; + size_type capacity_; + size_type size_; + size_type block_size; + size_type free_list; + pointer all_items; +}; + +/*template < class T, class Allocator, class Increment_policy > +void Compact_container_with_index::merge(Self &d) +{ + CGAL_precondition(&d != this); + + // Allocators must be "compatible" : + CGAL_precondition(get_allocator() == d.get_allocator()); + + // Concatenate the free_lists. + if (free_list == 0) { + free_list = d.free_list; + } else if (d.free_list != 0) { + size_type e = free_list; + while (get_val(e) != 0) + e = get_val(e); + set_val(e, d.free_list, FREE); + } + // Add the sizes. + size_ += d.size_; + // Add the capacities. + capacity_ += d.capacity_; + // It seems reasonnable to take the max of the block sizes. + block_size = (std::max)(block_size, d.block_size); + // Clear d. + d.init(); +}*/ + +template < class T, class Allocator, class Increment_policy > +void Compact_container_with_index_2::clear() +{ + for (size_type i=0; i +void Compact_container_with_index_2::allocate_new_block() +{ + size_type oldcapacity=capacity_; + capacity_ += block_size; + + if ( all_items==NULL ) + all_items = (pointer)malloc(capacity_*sizeof(T)); + else + all_items = (pointer)realloc(all_items, capacity_*sizeof(T)); + + // We mark them free in reverse order, so that the insertion order + // will correspond to the iterator order... + for (size_type index = capacity_-1; index>oldcapacity; --index) + put_on_free_list(index); + + put_on_free_list(oldcapacity); + + // Increase the block_size for the next time. + Increment_policy::increase_size(*this); +} + +} //namespace CGAL + +#endif // CGAL_COMPACT_CONTAINER_WITH_INDEX_2_H