/////////////////////////////////////////////////////////////////////////////// // sequence_stack.hpp // // Copyright 2008 Eric Niebler. Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) #ifndef BOOST_XPRESSIVE_DETAIL_SEQUENCE_STACK_HPP_EAN_10_04_2005 #define BOOST_XPRESSIVE_DETAIL_SEQUENCE_STACK_HPP_EAN_10_04_2005 // MS compatible compilers support #pragma once #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma once # pragma warning(push) # pragma warning(disable : 4127) // conditional expression constant #endif #include #include namespace boost { namespace xpressive { namespace detail { struct fill_t {} const fill = {}; ////////////////////////////////////////////////////////////////////////// // sequence_stack // // For storing a stack of sequences of type T, where each sequence // is guaranteed to be stored in contiguous memory. template struct sequence_stack { private: static T *allocate(std::size_t size, T const &t) { std::size_t i = 0; T *p = (T *)::operator new(size * sizeof(T)); try { for(; i < size; ++i) ::new((void *)(p+i)) T(t); } catch(...) { deallocate(p, i); throw; } return p; } static void deallocate(T *p, std::size_t i) { while(i-- > 0) (p+i)->~T(); ::operator delete(p); } struct chunk { chunk(std::size_t size, T const &t, std::size_t count, chunk *back, chunk *next) : begin_(allocate(size, t)) , curr_(begin_ + count) , end_(begin_ + size) , back_(back) , next_(next) { if(this->back_) this->back_->next_ = this; if(this->next_) this->next_->back_ = this; } ~chunk() { deallocate(this->begin_, this->size()); } std::size_t size() const { return static_cast(this->end_ - this->begin_); } T *const begin_, *curr_, *const end_; chunk *back_, *next_; private: chunk &operator =(chunk const &); }; chunk *current_chunk_; // Cache these for faster access T *begin_; T *curr_; T *end_; T *grow_(std::size_t count, T const &t) { if(this->current_chunk_) { // write the cached value of current into the expr. // OK to do this even if later statements throw. this->current_chunk_->curr_ = this->curr_; // Do we have a expr with enough available memory already? if(this->current_chunk_->next_ && count <= this->current_chunk_->next_->size()) { this->current_chunk_ = this->current_chunk_->next_; this->curr_ = this->current_chunk_->curr_ = this->current_chunk_->begin_ + count; this->end_ = this->current_chunk_->end_; this->begin_ = this->current_chunk_->begin_; std::fill_n(this->begin_, count, t); return this->begin_; } // grow exponentially std::size_t new_size = (std::max)(count, static_cast(this->current_chunk_->size() * 1.5)); // Create a new expr and insert it into the list this->current_chunk_ = new chunk(new_size, t, count, this->current_chunk_, this->current_chunk_->next_); } else { // first chunk is 256 std::size_t new_size = (std::max)(count, static_cast(256U)); // Create a new expr and insert it into the list this->current_chunk_ = new chunk(new_size, t, count, 0, 0); } this->begin_ = this->current_chunk_->begin_; this->curr_ = this->current_chunk_->curr_; this->end_ = this->current_chunk_->end_; return this->begin_; } void unwind_chunk_() { // write the cached value of curr_ into current_chunk_ this->current_chunk_->curr_ = this->begin_; // make the previous chunk the current this->current_chunk_ = this->current_chunk_->back_; // update the cache this->begin_ = this->current_chunk_->begin_; this->curr_ = this->current_chunk_->curr_; this->end_ = this->current_chunk_->end_; } bool in_current_chunk(T *ptr) const { return !std::less()(ptr, this->begin_) && std::less()(ptr, this->end_); } public: sequence_stack() : current_chunk_(0) , begin_(0) , curr_(0) , end_(0) { } ~sequence_stack() { this->clear(); } // walk to the front of the linked list void unwind() { if(this->current_chunk_) { while(this->current_chunk_->back_) { this->current_chunk_->curr_ = this->current_chunk_->begin_; this->current_chunk_ = this->current_chunk_->back_; } this->begin_ = this->curr_ = this->current_chunk_->curr_ = this->current_chunk_->begin_; this->end_ = this->current_chunk_->end_; } } void clear() { // walk to the front of the list this->unwind(); // delete the list for(chunk *next; this->current_chunk_; this->current_chunk_ = next) { next = this->current_chunk_->next_; delete this->current_chunk_; } this->begin_ = this->curr_ = this->end_ = 0; } T *push_sequence(std::size_t count, T const &t) { // This is the ptr to return T *ptr = this->curr_; // Advance the high-water mark this->curr_ += count; // Check to see if we have overflowed this buffer if(std::less()(this->end_, this->curr_)) { // oops, back this out. this->curr_ = ptr; // allocate a new block and return a ptr to the new memory return this->grow_(count, t); } return ptr; } T *push_sequence(std::size_t count, T const &t, fill_t) { T *ptr = this->push_sequence(count, t); std::fill_n(ptr, count, t); return ptr; } void unwind_to(T *ptr) { while(!this->in_current_chunk(ptr)) { // completely unwind the current chunk, move to the previous chunk this->unwind_chunk_(); } this->current_chunk_->curr_ = this->curr_ = ptr; } // shrink-to-fit: remove any unused nodes in the chain void conserve() { if(this->current_chunk_) { for(chunk *next; this->current_chunk_->next_; this->current_chunk_->next_ = next) { next = this->current_chunk_->next_->next_; delete this->current_chunk_->next_; } } } }; }}} // namespace boost::xpressive::detail #if defined(_MSC_VER) && (_MSC_VER >= 1020) # pragma warning(pop) #endif #endif