/* * 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 */ #pragma once #include "direct.hpp" #include "../../cast.hpp" #include "../../pointer.hpp" #include "../../debug/assert.hpp" #include 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 class foreign { public: template foreign (cruft::view _data, std::size_t _alignment, Args &&...args): m_successor ( view { reinterpret_cast (_alignment), reinterpret_cast (_alignment + _data.size ()), }, _alignment, std::forward (args)... ), m_offset (_data.data () - m_successor.data ()), m_alignment (_alignment) { ; } cruft::view 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 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 (ptr) - m_offset, bytes, alignment); } decltype(auto) deallocate (void *ptr, std::size_t bytes) { return m_successor.deallocate (reinterpret_cast (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 ); } 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 m_successor; std::ptrdiff_t m_offset; std::size_t m_alignment; }; }