1 /////////////////////////////////////////////////////////////////////////////// 2 // sequence_stack.hpp 3 // 4 // Copyright 2008 Eric Niebler. Distributed under the Boost 5 // Software License, Version 1.0. (See accompanying file 6 // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 8 #ifndef BOOST_XPRESSIVE_DETAIL_SEQUENCE_STACK_HPP_EAN_10_04_2005 9 #define BOOST_XPRESSIVE_DETAIL_SEQUENCE_STACK_HPP_EAN_10_04_2005 10 11 // MS compatible compilers support #pragma once 12 #if defined(_MSC_VER) 13 # pragma once 14 # pragma warning(push) 15 # pragma warning(disable : 4127) // conditional expression constant 16 #endif 17 18 #include <cstddef> 19 #include <algorithm> 20 #include <functional> 21 22 namespace boost { namespace xpressive { namespace detail 23 { 24 25 struct fill_t {} const fill = {}; 26 27 ////////////////////////////////////////////////////////////////////////// 28 // sequence_stack 29 // 30 // For storing a stack of sequences of type T, where each sequence 31 // is guaranteed to be stored in contiguous memory. 32 template<typename T> 33 struct sequence_stack 34 { 35 struct allocate_guard_t; 36 friend struct allocate_guard_t; 37 struct allocate_guard_t 38 { 39 std::size_t i; 40 T *p; 41 bool dismissed; ~allocate_guard_tboost::xpressive::detail::sequence_stack::allocate_guard_t42 ~allocate_guard_t() 43 { 44 if(!this->dismissed) 45 sequence_stack::deallocate(this->p, this->i); 46 } 47 }; 48 private: allocateboost::xpressive::detail::sequence_stack49 static T *allocate(std::size_t size, T const &t) 50 { 51 allocate_guard_t guard = {0, (T *)::operator new(size * sizeof(T)), false}; 52 53 for(; guard.i < size; ++guard.i) 54 ::new((void *)(guard.p + guard.i)) T(t); 55 guard.dismissed = true; 56 57 return guard.p; 58 } 59 deallocateboost::xpressive::detail::sequence_stack60 static void deallocate(T *p, std::size_t i) 61 { 62 while(i-- > 0) 63 (p+i)->~T(); 64 ::operator delete(p); 65 } 66 67 struct chunk 68 { chunkboost::xpressive::detail::sequence_stack::chunk69 chunk(std::size_t size, T const &t, std::size_t count, chunk *back, chunk *next) 70 : begin_(allocate(size, t)) 71 , curr_(begin_ + count) 72 , end_(begin_ + size) 73 , back_(back) 74 , next_(next) 75 { 76 if(this->back_) 77 this->back_->next_ = this; 78 if(this->next_) 79 this->next_->back_ = this; 80 } 81 ~chunkboost::xpressive::detail::sequence_stack::chunk82 ~chunk() 83 { 84 deallocate(this->begin_, this->size()); 85 } 86 sizeboost::xpressive::detail::sequence_stack::chunk87 std::size_t size() const 88 { 89 return static_cast<std::size_t>(this->end_ - this->begin_); 90 } 91 92 T *const begin_, *curr_, *const end_; 93 chunk *back_, *next_; 94 95 private: 96 chunk &operator =(chunk const &); 97 }; 98 99 chunk *current_chunk_; 100 101 // Cache these for faster access 102 T *begin_; 103 T *curr_; 104 T *end_; 105 grow_boost::xpressive::detail::sequence_stack106 T *grow_(std::size_t count, T const &t) 107 { 108 if(this->current_chunk_) 109 { 110 // write the cached value of current into the expr. 111 // OK to do this even if later statements throw. 112 this->current_chunk_->curr_ = this->curr_; 113 114 // Do we have a expr with enough available memory already? 115 if(this->current_chunk_->next_ && count <= this->current_chunk_->next_->size()) 116 { 117 this->current_chunk_ = this->current_chunk_->next_; 118 this->curr_ = this->current_chunk_->curr_ = this->current_chunk_->begin_ + count; 119 this->end_ = this->current_chunk_->end_; 120 this->begin_ = this->current_chunk_->begin_; 121 std::fill_n(this->begin_, count, t); 122 return this->begin_; 123 } 124 125 // grow exponentially 126 std::size_t new_size = (std::max)( 127 count 128 , static_cast<std::size_t>(static_cast<double>(this->current_chunk_->size()) * 1.5) 129 ); 130 131 // Create a new expr and insert it into the list 132 this->current_chunk_ = new chunk(new_size, t, count, this->current_chunk_, this->current_chunk_->next_); 133 } 134 else 135 { 136 // first chunk is 256 137 std::size_t new_size = (std::max)(count, static_cast<std::size_t>(256U)); 138 139 // Create a new expr and insert it into the list 140 this->current_chunk_ = new chunk(new_size, t, count, 0, 0); 141 } 142 143 this->begin_ = this->current_chunk_->begin_; 144 this->curr_ = this->current_chunk_->curr_; 145 this->end_ = this->current_chunk_->end_; 146 return this->begin_; 147 } 148 unwind_chunk_boost::xpressive::detail::sequence_stack149 void unwind_chunk_() 150 { 151 // write the cached value of curr_ into current_chunk_ 152 this->current_chunk_->curr_ = this->begin_; 153 // make the previous chunk the current 154 this->current_chunk_ = this->current_chunk_->back_; 155 156 // update the cache 157 this->begin_ = this->current_chunk_->begin_; 158 this->curr_ = this->current_chunk_->curr_; 159 this->end_ = this->current_chunk_->end_; 160 } 161 in_current_chunkboost::xpressive::detail::sequence_stack162 bool in_current_chunk(T *ptr) const 163 { 164 return !std::less<void*>()(ptr, this->begin_) && std::less<void*>()(ptr, this->end_); 165 } 166 167 public: sequence_stackboost::xpressive::detail::sequence_stack168 sequence_stack() 169 : current_chunk_(0) 170 , begin_(0) 171 , curr_(0) 172 , end_(0) 173 { 174 } 175 ~sequence_stackboost::xpressive::detail::sequence_stack176 ~sequence_stack() 177 { 178 this->clear(); 179 } 180 181 // walk to the front of the linked list unwindboost::xpressive::detail::sequence_stack182 void unwind() 183 { 184 if(this->current_chunk_) 185 { 186 while(this->current_chunk_->back_) 187 { 188 this->current_chunk_->curr_ = this->current_chunk_->begin_; 189 this->current_chunk_ = this->current_chunk_->back_; 190 } 191 192 this->begin_ = this->curr_ = this->current_chunk_->curr_ = this->current_chunk_->begin_; 193 this->end_ = this->current_chunk_->end_; 194 } 195 } 196 clearboost::xpressive::detail::sequence_stack197 void clear() 198 { 199 // walk to the front of the list 200 this->unwind(); 201 202 // delete the list 203 for(chunk *next; this->current_chunk_; this->current_chunk_ = next) 204 { 205 next = this->current_chunk_->next_; 206 delete this->current_chunk_; 207 } 208 209 this->begin_ = this->curr_ = this->end_ = 0; 210 } 211 push_sequenceboost::xpressive::detail::sequence_stack212 T *push_sequence(std::size_t count, T const &t) 213 { 214 // Check to see if we have overflowed this buffer 215 std::size_t size_left = static_cast< std::size_t >(this->end_ - this->curr_); 216 if (size_left < count) 217 { 218 // allocate a new block and return a ptr to the new memory 219 return this->grow_(count, t); 220 } 221 222 // This is the ptr to return 223 T *ptr = this->curr_; 224 225 // Advance the high-water mark 226 this->curr_ += count; 227 228 return ptr; 229 } 230 push_sequenceboost::xpressive::detail::sequence_stack231 T *push_sequence(std::size_t count, T const &t, fill_t) 232 { 233 T *ptr = this->push_sequence(count, t); 234 std::fill_n(ptr, count, t); 235 return ptr; 236 } 237 unwind_toboost::xpressive::detail::sequence_stack238 void unwind_to(T *ptr) 239 { 240 while(!this->in_current_chunk(ptr)) 241 { 242 // completely unwind the current chunk, move to the previous chunk 243 this->unwind_chunk_(); 244 } 245 this->current_chunk_->curr_ = this->curr_ = ptr; 246 } 247 248 // shrink-to-fit: remove any unused nodes in the chain conserveboost::xpressive::detail::sequence_stack249 void conserve() 250 { 251 if(this->current_chunk_) 252 { 253 for(chunk *next; this->current_chunk_->next_; this->current_chunk_->next_ = next) 254 { 255 next = this->current_chunk_->next_->next_; 256 delete this->current_chunk_->next_; 257 } 258 } 259 } 260 }; 261 262 }}} // namespace boost::xpressive::detail 263 264 #if defined(_MSC_VER) 265 # pragma warning(pop) 266 #endif 267 268 #endif 269