WPILibC++ 2025.1.1
Loading...
Searching...
No Matches
joint_allocator.hpp
Go to the documentation of this file.
1// Copyright (C) 2015-2023 Jonathan Müller and foonathan/memory contributors
2// SPDX-License-Identifier: Zlib
3
4#ifndef WPI_MEMORY_JOINT_ALLOCATOR_HPP_INCLUDED
5#define WPI_MEMORY_JOINT_ALLOCATOR_HPP_INCLUDED
6
7/// \file
8/// Class template \ref wpi::memory::joint_ptr, \ref wpi::memory::joint_allocator and related.
9
10#include <initializer_list>
11#include <new>
12
13#include "detail/align.hpp"
15#include "detail/utility.hpp"
16#include "allocator_storage.hpp"
17#include "config.hpp"
18#include "default_allocator.hpp"
19#include "error.hpp"
20
21namespace wpi
22{
23 namespace memory
24 {
25 template <typename T, class RawAllocator>
26 class joint_ptr;
27
28 template <typename T>
29 class joint_type;
30
31 namespace detail
32 {
33 // the stack that allocates the joint memory
35 {
36 public:
37 joint_stack(void* mem, std::size_t cap) noexcept
38 : stack_(static_cast<char*>(mem)), end_(static_cast<char*>(mem) + cap)
39 {
40 }
41
42 void* allocate(std::size_t size, std::size_t alignment) noexcept
43 {
44 return stack_.allocate(end_, size, alignment, 0u);
45 }
46
47 bool bump(std::size_t offset) noexcept
48 {
49 if (offset > std::size_t(end_ - stack_.top()))
50 return false;
51 stack_.bump(offset);
52 return true;
53 }
54
55 char* top() noexcept
56 {
57 return stack_.top();
58 }
59
60 const char* top() const noexcept
61 {
62 return stack_.top();
63 }
64
65 void unwind(void* ptr) noexcept
66 {
67 stack_.unwind(static_cast<char*>(ptr));
68 }
69
70 std::size_t capacity(const char* mem) const noexcept
71 {
72 return std::size_t(end_ - mem);
73 }
74
75 std::size_t capacity_left() const noexcept
76 {
77 return std::size_t(end_ - top());
78 }
79
80 std::size_t capacity_used(const char* mem) const noexcept
81 {
82 return std::size_t(top() - mem);
83 }
84
85 private:
87 char* end_;
88 };
89
90 template <typename T>
92
93 template <typename T>
94 const detail::joint_stack& get_stack(const joint_type<T>& obj) noexcept;
95 } // namespace detail
96
97 /// Tag type that can't be created.
98 ///
99 /// It isued by \ref joint_ptr.
100 /// \ingroup memory_allocator
101 class joint
102 {
103 joint(std::size_t cap) noexcept : capacity(cap) {}
104
105 std::size_t capacity;
106
107 template <typename T, class RawAllocator>
108 friend class joint_ptr;
109 template <typename T>
110 friend class joint_type;
111 };
112
113 /// Tag type to make the joint size more explicit.
114 ///
115 /// It is used by \ref joint_ptr.
116 /// \ingroup memory_allocator
118 {
119 std::size_t size;
120
121 explicit joint_size(std::size_t s) noexcept : size(s) {}
122 };
123
124 /// CRTP base class for all objects that want to use joint memory.
125 ///
126 /// This will disable default copy/move operations
127 /// and inserts additional members for the joint memory management.
128 /// \ingroup memory_allocator
129 template <typename T>
131 {
132 protected:
133 /// \effects Creates the base class,
134 /// the tag type cannot be created by the user.
135 /// \note This ensures that you cannot create joint types yourself.
136 joint_type(joint j) noexcept;
137
138 joint_type(const joint_type&) = delete;
140
141 private:
142 detail::joint_stack stack_;
143
144 template <typename U>
146 template <typename U>
147 friend const detail::joint_stack& detail::get_stack(const joint_type<U>& obj) noexcept;
148 };
149
150 namespace detail
151 {
152 template <typename T>
154 {
155 return obj.stack_;
156 }
157
158 template <typename T>
159 const detail::joint_stack& get_stack(const joint_type<T>& obj) noexcept
160 {
161 return obj.stack_;
162 }
163
164 template <typename T>
165 char* get_memory(joint_type<T>& obj) noexcept
166 {
167 auto mem = static_cast<void*>(&obj);
168 return static_cast<char*>(mem) + sizeof(T);
169 }
170
171 template <typename T>
172 const char* get_memory(const joint_type<T>& obj) noexcept
173 {
174 auto mem = static_cast<const void*>(&obj);
175 return static_cast<const char*>(mem) + sizeof(T);
176 }
177
178 } // namespace detail
179
180 template <typename T>
181 joint_type<T>::joint_type(joint j) noexcept : stack_(detail::get_memory(*this), j.capacity)
182 {
183 WPI_MEMORY_ASSERT(stack_.top() == detail::get_memory(*this));
184 WPI_MEMORY_ASSERT(stack_.capacity_left() == j.capacity);
185 }
186
187 /// A pointer to an object where all allocations are joint.
188 ///
189 /// It can either own an object or not (be `nullptr`).
190 /// When it owns an object, it points to a memory block.
191 /// This memory block contains both the actual object (of the type `T`)
192 /// and space for allocations of `T`s members.
193 ///
194 /// The type `T` must be derived from \ref joint_type and every constructor must take \ref joint
195 /// as first parameter.
196 /// This prevents that you create joint objects yourself,
197 /// without the additional storage.
198 /// The default copy and move constructors are also deleted,
199 /// you need to write them yourself.
200 ///
201 /// You can only access the object through the pointer,
202 /// use \ref joint_allocator or \ref joint_array as members of `T`,
203 /// to enable the memory sharing.
204 /// If you are using \ref joint_allocator inside STL containers,
205 /// make sure that you do not call their regular copy/move constructors,
206 /// but instead the version where you pass an allocator.
207 ///
208 /// The memory block will be managed by the given RawAllocator,
209 /// it is stored in an \ref allocator_reference and not owned by the pointer directly.
210 /// \ingroup memory_allocator
211 template <typename T, class RawAllocator>
212 class joint_ptr : WPI_EBO(allocator_reference<RawAllocator>)
213 {
214 static_assert(std::is_base_of<joint_type<T>, T>::value,
215 "T must be derived of joint_type<T>");
216
217 public:
218 using element_type = T;
220
221 //=== constructors/destructor/assignment ===//
222 /// @{
223 /// \effects Creates it with a RawAllocator, but does not own a new object.
224 explicit joint_ptr(allocator_type& alloc) noexcept
225 : allocator_reference<RawAllocator>(alloc), ptr_(nullptr)
226 {
227 }
228
229 explicit joint_ptr(const allocator_type& alloc) noexcept
230 : allocator_reference<RawAllocator>(alloc), ptr_(nullptr)
231 {
232 }
233 /// @}
234
235 /// @{
236 /// \effects Reserves memory for the object and the additional size,
237 /// and creates the object by forwarding the arguments to its constructor.
238 /// The RawAllocator will be used for the allocation.
239 template <typename... Args>
240 joint_ptr(allocator_type& alloc, joint_size additional_size, Args&&... args)
241 : joint_ptr(alloc)
242 {
243 create(additional_size.size, detail::forward<Args>(args)...);
244 }
245
246 template <typename... Args>
247 joint_ptr(const allocator_type& alloc, joint_size additional_size, Args&&... args)
248 : joint_ptr(alloc)
249 {
250 create(additional_size.size, detail::forward<Args>(args)...);
251 }
252 /// @}
253
254 /// \effects Move-constructs the pointer.
255 /// Ownership will be transferred from `other` to the new object.
256 joint_ptr(joint_ptr&& other) noexcept
257 : allocator_reference<RawAllocator>(detail::move(other)), ptr_(other.ptr_)
258 {
259 other.ptr_ = nullptr;
260 }
261
262 /// \effects Destroys the object and deallocates its storage.
263 ~joint_ptr() noexcept
264 {
265 reset();
266 }
267
268 /// \effects Move-assings the pointer.
269 /// The previously owned object will be destroyed,
270 /// and ownership of `other` transferred.
271 joint_ptr& operator=(joint_ptr&& other) noexcept
272 {
273 joint_ptr tmp(detail::move(other));
274 swap(*this, tmp);
275 return *this;
276 }
277
278 /// \effects Same as `reset()`.
279 joint_ptr& operator=(std::nullptr_t) noexcept
280 {
281 reset();
282 return *this;
283 }
284
285 /// \effects Swaps to pointers and their ownership and allocator.
286 friend void swap(joint_ptr& a, joint_ptr& b) noexcept
287 {
289 static_cast<allocator_reference<RawAllocator>&>(b));
290 detail::adl_swap(a.ptr_, b.ptr_);
291 }
292
293 //=== modifiers ===//
294 /// \effects Destroys the object it refers to,
295 /// if there is any.
296 void reset() noexcept
297 {
298 if (ptr_)
299 {
300 (**this).~element_type();
301 this->deallocate_node(ptr_,
302 sizeof(element_type)
304 detail::get_memory(*ptr_)),
305 alignof(element_type));
306 ptr_ = nullptr;
307 }
308 }
309
310 //=== accessors ===//
311 /// \returns `true` if the pointer does own an object,
312 /// `false` otherwise.
313 explicit operator bool() const noexcept
314 {
315 return ptr_ != nullptr;
316 }
317
318 /// \returns A reference to the object it owns.
319 /// \requires The pointer must own an object,
320 /// i.e. `operator bool()` must return `true`.
321 element_type& operator*() const noexcept
322 {
323 WPI_MEMORY_ASSERT(ptr_);
324 return *get();
325 }
326
327 /// \returns A pointer to the object it owns.
328 /// \requires The pointer must own an object,
329 /// i.e. `operator bool()` must return `true`.
330 element_type* operator->() const noexcept
331 {
332 WPI_MEMORY_ASSERT(ptr_);
333 return get();
334 }
335
336 /// \returns A pointer to the object it owns
337 /// or `nullptr`, if it does not own any object.
338 element_type* get() const noexcept
339 {
340 return static_cast<element_type*>(ptr_);
341 }
342
343 /// \returns A reference to the allocator it will use for the deallocation.
344 auto get_allocator() const noexcept
345 -> decltype(std::declval<allocator_reference<allocator_type>>().get_allocator())
346 {
348 }
349
350 private:
351 template <typename... Args>
352 void create(std::size_t additional_size, Args&&... args)
353 {
354 auto mem = this->allocate_node(sizeof(element_type) + additional_size,
355 alignof(element_type));
356
357 element_type* ptr = nullptr;
358#if WPI_HAS_EXCEPTION_SUPPORT
359 try
360 {
361 ptr = ::new (mem)
362 element_type(joint(additional_size), detail::forward<Args>(args)...);
363 }
364 catch (...)
365 {
366 this->deallocate_node(mem, sizeof(element_type) + additional_size,
367 alignof(element_type));
368 throw;
369 }
370#else
371 ptr = ::new (mem)
372 element_type(joint(additional_size), detail::forward<Args>(args)...);
373#endif
374 ptr_ = ptr;
375 }
376
377 joint_type<T>* ptr_;
378
379 friend class joint_allocator;
380 };
381
382 /// @{
383 /// \returns `!ptr`,
384 /// i.e. if `ptr` does not own anything.
385 /// \relates joint_ptr
386 template <typename T, class RawAllocator>
387 bool operator==(const joint_ptr<T, RawAllocator>& ptr, std::nullptr_t)
388 {
389 return !ptr;
390 }
391
392 template <typename T, class RawAllocator>
393 bool operator==(std::nullptr_t, const joint_ptr<T, RawAllocator>& ptr)
394 {
395 return ptr == nullptr;
396 }
397 /// @}
398
399 /// @{
400 /// \returns `ptr.get() == p`,
401 /// i.e. if `ptr` ownws the object referred to by `p`.
402 /// \relates joint_ptr
403 template <typename T, class RawAllocator>
404 bool operator==(const joint_ptr<T, RawAllocator>& ptr, T* p)
405 {
406 return ptr.get() == p;
407 }
408
409 template <typename T, class RawAllocator>
411 {
412 return ptr == p;
413 }
414 /// @}
415
416 /// @{
417 /// \returns `!(ptr == nullptr)`,
418 /// i.e. if `ptr` does own something.
419 /// \relates joint_ptr
420 template <typename T, class RawAllocator>
421 bool operator!=(const joint_ptr<T, RawAllocator>& ptr, std::nullptr_t)
422 {
423 return !(ptr == nullptr);
424 }
425
426 template <typename T, class RawAllocator>
427 bool operator!=(std::nullptr_t, const joint_ptr<T, RawAllocator>& ptr)
428 {
429 return ptr != nullptr;
430 }
431 /// @}
432
433 /// @{
434 /// \returns `!(ptr == p)`,
435 /// i.e. if `ptr` does not ownw the object referred to by `p`.
436 /// \relates joint_ptr
437 template <typename T, class RawAllocator>
438 bool operator!=(const joint_ptr<T, RawAllocator>& ptr, T* p)
439 {
440 return !(ptr == p);
441 }
442
443 template <typename T, class RawAllocator>
445 {
446 return ptr != p;
447 }
448 /// @}
449
450 /// @{
451 /// \returns A new \ref joint_ptr as if created with the same arguments passed to the constructor.
452 /// \relatesalso joint_ptr
453 /// \ingroup memory_allocator
454 template <typename T, class RawAllocator, typename... Args>
455 auto allocate_joint(RawAllocator& alloc, joint_size additional_size, Args&&... args)
457 {
458 return joint_ptr<T, RawAllocator>(alloc, additional_size,
459 detail::forward<Args>(args)...);
460 }
461
462 template <typename T, class RawAllocator, typename... Args>
463 auto allocate_joint(const RawAllocator& alloc, joint_size additional_size, Args&&... args)
465 {
466 return joint_ptr<T, RawAllocator>(alloc, additional_size,
467 detail::forward<Args>(args)...);
468 }
469 /// @}
470
471 /// @{
472 /// \returns A new \ref joint_ptr that points to a copy of `joint`.
473 /// It will allocate as much memory as needed and forward to the copy constructor.
474 /// \ingroup memory_allocator
475 template <class RawAllocator, typename T>
476 auto clone_joint(RawAllocator& alloc, const joint_type<T>& joint)
478 {
479 return joint_ptr<T, RawAllocator>(alloc,
480 joint_size(detail::get_stack(joint).capacity_used(
482 static_cast<const T&>(joint));
483 }
484
485 template <class RawAllocator, typename T>
486 auto clone_joint(const RawAllocator& alloc, const joint_type<T>& joint)
488 {
489 return joint_ptr<T, RawAllocator>(alloc,
490 joint_size(detail::get_stack(joint).capacity_used(
492 static_cast<const T&>(joint));
493 }
494 /// @}
495
496 /// A RawAllocator that uses the additional joint memory for its allocation.
497 ///
498 /// It is somewhat limited and allows only allocation once.
499 /// All joint allocators for an object share the joint memory and must not be used in multiple threads.
500 /// The memory it returns is owned by a \ref joint_ptr and will be destroyed through it.
501 /// \ingroup memory_allocator
503 {
504 public:
505#if defined(__GNUC__) && (!defined(_GLIBCXX_USE_CXX11_ABI) || _GLIBCXX_USE_CXX11_ABI == 0)
506 // std::string requires default constructor for the small string optimization when using gcc's old ABI
507 // so add one, but it must never be used for allocation
508 joint_allocator() noexcept : stack_(nullptr) {}
509#endif
510
511 /// \effects Creates it using the joint memory of the given object.
512 template <typename T>
514 {
515 }
516
517 joint_allocator(const joint_allocator& other) noexcept = default;
518 joint_allocator& operator=(const joint_allocator& other) noexcept = default;
519
520 /// \effects Allocates a node with given properties.
521 /// \returns A pointer to the new node.
522 /// \throws \ref out_of_fixed_memory exception if this function has been called for a second time
523 /// or the joint memory block is exhausted.
524 void* allocate_node(std::size_t size, std::size_t alignment)
525 {
526 WPI_MEMORY_ASSERT(stack_);
527 auto mem = stack_->allocate(size, alignment);
528 if (!mem)
529 WPI_THROW(out_of_fixed_memory(info(), size));
530 return mem;
531 }
532
533 /// \effects Deallocates the node, if possible.
534 /// \note It is only possible if it was the last allocation.
535 void deallocate_node(void* ptr, std::size_t size, std::size_t) noexcept
536 {
537 WPI_MEMORY_ASSERT(stack_);
538 auto end = static_cast<char*>(ptr) + size;
539 if (end == stack_->top())
540 stack_->unwind(ptr);
541 }
542
543 private:
544 allocator_info info() const noexcept
545 {
546 return allocator_info(WPI_MEMORY_LOG_PREFIX "::joint_allocator", this);
547 }
548
549 detail::joint_stack* stack_;
550
551 friend bool operator==(const joint_allocator& lhs, const joint_allocator& rhs) noexcept;
552 };
553
554 /// @{
555 /// \returns Whether `lhs` and `rhs` use the same joint memory for the allocation.
556 /// \relates joint_allocator
557 inline bool operator==(const joint_allocator& lhs, const joint_allocator& rhs) noexcept
558 {
559 return lhs.stack_ == rhs.stack_;
560 }
561
562 inline bool operator!=(const joint_allocator& lhs, const joint_allocator& rhs) noexcept
563 {
564 return !(lhs == rhs);
565 }
566 /// @}
567
568 /// Specialization of \ref is_shared_allocator to mark \ref joint_allocator as shared.
569 /// This allows using it as \ref allocator_reference directly.
570 /// \ingroup memory_allocator
571 template <>
572 struct is_shared_allocator<joint_allocator> : std::true_type
573 {
574 };
575
576 /// Specialization of \ref is_thread_safe_allocator to mark \ref joint_allocator as thread safe.
577 /// This is an optimization to get rid of the mutex in \ref allocator_storage,
578 /// as joint allocator must not be shared between threads.
579 /// \note The allocator is *not* thread safe, it just must not be shared.
580 template <>
582 {
583 };
584
585#if !defined(DOXYGEN)
586 template <class RawAllocator>
587 struct propagation_traits;
588#endif
589
590 /// Specialization of the \ref propagation_traits for the \ref joint_allocator.
591 /// A joint allocator does not propagate on assignment
592 /// and it is not allowed to use the regular copy/move constructor of allocator aware containers,
593 /// instead it needs the copy/move constructor with allocator.
594 /// \note This is required because the container constructor will end up copying/moving the allocator.
595 /// But this is not allowed as you need the allocator with the correct joined memory.
596 /// Copying can be customized (i.e. forbidden), but sadly not move, so keep that in mind.
597 /// \ingroup memory_allocator
598 template <>
600 {
601 using propagate_on_container_swap = std::false_type;
604
605 template <class AllocReference>
606 static AllocReference select_on_container_copy_construction(const AllocReference&)
607 {
608 static_assert(always_false<AllocReference>::value,
609 "you must not use the regular copy constructor");
610 }
611
612 private:
613 template <typename T>
614 struct always_false : std::false_type
615 {
616 };
617 };
618
619 /// A zero overhead dynamic array using joint memory.
620 ///
621 /// If you use, e.g. `std::vector` with \ref joint_allocator,
622 /// this has a slight additional overhead.
623 /// This type is joint memory aware and has no overhead.
624 ///
625 /// It has a dynamic, but fixed size,
626 /// it cannot grow after it has been created.
627 /// \ingroup memory_allocator
628 template <typename T>
630 {
631 public:
632 using value_type = T;
635
636 //=== constructors ===//
637 /// \effects Creates with `size` default-constructed objects using the specified joint memory.
638 /// \throws \ref out_of_fixed_memory if `size` is too big
639 /// and anything thrown by `T`s constructor.
640 /// If an allocation is thrown, the memory will be released directly.
641 template <typename JointType>
642 joint_array(std::size_t size, joint_type<JointType>& j)
643 : joint_array(detail::get_stack(j), size)
644 {
645 }
646
647 /// \effects Creates with `size` copies of `val` using the specified joint memory.
648 /// \throws \ref out_of_fixed_memory if `size` is too big
649 /// and anything thrown by `T`s constructor.
650 /// If an allocation is thrown, the memory will be released directly.
651 template <typename JointType>
652 joint_array(std::size_t size, const value_type& val, joint_type<JointType>& j)
653 : joint_array(detail::get_stack(j), size, val)
654 {
655 }
656
657 /// \effects Creates with the copies of the objects in the initializer list using the specified joint memory.
658 /// \throws \ref out_of_fixed_memory if the size is too big
659 /// and anything thrown by `T`s constructor.
660 /// If an allocation is thrown, the memory will be released directly.
661 template <typename JointType>
662 joint_array(std::initializer_list<value_type> ilist, joint_type<JointType>& j)
663 : joint_array(detail::get_stack(j), ilist)
664 {
665 }
666
667 /// \effects Creates it by forwarding each element of the range to `T`s constructor using the specified joint memory.
668 /// \throws \ref out_of_fixed_memory if the size is too big
669 /// and anything thrown by `T`s constructor.
670 /// If an allocation is thrown, the memory will be released directly.
671 template <typename InIter, typename JointType,
672 typename = decltype(*std::declval<InIter&>()++)>
673 joint_array(InIter begin, InIter end, joint_type<JointType>& j)
674 : joint_array(detail::get_stack(j), begin, end)
675 {
676 }
677
678 joint_array(const joint_array&) = delete;
679
680 /// \effects Copy constructs each element from `other` into the storage of the specified joint memory.
681 /// \throws \ref out_of_fixed_memory if the size is too big
682 /// and anything thrown by `T`s constructor.
683 /// If an allocation is thrown, the memory will be released directly.
684 template <typename JointType>
686 : joint_array(detail::get_stack(j), other)
687 {
688 }
689
691
692 /// \effects Move constructs each element from `other` into the storage of the specified joint memory.
693 /// \throws \ref out_of_fixed_memory if the size is too big
694 /// and anything thrown by `T`s constructor.
695 /// If an allocation is thrown, the memory will be released directly.
696 template <typename JointType>
698 : joint_array(detail::get_stack(j), detail::move(other))
699 {
700 }
701
702 /// \effects Destroys all objects,
703 /// but does not release the storage.
704 ~joint_array() noexcept
705 {
706 for (std::size_t i = 0u; i != size_; ++i)
707 ptr_[i].~T();
708 }
709
712
713 //=== accessors ===//
714 /// @{
715 /// \returns A reference to the `i`th object.
716 /// \requires `i < size()`.
717 value_type& operator[](std::size_t i) noexcept
718 {
719 WPI_MEMORY_ASSERT(i < size_);
720 return ptr_[i];
721 }
722
723 const value_type& operator[](std::size_t i) const noexcept
724 {
725 WPI_MEMORY_ASSERT(i < size_);
726 return ptr_[i];
727 }
728 /// @}
729
730 /// @{
731 /// \returns A pointer to the first object.
732 /// It points to contiguous memory and can be used to access the objects directly.
733 value_type* data() noexcept
734 {
735 return ptr_;
736 }
737
738 const value_type* data() const noexcept
739 {
740 return ptr_;
741 }
742 /// @}
743
744 /// @{
745 /// \returns A random access iterator to the first element.
746 iterator begin() noexcept
747 {
748 return ptr_;
749 }
750
751 const_iterator begin() const noexcept
752 {
753 return ptr_;
754 }
755 /// @}
756
757 /// @{
758 /// \returns A random access iterator one past the last element.
759 iterator end() noexcept
760 {
761 return ptr_ + size_;
762 }
763
764 const_iterator end() const noexcept
765 {
766 return ptr_ + size_;
767 }
768 /// @}
769
770 /// \returns The number of elements in the array.
771 std::size_t size() const noexcept
772 {
773 return size_;
774 }
775
776 /// \returns `true` if the array is empty, `false` otherwise.
777 bool empty() const noexcept
778 {
779 return size_ == 0u;
780 }
781
782 private:
783 // allocate only
784 struct allocate_only
785 {
786 };
787 joint_array(allocate_only, detail::joint_stack& stack, std::size_t size)
788 : ptr_(nullptr), size_(0u)
789 {
790 ptr_ = static_cast<T*>(stack.allocate(size * sizeof(T), alignof(T)));
791 if (!ptr_)
792 WPI_THROW(out_of_fixed_memory(info(), size * sizeof(T)));
793 }
794
795 class builder
796 {
797 public:
798 builder(detail::joint_stack& stack, T* ptr) noexcept
799 : stack_(&stack), objects_(ptr), size_(0u)
800 {
801 }
802
803 ~builder() noexcept
804 {
805 for (std::size_t i = 0u; i != size_; ++i)
806 objects_[i].~T();
807
808 if (size_)
809 stack_->unwind(objects_);
810 }
811
812 builder(builder&&) = delete;
813 builder& operator=(builder&&) = delete;
814
815 template <typename... Args>
816 T* create(Args&&... args)
817 {
818 auto ptr = ::new (static_cast<void*>(&objects_[size_]))
819 T(detail::forward<Args>(args)...);
820 ++size_;
821 return ptr;
822 }
823
824 std::size_t size() const noexcept
825 {
826 return size_;
827 }
828
829 std::size_t release() noexcept
830 {
831 auto res = size_;
832 size_ = 0u;
833 return res;
834 }
835
836 private:
837 detail::joint_stack* stack_;
838 T* objects_;
839 std::size_t size_;
840 };
841
842 joint_array(detail::joint_stack& stack, std::size_t size)
843 : joint_array(allocate_only{}, stack, size)
844 {
845 builder b(stack, ptr_);
846 for (auto i = 0u; i != size; ++i)
847 b.create();
848 size_ = b.release();
849 }
850
851 joint_array(detail::joint_stack& stack, std::size_t size, const value_type& value)
852 : joint_array(allocate_only{}, stack, size)
853 {
854 builder b(stack, ptr_);
855 for (auto i = 0u; i != size; ++i)
856 b.create(value);
857 size_ = b.release();
858 }
859
860 joint_array(detail::joint_stack& stack, std::initializer_list<value_type> ilist)
861 : joint_array(allocate_only{}, stack, ilist.size())
862 {
863 builder b(stack, ptr_);
864 for (auto& elem : ilist)
865 b.create(elem);
866 size_ = b.release();
867 }
868
869 joint_array(detail::joint_stack& stack, const joint_array& other)
870 : joint_array(allocate_only{}, stack, other.size())
871 {
872 builder b(stack, ptr_);
873 for (auto& elem : other)
874 b.create(elem);
875 size_ = b.release();
876 }
877
878 joint_array(detail::joint_stack& stack, joint_array&& other)
879 : joint_array(allocate_only{}, stack, other.size())
880 {
881 builder b(stack, ptr_);
882 for (auto& elem : other)
883 b.create(detail::move(elem));
884 size_ = b.release();
885 }
886
887 template <typename InIter>
888 joint_array(detail::joint_stack& stack, InIter begin, InIter end)
889 : ptr_(nullptr), size_(0u)
890 {
891 if (begin == end)
892 return;
893
894 ptr_ = static_cast<T*>(stack.allocate(sizeof(T), alignof(T)));
895 if (!ptr_)
896 WPI_THROW(out_of_fixed_memory(info(), sizeof(T)));
897
898 builder b(stack, ptr_);
899 b.create(*begin++);
900
901 for (auto last = ptr_; begin != end; ++begin)
902 {
903 // just bump stack to get more memory
904 if (!stack.bump(sizeof(T)))
905 WPI_THROW(out_of_fixed_memory(info(), b.size() * sizeof(T)));
906
907 auto cur = b.create(*begin);
908 WPI_MEMORY_ASSERT(last + 1 == cur);
909 last = cur;
910 }
911
912 size_ = b.release();
913 }
914
915 allocator_info info() const noexcept
916 {
917 return {WPI_MEMORY_LOG_PREFIX "::joint_array", this};
918 }
919
920 value_type* ptr_;
921 std::size_t size_;
922 };
923 } // namespace memory
924} // namespace wpi
925
926#endif // WPI_MEMORY_JOINT_ALLOCATOR_HPP_INCLUDED
Class template wpi::memory::allocator_storage, some policies and resulting typedefs.
An alias template for allocator_storage using the reference_storage policy.
Definition allocator_storage.hpp:900
typename reference_storage< RawAllocator >::allocator_type allocator_type
Definition allocator_storage.hpp:106
auto get_allocator() noexcept -> decltype(std::declval< storage_policy >().get_allocator())
Definition allocator_storage.hpp:277
Definition memory_stack.hpp:22
void unwind(char *top) noexcept
Definition memory_stack.hpp:100
void bump(std::size_t offset) noexcept
Definition memory_stack.hpp:47
void * allocate(const char *end, std::size_t size, std::size_t alignment, std::size_t fence_size=debug_fence_size) noexcept
Definition memory_stack.hpp:71
char * top() const noexcept
Definition memory_stack.hpp:107
Definition joint_allocator.hpp:35
void * allocate(std::size_t size, std::size_t alignment) noexcept
Definition joint_allocator.hpp:42
char * top() noexcept
Definition joint_allocator.hpp:55
bool bump(std::size_t offset) noexcept
Definition joint_allocator.hpp:47
std::size_t capacity_used(const char *mem) const noexcept
Definition joint_allocator.hpp:80
const char * top() const noexcept
Definition joint_allocator.hpp:60
void unwind(void *ptr) noexcept
Definition joint_allocator.hpp:65
std::size_t capacity_left() const noexcept
Definition joint_allocator.hpp:75
std::size_t capacity(const char *mem) const noexcept
Definition joint_allocator.hpp:70
joint_stack(void *mem, std::size_t cap) noexcept
Definition joint_allocator.hpp:37
A RawAllocator that uses the additional joint memory for its allocation.
Definition joint_allocator.hpp:503
joint_allocator & operator=(const joint_allocator &other) noexcept=default
void deallocate_node(void *ptr, std::size_t size, std::size_t) noexcept
Definition joint_allocator.hpp:535
joint_allocator(const joint_allocator &other) noexcept=default
joint_allocator(joint_type< T > &j) noexcept
Definition joint_allocator.hpp:513
void * allocate_node(std::size_t size, std::size_t alignment)
Definition joint_allocator.hpp:524
friend bool operator==(const joint_allocator &lhs, const joint_allocator &rhs) noexcept
A zero overhead dynamic array using joint memory.
Definition joint_allocator.hpp:630
joint_array(joint_array &&other, joint_type< JointType > &j)
Definition joint_allocator.hpp:697
const value_type * const_iterator
Definition joint_allocator.hpp:634
value_type & operator[](std::size_t i) noexcept
Definition joint_allocator.hpp:717
joint_array & operator=(const joint_array &)=delete
iterator end() noexcept
Definition joint_allocator.hpp:759
~joint_array() noexcept
Definition joint_allocator.hpp:704
value_type * iterator
Definition joint_allocator.hpp:633
const_iterator end() const noexcept
Definition joint_allocator.hpp:764
joint_array(const joint_array &other, joint_type< JointType > &j)
Definition joint_allocator.hpp:685
value_type * data() noexcept
Definition joint_allocator.hpp:733
joint_array(std::size_t size, joint_type< JointType > &j)
Definition joint_allocator.hpp:642
joint_array(const joint_array &)=delete
T value_type
Definition joint_allocator.hpp:632
bool empty() const noexcept
Definition joint_allocator.hpp:777
joint_array(joint_array &&)=delete
std::size_t size() const noexcept
Definition joint_allocator.hpp:771
joint_array(std::size_t size, const value_type &val, joint_type< JointType > &j)
Definition joint_allocator.hpp:652
const value_type * data() const noexcept
Definition joint_allocator.hpp:738
const_iterator begin() const noexcept
Definition joint_allocator.hpp:751
joint_array & operator=(joint_array &&)=delete
const value_type & operator[](std::size_t i) const noexcept
Definition joint_allocator.hpp:723
joint_array(std::initializer_list< value_type > ilist, joint_type< JointType > &j)
Definition joint_allocator.hpp:662
joint_array(InIter begin, InIter end, joint_type< JointType > &j)
Definition joint_allocator.hpp:673
iterator begin() noexcept
Definition joint_allocator.hpp:746
A pointer to an object where all allocations are joint.
Definition joint_allocator.hpp:213
friend void swap(joint_ptr &a, joint_ptr &b) noexcept
Definition joint_allocator.hpp:286
typename allocator_reference< RawAllocator >::allocator_type allocator_type
Definition joint_allocator.hpp:219
joint_ptr(allocator_type &alloc, joint_size additional_size, Args &&... args)
Definition joint_allocator.hpp:240
T element_type
Definition joint_allocator.hpp:218
auto get_allocator() const noexcept -> decltype(std::declval< allocator_reference< allocator_type > >().get_allocator())
Definition joint_allocator.hpp:344
joint_ptr(const allocator_type &alloc, joint_size additional_size, Args &&... args)
Definition joint_allocator.hpp:247
void reset() noexcept
Definition joint_allocator.hpp:296
joint_ptr & operator=(std::nullptr_t) noexcept
Definition joint_allocator.hpp:279
element_type * operator->() const noexcept
Definition joint_allocator.hpp:330
joint_ptr(joint_ptr &&other) noexcept
Definition joint_allocator.hpp:256
joint_ptr(allocator_type &alloc) noexcept
Definition joint_allocator.hpp:224
element_type & operator*() const noexcept
Definition joint_allocator.hpp:321
joint_ptr(const allocator_type &alloc) noexcept
Definition joint_allocator.hpp:229
joint_ptr & operator=(joint_ptr &&other) noexcept
Definition joint_allocator.hpp:271
~joint_ptr() noexcept
Definition joint_allocator.hpp:263
element_type * get() const noexcept
Definition joint_allocator.hpp:338
CRTP base class for all objects that want to use joint memory.
Definition joint_allocator.hpp:131
joint_type(joint_type &&)=delete
joint_type(const joint_type &)=delete
joint_type(joint j) noexcept
Definition joint_allocator.hpp:181
Tag type that can't be created.
Definition joint_allocator.hpp:102
A special case of out_of_memory errors thrown when a low-level allocator with a fixed size runs out o...
Definition error.hpp:121
Configuration macros.
#define WPI_MEMORY_LOG_PREFIX
Definition config.hpp:46
#define WPI_THROW(Ex)
Definition config.hpp:33
The typedef wpi::memory::default_allocator.
The exception classes.
auto ptr(T p) -> const void *
Converts p to const void* for pointer formatting.
Definition format.h:3821
auto allocate_joint(RawAllocator &alloc, joint_size additional_size, Args &&... args) -> joint_ptr< T, RawAllocator >
Definition joint_allocator.hpp:455
auto clone_joint(RawAllocator &alloc, const joint_type< T > &joint) -> joint_ptr< T, RawAllocator >
Definition joint_allocator.hpp:476
detail namespace with internal helper functions
Definition input_adapters.h:32
Implement std::hash so that hash_code can be used in STL containers.
Definition PointerIntPair.h:280
b
Definition data.h:44
void adl_swap(T &a, T &b) noexcept
Definition utility.hpp:60
T && forward(typename std::remove_reference< T >::type &t) noexcept
Definition utility.hpp:31
std::remove_reference< T >::type && move(T &&arg) noexcept
Definition utility.hpp:25
char * get_memory(joint_type< T > &obj) noexcept
Definition joint_allocator.hpp:165
detail::joint_stack & get_stack(joint_type< T > &obj) noexcept
Definition joint_allocator.hpp:153
Memory namespace.
Definition heap_allocator.hpp:20
bool operator!=(const memory_resource_allocator &lhs, const memory_resource_allocator &rhs) noexcept
Definition memory_resource_adapter.hpp:216
bool operator==(std::nullptr_t, const joint_ptr< T, RawAllocator > &ptr)
Definition joint_allocator.hpp:393
Foonathan namespace.
Definition ntcore_cpp.h:26
Contains information about an allocator.
Definition error.hpp:23
Specifies whether or not a RawAllocator has shared semantics.
Definition allocator_storage.hpp:534
Specifies whether or not a RawAllocator is thread safe as-is.
Definition threading.hpp:46
Tag type to make the joint size more explicit.
Definition joint_allocator.hpp:118
joint_size(std::size_t s) noexcept
Definition joint_allocator.hpp:121
std::size_t size
Definition joint_allocator.hpp:119
std::false_type propagate_on_container_swap
Definition joint_allocator.hpp:601
std::false_type propagate_on_container_copy_assignment
Definition joint_allocator.hpp:603
static AllocReference select_on_container_copy_construction(const AllocReference &)
Definition joint_allocator.hpp:606
std::false_type propagate_on_container_move_assignment
Definition joint_allocator.hpp:602
Controls the propagation of a std_allocator for a certain RawAllocator.
Definition std_allocator.hpp:50
#define WPI_MEMORY_ASSERT(Expr)
Definition assert.hpp:46