280 lines
8.0 KiB
C++
280 lines
8.0 KiB
C++
/*
|
|
* 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 "../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;
|
|
};
|
|
};
|