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 <algorithm>
19 #include <functional>
20 
21 namespace boost { namespace xpressive { namespace detail
22 {
23 
24 struct fill_t {} const fill = {};
25 
26 //////////////////////////////////////////////////////////////////////////
27 // sequence_stack
28 //
29 //   For storing a stack of sequences of type T, where each sequence
30 //   is guaranteed to be stored in contiguous memory.
31 template<typename T>
32 struct sequence_stack
33 {
34     struct allocate_guard_t;
35     friend struct allocate_guard_t;
36     struct allocate_guard_t
37     {
38         std::size_t i;
39         T *p;
40         bool dismissed;
~allocate_guard_tboost::xpressive::detail::sequence_stack::allocate_guard_t41         ~allocate_guard_t()
42         {
43             if(!this->dismissed)
44                 sequence_stack::deallocate(this->p, this->i);
45         }
46     };
47 private:
allocateboost::xpressive::detail::sequence_stack48     static T *allocate(std::size_t size, T const &t)
49     {
50         allocate_guard_t guard = {0, (T *)::operator new(size * sizeof(T)), false};
51 
52         for(; guard.i < size; ++guard.i)
53             ::new((void *)(guard.p + guard.i)) T(t);
54         guard.dismissed = true;
55 
56         return guard.p;
57     }
58 
deallocateboost::xpressive::detail::sequence_stack59     static void deallocate(T *p, std::size_t i)
60     {
61         while(i-- > 0)
62             (p+i)->~T();
63         ::operator delete(p);
64     }
65 
66     struct chunk
67     {
chunkboost::xpressive::detail::sequence_stack::chunk68         chunk(std::size_t size, T const &t, std::size_t count, chunk *back, chunk *next)
69           : begin_(allocate(size, t))
70           , curr_(begin_ + count)
71           , end_(begin_ + size)
72           , back_(back)
73           , next_(next)
74         {
75             if(this->back_)
76                 this->back_->next_ = this;
77             if(this->next_)
78                 this->next_->back_ = this;
79         }
80 
~chunkboost::xpressive::detail::sequence_stack::chunk81         ~chunk()
82         {
83             deallocate(this->begin_, this->size());
84         }
85 
sizeboost::xpressive::detail::sequence_stack::chunk86         std::size_t size() const
87         {
88             return static_cast<std::size_t>(this->end_ - this->begin_);
89         }
90 
91         T *const begin_, *curr_, *const end_;
92         chunk *back_, *next_;
93 
94     private:
95         chunk &operator =(chunk const &);
96     };
97 
98     chunk *current_chunk_;
99 
100     // Cache these for faster access
101     T *begin_;
102     T *curr_;
103     T *end_;
104 
grow_boost::xpressive::detail::sequence_stack105     T *grow_(std::size_t count, T const &t)
106     {
107         if(this->current_chunk_)
108         {
109             // write the cached value of current into the expr.
110             // OK to do this even if later statements throw.
111             this->current_chunk_->curr_ = this->curr_;
112 
113             // Do we have a expr with enough available memory already?
114             if(this->current_chunk_->next_ && count <= this->current_chunk_->next_->size())
115             {
116                 this->current_chunk_ = this->current_chunk_->next_;
117                 this->curr_ = this->current_chunk_->curr_ = this->current_chunk_->begin_ + count;
118                 this->end_ = this->current_chunk_->end_;
119                 this->begin_ = this->current_chunk_->begin_;
120                 std::fill_n(this->begin_, count, t);
121                 return this->begin_;
122             }
123 
124             // grow exponentially
125             std::size_t new_size = (std::max)(
126                 count
127               , static_cast<std::size_t>(static_cast<double>(this->current_chunk_->size()) * 1.5)
128             );
129 
130             // Create a new expr and insert it into the list
131             this->current_chunk_ = new chunk(new_size, t, count, this->current_chunk_, this->current_chunk_->next_);
132         }
133         else
134         {
135             // first chunk is 256
136             std::size_t new_size = (std::max)(count, static_cast<std::size_t>(256U));
137 
138             // Create a new expr and insert it into the list
139             this->current_chunk_ = new chunk(new_size, t, count, 0, 0);
140         }
141 
142         this->begin_ = this->current_chunk_->begin_;
143         this->curr_ = this->current_chunk_->curr_;
144         this->end_ = this->current_chunk_->end_;
145         return this->begin_;
146     }
147 
unwind_chunk_boost::xpressive::detail::sequence_stack148     void unwind_chunk_()
149     {
150         // write the cached value of curr_ into current_chunk_
151         this->current_chunk_->curr_ = this->begin_;
152         // make the previous chunk the current
153         this->current_chunk_ = this->current_chunk_->back_;
154 
155         // update the cache
156         this->begin_ = this->current_chunk_->begin_;
157         this->curr_ = this->current_chunk_->curr_;
158         this->end_ = this->current_chunk_->end_;
159     }
160 
in_current_chunkboost::xpressive::detail::sequence_stack161     bool in_current_chunk(T *ptr) const
162     {
163         return !std::less<void*>()(ptr, this->begin_) && std::less<void*>()(ptr, this->end_);
164     }
165 
166 public:
sequence_stackboost::xpressive::detail::sequence_stack167     sequence_stack()
168       : current_chunk_(0)
169       , begin_(0)
170       , curr_(0)
171       , end_(0)
172     {
173     }
174 
~sequence_stackboost::xpressive::detail::sequence_stack175     ~sequence_stack()
176     {
177         this->clear();
178     }
179 
180     // walk to the front of the linked list
unwindboost::xpressive::detail::sequence_stack181     void unwind()
182     {
183         if(this->current_chunk_)
184         {
185             while(this->current_chunk_->back_)
186             {
187                 this->current_chunk_->curr_ = this->current_chunk_->begin_;
188                 this->current_chunk_ = this->current_chunk_->back_;
189             }
190 
191             this->begin_ = this->curr_ = this->current_chunk_->curr_ = this->current_chunk_->begin_;
192             this->end_ = this->current_chunk_->end_;
193         }
194     }
195 
clearboost::xpressive::detail::sequence_stack196     void clear()
197     {
198         // walk to the front of the list
199         this->unwind();
200 
201         // delete the list
202         for(chunk *next; this->current_chunk_; this->current_chunk_ = next)
203         {
204             next = this->current_chunk_->next_;
205             delete this->current_chunk_;
206         }
207 
208         this->begin_ = this->curr_ = this->end_ = 0;
209     }
210 
push_sequenceboost::xpressive::detail::sequence_stack211     T *push_sequence(std::size_t count, T const &t)
212     {
213         // This is the ptr to return
214         T *ptr = this->curr_;
215 
216         // Advance the high-water mark
217         this->curr_ += count;
218 
219         // Check to see if we have overflowed this buffer
220         if(std::less<void*>()(this->end_, this->curr_))
221         {
222             // oops, back this out.
223             this->curr_ = ptr;
224 
225             // allocate a new block and return a ptr to the new memory
226             return this->grow_(count, t);
227         }
228 
229         return ptr;
230     }
231 
push_sequenceboost::xpressive::detail::sequence_stack232     T *push_sequence(std::size_t count, T const &t, fill_t)
233     {
234         T *ptr = this->push_sequence(count, t);
235         std::fill_n(ptr, count, t);
236         return ptr;
237     }
238 
unwind_toboost::xpressive::detail::sequence_stack239     void unwind_to(T *ptr)
240     {
241         while(!this->in_current_chunk(ptr))
242         {
243             // completely unwind the current chunk, move to the previous chunk
244             this->unwind_chunk_();
245         }
246         this->current_chunk_->curr_ = this->curr_ = ptr;
247     }
248 
249     // shrink-to-fit: remove any unused nodes in the chain
conserveboost::xpressive::detail::sequence_stack250     void conserve()
251     {
252         if(this->current_chunk_)
253         {
254             for(chunk *next; this->current_chunk_->next_; this->current_chunk_->next_ = next)
255             {
256                 next = this->current_chunk_->next_->next_;
257                 delete this->current_chunk_->next_;
258             }
259         }
260     }
261 };
262 
263 }}} // namespace boost::xpressive::detail
264 
265 #if defined(_MSC_VER)
266 # pragma warning(pop)
267 #endif
268 
269 #endif
270