/* * 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 Danny Robson */ #ifndef CRUFT_UTIL_ALLOC_RAW_ALIGNED_OFFSET_HPP #define CRUFT_UTIL_ALLOC_RAW_ALIGNED_OFFSET_HPP #include "direct.hpp" #include "../../../cast.hpp" #include "../../../pointer.hpp" #include "../../../debug.hpp" #include #include namespace cruft::alloc::raw::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) { ; } template cruft::view allocate (std::size_t count) { auto root = m_successor.template allocate (count); auto base = root.template cast ().data (); // we can't use alignment cast here because it will almost // certainly fail the tests it performs. return { cruft::cast::ffs (base + m_offset), count }; } template auto deallocate (cruft::view ptr) { auto base = ptr.template cast (); 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 ( cruft::view { cruft::cast::ffs (next), ptr.size () } ); } 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; }; } #endif