alloc: rationalise the 'arena' allocator

This commit is contained in:
Danny Robson 2018-12-19 17:16:57 +11:00
parent 4e055f5f0b
commit 10d5c141f2
17 changed files with 617 additions and 125 deletions

View File

@ -195,10 +195,11 @@ list (
alloc/fwd.hpp alloc/fwd.hpp
alloc/allocator.cpp alloc/allocator.cpp
alloc/allocator.hpp alloc/allocator.hpp
alloc/arena.cpp #alloc/arena.cpp
alloc/arena.hpp #alloc/arena.hpp
alloc/forwarding.cpp alloc/easy.hpp
alloc/forwarding.hpp #alloc/forwarding.cpp
#alloc/forwarding.hpp
alloc/raw/traits.hpp alloc/raw/traits.hpp
alloc/raw/affix.cpp alloc/raw/affix.cpp
alloc/raw/affix.hpp alloc/raw/affix.hpp
@ -215,6 +216,8 @@ list (
alloc/raw/null.hpp alloc/raw/null.hpp
alloc/raw/stack.cpp alloc/raw/stack.cpp
alloc/raw/stack.hpp alloc/raw/stack.hpp
alloc/std.hpp
alloc/traits.hpp
annotation.hpp annotation.hpp
array/darray.hpp array/darray.hpp
array/sarray.cpp array/sarray.cpp
@ -521,11 +524,12 @@ if (TESTS)
algo/sort algo/sort
alloc/aligned/foreign alloc/aligned/foreign
alloc/aligned/direct alloc/aligned/direct
alloc/arena #alloc/arena
#alloc/dynamic #alloc/dynamic
alloc/easy
alloc/linear alloc/linear
alloc/stack alloc/stack
alloc/forwarding #alloc/forwarding
affine affine
array/darray array/darray
array/sarray array/sarray

279
alloc/easy.hpp Normal file
View File

@ -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 <danny@nerdcruft.net>
*/
#pragma once
#include "traits.hpp"
#include "../std.hpp"
#include "../view.hpp"
#include "../memory/buffer/traits.hpp"
#include <utility>
#include <memory>
namespace cruft::alloc::easy {
/// Provides an interface suitable for allocating and constructing typed
/// objects using a reference to an existing allocator.
template <typename AllocatorT>
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 <typename U, typename ...Args>
U*
acquire (Args&&...args)&
{
auto memory = m_allocator.allocate (sizeof (U), alignof (U));
try {
return new (memory.data ()) U (std::forward<Args> (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 <typename U>
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 <typename U, typename ...Args>
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> (args)...),
deleter {*this}
);
}
template <typename T>
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<is_allocator_v<AllocatorT>>
>
class backed {
public:
template <typename BufferT, typename ...Args>
explicit backed (BufferT &_buffer, Args&&...args)
: m_allocator (_buffer, std::forward<Args> (args)...)
{ ; }
/// Allocate, construct, and return a type `U` using memory from the
/// underlying allocator with the default alignment.
template <typename U, typename ...Args>
U*
acquire (Args&&...args)&
{
auto memory = m_allocator.allocate (sizeof (U), alignof (U));
try {
return new (memory.data ()) U (std::forward<Args> (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 <typename U>
void release (U *ptr)
{
ptr->~U ();
m_allocator.deallocate (
reinterpret_cast<u08*> (ptr),
sizeof (U),
alignof (U)
);
}
template <typename T>
cruft::view<T*>
array (std::size_t count)
{
auto mem = m_allocator.allocate (count * sizeof (T), alignof (T)).template cast<T*> ();
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 <typename U, typename ...Args>
auto unique (Args &&...args)&
{
struct deleter {
backed &m_owner;
void operator() (U *ptr)
{ return m_owner.release (ptr); }
};
return std::unique_ptr<U,deleter> (
acquire<U> (std::forward<Args> (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 <typename T>
decltype(auto)
offset (cruft::view<T*> 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<true
//&& memory::buffer::is_buffer_v<BufferT>
&& is_allocator_v<AllocatorT>
>
>
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 <typename U, typename ...Args>
U*
acquire (Args&&...args)&
{
auto memory = m_allocator.allocate (sizeof (U), alignof (U));
try {
return new (memory.data ()) U (std::forward<Args> (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 <typename U>
void release (U *ptr)
{
ptr->~U ();
m_allocator.deallocate (
reinterpret_cast<u08*> (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 <typename U, typename ...Args>
auto unique (Args &&...args)&
{
struct deleter {
owned &m_owner;
void operator() (U *ptr)
{ return m_owner.release (ptr); }
};
return std::unique_ptr<U,deleter> (
acquire<U> (std::forward<Args> (args)...),
deleter {*this}
);
}
private:
BufferT m_buffer;
AllocatorT m_allocator;
};
};

View File

@ -3,18 +3,18 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
* *
* Copyright 2016 Danny Robson <danny@nerdcruft.net> * Copyright 2016-2018 Danny Robson <danny@nerdcruft.net>
*/ */
#ifndef CRUFT_UTIL_ALLOC_RAW_ALIGNED_DIRECT_HPP #pragma once
#define CRUFT_UTIL_ALLOC_RAW_ALIGNED_DIRECT_HPP
#include "../../../std.hpp"
#include "../../../debug.hpp" #include "../../../debug.hpp"
#include "../../../view.hpp" #include "../../../view.hpp"
#include <cstddef>
#include <utility> #include <utility>
namespace cruft::alloc::raw::aligned { namespace cruft::alloc::raw::aligned {
/// wraps a child allocator and enforces a fixed alignment /// wraps a child allocator and enforces a fixed alignment
template <typename ChildT> template <typename ChildT>
@ -22,26 +22,43 @@ namespace cruft::alloc::raw::aligned {
public: public:
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
template <typename ...Args> template <typename ...Args>
direct (cruft::view<std::byte*> _data, std::size_t _alignment, Args &&...args): direct (
m_successor (_data, std::forward<Args> (args)...), cruft::view<u08*> _data,
m_alignment (_alignment) std::size_t _alignment,
Args &&...args
)
: m_successor (_data, std::forward<Args> (args)...)
, m_alignment (_alignment)
{ ; } { ; }
/////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////
template <typename T> auto allocate (std::size_t bytes)
auto
allocate (std::size_t count)
{ {
return m_successor.template allocate<T> (count, m_alignment); return m_successor.allocate (bytes, m_alignment);
} }
//--------------------------------------------------------------------- //---------------------------------------------------------------------
template <typename T> auto allocate (std::size_t bytes, std::size_t alignment)
auto
deallocate (cruft::view<T*> ptr)
{ {
return m_successor.template deallocate<T> (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; std::size_t m_alignment;
}; };
} }
#endif

View File

@ -3,11 +3,10 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
* *
* Copyright 2016 Danny Robson <danny@nerdcruft.net> * Copyright 2016-2018 Danny Robson <danny@nerdcruft.net>
*/ */
#ifndef CRUFT_UTIL_ALLOC_RAW_ALIGNED_OFFSET_HPP #pragma once
#define CRUFT_UTIL_ALLOC_RAW_ALIGNED_OFFSET_HPP
#include "direct.hpp" #include "direct.hpp"
@ -15,9 +14,9 @@
#include "../../../pointer.hpp" #include "../../../pointer.hpp"
#include "../../../debug.hpp" #include "../../../debug.hpp"
#include <cstddef>
#include <utility> #include <utility>
namespace cruft::alloc::raw::aligned { namespace cruft::alloc::raw::aligned {
/// wraps a child allocator and enforces a fixed alignment that is /// wraps a child allocator and enforces a fixed alignment that is
/// independant of the alignment of the provided source buffer. /// independant of the alignment of the provided source buffer.
@ -45,11 +44,11 @@ namespace cruft::alloc::raw::aligned {
class foreign { class foreign {
public: public:
template <typename ...Args> template <typename ...Args>
foreign (cruft::view<std::byte*> _data, std::size_t _alignment, Args &&...args): foreign (cruft::view<u08*> _data, std::size_t _alignment, Args &&...args):
m_successor ( m_successor (
view<std::byte*> { view<u08*> {
reinterpret_cast<std::byte*> (_alignment), reinterpret_cast<u08*> (_alignment),
reinterpret_cast<std::byte*> (_alignment + _data.size ()), reinterpret_cast<u08*> (_alignment + _data.size ()),
}, },
_alignment, _alignment,
std::forward<Args> (args)... std::forward<Args> (args)...
@ -59,38 +58,43 @@ namespace cruft::alloc::raw::aligned {
{ ; } { ; }
template <typename T> cruft::view<u08*>
cruft::view<T*> allocate (std::size_t bytes, std::size_t alignment)
allocate (std::size_t count)
{ {
auto root = m_successor.template allocate<T> (count); (void)alignment;
auto base = root.template cast<char*> ().data (); auto inner = m_successor.allocate (bytes, m_alignment);
return cruft::view { inner.begin () + m_offset, bytes };
// we can't use alignment cast here because it will almost
// certainly fail the tests it performs.
return { cruft::cast::ffs<T*> (base + m_offset), count };
} }
template <typename T>
auto
deallocate (cruft::view<T*> ptr)
{
auto base = ptr.template cast<char*> ();
auto next = base - m_offset;
// we can't use alignment cast here because it will almost cruft::view<u08*>
// certainly fail the tests it performs. allocate (std::size_t bytes)
return m_successor.template deallocate<T> ( {
cruft::view<T*> { cruft::cast::ffs<T*> (next), ptr.size () } 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; } constexpr auto alignment (void) const noexcept { return m_alignment; }
auto offset (const void *ptr) const auto offset (const void *ptr) const
{ {
return m_successor.offset ( return m_successor.offset (
reinterpret_cast<const std::byte*> (ptr) - m_offset reinterpret_cast<u08 const*> (ptr) - m_offset
); );
} }
@ -115,5 +119,3 @@ namespace cruft::alloc::raw::aligned {
std::size_t m_alignment; std::size_t m_alignment;
}; };
} }
#endif

View File

@ -8,6 +8,8 @@
#include "linear.hpp" #include "linear.hpp"
#include "../traits.hpp"
#include "../../pointer.hpp" #include "../../pointer.hpp"
#include "../../debug.hpp" #include "../../debug.hpp"
@ -15,7 +17,7 @@ using cruft::alloc::raw::linear;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
linear::linear (cruft::view<std::byte*> _data): linear::linear (cruft::view<u08*> _data):
m_begin (_data.begin ()), m_begin (_data.begin ()),
m_end (_data.end ()), m_end (_data.end ()),
m_cursor (_data.begin ()) m_cursor (_data.begin ())
@ -43,7 +45,7 @@ linear::operator= (cruft::alloc::raw::linear &&rhs)
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
std::byte* u08*
linear::data (void) linear::data (void)
{ {
return m_begin; return m_begin;
@ -51,7 +53,7 @@ linear::data (void)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
const std::byte* const u08*
linear::data (void) const linear::data (void) const
{ {
return m_begin; return m_begin;
@ -59,7 +61,7 @@ linear::data (void) const
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
std::byte* u08*
linear::begin (void) linear::begin (void)
{ {
return m_begin; return m_begin;
@ -67,7 +69,7 @@ linear::begin (void)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
const std::byte* const u08*
linear::begin (void) const linear::begin (void) const
{ {
return m_begin; return m_begin;
@ -75,7 +77,7 @@ linear::begin (void) const
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
std::byte* u08*
linear::end (void) linear::end (void)
{ {
return m_end; return m_end;
@ -83,7 +85,7 @@ linear::end (void)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
const std::byte* const u08*
linear::end (void) const linear::end (void) const
{ {
return m_end; return m_end;
@ -94,7 +96,7 @@ linear::end (void) const
size_t size_t
linear::offset (const void *_ptr) const linear::offset (const void *_ptr) const
{ {
auto ptr = reinterpret_cast<const std::byte*> (_ptr); auto ptr = reinterpret_cast<const u08*> (_ptr);
CHECK_GE (ptr, m_begin); CHECK_GE (ptr, m_begin);
return ptr - m_begin; return ptr - m_begin;
@ -131,3 +133,7 @@ linear::remain (void) const
{ {
return capacity () - used (); return capacity () - used ();
} }
///////////////////////////////////////////////////////////////////////////////
static_assert (cruft::alloc::is_allocator_v<linear>);

View File

@ -3,18 +3,20 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
* *
* Copyright 2015 Danny Robson <danny@nerdcruft.net> * Copyright 2015-2018 Danny Robson <danny@nerdcruft.net>
*/ */
#ifndef CRUFT_UTIL_ALLOC_RAW_LINEAR_HPP #pragma once
#define CRUFT_UTIL_ALLOC_RAW_LINEAR_HPP
#include "../../std.hpp"
#include "../../view.hpp" #include "../../view.hpp"
#include "../../pointer.hpp" #include "../../pointer.hpp"
#include "../../memory/buffer/traits.hpp"
#include <cstddef> #include <cstddef>
#include <iterator> #include <iterator>
namespace cruft::alloc::raw { namespace cruft::alloc::raw {
// allocate progressively across a buffer without concern for deallocation. // allocate progressively across a buffer without concern for deallocation.
// deallocation is a noop; the only way to free allocations is via reset. // deallocation is a noop; the only way to free allocations is via reset.
@ -26,52 +28,67 @@ namespace cruft::alloc::raw {
linear (linear&&); linear (linear&&);
linear& operator= (linear&&); linear& operator= (linear&&);
linear (cruft::view<std::byte*> _data); linear (cruft::view<u08*> _data);
template <typename T> template <
cruft::view<T*> typename BufferT,
allocate (size_t count) typename = std::enable_if_t<
memory::buffer::is_buffer_v<BufferT>
>
>
linear (BufferT &_buffer)
: linear (cruft::view (_buffer))
{ ; }
cruft::view<u08*>
allocate (size_t bytes)
{ {
auto const bytes = count * sizeof (T);
if (m_cursor + bytes > m_end) if (m_cursor + bytes > m_end)
throw std::bad_alloc (); throw std::bad_alloc ();
auto ptr = m_cursor; auto ptr = m_cursor;
m_cursor += bytes; m_cursor += bytes;
return { cruft::cast::alignment<T*> (ptr), count }; return { ptr, bytes };
} }
template <typename T> cruft::view<u08*>
cruft::view<T*> allocate (size_t bytes, std::size_t alignment)
allocate (size_t count, 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) if (ptr + bytes > m_end)
throw std::bad_alloc (); throw std::bad_alloc ();
m_cursor = ptr + bytes; m_cursor = ptr + bytes;
return { cruft::cast::alignment<T*> (ptr), count }; return { ptr, bytes };
} }
template <typename T>
void deallocate (cruft::view<T*> ptr) void deallocate (void *ptr, std::size_t bytes, std::size_t alignment)
{ {
(void)ptr; (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; void deallocate (void *ptr, std::size_t bytes)
const std::byte* begin (void) const; {
const std::byte* end (void) const; return deallocate (ptr, bytes, alignof (std::max_align_t));
const std::byte* cursor (void) const; }
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; size_t offset (const void*) const;
@ -101,11 +118,9 @@ namespace cruft::alloc::raw {
protected: protected:
// The begin and end iterators should be constant but that interferes // The begin and end iterators should be constant but that interferes
// with move operators so we need to leave them mutable. // with move operators so we need to leave them mutable.
std::byte *m_begin; u08 *m_begin;
std::byte *m_end; u08 *m_end;
std::byte *m_cursor; u08 *m_cursor;
}; };
} }
#endif

View File

@ -40,7 +40,7 @@ namespace cruft::alloc::raw {
// align the outgoing pointer if required // align the outgoing pointer if required
alignment = cruft::max (MIN_ALIGNMENT, alignment); 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 // ensure we haven't overrun our allocated segment
if (ptr + bytes > m_end) if (ptr + bytes > m_end)

34
alloc/std.hpp Normal file
View File

@ -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 <danny@nerdcruft.net>
*/
#pragma once
#include <cstddef>
namespace cruft::alloc {
template <typename ValueT, typename AllocatorT>
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;
};
}

55
alloc/traits.hpp Normal file
View File

@ -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 <danny@nerdcruft.net>
*/
#pragma once
#include <type_traits>
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 <typename AllocatorT>
struct is_allocator<AllocatorT,
std::void_t<
// Provides aligned and unaligned allocation
decltype(std::declval<AllocatorT> ().allocate (0 )),
decltype(std::declval<AllocatorT> ().allocate (0, 0)),
//// Provides aligned an unaligned deallocation
decltype(std::declval<AllocatorT> ().deallocate (nullptr, 0)),
decltype(std::declval<AllocatorT> ().deallocate (nullptr, 0, 0)),
//// Provides capacity/used/remain
decltype(std::declval<AllocatorT> ().capacity ()),
decltype(std::declval<AllocatorT> ().used ()),
decltype(std::declval<AllocatorT> ().remain ()),
decltype(std::declval<AllocatorT> ().begin ()),
decltype(std::declval<AllocatorT> ().end ()),
void
>
> : public std::true_type
{ };
//-------------------------------------------------------------------------
template <typename AllocatorT>
constexpr auto is_allocator_v = is_allocator<AllocatorT>::value;
}

View File

@ -89,8 +89,8 @@ paged::apply_prot (cruft::view<value_type*> region, int prot)
// bump the request up to page aligned // bump the request up to page aligned
static_assert (sizeof (value_type) == 1); static_assert (sizeof (value_type) == 1);
auto const alignment = pagesize (); auto const alignment = pagesize ();
auto const first = align_down (region.begin (), alignment); auto const first = align::down (region.begin (), alignment);
auto const last = align_up (region.end (), alignment); auto const last = align::up (region.end (), alignment);
if (MAP_FAILED == mmap (first, last - first, prot, if (MAP_FAILED == mmap (first, last - first, prot,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,

View File

@ -8,6 +8,8 @@
#include "simple.hpp" #include "simple.hpp"
#include "traits.hpp"
using cruft::memory::buffer::simple; 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::begin (void) const& { return m_base.get (); }
simple::value_type const* simple::end (void) const& { return begin () + m_size; } simple::value_type const* simple::end (void) const& { return begin () + m_size; }
///////////////////////////////////////////////////////////////////////////////
static_assert (cruft::memory::buffer::is_buffer_v<cruft::memory::buffer::simple>);

View File

@ -48,7 +48,7 @@ namespace cruft::memory::buffer {
std::size_t capacity (void) const; std::size_t capacity (void) const;
private: private:
std::unique_ptr<u08> m_base; std::unique_ptr<u08[]> m_base;
std::size_t m_size; std::size_t m_size;
}; };
} }

View File

@ -12,11 +12,11 @@
#include <cstdint> #include <cstdint>
namespace cruft { namespace cruft::align {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
/// round the pointer upwards to satisfy the provided alignment /// round the pointer upwards to satisfy the provided alignment
constexpr inline uintptr_t 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 // we perform this as two steps to avoid unnecessarily incrementing when
// remainder is zero. // remainder is zero.
@ -30,23 +30,23 @@ namespace cruft {
/// round the pointer upwards to satisfy the provided alignment /// round the pointer upwards to satisfy the provided alignment
template <typename T> template <typename T>
constexpr T* 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 // we perform this as two steps to avoid unnecessarily incrementing when
// remainder is zero. // remainder is zero.
return reinterpret_cast<T*>( return reinterpret_cast<T*>(
align_up (reinterpret_cast<uintptr_t> (_ptr), alignment) up (reinterpret_cast<uintptr_t> (ptr), alignment)
); );
} }
///------------------------------------------------------------------------ //-------------------------------------------------------------------------
/// round the pointer upwards to the nearest valid alignment for T
template <typename T> template <typename T>
constexpr auto constexpr T*
align_up (T *t) up (T *ptr, std::align_val_t _alignment)
{ {
return align_up (t, alignof (T)); auto alignment = std::underlying_type_t<decltype(_alignment)> (_alignment);
return up (ptr, alignment);
} }
@ -54,15 +54,25 @@ namespace cruft {
/// round the pointer upwards to the nearest valid alignment for T /// round the pointer upwards to the nearest valid alignment for T
template <typename T> template <typename T>
constexpr auto 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 <typename T>
constexpr auto
up (uintptr_t ptr)
{
return up (ptr, alignof (T));
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
constexpr inline uintptr_t constexpr inline uintptr_t
align_down (uintptr_t ptr, size_t alignment) down (uintptr_t ptr, size_t alignment)
{ {
return ptr - ptr % alignment; return ptr - ptr % alignment;
} }
@ -71,10 +81,10 @@ namespace cruft {
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template <typename T> template <typename T>
constexpr T* constexpr T*
align_down (T *ptr, size_t alignment) down (T *ptr, size_t alignment)
{ {
return reinterpret_cast<T*> ( return reinterpret_cast<T*> (
align_down (reinterpret_cast<uintptr_t> (ptr), alignment) down (reinterpret_cast<uintptr_t> (ptr), alignment)
); );
} }
} }

View File

@ -13,7 +13,7 @@ main (int, char**)
// satisfy a sane allocation request during testing, just in case the // satisfy a sane allocation request during testing, just in case the
// underlying code actually decides to do something; we don't be touching // underlying code actually decides to do something; we don't be touching
// it ourselves. // 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 // pick an alignment that isn't likely to be satisfied by any likely
// underlying allocator. if the allocation fulfills this alignment then // 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 // alignment to produce a likely system alignment. eg, 3 + 5 == 8 which is
// a power-of-2. // a power-of-2.
uintptr_t result[4] = { uintptr_t result[4] = {
reinterpret_cast<uintptr_t>(alloc.allocate<char> (9).data ()), // just over a power of two reinterpret_cast<uintptr_t>(alloc.allocate ( 9).data ()), // just over a power of two
reinterpret_cast<uintptr_t>(alloc.allocate<char> (1).data ()), // a single byte reinterpret_cast<uintptr_t>(alloc.allocate ( 1).data ()), // a single byte
reinterpret_cast<uintptr_t>(alloc.allocate<char> (64).data ()), // a cache line reinterpret_cast<uintptr_t>(alloc.allocate ( 64).data ()), // a cache line
reinterpret_cast<uintptr_t>(alloc.allocate<char> (250).data ()) // multiple lines, but not a power of two reinterpret_cast<uintptr_t>(alloc.allocate (250).data ()) // multiple lines, but not a power of two
}; };
tap.expect ( tap.expect (

View File

@ -6,13 +6,13 @@
int int
main () main ()
{ {
static std::byte buffer[1024*1024]; static u08 buffer[1024*1024];
static constexpr std::size_t alignment = 3; static constexpr std::size_t alignment = 3;
static constexpr std::size_t increment = 1; static constexpr std::size_t increment = 1;
// ensure we have an base pointer that's off-by-one to a likely natural // ensure we have an base pointer that's off-by-one to a likely natural
// system alignment // system alignment
std::byte* base = cruft::align_up ( u08* base = cruft::align::up (
std::data (buffer), std::data (buffer),
alignof (std::max_align_t) alignof (std::max_align_t)
) + increment; ) + increment;
@ -27,7 +27,7 @@ main ()
// ensure the first element allocated falls at the base address // 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.data (), "allocator base address is the supplied base address");
tap.expect_eq (base, alloc.allocate<std::byte> (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. // 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 // don't choose values which are likely to combine with the testing
@ -45,7 +45,7 @@ main ()
}; };
for (const auto &t: TESTS) { for (const auto &t: TESTS) {
auto ptr = reinterpret_cast<uintptr_t> (alloc.allocate<char> (t.size).data ()); auto ptr = reinterpret_cast<uintptr_t> (alloc.allocate (t.size).data ());
auto offset = ptr - reinterpret_cast<uintptr_t> (base); auto offset = ptr - reinterpret_cast<uintptr_t> (base);
tap.expect_mod (offset, alignment, "%s", t.message); tap.expect_mod (offset, alignment, "%s", t.message);
} }

66
test/alloc/easy.cpp Normal file
View File

@ -0,0 +1,66 @@
#include "alloc/easy.hpp"
#include "alloc/raw/linear.hpp"
#include "memory/buffer/simple.hpp"
#include "tap.hpp"
#include <cstdlib>
///////////////////////////////////////////////////////////////////////////////
struct setter {
setter (const setter&) = delete;
setter (bool &_target):
target (_target)
{ target = false; }
~setter ()
{ target = true; }
bool &target;
};
///////////////////////////////////////////////////////////////////////////////
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<setter> (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<setter> (flag);
tap.expect_eq (flag, false, "backed::unique acquire calls constructor");
}
tap.expect_eq (flag, true, "backed::unique release calls destructor");
return tap.status ();
}

View File

@ -10,7 +10,7 @@ main (void)
constexpr size_t BUFFER_SIZE = 1024; 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)); cruft::alloc::raw::linear store (cruft::make_view (memory));
tap.expect_eq (store.begin (), std::begin (memory), "base pointers match"); 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_eq (store.capacity (), BUFFER_SIZE, "bytes capacity matches");
tap.expect_throw<std::bad_alloc> ( tap.expect_throw<std::bad_alloc> (
[&] (void) { store.allocate<char> (BUFFER_SIZE + 1, 1); }, [&] (void) { store.allocate (BUFFER_SIZE + 1, 1); },
"excessive allocation throws bad_alloc" "excessive allocation throws bad_alloc"
); );
tap.expect_nothrow ( tap.expect_nothrow (
[&] (void) { store.allocate<char> (BUFFER_SIZE); }, [&] (void) { store.allocate (BUFFER_SIZE); },
"maximum allocation succeeds" "maximum allocation succeeds"
); );
@ -31,14 +31,14 @@ main (void)
tap.expect_eq (store.remain (), 0u, "bytes remain matches"); tap.expect_eq (store.remain (), 0u, "bytes remain matches");
tap.expect_throw<std::bad_alloc> ( tap.expect_throw<std::bad_alloc> (
[&] (void) { store.allocate<char> (1, 1); }, [&] (void) { store.allocate (1, 1); },
"minimum allocation fails after exhaustion" "minimum allocation fails after exhaustion"
); );
store.reset (); store.reset ();
tap.expect_nothrow ( tap.expect_nothrow (
[&] (void) { store.allocate<char> (1, 1); }, [&] (void) { store.allocate (1, 1); },
"minimum allocation succeeds after reset" "minimum allocation succeeds after reset"
); );