GCS  0.2.3
gu_reserved_container.hpp
1 // Copyright (C) 2013 Codership Oy <info@codership.com>
2 
16 #ifndef _GU_RESERVED_CONTAINER_
17 #define _GU_RESERVED_CONTAINER_
18 
19 #include "chromium/aligned_memory.h"
20 
21 #include "gu_logger.hpp"
22 
23 #include <cstddef> // size_t, ptrdiff_t and NULL
24 #include <cstdlib> // malloc() and free()
25 #include <cassert>
26 #include <new> // placement new and std::bad_alloc
27 
28 namespace gu
29 {
30 
45 template <typename T, int reserved, bool diagnostic = false>
47 {
48 public:
49 
50  typedef chromium::AlignedBuffer<T, reserved> Buffer;
51 
52  typedef T* pointer;
53  typedef const T* const_pointer;
54  typedef T& reference;
55  typedef const T& const_reference;
56  typedef T value_type;
57  typedef size_t size_type;
58  // making size_type unsigned int does not seem to reduce footprint
59  typedef ptrdiff_t difference_type;
60 
61  template <typename U>
63 
64  T* address(T& t) const { return &t; }
65  const T* address(const T& t) const { return &t; }
66  size_type max_size() const { return size_type(-1)/2/sizeof(T); }
67 
68  void construct (T* const p, const T& t) const { new (p) T(t); }
69  void destroy (T* const p) const { p->~T(); }
70 
71  // Storage allocated from this can't be deallocated from other
72  bool operator==(const ReservedAllocator& other) const
73  {
74  return (buffer_ == other.buffer_);
75  }
76 
77  bool operator!=(const ReservedAllocator& other) const
78  {
79  return !(*this == other);
80  }
81 
82  ReservedAllocator(Buffer& buf, size_type n = 0)
83  :
84  buffer_(&buf),
85  used_(n)
86  {}
87 
88  ReservedAllocator(const ReservedAllocator& other)
89  :
90  buffer_(other.buffer_),
91  used_(other.used_)
92  {
93 // log_debug << "Copy ctor\n";
94  }
95 
96  template <typename U, size_type c, bool d>
97  ReservedAllocator(const ReservedAllocator<U, c, d>&)
98  :
99  buffer_(NULL),
100  used_(reserved)
101  {
102 // log_debug << "Rebinding ctor\n";
103  }
104 
105  ~ReservedAllocator() {}
106 
107  T* allocate(size_type const n, void* hint = NULL)
108  {
109  if (n == 0) return NULL;
110 
111  if (reserved - used_ >= n /* && buffer_ != NULL */)
112  {
113  assert (buffer_ != NULL);
114 
115  if (diagnostic)
116  { log_info << "Allocating " << n << '/' << (reserved - used_)
117  << " from reserve"; }
118 
119  T* const ret(buffer_->base_ptr() + used_);
120  used_ += n;
121  return ret;
122  }
123 
124  if (n <= max_size())
125  {
126  if (diagnostic)
127  { log_warn << "Allocating " << n << " from heap"; }
128 
129  void* ret = malloc(n * sizeof(T));
130  if (NULL != ret) return static_cast<T*>(ret);
131  }
132 
133  throw std::bad_alloc();
134  }
135 
136  void deallocate(T* const p, size_type const n)
137  {
138  if (size_type(p - buffer_->base_ptr()) < reserved)
139  {
140  assert (used_ > 0);
141 
142  if (buffer_->base_ptr() + used_ == p + n)
143  {
144  /* last allocated buffer, can shrink */
145  used_ -= n;
146  }
147  else
148  {
149  /* cannot recycle reserved space in this case */
150  assert(p + n <= buffer_->base_ptr() + used_);
151  }
152  }
153  else
154  {
155  free(p);
156  }
157  }
158 
159  size_type used() const { return used_; }
160 
161 private:
162 
163  /* even though we initially allocate buffer in ReservedContainer directly
164  * before this, STL containers insist on copying allocators, so we need
165  * a pointer to buffer to be an explicit member (and waste another 8 bytes*/
166  Buffer* buffer_;
167  size_type used_;
168 
169  ReservedAllocator& operator=(const ReservedAllocator&);
170 
171 }; /* class ReservedAllocator */
172 
182 template <typename ContainerType, int reserved>
184 {
185 public:
186 
188  buffer_ (),
189  /* Actual Allocator instance used by container_ should be
190  * copy-constructed from the temporary passed to container ctor.
191  * Copy-construction preserves pointer to buffer, which is not
192  * temporary. This works at least with std::vector */
193  container_(Allocator(buffer_))
194  {
195  /* Make the container use most of the buffer by reserving our buffer
196  * size before doing anything else. */
197  container_.reserve(reserved);
198  }
199 
200  /*
201  * Getters for the actual container.
202  *
203  * Danger: any copies of this made using the copy constructor must have
204  * shorter lifetimes than the source. The copy will share the same allocator
205  * and therefore the same stack buffer as the original. Use std::copy to
206  * copy into a "real" container for longer-lived objects.
207  */
208  ContainerType& container() { return container_; }
209  const ContainerType& container() const { return container_; }
210  ContainerType& operator()() { return container_; }
211  const ContainerType& operator()() const { return container_; }
212 
213  /*
214  * Support operator-> to get to the container.
215  * This allows nicer syntax like:
216  * ReservedContainer<...> foo;
217  * std::sort(foo->begin(), foo->end());
218  */
219  ContainerType* operator->() { return &container_; }
220  const ContainerType* operator->() const { return &container_; }
221 
222  /* For testing only */
223  typedef typename ContainerType::value_type ContainedType;
224  const ContainedType* reserved_buffer() const { return buffer_.base_ptr(); }
225 
226 private:
227 
229  typedef typename Allocator::Buffer Buffer;
230 
231  Buffer buffer_;
232  ContainerType container_;
233  /* Note that container will use another instance of Allocator, copy
234  * constructed from allocator_, so any changes won't be re*/
235 
237  ReservedContainer& operator=(const ReservedContainer&);
238 
239 }; /* class ReservedContainer */
240 
241 } /* namespace gu */
242 
243 #endif /* _GU_RESERVED_CONTAINER_ */
Definition: gu_reserved_container.hpp:183
Definition: gu_reserved_container.hpp:62
Definition: gu_reserved_container.hpp:46