alloc: all allocators should take a type for alloc/dealloc
This commit is contained in:
parent
117a870b19
commit
38e1a19e37
@ -161,7 +161,7 @@ list (
|
|||||||
alloc/raw/affix.hpp
|
alloc/raw/affix.hpp
|
||||||
alloc/raw/aligned/direct.hpp
|
alloc/raw/aligned/direct.hpp
|
||||||
alloc/raw/aligned/foreign.hpp
|
alloc/raw/aligned/foreign.hpp
|
||||||
alloc/raw/dynamic.hpp
|
#alloc/raw/dynamic.hpp
|
||||||
alloc/raw/fallback.cpp
|
alloc/raw/fallback.cpp
|
||||||
alloc/raw/fallback.hpp
|
alloc/raw/fallback.hpp
|
||||||
alloc/raw/linear.cpp
|
alloc/raw/linear.cpp
|
||||||
@ -478,7 +478,7 @@ if (TESTS)
|
|||||||
alloc/aligned/foreign
|
alloc/aligned/foreign
|
||||||
alloc/aligned/direct
|
alloc/aligned/direct
|
||||||
alloc/arena
|
alloc/arena
|
||||||
alloc/dynamic
|
#alloc/dynamic
|
||||||
alloc/linear
|
alloc/linear
|
||||||
alloc/stack
|
alloc/stack
|
||||||
alloc/forwarding
|
alloc/forwarding
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "../memory/deleter.hpp"
|
#include "../memory/deleter.hpp"
|
||||||
#include "../cast.hpp"
|
#include "../cast.hpp"
|
||||||
|
#include "../view.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@ -38,14 +39,12 @@ namespace util::alloc {
|
|||||||
U*
|
U*
|
||||||
acquire (Args&&... args)
|
acquire (Args&&... args)
|
||||||
{
|
{
|
||||||
U *data = util::cast::alignment<U*> (
|
U *data = m_store.template allocate<U> (1).data ();
|
||||||
m_store.allocate (sizeof (U), alignof (U)).data ()
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
new (data) U (std::forward<Args> (args)...);
|
new (data) U (std::forward<Args> (args)...);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
m_store.deallocate (data, sizeof (U));
|
m_store.template deallocate<U> ({data,1});
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +57,7 @@ namespace util::alloc {
|
|||||||
release (U *u)
|
release (U *u)
|
||||||
{
|
{
|
||||||
u->~U ();
|
u->~U ();
|
||||||
m_store.deallocate (reinterpret_cast<void*> (u), sizeof (U));
|
m_store.template deallocate<U> (util::view {u,1u});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,16 +27,16 @@ namespace util::alloc {
|
|||||||
m_backing (backing)
|
m_backing (backing)
|
||||||
{ ; }
|
{ ; }
|
||||||
|
|
||||||
template <typename ...Args>
|
template <typename T, typename ...Args>
|
||||||
auto allocate (Args &&...args)
|
auto allocate (Args &&...args)
|
||||||
{
|
{
|
||||||
return m_backing.allocate (std::forward<Args> (args)...);
|
return m_backing.template allocate<T> (std::forward<Args> (args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename ...Args>
|
template <typename T, typename ...Args>
|
||||||
auto deallocate (Args &&...args)
|
auto deallocate (Args &&...args)
|
||||||
{
|
{
|
||||||
return m_backing.deallocate (std::forward<Args> (args)...);
|
return m_backing.template deallocate<T> (std::forward<Args> (args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto data (void) { return m_backing.data (); }
|
auto data (void) { return m_backing.data (); }
|
||||||
|
@ -31,13 +31,15 @@ namespace util::alloc::raw {
|
|||||||
/// useful for sentinels, reference counts, etc.
|
/// useful for sentinels, reference counts, etc.
|
||||||
template <class ParentT, class PrefixT, class SuffixT>
|
template <class ParentT, class PrefixT, class SuffixT>
|
||||||
class affix {
|
class affix {
|
||||||
util::view<std::byte*> allocate (size_t bytes);
|
|
||||||
util::view<std::byte*> allocate (size_t bytes, size_t align);
|
|
||||||
|
|
||||||
void deallocate (util::view<std::byte*> ptr) { return deallocate (ptr.data (), ptr.size ()); }
|
template <typename T>
|
||||||
void deallocate (util::view<std::byte*> ptr, size_t alignment) { return deallocate (ptr.data (), ptr.size (), alignment); }
|
util::view<T*> allocate (size_t bytes);
|
||||||
void deallocate (void *ptr, size_t bytes);
|
|
||||||
void deallocate (void *ptr, size_t bytes, size_t align);
|
template <typename T>
|
||||||
|
util::view<T*> allocate (size_t bytes, size_t align);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void deallocate (util::view<std::byte*>);
|
||||||
|
|
||||||
std::byte* begin (void);
|
std::byte* begin (void);
|
||||||
const std::byte* begin (void) const;
|
const std::byte* begin (void) const;
|
||||||
|
@ -37,17 +37,19 @@ namespace util::alloc::raw::aligned {
|
|||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
template <typename T>
|
||||||
auto
|
auto
|
||||||
allocate (std::size_t bytes)
|
allocate (std::size_t count)
|
||||||
{
|
{
|
||||||
return m_successor.allocate (bytes, m_alignment);
|
return m_successor.template allocate<T> (count, m_alignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
//---------------------------------------------------------------------
|
||||||
|
template <typename T>
|
||||||
auto
|
auto
|
||||||
deallocate (void *ptr, std::size_t bytes)
|
deallocate (util::view<T*> ptr)
|
||||||
{
|
{
|
||||||
return m_successor.deallocate (ptr, bytes);
|
return m_successor.template deallocate<T> (ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "direct.hpp"
|
#include "direct.hpp"
|
||||||
|
|
||||||
|
#include "../../../cast.hpp"
|
||||||
#include "../../../pointer.hpp"
|
#include "../../../pointer.hpp"
|
||||||
#include "../../../debug.hpp"
|
#include "../../../debug.hpp"
|
||||||
|
|
||||||
@ -66,20 +67,29 @@ namespace util::alloc::raw::aligned {
|
|||||||
{ ; }
|
{ ; }
|
||||||
|
|
||||||
|
|
||||||
void*
|
template <typename T>
|
||||||
allocate (std::size_t size)
|
util::view<T*>
|
||||||
|
allocate (std::size_t count)
|
||||||
{
|
{
|
||||||
auto ptr= reinterpret_cast<std::byte*> (
|
auto root = m_successor.template allocate<T> (count);
|
||||||
m_successor.allocate (size).data ()
|
auto base = root.template cast<char*> ().data ();
|
||||||
);
|
|
||||||
return ptr + m_offset;
|
// we can't use alignment cast here because it will almost
|
||||||
|
// certainly fail the tests it performs.
|
||||||
|
return { util::cast::ffs<T*> (base + m_offset), count };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
auto
|
auto
|
||||||
deallocate (void *ptr, std::size_t size)
|
deallocate (util::view<T*> ptr)
|
||||||
{
|
{
|
||||||
return m_successor.deallocate (
|
auto base = ptr.template cast<char*> ();
|
||||||
reinterpret_cast<std::byte*> (ptr) - m_offset, size
|
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<T> (
|
||||||
|
util::view<T*> { util::cast::ffs<T*> (next), ptr.size () }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,8 +58,11 @@ namespace util::alloc::raw {
|
|||||||
// dynamically eliminate the function altogether given run-time
|
// dynamically eliminate the function altogether given run-time
|
||||||
// dynamic dispatch needs the common calls exposed to the clients, and
|
// dynamic dispatch needs the common calls exposed to the clients, and
|
||||||
// aligned allocate is stupid useful.
|
// aligned allocate is stupid useful.
|
||||||
auto allocate (size_t bytes) { return m_child->allocate (bytes); }
|
template <typename T>
|
||||||
auto allocate (size_t bytes, size_t alignment) { return m_child->allocate (bytes, alignment); }
|
auto allocate (size_t bytes) { return m_child->allocate<T> (bytes); }
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
auto allocate (size_t bytes, size_t alignment) { return m_child->allocate<T> (bytes, alignment); }
|
||||||
|
|
||||||
auto deallocate (void *ptr, size_t bytes)
|
auto deallocate (void *ptr, size_t bytes)
|
||||||
{ return m_child->deallocate (ptr, bytes); }
|
{ return m_child->deallocate (ptr, bytes); }
|
||||||
|
@ -32,13 +32,16 @@ namespace util::alloc::raw {
|
|||||||
m_children (_children...)
|
m_children (_children...)
|
||||||
{ ; }
|
{ ; }
|
||||||
|
|
||||||
util::view<std::byte*> allocate (size_t bytes);
|
template <typename T>
|
||||||
util::view<std::byte*> allocate (size_t bytes, size_t align);
|
util::view<T*>
|
||||||
|
allocate (size_t bytes);
|
||||||
|
|
||||||
void deallocate (util::view<std::byte*> ptr) { return deallocate (ptr.data (), ptr.size ()); }
|
template <typename T>
|
||||||
void deallocate (util::view<std::byte*> ptr, size_t alignment) { return deallocate (ptr.data (), ptr.size (), alignment); }
|
util::view<T*>
|
||||||
void deallocate (void *ptr, size_t bytes);
|
allocate (size_t bytes, size_t align);
|
||||||
void deallocate (void *ptr, size_t bytes, size_t align);
|
|
||||||
|
template <typename T>
|
||||||
|
void deallocate (util::view<std::byte*> ptr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::tuple<ChildT&...> m_children;
|
std::tuple<ChildT&...> m_children;
|
||||||
|
@ -31,51 +31,6 @@ linear::linear (util::view<std::byte*> _data):
|
|||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
util::view<std::byte*>
|
|
||||||
linear::allocate (size_t bytes)
|
|
||||||
{
|
|
||||||
if (m_cursor + bytes > m_end)
|
|
||||||
throw std::bad_alloc ();
|
|
||||||
|
|
||||||
auto ptr = m_cursor;
|
|
||||||
m_cursor += bytes;
|
|
||||||
return { ptr, bytes };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
util::view<std::byte*>
|
|
||||||
linear::allocate (size_t bytes, size_t alignment)
|
|
||||||
{
|
|
||||||
auto ptr = align (m_cursor, alignment);
|
|
||||||
if (ptr + bytes > m_end)
|
|
||||||
throw std::bad_alloc ();
|
|
||||||
|
|
||||||
m_cursor = ptr + bytes;
|
|
||||||
|
|
||||||
return { ptr, bytes };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void
|
|
||||||
linear::deallocate (void *ptr, size_t bytes)
|
|
||||||
{
|
|
||||||
deallocate (ptr, bytes, alignof (std::max_align_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void
|
|
||||||
linear::deallocate (void *ptr, size_t bytes, size_t alignment)
|
|
||||||
{
|
|
||||||
(void)ptr;
|
|
||||||
(void)bytes;
|
|
||||||
(void)alignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
std::byte*
|
std::byte*
|
||||||
linear::data (void)
|
linear::data (void)
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#define CRUFT_UTIL_ALLOC_RAW_LINEAR_HPP
|
#define CRUFT_UTIL_ALLOC_RAW_LINEAR_HPP
|
||||||
|
|
||||||
#include "../../view.hpp"
|
#include "../../view.hpp"
|
||||||
|
#include "../../pointer.hpp"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
@ -34,13 +35,40 @@ namespace util::alloc::raw {
|
|||||||
|
|
||||||
linear (util::view<std::byte*> _data);
|
linear (util::view<std::byte*> _data);
|
||||||
|
|
||||||
util::view<std::byte*> allocate (size_t bytes);
|
template <typename T>
|
||||||
util::view<std::byte*> allocate (size_t bytes, size_t alignment);
|
util::view<T*>
|
||||||
|
allocate (size_t count)
|
||||||
|
{
|
||||||
|
auto const bytes = count * sizeof (T);
|
||||||
|
if (m_cursor + bytes > m_end)
|
||||||
|
throw std::bad_alloc ();
|
||||||
|
|
||||||
void deallocate (util::view<std::byte*> ptr) { return deallocate (ptr.data (), ptr.size ()); }
|
auto ptr = m_cursor;
|
||||||
void deallocate (util::view<std::byte*> ptr, size_t alignment) { return deallocate (ptr.data (), ptr.size (), alignment); }
|
m_cursor += bytes;
|
||||||
void deallocate (void *ptr, size_t bytes);
|
return { util::cast::alignment<T*> (ptr), count };
|
||||||
void deallocate (void *ptr, size_t bytes, size_t alignment);
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
util::view<T*>
|
||||||
|
allocate (size_t count, size_t alignment)
|
||||||
|
{
|
||||||
|
auto const bytes = count * sizeof (T);
|
||||||
|
|
||||||
|
auto ptr = util::align (m_cursor, alignment);
|
||||||
|
if (ptr + bytes > m_end)
|
||||||
|
throw std::bad_alloc ();
|
||||||
|
|
||||||
|
m_cursor = ptr + bytes;
|
||||||
|
|
||||||
|
return { util::cast::alignment<T*> (ptr), count };
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void deallocate (util::view<T*> ptr)
|
||||||
|
{
|
||||||
|
(void)ptr;
|
||||||
|
}
|
||||||
|
|
||||||
std::byte* data (void);
|
std::byte* data (void);
|
||||||
std::byte* begin (void);
|
std::byte* begin (void);
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
*
|
||||||
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
|
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "malloc.hpp"
|
#include "malloc.hpp"
|
||||||
@ -20,43 +20,3 @@
|
|||||||
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
using util::alloc::raw::malloc;
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
util::view<std::byte*>
|
|
||||||
malloc::allocate (size_t bytes)
|
|
||||||
{
|
|
||||||
return allocate (bytes, alignof (std::max_align_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
util::view<std::byte*>
|
|
||||||
malloc::allocate (size_t bytes, size_t align)
|
|
||||||
{
|
|
||||||
// C malloc guarantees maximal alignment
|
|
||||||
CHECK_LE (align, alignof (std::max_align_t));
|
|
||||||
(void)align;
|
|
||||||
|
|
||||||
return util::view<std::byte*> (reinterpret_cast<std::byte*> (::malloc (bytes)), bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void
|
|
||||||
malloc::deallocate (void *ptr, size_t bytes)
|
|
||||||
{
|
|
||||||
return deallocate (ptr, bytes, alignof (std::max_align_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void
|
|
||||||
malloc::deallocate (void *ptr, size_t bytes, size_t align)
|
|
||||||
{
|
|
||||||
(void)bytes;
|
|
||||||
(void)align;
|
|
||||||
|
|
||||||
::free (ptr);
|
|
||||||
}
|
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
*
|
||||||
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
|
* Copyright 2018 Danny Robson <danny@nerdcruft.net>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef CRUFT_UTIL_ALLOC_RAW_MALLOC_HPP
|
#ifndef CRUFT_UTIL_ALLOC_RAW_MALLOC_HPP
|
||||||
@ -25,13 +25,33 @@
|
|||||||
namespace util::alloc::raw {
|
namespace util::alloc::raw {
|
||||||
class malloc {
|
class malloc {
|
||||||
public:
|
public:
|
||||||
util::view<std::byte*> allocate (size_t bytes);
|
template <typename T>
|
||||||
util::view<std::byte*> allocate (size_t bytes, size_t align);
|
util::view<T*> allocate (size_t count)
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
reinterpret_cast<T*> (malloc (sizeof (T) * count)),
|
||||||
|
count
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void deallocate (util::view<std::byte*> ptr) { return deallocate (ptr.data (), ptr.size ()); }
|
template <typename T>
|
||||||
void deallocate (util::view<std::byte*> ptr, size_t alignment) { return deallocate (ptr.data (), ptr.size (), alignment); }
|
util::view<T*>
|
||||||
void deallocate (void *ptr, size_t bytes);
|
allocate (size_t count, size_t align)
|
||||||
void deallocate (void *ptr, size_t bytes, size_t align);
|
{
|
||||||
|
void* ptr;
|
||||||
|
posix_memalign (&ptr, align, sizeof (T) * count);
|
||||||
|
if (!ptr)
|
||||||
|
throw std::bad_alloc ();
|
||||||
|
|
||||||
|
return { reinterpret_cast<T*> (ptr), count };
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void
|
||||||
|
deallocate (util::view<std::byte*> ptr)
|
||||||
|
{
|
||||||
|
::free (ptr.data ());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,48 +24,6 @@
|
|||||||
using util::alloc::raw::null;
|
using util::alloc::raw::null;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
util::view<std::byte*>
|
|
||||||
null::allocate (size_t bytes)
|
|
||||||
{
|
|
||||||
return allocate (bytes, alignof (std::max_align_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
util::view<std::byte*>
|
|
||||||
null::allocate (size_t bytes, size_t align)
|
|
||||||
{
|
|
||||||
(void)bytes;
|
|
||||||
(void)align;
|
|
||||||
|
|
||||||
throw std::bad_alloc ();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void
|
|
||||||
null::deallocate (void *ptr, size_t bytes)
|
|
||||||
{
|
|
||||||
return deallocate (ptr, bytes, alignof (std::max_align_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// calling deallocate with a non-null pointer is undefined, but we may as well
|
|
||||||
// let the application continuing running if we're not in a debug context.
|
|
||||||
void
|
|
||||||
null::deallocate (void *ptr, size_t bytes, size_t align)
|
|
||||||
{
|
|
||||||
(void)ptr;
|
|
||||||
(void)bytes;
|
|
||||||
(void)align;
|
|
||||||
|
|
||||||
// cast to void* to assist some of the printing machinery in the assertion
|
|
||||||
CHECK_EQ (ptr, static_cast<const void*> (nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
std::byte*
|
std::byte*
|
||||||
null::begin (void)
|
null::begin (void)
|
||||||
|
@ -32,13 +32,30 @@ namespace util::alloc::raw {
|
|||||||
null (const null&) = delete;
|
null (const null&) = delete;
|
||||||
null& operator= (const null&) = delete;
|
null& operator= (const null&) = delete;
|
||||||
|
|
||||||
util::view<std::byte*> allocate (size_t bytes);
|
template <typename T>
|
||||||
util::view<std::byte*> allocate (size_t bytes, size_t align);
|
util::view<T*>
|
||||||
|
allocate (size_t count)
|
||||||
|
{
|
||||||
|
return allocate<T> (count, alignof (T));
|
||||||
|
}
|
||||||
|
|
||||||
void deallocate (util::view<std::byte*> ptr) { return deallocate (ptr.data (), ptr.size ()); }
|
template <typename T>
|
||||||
void deallocate (util::view<std::byte*> ptr, size_t alignment) { return deallocate (ptr.data (), ptr.size (), alignment); }
|
util::view<T*>
|
||||||
void deallocate (void *ptr, size_t bytes);
|
allocate (size_t count, size_t align)
|
||||||
void deallocate (void *ptr, size_t bytes, size_t align);
|
{
|
||||||
|
(void)count;
|
||||||
|
(void)align;
|
||||||
|
|
||||||
|
throw std::bad_alloc ();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void
|
||||||
|
deallocate (util::view<T*> ptr)
|
||||||
|
{
|
||||||
|
(void)ptr;
|
||||||
|
CHECK_EQ (ptr.data (), static_cast<const void*> (nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
util::view<std::byte*> data (void);
|
util::view<std::byte*> data (void);
|
||||||
util::view<const std::byte*> data (void) const;
|
util::view<const std::byte*> data (void) const;
|
||||||
|
@ -33,86 +33,6 @@ stack::stack (util::view<std::byte*> _data):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
union record {
|
|
||||||
using offset_t = uint32_t;
|
|
||||||
|
|
||||||
std::byte *as_bytes;
|
|
||||||
offset_t *as_offset;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
constexpr auto MIN_ALIGNMENT = sizeof (record::offset_t);
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
util::view<std::byte*>
|
|
||||||
stack::allocate (size_t bytes)
|
|
||||||
{
|
|
||||||
return allocate (bytes, alignof (std::max_align_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
util::view<std::byte*>
|
|
||||||
stack::allocate (size_t bytes, size_t alignment)
|
|
||||||
{
|
|
||||||
// reserve space at the front of the allocation to record the total
|
|
||||||
// allocation size so we can account for alignment if required.
|
|
||||||
auto ptr = m_cursor + sizeof (record::offset_t);
|
|
||||||
|
|
||||||
// align the outgoing pointer if required
|
|
||||||
alignment = util::max (MIN_ALIGNMENT, alignment);
|
|
||||||
ptr = align (ptr, alignment);
|
|
||||||
|
|
||||||
// ensure we haven't overrun our allocated segment
|
|
||||||
if (ptr + bytes > m_end)
|
|
||||||
throw std::bad_alloc ();
|
|
||||||
|
|
||||||
// use a 'record' struct as a window into the reserved space at the front
|
|
||||||
// of the allocation and store the offset to the previous allocation head
|
|
||||||
// (from the record struct). allows us to account for alignment.
|
|
||||||
record record;
|
|
||||||
record.as_bytes = ptr - sizeof (record::offset_t);
|
|
||||||
*record.as_offset = util::cast::lossless <uint32_t> (ptr - m_cursor);
|
|
||||||
|
|
||||||
m_cursor = ptr + bytes;
|
|
||||||
return { ptr, bytes };
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void
|
|
||||||
stack::deallocate (void *ptr, size_t bytes)
|
|
||||||
{
|
|
||||||
return deallocate (ptr, bytes, alignof (std::max_align_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
void
|
|
||||||
stack::deallocate (void *_ptr, size_t bytes, size_t alignment)
|
|
||||||
{
|
|
||||||
(void)bytes;
|
|
||||||
|
|
||||||
// TODO: use alignment
|
|
||||||
(void)alignment;
|
|
||||||
//alignment = util::max (MIN_ALIGNMENT, alignment);
|
|
||||||
|
|
||||||
auto ptr = reinterpret_cast<std::byte*> (_ptr);
|
|
||||||
|
|
||||||
record record;
|
|
||||||
record.as_bytes = ptr - sizeof (record::offset_t);
|
|
||||||
|
|
||||||
//CHECK_LE (bytes, *record.as_offset);
|
|
||||||
CHECK_GE (m_cursor - *record.as_offset, m_begin);
|
|
||||||
|
|
||||||
m_cursor -= bytes;
|
|
||||||
m_cursor -= *record.as_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
std::byte*
|
std::byte*
|
||||||
stack::begin (void)
|
stack::begin (void)
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#define CRUFT_UTIL_ALLOC_RAW_STACK_HPP
|
#define CRUFT_UTIL_ALLOC_RAW_STACK_HPP
|
||||||
|
|
||||||
#include "../../view.hpp"
|
#include "../../view.hpp"
|
||||||
|
#include "../../pointer.hpp"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
@ -34,13 +35,60 @@ namespace util::alloc::raw {
|
|||||||
|
|
||||||
stack (util::view<std::byte*> _data);
|
stack (util::view<std::byte*> _data);
|
||||||
|
|
||||||
util::view<std::byte*> allocate (size_t bytes, size_t alignment);
|
template <typename T>
|
||||||
util::view<std::byte*> allocate (size_t bytes);
|
util::view<T*>
|
||||||
|
allocate (size_t count, size_t alignment)
|
||||||
|
{
|
||||||
|
constexpr auto MIN_ALIGNMENT = sizeof (record::offset_t);
|
||||||
|
const auto bytes = count * sizeof (T);
|
||||||
|
|
||||||
|
// reserve space at the front of the allocation to record the total
|
||||||
|
// allocation size so we can account for alignment if required.
|
||||||
|
auto ptr = m_cursor + sizeof (record::offset_t);
|
||||||
|
|
||||||
|
// align the outgoing pointer if required
|
||||||
|
alignment = util::max (MIN_ALIGNMENT, alignment);
|
||||||
|
ptr = util::align (ptr, alignment);
|
||||||
|
|
||||||
|
// ensure we haven't overrun our allocated segment
|
||||||
|
if (ptr + bytes > m_end)
|
||||||
|
throw std::bad_alloc ();
|
||||||
|
|
||||||
|
// use a 'record' struct as a window into the reserved space at the front
|
||||||
|
// of the allocation and store the offset to the previous allocation head
|
||||||
|
// (from the record struct). allows us to account for alignment.
|
||||||
|
record r;
|
||||||
|
r.as_bytes = ptr - sizeof (record::offset_t);
|
||||||
|
*r.as_offset = util::cast::lossless <uint32_t> (ptr - m_cursor);
|
||||||
|
|
||||||
|
m_cursor = ptr + bytes;
|
||||||
|
return { util::cast::alignment<T*> (ptr), count };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
util::view<T*>
|
||||||
|
allocate (size_t count)
|
||||||
|
{
|
||||||
|
return allocate<T> (count, alignof (T));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void deallocate (util::view<T*> _ptr)
|
||||||
|
{
|
||||||
|
auto ptr = reinterpret_cast<std::byte*> (_ptr.data ());
|
||||||
|
|
||||||
|
record r;
|
||||||
|
r.as_bytes = ptr - sizeof (record::offset_t);
|
||||||
|
|
||||||
|
//CHECK_LE (bytes, *record.as_offset);
|
||||||
|
CHECK_GE (m_cursor - *r.as_offset, m_begin);
|
||||||
|
|
||||||
|
m_cursor -= sizeof (T) * _ptr.size ();
|
||||||
|
m_cursor -= *r.as_offset;
|
||||||
|
}
|
||||||
|
|
||||||
void deallocate (util::view<std::byte*> ptr) { return deallocate (ptr.data (), ptr.size ()); }
|
|
||||||
void deallocate (util::view<std::byte*> ptr, size_t alignment) { return deallocate (ptr.data (), ptr.size (), alignment); }
|
|
||||||
void deallocate (void *ptr, size_t bytes);
|
|
||||||
void deallocate (void *ptr, size_t bytes, size_t alignment);
|
|
||||||
|
|
||||||
util::view<std::byte*> data (void);
|
util::view<std::byte*> data (void);
|
||||||
util::view<const std::byte*> data (void) const;
|
util::view<const std::byte*> data (void) const;
|
||||||
@ -60,6 +108,13 @@ namespace util::alloc::raw {
|
|||||||
size_t remain (void) const;
|
size_t remain (void) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
union record {
|
||||||
|
using offset_t = uint32_t;
|
||||||
|
|
||||||
|
std::byte *as_bytes;
|
||||||
|
offset_t *as_offset;
|
||||||
|
};
|
||||||
|
|
||||||
std::byte *const m_begin;
|
std::byte *const m_begin;
|
||||||
std::byte *const m_end;
|
std::byte *const m_end;
|
||||||
std::byte *m_cursor;
|
std::byte *m_cursor;
|
||||||
|
@ -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 (9).data ()), // just over a power of two
|
reinterpret_cast<uintptr_t>(alloc.allocate<char> (9).data ()), // just over a power of two
|
||||||
reinterpret_cast<uintptr_t>(alloc.allocate (1).data ()), // a single byte
|
reinterpret_cast<uintptr_t>(alloc.allocate<char> (1).data ()), // a single byte
|
||||||
reinterpret_cast<uintptr_t>(alloc.allocate (64).data ()), // a cache line
|
reinterpret_cast<uintptr_t>(alloc.allocate<char> (64).data ()), // a cache line
|
||||||
reinterpret_cast<uintptr_t>(alloc.allocate (250).data ()) // multiple cache lines, but not a power of two
|
reinterpret_cast<uintptr_t>(alloc.allocate<char> (250).data ()) // multiple lines, but not a power of two
|
||||||
};
|
};
|
||||||
|
|
||||||
tap.expect (
|
tap.expect (
|
||||||
|
@ -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 (8), "first allocation is the supplied base address");
|
tap.expect_eq (base, alloc.allocate<std::byte> (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 (t.size));
|
auto ptr = reinterpret_cast<uintptr_t> (alloc.allocate<char> (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);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ main ()
|
|||||||
|
|
||||||
util::TAP::logger tap;
|
util::TAP::logger tap;
|
||||||
tap.expect_eq (linear.used (), 0u, "construction does not allocate");
|
tap.expect_eq (linear.used (), 0u, "construction does not allocate");
|
||||||
forwarding.allocate (16u);
|
forwarding.allocate<uint8_t> (16u);
|
||||||
tap.expect_eq (linear.used (), 16u, "allocation size is exactly committed");
|
tap.expect_eq (linear.used (), 16u, "allocation size is exactly committed");
|
||||||
|
|
||||||
return tap.status ();
|
return tap.status ();
|
||||||
|
@ -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 (BUFFER_SIZE + 1, 1); },
|
[&] (void) { store.allocate<char> (BUFFER_SIZE + 1, 1); },
|
||||||
"excessive allocation throws bad_alloc"
|
"excessive allocation throws bad_alloc"
|
||||||
);
|
);
|
||||||
|
|
||||||
tap.expect_nothrow (
|
tap.expect_nothrow (
|
||||||
[&] (void) { store.allocate (BUFFER_SIZE); },
|
[&] (void) { store.allocate<char> (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 (1, 1); },
|
[&] (void) { store.allocate<char> (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 (1, 1); },
|
[&] (void) { store.allocate<char> (1, 1); },
|
||||||
"minimum allocation succeeds after reset"
|
"minimum allocation succeeds after reset"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -10,8 +10,8 @@ n_allocations (util::alloc::raw::stack &store,
|
|||||||
size_t alignment = alignof (std::max_align_t))
|
size_t alignment = alignof (std::max_align_t))
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < count; ++i) {
|
for (unsigned i = 0; i < count; ++i) {
|
||||||
auto ptr = store.allocate (bytes, alignment);
|
auto ptr = store.allocate<uint8_t> (bytes, alignment);
|
||||||
store.deallocate (ptr, alignment);
|
store.deallocate (ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ main (void)
|
|||||||
|
|
||||||
// larger than total allocations should throw
|
// larger than total allocations should throw
|
||||||
tap.expect_throw<std::bad_alloc> (
|
tap.expect_throw<std::bad_alloc> (
|
||||||
[&store] (void) { store.allocate (BUFFER_AVAILABLE + 1, 1); },
|
[&store] (void) { store.allocate<uint8_t> (BUFFER_AVAILABLE + 1, 1); },
|
||||||
"excessive allocation throws bad_alloc"
|
"excessive allocation throws bad_alloc"
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -53,11 +53,11 @@ main (void)
|
|||||||
|
|
||||||
// perform two near maximum allocations and check for exhaustion through
|
// perform two near maximum allocations and check for exhaustion through
|
||||||
// bad_alloc
|
// bad_alloc
|
||||||
auto ptr = store.allocate (BUFFER_REQUEST);
|
auto ptr = store.allocate<uint8_t> (BUFFER_REQUEST);
|
||||||
(void)ptr;
|
(void)ptr;
|
||||||
|
|
||||||
tap.expect_throw<std::bad_alloc> (
|
tap.expect_throw<std::bad_alloc> (
|
||||||
[&store] (void) { store.allocate (BUFFER_REQUEST); },
|
[&store] (void) { store.allocate<uint8_t> (BUFFER_REQUEST); },
|
||||||
"bad_alloc thrown on exhaustion"
|
"bad_alloc thrown on exhaustion"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user