You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
259 lines
7.0 KiB
259 lines
7.0 KiB
/////////////////////////////////////////////////////////////////////////////// |
|
// 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 <algorithm> |
|
#include <functional> |
|
|
|
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<typename T> |
|
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<std::size_t>(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<std::size_t>(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<std::size_t>(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<void*>()(ptr, this->begin_) && std::less<void*>()(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<void*>()(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
|
|
|