/****************************************************************************** * Author: Pete Goodliffe * * ---------------------------------------------------------------------------- * Copyright 2002 Pete Goodliffe All rights reserved. * * ---------------------------------------------------------------------------- * Purpose: STL-style circular buffer * * Formatting changed by jabe@gempa.de *****************************************************************************/ #ifndef CIRCULAR_BUFFER_H #define CIRCULAR_BUFFER_H #include #include #include #include /****************************************************************************** * Iterators *****************************************************************************/ /** * Iterator type for the circular_buffer class. * * This one template class provides all variants of forward/reverse * const/non const iterators through plentiful template magic. * * You don't need to instantiate it directly, use the good public functions * availble in circular_buffer. */ template //+ const for const iter class circular_buffer_iterator { public: typedef circular_buffer_iterator self_type; typedef T cbuf_type; typedef std::random_access_iterator_tag iterator_category; typedef typename cbuf_type::value_type value_type; typedef typename cbuf_type::size_type size_type; typedef typename cbuf_type::pointer pointer; typedef typename cbuf_type::const_pointer const_pointer; typedef typename cbuf_type::reference reference; typedef typename cbuf_type::const_reference const_reference; typedef typename cbuf_type::difference_type difference_type; circular_buffer_iterator(cbuf_type *b, size_type p) : buf_(b), pos_(p) {} // Converting a non-const iterator to a const iterator circular_buffer_iterator(const circular_buffer_iterator &other) : buf_(other.buf_), pos_(other.pos_) {} friend class circular_buffer_iterator ; // Use compiler generated copy ctor, copy assignment operator and dtor elem_type &operator*() { return (*buf_)[pos_]; } elem_type *operator->() { return &(operator*()); } self_type &operator++() { pos_ += 1; return *this; } self_type operator++(int) { self_type tmp(*this); ++(*this); return tmp; } self_type &operator--() { pos_ -= 1; return *this; } self_type operator--(int) { self_type tmp(*this); --(*this); return tmp; } self_type operator+(difference_type n) const { self_type tmp(*this); tmp.pos_ += n; return tmp; } self_type &operator+=(difference_type n) { pos_ += n; return *this; } self_type operator-(difference_type n) const { self_type tmp(*this); tmp.pos_ -= n; return tmp; } self_type &operator-=(difference_type n) { pos_ -= n; return *this; } difference_type operator-(const self_type &c) const { return pos_ - c.pos_; } bool operator==(const self_type &other) const { return pos_ == other.pos_ && buf_ == other.buf_; } bool operator!=(const self_type &other) const { return pos_ != other.pos_ && buf_ == other.buf_; } bool operator>(const self_type &other) const { return pos_ > other.pos_; } bool operator>=(const self_type &other) const { return pos_ >= other.pos_; } bool operator<(const self_type &other) const { return pos_ < other.pos_; } bool operator<=(const self_type &other) const { return pos_ <= other.pos_; } private: cbuf_type *buf_; size_type pos_; }; template circular_buffer_iterator_t operator+( const typename circular_buffer_iterator_t::difference_type &a, const circular_buffer_iterator_t &b) { return circular_buffer_iterator_t(a) + b; } template circular_buffer_iterator_t operator-( const typename circular_buffer_iterator_t::difference_type &a, const circular_buffer_iterator_t &b) { return circular_buffer_iterator_t(a) - b; } /****************************************************************************** * circular_buffer *****************************************************************************/ /** * This class provides a circular buffer in the STL style. * * You can add data to the end using the @ref push_back function, read data * using @ref front() and remove data using @ref pop_front(). * * The class also provides random access through the @ref operator[]() * function and its random access iterator. Subscripting the array with * an invalid (out of range) index number leads to undefined results, both * for reading and writing. * * This class template accepts three template parameters: *
  • T The type of object contained *
  • always_accept_data_when_full Determines the behaviour of * @ref push_back when the buffer is full. * Set to true new data is always added, the * old "end" data is thrown away. * Set to false, the new data is not added. * No error is returned neither is an * exception raised. *
  • Alloc Allocator type to use (in line with other * STL containers). * * @short STL style circule buffer * @author Pete Goodliffe * @version 1.00 */ template > class circular_buffer { public: enum { version_major = 1, version_minor = 0 }; // Typedefs typedef circular_buffer self_type; typedef Alloc allocator_type; typedef typename Alloc::value_type value_type; typedef typename Alloc::pointer pointer; typedef typename Alloc::const_pointer const_pointer; typedef typename Alloc::reference reference; typedef typename Alloc::const_reference const_reference; typedef typename Alloc::size_type size_type; typedef typename Alloc::difference_type difference_type; typedef circular_buffer_iterator iterator; typedef circular_buffer_iterator const_iterator; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; // Lifetime enum { default_capacity = 100 }; explicit circular_buffer(size_type capacity = default_capacity) : array_(alloc_.allocate(capacity)) , array_size_(capacity) , head_(1) , tail_(0) , contents_size_(0) {} circular_buffer(const circular_buffer &other) : array_(alloc_.allocate(other.array_size_)) , array_size_(other.array_size_), head_(other.head_) , tail_(other.tail_), contents_size_(other.contents_size_) { try { assign_into(other.begin(), other.end()); } catch ( ... ) { destroy_all_elements(); alloc_.deallocate(array_, array_size_); throw; } } template circular_buffer(InputIterator from, InputIterator to) : array_(alloc_.allocate(1)), array_size_(1) , head_(1), tail_(0), contents_size_(0) { circular_buffer tmp; tmp.assign_into_reserving(from, to); swap(tmp); } ~circular_buffer() { destroy_all_elements(); alloc_.deallocate(array_, array_size_); } circular_buffer &operator=(const self_type &other) { circular_buffer tmp(other); swap(tmp); return *this; } void swap(circular_buffer &other) { std::swap(array_, other.array_); std::swap(array_size_, other.array_size_); std::swap(head_, other.head_); std::swap(tail_, other.tail_); std::swap(contents_size_, other.contents_size_); } allocator_type get_allocator() const { return alloc_; } // Iterators iterator begin() { return iterator(this, 0); } iterator end() { return iterator(this, size()); } const_iterator begin() const { return const_iterator(this, 0); } const_iterator end() const { return const_iterator(this, size()); } 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()); } // Size size_type size() const { return contents_size_; } size_type capacity() const { return array_size_; } bool empty() const { return !contents_size_; } size_type max_size() const { return alloc_.max_size(); } void reserve(size_type new_size) { if ( capacity() < new_size ) { circular_buffer tmp(new_size); tmp.assign_into(begin(), end()); swap(tmp); } } // Accessing reference front() { return array_[head_]; } reference back() { return array_[tail_]; } const_reference front() const { return array_[head_]; } const_reference back() const { return array_[tail_]; } void push_back(const value_type &item) { size_type next = next_tail(); if ( contents_size_ == array_size_ ) { if ( always_accept_data_when_full ) { array_[next] = item; increment_head(); } } else { alloc_.construct(array_ + next, item); } increment_tail(); } void pop_front() { size_type destroy_pos = head_; increment_head(); alloc_.destroy(array_ + destroy_pos); } void clear() { for ( size_type n = 0; n < contents_size_; ++n ) { alloc_.destroy(array_ + index_to_subscript(n)); } head_ = 1; tail_ = contents_size_ = 0; } reference operator[](size_type n) { return at_unchecked(n); } const_reference operator[](size_type n) const { return at_unchecked(n); } reference at(size_type n) { return at_checked(n); } const_reference at(size_type n) const { return at_checked(n); } private: reference at_unchecked(size_type index) const { return array_[index_to_subscript(index)]; } reference at_checked(size_type index) const { if ( index >= contents_size_ ) { throw std::out_of_range("index out of bounds"); } return at_unchecked(index); } // Rounds an unbounded to an index into array_ size_type normalise(size_type n) const { return n % array_size_; } // Converts external index to an array subscript size_type index_to_subscript(size_type index) const { return normalise(index + head_); } void increment_tail() { ++contents_size_; tail_ = next_tail(); } size_type next_tail() { return (tail_ + 1 == array_size_) ? 0 : tail_ + 1; } void increment_head() { // precondition: !empty() ++head_; --contents_size_; if ( head_ == array_size_ ) head_ = 0; } template void assign_into(f_iter from, f_iter to) { if ( contents_size_ ) clear(); while ( from != to ) { push_back(*from); ++from; } } template void assign_into_reserving(f_iter from, f_iter to) { if ( contents_size_ ) clear(); while ( from != to ) { if ( contents_size_ == array_size_ ) { reserve(static_cast(array_size_ * 1.5)); } push_back(*from); ++from; } } void destroy_all_elements() { for ( size_type n = 0; n < contents_size_; ++n ) { alloc_.destroy(array_ + index_to_subscript(n)); } } allocator_type alloc_; value_type *array_; size_type array_size_; size_type head_; size_type tail_; size_type contents_size_; }; template bool operator==(const circular_buffer &a, const circular_buffer &b) { return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); } template bool operator!=(const circular_buffer &a, const circular_buffer &b) { return a.size() != b.size() || !std::equal(a.begin(), a.end(), b.begin()); } template bool operator<(const circular_buffer &a, const circular_buffer &b) { return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); } #endif