sequence_stack.hpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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. #ifndef BOOST_XPRESSIVE_DETAIL_SEQUENCE_STACK_HPP_EAN_10_04_2005
  8. #define BOOST_XPRESSIVE_DETAIL_SEQUENCE_STACK_HPP_EAN_10_04_2005
  9. // MS compatible compilers support #pragma once
  10. #if defined(_MSC_VER)
  11. # pragma once
  12. # pragma warning(push)
  13. # pragma warning(disable : 4127) // conditional expression constant
  14. #endif
  15. #include <cstddef>
  16. #include <algorithm>
  17. #include <functional>
  18. namespace boost { namespace xpressive { namespace detail
  19. {
  20. struct fill_t {} const fill = {};
  21. //////////////////////////////////////////////////////////////////////////
  22. // sequence_stack
  23. //
  24. // For storing a stack of sequences of type T, where each sequence
  25. // is guaranteed to be stored in contiguous memory.
  26. template<typename T>
  27. struct sequence_stack
  28. {
  29. struct allocate_guard_t;
  30. friend struct allocate_guard_t;
  31. struct allocate_guard_t
  32. {
  33. std::size_t i;
  34. T *p;
  35. bool dismissed;
  36. ~allocate_guard_t()
  37. {
  38. if(!this->dismissed)
  39. sequence_stack::deallocate(this->p, this->i);
  40. }
  41. };
  42. private:
  43. static T *allocate(std::size_t size, T const &t)
  44. {
  45. allocate_guard_t guard = {0, (T *)::operator new(size * sizeof(T)), false};
  46. for(; guard.i < size; ++guard.i)
  47. ::new((void *)(guard.p + guard.i)) T(t);
  48. guard.dismissed = true;
  49. return guard.p;
  50. }
  51. static void deallocate(T *p, std::size_t i)
  52. {
  53. while(i-- > 0)
  54. (p+i)->~T();
  55. ::operator delete(p);
  56. }
  57. struct chunk
  58. {
  59. chunk(std::size_t size, T const &t, std::size_t count, chunk *back, chunk *next)
  60. : begin_(allocate(size, t))
  61. , curr_(begin_ + count)
  62. , end_(begin_ + size)
  63. , back_(back)
  64. , next_(next)
  65. {
  66. if(this->back_)
  67. this->back_->next_ = this;
  68. if(this->next_)
  69. this->next_->back_ = this;
  70. }
  71. ~chunk()
  72. {
  73. deallocate(this->begin_, this->size());
  74. }
  75. std::size_t size() const
  76. {
  77. return static_cast<std::size_t>(this->end_ - this->begin_);
  78. }
  79. T *const begin_, *curr_, *const end_;
  80. chunk *back_, *next_;
  81. private:
  82. chunk &operator =(chunk const &);
  83. };
  84. chunk *current_chunk_;
  85. // Cache these for faster access
  86. T *begin_;
  87. T *curr_;
  88. T *end_;
  89. T *grow_(std::size_t count, T const &t)
  90. {
  91. if(this->current_chunk_)
  92. {
  93. // write the cached value of current into the expr.
  94. // OK to do this even if later statements throw.
  95. this->current_chunk_->curr_ = this->curr_;
  96. // Do we have a expr with enough available memory already?
  97. if(this->current_chunk_->next_ && count <= this->current_chunk_->next_->size())
  98. {
  99. this->current_chunk_ = this->current_chunk_->next_;
  100. this->curr_ = this->current_chunk_->curr_ = this->current_chunk_->begin_ + count;
  101. this->end_ = this->current_chunk_->end_;
  102. this->begin_ = this->current_chunk_->begin_;
  103. std::fill_n(this->begin_, count, t);
  104. return this->begin_;
  105. }
  106. // grow exponentially
  107. std::size_t new_size = (std::max)(
  108. count
  109. , static_cast<std::size_t>(static_cast<double>(this->current_chunk_->size()) * 1.5)
  110. );
  111. // Create a new expr and insert it into the list
  112. this->current_chunk_ = new chunk(new_size, t, count, this->current_chunk_, this->current_chunk_->next_);
  113. }
  114. else
  115. {
  116. // first chunk is 256
  117. std::size_t new_size = (std::max)(count, static_cast<std::size_t>(256U));
  118. // Create a new expr and insert it into the list
  119. this->current_chunk_ = new chunk(new_size, t, count, 0, 0);
  120. }
  121. this->begin_ = this->current_chunk_->begin_;
  122. this->curr_ = this->current_chunk_->curr_;
  123. this->end_ = this->current_chunk_->end_;
  124. return this->begin_;
  125. }
  126. void unwind_chunk_()
  127. {
  128. // write the cached value of curr_ into current_chunk_
  129. this->current_chunk_->curr_ = this->begin_;
  130. // make the previous chunk the current
  131. this->current_chunk_ = this->current_chunk_->back_;
  132. // update the cache
  133. this->begin_ = this->current_chunk_->begin_;
  134. this->curr_ = this->current_chunk_->curr_;
  135. this->end_ = this->current_chunk_->end_;
  136. }
  137. bool in_current_chunk(T *ptr) const
  138. {
  139. return !std::less<void*>()(ptr, this->begin_) && std::less<void*>()(ptr, this->end_);
  140. }
  141. public:
  142. sequence_stack()
  143. : current_chunk_(0)
  144. , begin_(0)
  145. , curr_(0)
  146. , end_(0)
  147. {
  148. }
  149. ~sequence_stack()
  150. {
  151. this->clear();
  152. }
  153. // walk to the front of the linked list
  154. void unwind()
  155. {
  156. if(this->current_chunk_)
  157. {
  158. while(this->current_chunk_->back_)
  159. {
  160. this->current_chunk_->curr_ = this->current_chunk_->begin_;
  161. this->current_chunk_ = this->current_chunk_->back_;
  162. }
  163. this->begin_ = this->curr_ = this->current_chunk_->curr_ = this->current_chunk_->begin_;
  164. this->end_ = this->current_chunk_->end_;
  165. }
  166. }
  167. void clear()
  168. {
  169. // walk to the front of the list
  170. this->unwind();
  171. // delete the list
  172. for(chunk *next; this->current_chunk_; this->current_chunk_ = next)
  173. {
  174. next = this->current_chunk_->next_;
  175. delete this->current_chunk_;
  176. }
  177. this->begin_ = this->curr_ = this->end_ = 0;
  178. }
  179. T *push_sequence(std::size_t count, T const &t)
  180. {
  181. // Check to see if we have overflowed this buffer
  182. std::size_t size_left = static_cast< std::size_t >(this->end_ - this->curr_);
  183. if (size_left < count)
  184. {
  185. // allocate a new block and return a ptr to the new memory
  186. return this->grow_(count, t);
  187. }
  188. // This is the ptr to return
  189. T *ptr = this->curr_;
  190. // Advance the high-water mark
  191. this->curr_ += count;
  192. return ptr;
  193. }
  194. T *push_sequence(std::size_t count, T const &t, fill_t)
  195. {
  196. T *ptr = this->push_sequence(count, t);
  197. std::fill_n(ptr, count, t);
  198. return ptr;
  199. }
  200. void unwind_to(T *ptr)
  201. {
  202. while(!this->in_current_chunk(ptr))
  203. {
  204. // completely unwind the current chunk, move to the previous chunk
  205. this->unwind_chunk_();
  206. }
  207. this->current_chunk_->curr_ = this->curr_ = ptr;
  208. }
  209. // shrink-to-fit: remove any unused nodes in the chain
  210. void conserve()
  211. {
  212. if(this->current_chunk_)
  213. {
  214. for(chunk *next; this->current_chunk_->next_; this->current_chunk_->next_ = next)
  215. {
  216. next = this->current_chunk_->next_->next_;
  217. delete this->current_chunk_->next_;
  218. }
  219. }
  220. }
  221. };
  222. }}} // namespace boost::xpressive::detail
  223. #if defined(_MSC_VER)
  224. # pragma warning(pop)
  225. #endif
  226. #endif