libcruft-util/alloc/aligned/foreign.hpp

123 lines
4.1 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 2016-2018 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "direct.hpp"
#include "../../cast.hpp"
#include "../../pointer.hpp"
#include "../../debug/assert.hpp"
#include <utility>
namespace cruft::alloc::aligned {
/// wraps a child allocator and enforces a fixed alignment that is
/// independant of the alignment of the provided source buffer.
///
/// we use the approach of:
/// * shifting the source buffer's alignment to satisfy the requested
/// alignment
/// * using a direct alignment child to service the requests
/// * then applying the reverse offset to values as we return the values
///
/// This approach will explode if a child allocator wants to write to the
/// range, so the user is mostly restricted to very simple allocators
/// (like 'linear').
///
/// We supply a proxy view at the (probably) invalid address:
/// (void*)alignment
/// This removes any potential bias in the successor allocators. We very
/// specifically cannot use nullptr because it breaks under optimisations
/// due to undefined behaviour of null pointers.
///
/// The proxy view will _probably_ result in a segfault if the successor
/// allocator attempts to read/write directly to it given typical
/// alignments will give addresses far below typical mappings.
template <typename ChildT>
class foreign {
public:
template <typename ...Args>
foreign (cruft::view<u08*> _data, std::size_t _alignment, Args &&...args):
m_successor (
view<u08*> {
reinterpret_cast<u08*> (_alignment),
reinterpret_cast<u08*> (_alignment + _data.size ()),
},
_alignment,
std::forward<Args> (args)...
),
m_offset (_data.data () - m_successor.data ()),
m_alignment (_alignment)
{ ; }
cruft::view<u08*>
allocate (std::size_t bytes, std::size_t alignment)
{
(void)alignment;
auto inner = m_successor.allocate (bytes, m_alignment);
return cruft::view { inner.begin () + m_offset, bytes };
}
cruft::view<u08*>
allocate (std::size_t bytes)
{
auto res = m_successor.allocate (bytes);
return { res.begin () + m_offset, bytes };
}
decltype(auto)
deallocate (void *ptr, std::size_t bytes, std::size_t alignment)
{
CHECK_MOD (m_alignment, alignment);
return m_successor.deallocate (reinterpret_cast<u08*> (ptr) - m_offset, bytes, alignment);
}
decltype(auto)
deallocate (void *ptr, std::size_t bytes)
{
return m_successor.deallocate (reinterpret_cast<u08*> (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<u08 const*> (ptr) - m_offset
);
}
auto data (void) { return m_successor.data () + m_offset; }
auto data (void) const { return m_successor.data () + m_offset; }
auto begin (void) { return m_successor.begin () + m_offset; }
auto begin (void) const { return m_successor.begin () + m_offset; }
auto end (void) { return m_successor.end () + m_offset; }
auto end (void) const { return m_successor.end () + m_offset; }
auto reset (void) { return m_successor.reset (); }
auto capacity (void) const { return m_successor.capacity (); }
auto used (void) const { return m_successor.used (); }
auto remain (void) const { return m_successor.remain (); }
private:
direct<ChildT> m_successor;
std::ptrdiff_t m_offset;
std::size_t m_alignment;
};
}