From 10d5c141f2f0fbd89a928cef2173f321f4d11af7 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Wed, 19 Dec 2018 17:16:57 +1100 Subject: [PATCH] alloc: rationalise the 'arena' allocator --- CMakeLists.txt | 16 +- alloc/easy.hpp | 279 +++++++++++++++++++++++++++++++++ alloc/raw/aligned/direct.hpp | 49 ++++-- alloc/raw/aligned/foreign.hpp | 64 ++++---- alloc/raw/linear.cpp | 22 ++- alloc/raw/linear.hpp | 77 +++++---- alloc/raw/stack.hpp | 2 +- alloc/std.hpp | 34 ++++ alloc/traits.hpp | 55 +++++++ memory/buffer/paged.cpp | 4 +- memory/buffer/simple.cpp | 6 + memory/buffer/simple.hpp | 2 +- pointer.hpp | 38 +++-- test/alloc/aligned/direct.cpp | 10 +- test/alloc/aligned/foreign.cpp | 8 +- test/alloc/easy.cpp | 66 ++++++++ test/alloc/linear.cpp | 10 +- 17 files changed, 617 insertions(+), 125 deletions(-) create mode 100644 alloc/easy.hpp create mode 100644 alloc/std.hpp create mode 100644 alloc/traits.hpp create mode 100644 test/alloc/easy.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 765bb32e..6ecd6982 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -195,10 +195,11 @@ list ( alloc/fwd.hpp alloc/allocator.cpp alloc/allocator.hpp - alloc/arena.cpp - alloc/arena.hpp - alloc/forwarding.cpp - alloc/forwarding.hpp + #alloc/arena.cpp + #alloc/arena.hpp + alloc/easy.hpp + #alloc/forwarding.cpp + #alloc/forwarding.hpp alloc/raw/traits.hpp alloc/raw/affix.cpp alloc/raw/affix.hpp @@ -215,6 +216,8 @@ list ( alloc/raw/null.hpp alloc/raw/stack.cpp alloc/raw/stack.hpp + alloc/std.hpp + alloc/traits.hpp annotation.hpp array/darray.hpp array/sarray.cpp @@ -521,11 +524,12 @@ if (TESTS) algo/sort alloc/aligned/foreign alloc/aligned/direct - alloc/arena + #alloc/arena #alloc/dynamic + alloc/easy alloc/linear alloc/stack - alloc/forwarding + #alloc/forwarding affine array/darray array/sarray diff --git a/alloc/easy.hpp b/alloc/easy.hpp new file mode 100644 index 00000000..6bc8b995 --- /dev/null +++ b/alloc/easy.hpp @@ -0,0 +1,279 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright 2018 Danny Robson + */ + +#pragma once + + +#include "traits.hpp" + +#include "../std.hpp" +#include "../view.hpp" +#include "../memory/buffer/traits.hpp" + +#include +#include + + +namespace cruft::alloc::easy { + /// Provides an interface suitable for allocating and constructing typed + /// objects using a reference to an existing allocator. + template + class passthrough { + public: + explicit passthrough (AllocatorT &_allocator) + : m_allocator (_allocator) + { ; } + + + /// Allocate, construct, and return a type `U` using memory from the + /// underlying allocator with the default alignment. + template + U* + acquire (Args&&...args)& + { + auto memory = m_allocator.allocate (sizeof (U), alignof (U)); + try { + return new (memory.data ()) U (std::forward (args)...); + } catch (...) { + m_allocator.deallocate (memory.data (), sizeof (U), alignof (U)); + throw; + } + } + + + /// Destruct and deallocate an object which has previously been + /// allocated through this interface. + template + void release (U *ptr) + { + ptr->~U (); + m_allocator.deallocate (ptr, sizeof (U), alignof (U)); + } + + + /// Create an object of type `U` using `acquire` and wrap the result + /// in a std::unique_ptr that will call release on this interface at d + /// struction time. + template + auto unique (Args &&...args)& + { + struct deleter { + passthrough &m_owner; + + void operator() (U *ptr) + { return m_owner.release (ptr); } + }; + + return std::unique_ptr ( + acquire (std::forward (args)...), + deleter {*this} + ); + } + + + template + decltype (auto) + offset (T const *ptr) + { + return m_allocator.offset (ptr); + } + + + decltype(auto) data (void) { return m_allocator.data (); } + decltype(auto) begin (void) { return m_allocator.begin (); } + decltype(auto) end (void) { return m_allocator.end (); } + + decltype(auto) remain (void) { return m_allocator.remain (); } + + decltype(auto) offset (void const *ptr) { return m_allocator.offset (ptr); } + + + private: + AllocatorT &m_allocator; + }; + + + /// An interfaces that manages an allocator and a backing store, and + /// provides a simple interface for constructing arbitrary objects using + /// the child objects. + template < + typename AllocatorT, + typename = std::enable_if_t> + > + class backed { + public: + template + explicit backed (BufferT &_buffer, Args&&...args) + : m_allocator (_buffer, std::forward (args)...) + { ; } + + + /// Allocate, construct, and return a type `U` using memory from the + /// underlying allocator with the default alignment. + template + U* + acquire (Args&&...args)& + { + auto memory = m_allocator.allocate (sizeof (U), alignof (U)); + try { + return new (memory.data ()) U (std::forward (args)...); + } catch (...) { + m_allocator.deallocate (memory.data (), memory.size (), alignof (U)); + throw; + } + } + + + /// Destruct and deallocate an object which has previously been + /// allocated through this interface. + template + void release (U *ptr) + { + ptr->~U (); + m_allocator.deallocate ( + reinterpret_cast (ptr), + sizeof (U), + alignof (U) + ); + } + + + + template + cruft::view + array (std::size_t count) + { + auto mem = m_allocator.allocate (count * sizeof (T), alignof (T)).template cast (); + try { + new (mem.data ()) T[count]; + return mem; + } catch (...) { + m_allocator.deallocate (mem.data (), count * sizeof (T), alignof (T)); + throw; + } + } + + + + + /// Create an object of type `U` using `acquire` and wrap the result + /// in a std::unique_ptr that will call release on this interface at d + /// struction time. + template + auto unique (Args &&...args)& + { + struct deleter { + backed &m_owner; + + void operator() (U *ptr) + { return m_owner.release (ptr); } + }; + + return std::unique_ptr ( + acquire (std::forward (args)...), + deleter {*this} + ); + } + + + decltype(auto) data (void) { return m_allocator.data (); } + decltype(auto) begin (void) { return m_allocator.begin (); } + decltype(auto) end (void) { return m_allocator.end (); } + + decltype(auto) used (void) { return m_allocator.used (); } + decltype(auto) remain (void) { return m_allocator.remain (); } + + decltype(auto) offset (void const *ptr) { return m_allocator.offset (ptr); } + + template + decltype(auto) + offset (cruft::view ptr) + { + return offset (ptr.data ()); + } + + + private: + AllocatorT m_allocator; + }; + + + /// An interfaces that manages an allocator and a backing store, and + /// provides a simple interface for constructing arbitrary objects using + /// the child objects. + template < + typename AllocatorT, + typename BufferT, + typename = std::enable_if_t + && is_allocator_v + > + > + class owned { + public: + explicit owned (BufferT &&_buffer) + : m_buffer (std::move (_buffer)) + , m_allocator (m_buffer) + { ; } + + + /// Allocate, construct, and return a type `U` using memory from the + /// underlying allocator with the default alignment. + template + U* + acquire (Args&&...args)& + { + auto memory = m_allocator.allocate (sizeof (U), alignof (U)); + try { + return new (memory.data ()) U (std::forward (args)...); + } catch (...) { + m_allocator.deallocate (memory.data (), memory.size (), alignof (U)); + throw; + } + } + + + /// Destruct and deallocate an object which has previously been + /// allocated through this interface. + template + void release (U *ptr) + { + ptr->~U (); + m_allocator.deallocate ( + reinterpret_cast (ptr), + sizeof (U), + alignof (U) + ); + } + + + /// Create an object of type `U` using `acquire` and wrap the result + /// in a std::unique_ptr that will call release on this interface at d + /// struction time. + template + auto unique (Args &&...args)& + { + struct deleter { + owned &m_owner; + + void operator() (U *ptr) + { return m_owner.release (ptr); } + }; + + return std::unique_ptr ( + acquire (std::forward (args)...), + deleter {*this} + ); + } + + + private: + BufferT m_buffer; + AllocatorT m_allocator; + }; +}; diff --git a/alloc/raw/aligned/direct.hpp b/alloc/raw/aligned/direct.hpp index 99720682..f5dea4b8 100644 --- a/alloc/raw/aligned/direct.hpp +++ b/alloc/raw/aligned/direct.hpp @@ -3,18 +3,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * - * Copyright 2016 Danny Robson + * Copyright 2016-2018 Danny Robson */ -#ifndef CRUFT_UTIL_ALLOC_RAW_ALIGNED_DIRECT_HPP -#define CRUFT_UTIL_ALLOC_RAW_ALIGNED_DIRECT_HPP +#pragma once +#include "../../../std.hpp" #include "../../../debug.hpp" #include "../../../view.hpp" -#include #include + namespace cruft::alloc::raw::aligned { /// wraps a child allocator and enforces a fixed alignment template @@ -22,26 +22,43 @@ namespace cruft::alloc::raw::aligned { public: /////////////////////////////////////////////////////////////////////// template - direct (cruft::view _data, std::size_t _alignment, Args &&...args): - m_successor (_data, std::forward (args)...), - m_alignment (_alignment) + direct ( + cruft::view _data, + std::size_t _alignment, + Args &&...args + ) + : m_successor (_data, std::forward (args)...) + , m_alignment (_alignment) { ; } /////////////////////////////////////////////////////////////////////// - template - auto - allocate (std::size_t count) + auto allocate (std::size_t bytes) { - return m_successor.template allocate (count, m_alignment); + return m_successor.allocate (bytes, m_alignment); } + //--------------------------------------------------------------------- - template - auto - deallocate (cruft::view ptr) + auto allocate (std::size_t bytes, std::size_t alignment) { - return m_successor.template deallocate (ptr); + (void)alignment; + return m_successor.allocate (bytes, m_alignment); + } + + + //--------------------------------------------------------------------- + auto deallocate (u08 *ptr, std::size_t bytes, std::size_t alignment) + { + (void)alignment; + return m_successor.deallocate (ptr, bytes, m_alignment); + } + + + //--------------------------------------------------------------------- + auto deallocate (u08 *ptr, std::size_t bytes) + { + return m_successor.deallocate (ptr, bytes, m_alignment); } @@ -82,5 +99,3 @@ namespace cruft::alloc::raw::aligned { std::size_t m_alignment; }; } - -#endif diff --git a/alloc/raw/aligned/foreign.hpp b/alloc/raw/aligned/foreign.hpp index 3ac19d3f..dec13839 100644 --- a/alloc/raw/aligned/foreign.hpp +++ b/alloc/raw/aligned/foreign.hpp @@ -3,11 +3,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * - * Copyright 2016 Danny Robson + * Copyright 2016-2018 Danny Robson */ -#ifndef CRUFT_UTIL_ALLOC_RAW_ALIGNED_OFFSET_HPP -#define CRUFT_UTIL_ALLOC_RAW_ALIGNED_OFFSET_HPP +#pragma once #include "direct.hpp" @@ -15,9 +14,9 @@ #include "../../../pointer.hpp" #include "../../../debug.hpp" -#include #include + namespace cruft::alloc::raw::aligned { /// wraps a child allocator and enforces a fixed alignment that is /// independant of the alignment of the provided source buffer. @@ -45,11 +44,11 @@ namespace cruft::alloc::raw::aligned { class foreign { public: template - foreign (cruft::view _data, std::size_t _alignment, Args &&...args): + foreign (cruft::view _data, std::size_t _alignment, Args &&...args): m_successor ( - view { - reinterpret_cast (_alignment), - reinterpret_cast (_alignment + _data.size ()), + view { + reinterpret_cast (_alignment), + reinterpret_cast (_alignment + _data.size ()), }, _alignment, std::forward (args)... @@ -59,38 +58,43 @@ namespace cruft::alloc::raw::aligned { { ; } - template - cruft::view - allocate (std::size_t count) + cruft::view + allocate (std::size_t bytes, std::size_t alignment) { - auto root = m_successor.template allocate (count); - auto base = root.template cast ().data (); - - // we can't use alignment cast here because it will almost - // certainly fail the tests it performs. - return { cruft::cast::ffs (base + m_offset), count }; + (void)alignment; + auto inner = m_successor.allocate (bytes, m_alignment); + return cruft::view { inner.begin () + m_offset, bytes }; } - template - auto - deallocate (cruft::view ptr) - { - auto base = ptr.template cast (); - auto next = base - m_offset; - // we can't use alignment cast here because it will almost - // certainly fail the tests it performs. - return m_successor.template deallocate ( - cruft::view { cruft::cast::ffs (next), ptr.size () } - ); + cruft::view + allocate (std::size_t bytes) + { + return m_successor.allocate (bytes); } + + decltype(auto) + deallocate (u08 *ptr, std::size_t bytes, std::size_t alignment) + { + CHECK_MOD (m_alignment, alignment); + return m_successor.deallocate (ptr - m_offset, bytes, alignment); + } + + + decltype(auto) + deallocate (u08 *ptr, std::size_t bytes) + { + return m_successor.deallocate (ptr - m_offset, bytes, m_alignment); + } + + constexpr auto alignment (void) const noexcept { return m_alignment; } auto offset (const void *ptr) const { return m_successor.offset ( - reinterpret_cast (ptr) - m_offset + reinterpret_cast (ptr) - m_offset ); } @@ -115,5 +119,3 @@ namespace cruft::alloc::raw::aligned { std::size_t m_alignment; }; } - -#endif diff --git a/alloc/raw/linear.cpp b/alloc/raw/linear.cpp index 85661786..5c280ff2 100644 --- a/alloc/raw/linear.cpp +++ b/alloc/raw/linear.cpp @@ -8,6 +8,8 @@ #include "linear.hpp" +#include "../traits.hpp" + #include "../../pointer.hpp" #include "../../debug.hpp" @@ -15,7 +17,7 @@ using cruft::alloc::raw::linear; /////////////////////////////////////////////////////////////////////////////// -linear::linear (cruft::view _data): +linear::linear (cruft::view _data): m_begin (_data.begin ()), m_end (_data.end ()), m_cursor (_data.begin ()) @@ -43,7 +45,7 @@ linear::operator= (cruft::alloc::raw::linear &&rhs) /////////////////////////////////////////////////////////////////////////////// -std::byte* +u08* linear::data (void) { return m_begin; @@ -51,7 +53,7 @@ linear::data (void) //----------------------------------------------------------------------------- -const std::byte* +const u08* linear::data (void) const { return m_begin; @@ -59,7 +61,7 @@ linear::data (void) const //----------------------------------------------------------------------------- -std::byte* +u08* linear::begin (void) { return m_begin; @@ -67,7 +69,7 @@ linear::begin (void) //----------------------------------------------------------------------------- -const std::byte* +const u08* linear::begin (void) const { return m_begin; @@ -75,7 +77,7 @@ linear::begin (void) const //----------------------------------------------------------------------------- -std::byte* +u08* linear::end (void) { return m_end; @@ -83,7 +85,7 @@ linear::end (void) //----------------------------------------------------------------------------- -const std::byte* +const u08* linear::end (void) const { return m_end; @@ -94,7 +96,7 @@ linear::end (void) const size_t linear::offset (const void *_ptr) const { - auto ptr = reinterpret_cast (_ptr); + auto ptr = reinterpret_cast (_ptr); CHECK_GE (ptr, m_begin); return ptr - m_begin; @@ -131,3 +133,7 @@ linear::remain (void) const { return capacity () - used (); } + + +/////////////////////////////////////////////////////////////////////////////// +static_assert (cruft::alloc::is_allocator_v); diff --git a/alloc/raw/linear.hpp b/alloc/raw/linear.hpp index f1b57587..dfd047c4 100644 --- a/alloc/raw/linear.hpp +++ b/alloc/raw/linear.hpp @@ -3,18 +3,20 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * - * Copyright 2015 Danny Robson + * Copyright 2015-2018 Danny Robson */ -#ifndef CRUFT_UTIL_ALLOC_RAW_LINEAR_HPP -#define CRUFT_UTIL_ALLOC_RAW_LINEAR_HPP +#pragma once +#include "../../std.hpp" #include "../../view.hpp" #include "../../pointer.hpp" +#include "../../memory/buffer/traits.hpp" #include #include + namespace cruft::alloc::raw { // allocate progressively across a buffer without concern for deallocation. // deallocation is a noop; the only way to free allocations is via reset. @@ -26,52 +28,67 @@ namespace cruft::alloc::raw { linear (linear&&); linear& operator= (linear&&); - linear (cruft::view _data); + linear (cruft::view _data); - template - cruft::view - allocate (size_t count) + template < + typename BufferT, + typename = std::enable_if_t< + memory::buffer::is_buffer_v + > + > + linear (BufferT &_buffer) + : linear (cruft::view (_buffer)) + { ; } + + + cruft::view + allocate (size_t bytes) { - auto const bytes = count * sizeof (T); if (m_cursor + bytes > m_end) throw std::bad_alloc (); auto ptr = m_cursor; m_cursor += bytes; - return { cruft::cast::alignment (ptr), count }; + return { ptr, bytes }; } - template - cruft::view - allocate (size_t count, size_t alignment) + cruft::view + allocate (size_t bytes, std::size_t alignment) { - auto const bytes = count * sizeof (T); - - auto ptr = cruft::align_up (m_cursor, alignment); + auto ptr = cruft::align::up (m_cursor, alignment); if (ptr + bytes > m_end) throw std::bad_alloc (); m_cursor = ptr + bytes; - return { cruft::cast::alignment (ptr), count }; + return { ptr, bytes }; } - template - void deallocate (cruft::view ptr) + + void deallocate (void *ptr, std::size_t bytes, std::size_t alignment) { (void)ptr; + (void)bytes; + (void)alignment; } - std::byte* data (void); - std::byte* begin (void); - std::byte* end (void); - std::byte* cursor (void); - const std::byte* data (void) const; - const std::byte* begin (void) const; - const std::byte* end (void) const; - const std::byte* cursor (void) const; + void deallocate (void *ptr, std::size_t bytes) + { + return deallocate (ptr, bytes, alignof (std::max_align_t)); + } + + + u08* data (void); + u08* begin (void); + u08* end (void); + u08* cursor (void); + + u08 const* data (void) const; + u08 const* begin (void) const; + u08 const* end (void) const; + u08 const* cursor (void) const; size_t offset (const void*) const; @@ -101,11 +118,9 @@ namespace cruft::alloc::raw { protected: // The begin and end iterators should be constant but that interferes // with move operators so we need to leave them mutable. - std::byte *m_begin; - std::byte *m_end; + u08 *m_begin; + u08 *m_end; - std::byte *m_cursor; + u08 *m_cursor; }; } - -#endif diff --git a/alloc/raw/stack.hpp b/alloc/raw/stack.hpp index 382f2b7c..88cded2c 100644 --- a/alloc/raw/stack.hpp +++ b/alloc/raw/stack.hpp @@ -40,7 +40,7 @@ namespace cruft::alloc::raw { // align the outgoing pointer if required alignment = cruft::max (MIN_ALIGNMENT, alignment); - ptr = cruft::align_up (ptr, alignment); + ptr = cruft::align::up (ptr, alignment); // ensure we haven't overrun our allocated segment if (ptr + bytes > m_end) diff --git a/alloc/std.hpp b/alloc/std.hpp new file mode 100644 index 00000000..6540cea2 --- /dev/null +++ b/alloc/std.hpp @@ -0,0 +1,34 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright 2018 Danny Robson + */ + +#pragma once + +#include + +namespace cruft::alloc { + template + struct std { + using value_type = ValueT; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + + [[nodiscard]] ValueT* allocate (std::size_t n) + { + return m_allocator.allocate (n * sizeof (ValueT), alignof (ValueT)); + } + + void + deallocate (ValueT *ptr, std::size_t n) + { + m_allocator.deallocate (ptr, n * sizeof (ValueT), alignof (ValueT)); + } + + private: + AllocatorT m_allocator; + }; +} \ No newline at end of file diff --git a/alloc/traits.hpp b/alloc/traits.hpp new file mode 100644 index 00000000..f70abb0b --- /dev/null +++ b/alloc/traits.hpp @@ -0,0 +1,55 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright 2018 Danny Robson + */ + +#pragma once + +#include + + +namespace cruft::alloc { + /////////////////////////////////////////////////////////////////////////// + /// A trait that evaluates to true if the queried type models cruft::allocator + template < + typename AllocatorT, + typename = std::void_t<> + > + struct is_allocator + : public std::false_type + { }; + + + //------------------------------------------------------------------------- + template + struct is_allocator ().allocate (0 )), + decltype(std::declval ().allocate (0, 0)), + + //// Provides aligned an unaligned deallocation + decltype(std::declval ().deallocate (nullptr, 0)), + decltype(std::declval ().deallocate (nullptr, 0, 0)), + + //// Provides capacity/used/remain + decltype(std::declval ().capacity ()), + decltype(std::declval ().used ()), + decltype(std::declval ().remain ()), + + decltype(std::declval ().begin ()), + decltype(std::declval ().end ()), + + void + > + > : public std::true_type + { }; + + + //------------------------------------------------------------------------- + template + constexpr auto is_allocator_v = is_allocator::value; +} diff --git a/memory/buffer/paged.cpp b/memory/buffer/paged.cpp index 14f432a6..0fb715fd 100644 --- a/memory/buffer/paged.cpp +++ b/memory/buffer/paged.cpp @@ -89,8 +89,8 @@ paged::apply_prot (cruft::view region, int prot) // bump the request up to page aligned static_assert (sizeof (value_type) == 1); auto const alignment = pagesize (); - auto const first = align_down (region.begin (), alignment); - auto const last = align_up (region.end (), alignment); + auto const first = align::down (region.begin (), alignment); + auto const last = align::up (region.end (), alignment); if (MAP_FAILED == mmap (first, last - first, prot, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, diff --git a/memory/buffer/simple.cpp b/memory/buffer/simple.cpp index 29de296d..739a1758 100644 --- a/memory/buffer/simple.cpp +++ b/memory/buffer/simple.cpp @@ -8,6 +8,8 @@ #include "simple.hpp" +#include "traits.hpp" + using cruft::memory::buffer::simple; @@ -26,3 +28,7 @@ simple::value_type* simple::end (void)& { return begin () + m_size; } //----------------------------------------------------------------------------- simple::value_type const* simple::begin (void) const& { return m_base.get (); } simple::value_type const* simple::end (void) const& { return begin () + m_size; } + + +/////////////////////////////////////////////////////////////////////////////// +static_assert (cruft::memory::buffer::is_buffer_v); diff --git a/memory/buffer/simple.hpp b/memory/buffer/simple.hpp index a515bb5a..3570ed24 100644 --- a/memory/buffer/simple.hpp +++ b/memory/buffer/simple.hpp @@ -48,7 +48,7 @@ namespace cruft::memory::buffer { std::size_t capacity (void) const; private: - std::unique_ptr m_base; + std::unique_ptr m_base; std::size_t m_size; }; } diff --git a/pointer.hpp b/pointer.hpp index c87f8cb1..5e871de8 100644 --- a/pointer.hpp +++ b/pointer.hpp @@ -12,11 +12,11 @@ #include -namespace cruft { +namespace cruft::align { /////////////////////////////////////////////////////////////////////////// /// round the pointer upwards to satisfy the provided alignment constexpr inline uintptr_t - align_up (uintptr_t ptr, size_t alignment) + up (uintptr_t ptr, size_t alignment) { // we perform this as two steps to avoid unnecessarily incrementing when // remainder is zero. @@ -30,23 +30,23 @@ namespace cruft { /// round the pointer upwards to satisfy the provided alignment template constexpr T* - align_up (T *_ptr, size_t alignment) + up (T *ptr, size_t alignment) { // we perform this as two steps to avoid unnecessarily incrementing when // remainder is zero. return reinterpret_cast( - align_up (reinterpret_cast (_ptr), alignment) + up (reinterpret_cast (ptr), alignment) ); } - ///------------------------------------------------------------------------ - /// round the pointer upwards to the nearest valid alignment for T + //------------------------------------------------------------------------- template - constexpr auto - align_up (T *t) + constexpr T* + up (T *ptr, std::align_val_t _alignment) { - return align_up (t, alignof (T)); + auto alignment = std::underlying_type_t (_alignment); + return up (ptr, alignment); } @@ -54,15 +54,25 @@ namespace cruft { /// round the pointer upwards to the nearest valid alignment for T template constexpr auto - align_up (uintptr_t ptr) + up (T *t) { - return align_up (ptr, alignof (T)); + return up (t, alignof (T)); + } + + + ///------------------------------------------------------------------------ + /// round the pointer upwards to the nearest valid alignment for T + template + constexpr auto + up (uintptr_t ptr) + { + return up (ptr, alignof (T)); } /////////////////////////////////////////////////////////////////////////// constexpr inline uintptr_t - align_down (uintptr_t ptr, size_t alignment) + down (uintptr_t ptr, size_t alignment) { return ptr - ptr % alignment; } @@ -71,10 +81,10 @@ namespace cruft { //------------------------------------------------------------------------- template constexpr T* - align_down (T *ptr, size_t alignment) + down (T *ptr, size_t alignment) { return reinterpret_cast ( - align_down (reinterpret_cast (ptr), alignment) + down (reinterpret_cast (ptr), alignment) ); } } diff --git a/test/alloc/aligned/direct.cpp b/test/alloc/aligned/direct.cpp index 166fcd11..a049123c 100644 --- a/test/alloc/aligned/direct.cpp +++ b/test/alloc/aligned/direct.cpp @@ -13,7 +13,7 @@ main (int, char**) // satisfy a sane allocation request during testing, just in case the // underlying code actually decides to do something; we don't be touching // it ourselves. - static std::byte buffer[1024*1024]; + static u08 buffer[1024*1024]; // pick an alignment that isn't likely to be satisfied by any likely // underlying allocator. if the allocation fulfills this alignment then @@ -29,10 +29,10 @@ main (int, char**) // alignment to produce a likely system alignment. eg, 3 + 5 == 8 which is // a power-of-2. uintptr_t result[4] = { - reinterpret_cast(alloc.allocate (9).data ()), // just over a power of two - reinterpret_cast(alloc.allocate (1).data ()), // a single byte - reinterpret_cast(alloc.allocate (64).data ()), // a cache line - reinterpret_cast(alloc.allocate (250).data ()) // multiple lines, but not a power of two + reinterpret_cast(alloc.allocate ( 9).data ()), // just over a power of two + reinterpret_cast(alloc.allocate ( 1).data ()), // a single byte + reinterpret_cast(alloc.allocate ( 64).data ()), // a cache line + reinterpret_cast(alloc.allocate (250).data ()) // multiple lines, but not a power of two }; tap.expect ( diff --git a/test/alloc/aligned/foreign.cpp b/test/alloc/aligned/foreign.cpp index 395f9cb9..42bb041c 100644 --- a/test/alloc/aligned/foreign.cpp +++ b/test/alloc/aligned/foreign.cpp @@ -6,13 +6,13 @@ int main () { - static std::byte buffer[1024*1024]; + static u08 buffer[1024*1024]; static constexpr std::size_t alignment = 3; static constexpr std::size_t increment = 1; // ensure we have an base pointer that's off-by-one to a likely natural // system alignment - std::byte* base = cruft::align_up ( + u08* base = cruft::align::up ( std::data (buffer), alignof (std::max_align_t) ) + increment; @@ -27,7 +27,7 @@ main () // ensure the first element allocated falls at the base address tap.expect_eq (base, alloc.data (), "allocator base address is the supplied base address"); - tap.expect_eq (base, alloc.allocate (8).data (), "first allocation is the supplied base address"); + tap.expect_eq (base, alloc.allocate (8).data (), "first allocation is the supplied base address"); // allocate a range of values and make sure they all satisfy our alignment. // don't choose values which are likely to combine with the testing @@ -45,7 +45,7 @@ main () }; for (const auto &t: TESTS) { - auto ptr = reinterpret_cast (alloc.allocate (t.size).data ()); + auto ptr = reinterpret_cast (alloc.allocate (t.size).data ()); auto offset = ptr - reinterpret_cast (base); tap.expect_mod (offset, alignment, "%s", t.message); } diff --git a/test/alloc/easy.cpp b/test/alloc/easy.cpp new file mode 100644 index 00000000..c9162be5 --- /dev/null +++ b/test/alloc/easy.cpp @@ -0,0 +1,66 @@ +#include "alloc/easy.hpp" +#include "alloc/raw/linear.hpp" +#include "memory/buffer/simple.hpp" +#include "tap.hpp" + +#include + + +/////////////////////////////////////////////////////////////////////////////// +struct setter { + setter (const setter&) = delete; + + setter (bool &_target): + target (_target) + { target = false; } + + ~setter () + { target = true; } + + bool ⌖ +}; + + +/////////////////////////////////////////////////////////////////////////////// +int main () +{ + static constexpr std::size_t elements = 4096; + cruft::memory::buffer::simple buf (elements); + + cruft::alloc::easy::owned < + cruft::alloc::raw::linear, + cruft::memory::buffer::simple + > alloc ( + std::move (buf) + ); + + cruft::TAP::logger tap; + + bool flag = true; + + // double check our testing object is working, because I'm tired and stupid + { + setter val (flag); + CHECK (!flag); + } + CHECK (flag); + + // ensure manual acquire and release calls constructors and destructors + { + auto obj = alloc.acquire (flag); + tap.expect_eq (flag, false, "backed::acquire calls constructor"); + + alloc.release (obj); + tap.expect_eq (flag, true, "backed::release calls destructor"); + } + + // ensure unique_ptr like objects call constructors and destructors + { + auto obj = alloc.unique (flag); + tap.expect_eq (flag, false, "backed::unique acquire calls constructor"); + } + + tap.expect_eq (flag, true, "backed::unique release calls destructor"); + + return tap.status (); +} \ No newline at end of file diff --git a/test/alloc/linear.cpp b/test/alloc/linear.cpp index 10846eec..40e199db 100644 --- a/test/alloc/linear.cpp +++ b/test/alloc/linear.cpp @@ -10,7 +10,7 @@ main (void) constexpr size_t BUFFER_SIZE = 1024; - alignas (std::max_align_t) std::byte memory[BUFFER_SIZE]; + alignas (std::max_align_t) u08 memory[BUFFER_SIZE]; cruft::alloc::raw::linear store (cruft::make_view (memory)); tap.expect_eq (store.begin (), std::begin (memory), "base pointers match"); @@ -18,12 +18,12 @@ main (void) tap.expect_eq (store.capacity (), BUFFER_SIZE, "bytes capacity matches"); tap.expect_throw ( - [&] (void) { store.allocate (BUFFER_SIZE + 1, 1); }, + [&] (void) { store.allocate (BUFFER_SIZE + 1, 1); }, "excessive allocation throws bad_alloc" ); tap.expect_nothrow ( - [&] (void) { store.allocate (BUFFER_SIZE); }, + [&] (void) { store.allocate (BUFFER_SIZE); }, "maximum allocation succeeds" ); @@ -31,14 +31,14 @@ main (void) tap.expect_eq (store.remain (), 0u, "bytes remain matches"); tap.expect_throw ( - [&] (void) { store.allocate (1, 1); }, + [&] (void) { store.allocate (1, 1); }, "minimum allocation fails after exhaustion" ); store.reset (); tap.expect_nothrow ( - [&] (void) { store.allocate (1, 1); }, + [&] (void) { store.allocate (1, 1); }, "minimum allocation succeeds after reset" );