rippled
qalloc.h
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #ifndef RIPPLE_BASICS_QALLOC_H_INCLUDED
21 #define RIPPLE_BASICS_QALLOC_H_INCLUDED
22 
23 #include <ripple/basics/ByteUtilities.h>
24 #include <ripple/basics/contract.h>
25 #include <boost/intrusive/list.hpp>
26 #include <cstddef>
27 #include <cstdint>
28 #include <cstdlib>
29 #include <limits>
30 #include <memory>
31 #include <new>
32 #include <sstream>
33 #include <type_traits>
34 
35 namespace ripple {
36 
37 namespace detail {
38 
39 template <class = void>
41 {
42 private:
43  class block
44  {
45  private:
50 
51  public:
53 
54  block(block const&) = delete;
55  block&
56  operator=(block const&) = delete;
57 
58  explicit block(std::size_t bytes);
59 
60  void*
61  allocate(std::size_t bytes, std::size_t align);
62 
63  bool
64  deallocate();
65  };
66 
67  block* used_ = nullptr;
68  block* free_ = nullptr;
69 
70 public:
71  static constexpr auto block_size = kilobytes(256);
72 
73  qalloc_impl() = default;
74  qalloc_impl(qalloc_impl const&) = delete;
76  operator=(qalloc_impl const&) = delete;
77 
78  ~qalloc_impl();
79 
80  void*
81  allocate(std::size_t bytes, std::size_t align);
82 
83  void
84  deallocate(void* p);
85 };
86 
87 } // namespace detail
88 
89 template <class T, bool ShareOnCopy = true>
91 {
92 private:
93  template <class, bool>
94  friend class qalloc_type;
95 
97 
98 public:
99  using value_type = T;
100  using pointer = T*;
101  using const_pointer = T const*;
105 
106  template <class U>
107  struct rebind
108  {
109  explicit rebind() = default;
110 
112  };
113 
114  qalloc_type(qalloc_type const&) = default;
115  qalloc_type(qalloc_type&& other) noexcept = default;
116  qalloc_type&
117  operator=(qalloc_type const&) = default;
118  qalloc_type&
119  operator=(qalloc_type&&) noexcept = default;
120 
121  qalloc_type();
122 
123  template <class U>
124  qalloc_type(qalloc_type<U, ShareOnCopy> const& u);
125 
126  template <class U>
127  U*
128  alloc(std::size_t n);
129 
130  template <class U>
131  void
132  dealloc(U* p, std::size_t n);
133 
134  T*
135  allocate(std::size_t n);
136 
137  void
138  deallocate(T* p, std::size_t n);
139 
140  template <class U>
141  bool
142  operator==(qalloc_type<U, ShareOnCopy> const& u);
143 
144  template <class U>
145  bool
146  operator!=(qalloc_type<U, ShareOnCopy> const& u);
147 
150 
151 private:
152  qalloc_type select_on_copy(std::true_type) const;
153 
154  qalloc_type select_on_copy(std::false_type) const;
155 };
156 
167 using qalloc = qalloc_type<int, true>;
168 
169 //------------------------------------------------------------------------------
170 
171 namespace detail {
172 
173 template <class _>
175  : bytes_(bytes - sizeof(*this))
176  , remain_(bytes_)
177  , free_(reinterpret_cast<std::uint8_t*>(this + 1))
178 {
179 }
180 
181 template <class _>
182 void*
184 {
186  auto pad = [](void const* p, std::size_t a) {
187  auto const i = reinterpret_cast<std::uintptr_t>(p);
188  return (a - (i % a)) % a;
189  };
190 
191  auto const n0 = pad(free_ + sizeof(block*), align);
192  auto const n1 = n0 + sizeof(block*) + bytes;
193  if (remain_ < n1)
194  return nullptr;
195  auto p = reinterpret_cast<block**>(free_ + n0 + sizeof(block*));
196  assert(pad(p - 1, std::alignment_of<block*>::value) == 0);
197  p[-1] = this;
198  ++count_;
199  free_ += n1;
200  remain_ -= n1;
201  return p;
202 }
203 
204 template <class _>
205 bool
207 {
208  --count_;
209  if (count_ > 0)
210  return false;
211  remain_ = bytes_;
212  free_ = reinterpret_cast<std::uint8_t*>(this + 1);
213  return true;
214 }
215 
216 template <class _>
218 {
219  if (used_)
220  {
221  used_->~block();
222  std::free(used_);
223  }
224  while (free_)
225  {
226  auto const next = free_->next;
227  free_->~block();
228  std::free(free_);
229  free_ = next;
230  }
231 }
232 
233 template <class _>
234 void*
236 {
237  if (used_)
238  {
239  auto const p = used_->allocate(bytes, align);
240  if (p)
241  return p;
242  used_ = nullptr;
243  }
244  if (free_)
245  {
246  auto const p = free_->allocate(bytes, align);
247  if (p)
248  {
249  used_ = free_;
250  free_ = free_->next;
251  return p;
252  }
253  }
254  std::size_t const adj_align =
256  std::size_t const min_alloc = // align up
257  ((sizeof(block) + sizeof(block*) + bytes) + (adj_align - 1)) &
258  ~(adj_align - 1);
259  auto const n = std::max<std::size_t>(block_size, min_alloc);
260  block* const b = new (std::malloc(n)) block(n);
261  if (!b)
262  Throw<std::bad_alloc>();
263  used_ = b;
264  // VFALCO This has to succeed
265  return used_->allocate(bytes, align);
266 }
267 
268 template <class _>
269 void
271 {
272  auto const b = reinterpret_cast<block**>(p)[-1];
273  if (b->deallocate())
274  {
275  if (used_ == b)
276  used_ = nullptr;
277  b->next = free_;
278  free_ = b;
279  }
280 }
281 
282 } // namespace detail
283 
284 //------------------------------------------------------------------------------
285 
286 template <class T, bool ShareOnCopy>
288  : impl_(std::make_shared<detail::qalloc_impl<>>())
289 {
290 }
291 
292 template <class T, bool ShareOnCopy>
293 template <class U>
295  : impl_(u.impl_)
296 {
297 }
298 
299 template <class T, bool ShareOnCopy>
300 template <class U>
301 U*
303 {
304  if (n > std::numeric_limits<std::size_t>::max() / sizeof(U))
305  Throw<std::bad_alloc>();
306  auto const bytes = n * sizeof(U);
307  return static_cast<U*>(impl_->allocate(bytes, std::alignment_of<U>::value));
308 }
309 
310 template <class T, bool ShareOnCopy>
311 template <class U>
312 inline void
314 {
315  impl_->deallocate(p);
316 }
317 
318 template <class T, bool ShareOnCopy>
319 T*
321 {
322  return alloc<T>(n);
323 }
324 
325 template <class T, bool ShareOnCopy>
326 inline void
328 {
329  dealloc(p, n);
330 }
331 
332 template <class T, bool ShareOnCopy>
333 template <class U>
334 inline bool
336 {
337  return impl_.get() == u.impl_.get();
338 }
339 
340 template <class T, bool ShareOnCopy>
341 template <class U>
342 inline bool
344 {
345  return !(*this == u);
346 }
347 
348 template <class T, bool ShareOnCopy>
349 auto
351  -> qalloc_type
352 {
353  return select_on_copy(std::integral_constant<bool, ShareOnCopy>{});
354 }
355 
356 template <class T, bool ShareOnCopy>
358  -> qalloc_type
359 {
360  return *this; // shared arena
361 }
362 
363 template <class T, bool ShareOnCopy>
365  -> qalloc_type
366 {
367  return {}; // new arena
368 }
369 
370 } // namespace ripple
371 
372 #endif
ripple::qalloc_type::select_on_copy
qalloc_type select_on_copy(std::true_type) const
Definition: qalloc.h:357
ripple::detail::qalloc_impl::qalloc_impl
qalloc_impl()=default
ripple::qalloc_type::allocate
T * allocate(std::size_t n)
Definition: qalloc.h:320
sstream
std::alignment_of
ripple::qalloc_type::operator=
qalloc_type & operator=(qalloc_type const &)=default
std::true_type
ripple::detail::qalloc_impl::allocate
void * allocate(std::size_t bytes, std::size_t align)
Definition: qalloc.h:235
ripple::qalloc_type::qalloc_type
friend class qalloc_type
Definition: qalloc.h:94
ripple::detail::qalloc_impl::block_size
static constexpr auto block_size
Definition: qalloc.h:71
std::shared_ptr
STL class.
ripple::detail::qalloc_impl::block
Definition: qalloc.h:43
ripple::qalloc_type< int, true >::const_reference
typename std::add_lvalue_reference< int const >::type const_reference
Definition: qalloc.h:103
std::shared_ptr::get
T get(T... args)
ripple::kilobytes
constexpr auto kilobytes(T value) noexcept
Definition: ByteUtilities.h:27
new
ripple::detail::qalloc_impl::block::allocate
void * allocate(std::size_t bytes, std::size_t align)
Definition: qalloc.h:183
ripple::qalloc_type
Definition: qalloc.h:90
ripple::qalloc_type::alloc
U * alloc(std::size_t n)
Definition: qalloc.h:302
ripple::qalloc_type::impl_
std::shared_ptr< detail::qalloc_impl<> > impl_
Definition: qalloc.h:96
ripple::qalloc_type< int, true >::reference
typename std::add_lvalue_reference< int >::type reference
Definition: qalloc.h:102
ripple::detail::qalloc_impl::operator=
qalloc_impl & operator=(qalloc_impl const &)=delete
ripple::detail::qalloc_impl::block::deallocate
bool deallocate()
Definition: qalloc.h:206
ripple::detail::qalloc_impl::deallocate
void deallocate(void *p)
Definition: qalloc.h:270
ripple::detail::qalloc_impl::free_
block * free_
Definition: qalloc.h:68
ripple::detail::qalloc_impl::~qalloc_impl
~qalloc_impl()
Definition: qalloc.h:217
std::add_lvalue_reference
cstddef
ripple::qalloc_type< int, true >::const_pointer
int const * const_pointer
Definition: qalloc.h:101
cstdint
ripple::detail::qalloc_impl::block::free_
std::uint8_t * free_
Definition: qalloc.h:49
std::uint8_t
ripple::detail::qalloc_impl::block::bytes_
std::size_t bytes_
Definition: qalloc.h:47
memory
ripple::qalloc_type::dealloc
void dealloc(U *p, std::size_t n)
Definition: qalloc.h:313
ripple::detail::qalloc_impl
Definition: qalloc.h:40
ripple::detail::qalloc_impl::block::remain_
std::size_t remain_
Definition: qalloc.h:48
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::qalloc_type::rebind::rebind
rebind()=default
cstdlib
limits
ripple::qalloc_type::rebind
Definition: qalloc.h:107
std
STL namespace.
ripple::detail::qalloc_impl::block::block
block(block const &)=delete
ripple::qalloc_type::select_on_container_copy_construction
qalloc_type select_on_container_copy_construction() const
Definition: qalloc.h:350
std::free
T free(T... args)
ripple::qalloc_type< int, true >::pointer
int * pointer
Definition: qalloc.h:100
std::size_t
std::malloc
T malloc(T... args)
ripple::detail::qalloc_impl::block::next
block * next
Definition: qalloc.h:52
std::max
T max(T... args)
ripple::detail::qalloc_impl::block::operator=
block & operator=(block const &)=delete
ripple::detail::qalloc_impl::used_
block * used_
Definition: qalloc.h:67
std::numeric_limits
ripple::detail::qalloc_impl::block::count_
std::size_t count_
Definition: qalloc.h:46
type_traits
ripple::qalloc_type::deallocate
void deallocate(T *p, std::size_t n)
Definition: qalloc.h:327
ripple::qalloc_type< int, true >::value_type
int value_type
Definition: qalloc.h:99