// include/bistrector.hh // This file is part of libpbe; see http://svn.chezphil.org/libpbe/ // (C) 2008 Philip Endecott // 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 // 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #ifndef libpbe_bistrector_hh #define libpbe_bistrector_hh // bistrector: bidirectionally-stretchy-vector // ------------------------------------------- // // Provides a vector which stretches when new elements are accessed // using operator[]. It stretches in both directions, i.e. only enough // space for elements between the minimum and maximem indices is // allocated. In contrast, a strector allocates space between 0 and // the maximum index. // // This is actually implemented using a pair of strectors, one for elements // above the first one added and a reversed one for those below it. A // consequence is that if elements are erased the bistrector cannot shrink // beyond the first element added. In fact, erasing elements in general // is problematic because of this and is not implemented, except for clear(). // // The rules for invalidation etc. are the same as for strector. // // An additional optional constructor parameter allows you to specify a value // used for new elements when the vector is resized during operator[] and // when a beyond-the-end element is read as above. #include "strector.hh" #include namespace pbe { template < typename T, typename ALLOC=std::allocator > class bistrector: public std::vector { typedef strector strector_t; strector_t bottom; strector_t top; size_t mid_index; public: typedef T value_type; typedef T* pointer; typedef T& reference; typedef const T& const_reference; typedef size_t size_type; typedef ssize_t difference_type; // Most of the vector ctors don't make much sense and aren't implemented. bistrector(): mid_index(0) {} explicit bistrector(const T& default_t): bottom(default_t), top(default_t), mid_index(0) {} bistrector(const bistrector& other): bottom(other.bottom), top(other.top), mid_index(other.mid_index) {} template void advance_iterator(iter_t& i, bool& top_half, size_t n) { if (n>0) { if (top_half) { i += n; } else if (i-bottom.begin() >= n) { i -= n; } else { i = top.begin() + (i-bottom.begin()) + (n-1); top_half = true; } } else { if (!top_half) { i -= n; } else if (i-top.begin() >= -n) { i += n; } else { i = bottom.begin() + (i-top.begin()) + (-n-1); top_half = false; } } } class iterator: public boost::iterator_facade< iterator, T, std::random_access_iterator_tag > { friend class boost::iterator_core_access; friend class bistrector; const bistrector& b; bool top_half; typename strector_t::iterator i; iterator(const bistrector& b_, bool top_half_, typename strector_t::iterator i_): b(b_), top_half(top_half_), i(i_) {} T& dereference() { return *i; } bool equal(const iterator& other) { return i == other.i; } void advance(size_t n) { b.advance_iterator(i,top_half,n); } void increment() { advance(1); } void decrement() { advance(-1); } }; class const_iterator: public boost::iterator_facade< const_iterator, T, std::random_access_iterator_tag, const T& > { friend class boost::iterator_core_access; friend class bistrector; const bistrector& b; bool top_half; typename strector_t::const_iterator i; const_iterator(const bistrector& b_, bool top_half_, typename strector_t::const_iterator i_): b(b_), top_half(top_half_), i(i_) {} const T& dereference() { return *i; } bool equal(const const_iterator& other) { return i == other.i; } void advance(size_t n) { bistrector::advance_iterator(i,top_half,n); } void increment() { advance(1); } void decrement() { advance(-1); } }; iterator begin() { if (bottom.empty()) { return iterator(*this,true,top.begin()); } else { return iterator(*this,false,bottom.end()-1); } } iterator end() { return iterator(*this,true,top.end()); } const_iterator begin() const { if (bottom.empty()) { return const_iterator(*this,true,top.begin()); } else { return const_iterator(*this,false,bottom.end()-1); } } const_iterator end() const { return const_iterator(*this,true,top.end()); } size_type size() const { return top.size() + bottom.size(); } size_type max_size() const { return top.max_size() + bottom.max_size(); } bool empty() const { return top.empty() && bottom.empty(); } reference operator[](size_type n) { if (top.empty() && bottom.empty()) { mid_index = n; } if (n>=mid_index) { return top[n-mid_index]; } else { return bottom[mid_index-n-1]; } } const_reference operator[](size_type n) const { if (n>=mid_index) { return top[n-mid_index]; } else { return bottom[mid_index-n-1]; } } reference front() { if (bottom.empty()) { return top.front(); } else { return bottom.back(); } } const_reference front() const { if (bottom.empty()) { return top.front(); } else { return bottom.back(); } } reference back() { if (top.empty()) { return bottom.front(); } else { return top.back(); } } const_reference back() const { if (top.empty()) { return bottom.front(); } else { return top.back(); } } void push_back(const T& t) { top.push_back(t); } void clear() { top.clear(); bottom.clear(); } }; }; #endif