diff --git a/Makefile.am b/Makefile.am index 855d5ef5..52b30061 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,6 +15,7 @@ UTIL_FILES = \ alloc/fwd.hpp \ alloc/affix.cpp \ alloc/affix.hpp \ + alloc/aligned.hpp \ alloc/allocator.cpp \ alloc/allocator.hpp \ alloc/allocator.ipp \ @@ -385,6 +386,7 @@ noinst_PROGRAMS = tools/scratch AM_CXXFLAGS += -I$(top_srcdir) TEST_BIN = \ + test/alloc/aligned \ test/alloc/arena \ test/alloc/dynamic \ test/alloc/linear \ diff --git a/alloc/aligned.hpp b/alloc/aligned.hpp new file mode 100644 index 00000000..f60f81fb --- /dev/null +++ b/alloc/aligned.hpp @@ -0,0 +1,79 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright 2016 Danny Robson + */ + +#ifndef __CRUFT_UTIL_ALLOC_ALIGNED_HPP +#define __CRUFT_UTIL_ALLOC_ALIGNED_HPP + +namespace util::alloc { + /// wraps a child allocator and enforces a fixed alignment + template + class aligned { + public: + /////////////////////////////////////////////////////////////////////// + template + aligned (std::size_t _alignment, Args &&...args): + m_successor (std::forward (args)...), + m_alignment (_alignment) + { ; } + + + /////////////////////////////////////////////////////////////////////// + auto + allocate (size_t bytes) + { + return m_successor.allocate (bytes, m_alignment); + } + + //--------------------------------------------------------------------- + void + deallocate (void *ptr, size_t bytes) + { + return m_successor.deallocate (ptr, bytes); + } + + + /////////////////////////////////////////////////////////////////////// + auto base (void) + { + return m_successor.base (); + } + + + //--------------------------------------------------------------------- + auto + offset (const void *ptr) + { + return m_successor.offset (ptr); + } + + + /////////////////////////////////////////////////////////////////////// + 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: + Successor m_successor; + std::size_t m_alignment; + }; +} + +#endif diff --git a/test/alloc/aligned.cpp b/test/alloc/aligned.cpp new file mode 100644 index 00000000..71fae127 --- /dev/null +++ b/test/alloc/aligned.cpp @@ -0,0 +1,48 @@ +#include "tap.hpp" + +#include "alloc/aligned.hpp" +#include "alloc/linear.hpp" + + +int +main (int, char**) +{ + util::TAP::logger tap; + + // set aside a sizeable buffer. it has to be large enough to conceivably + // satisfy a sane allocation request during testing, just in case the + // underlying code actually decides to do something; we don't be touching + // it ourselves. + static char buffer[1024*1024]; + + // pick an alignment that isn't likely to be satisfied by any likely + // underlying allocator. if the allocation fulfills this alignment then + // we're probably operating correctly. + static constexpr std::size_t alignment = 3; + + util::alloc::aligned alloc ( + alignment, std::begin (buffer), std::end (buffer) + ); + + // 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 + // alignment to produce a likely system alignment. eg, 3 + 5 == 8 which is + // a power-of-2. + uintptr_t result[4] = { + (uintptr_t)alloc.allocate (9), // just over a power of two + (uintptr_t)alloc.allocate (1), // a single byte + (uintptr_t)alloc.allocate (64), // a cache line + (uintptr_t)alloc.allocate (250) // multiple cache lines, but not a power of two + }; + + tap.expect ( + !(result[0] % alignment) && + !(result[1] % alignment) && + !(result[2] % alignment) && + !(result[3] % alignment), + + "allocations make alignment of %zu", alignment + ); + + return tap.status (); +}