From 1ec95822405c14e1457b13b3d8734c1467a0a6bf Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Fri, 13 Nov 2015 17:17:37 +1100 Subject: [PATCH] alloc: add initial allocator stubs --- Makefile.am | 19 ++++++++ alloc/affix.cpp | 22 +++++++++ alloc/affix.hpp | 32 +++++++++++++ alloc/allocator.cpp | 1 + alloc/allocator.hpp | 44 ++++++++++++++++++ alloc/allocator.ipp | 48 ++++++++++++++++++++ alloc/arena.cpp | 1 + alloc/arena.hpp | 44 ++++++++++++++++++ alloc/arena.ipp | 68 ++++++++++++++++++++++++++++ alloc/fallback.cpp | 1 + alloc/fallback.hpp | 33 ++++++++++++++ alloc/linear.cpp | 63 ++++++++++++++++++++++++++ alloc/linear.hpp | 48 ++++++++++++++++++++ alloc/malloc.cpp | 45 +++++++++++++++++++ alloc/malloc.hpp | 32 +++++++++++++ alloc/null.cpp | 50 +++++++++++++++++++++ alloc/null.hpp | 34 ++++++++++++++ alloc/stack.cpp | 101 ++++++++++++++++++++++++++++++++++++++++++ alloc/stack.hpp | 45 +++++++++++++++++++ alloc/stack.ipp | 23 ++++++++++ test/alloc/linear.cpp | 38 ++++++++++++++++ test/alloc/stack.cpp | 68 ++++++++++++++++++++++++++++ 22 files changed, 860 insertions(+) create mode 100644 alloc/affix.cpp create mode 100644 alloc/affix.hpp create mode 100644 alloc/allocator.cpp create mode 100644 alloc/allocator.hpp create mode 100644 alloc/allocator.ipp create mode 100644 alloc/arena.cpp create mode 100644 alloc/arena.hpp create mode 100644 alloc/arena.ipp create mode 100644 alloc/fallback.cpp create mode 100644 alloc/fallback.hpp create mode 100644 alloc/linear.cpp create mode 100644 alloc/linear.hpp create mode 100644 alloc/malloc.cpp create mode 100644 alloc/malloc.hpp create mode 100644 alloc/null.cpp create mode 100644 alloc/null.hpp create mode 100644 alloc/stack.cpp create mode 100644 alloc/stack.hpp create mode 100644 alloc/stack.ipp create mode 100644 test/alloc/linear.cpp create mode 100644 test/alloc/stack.cpp diff --git a/Makefile.am b/Makefile.am index e99e575b..d64fe46b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -11,6 +11,23 @@ AM_CXXFLAGS = $(BOOST_CPPFLAGS) $(ZLIB_CFLAGS) UTIL_FILES = \ adapter.hpp \ adapter.cpp \ + alloc/affix.cpp \ + alloc/affix.hpp \ + alloc/allocator.cpp \ + alloc/allocator.hpp \ + alloc/arena.cpp \ + alloc/arena.hpp \ + alloc/arena.ipp \ + alloc/fallback.cpp \ + alloc/fallback.hpp \ + alloc/linear.cpp \ + alloc/linear.hpp \ + alloc/malloc.cpp \ + alloc/malloc.hpp \ + alloc/null.cpp \ + alloc/null.hpp \ + alloc/stack.cpp \ + alloc/stack.hpp \ backtrace.hpp \ bezier.cpp \ bezier.hpp \ @@ -377,6 +394,8 @@ AM_LDFLAGS += $(BOOST_LDFLAGS) $(BOOST_FILESYSTEM_LIB) $(BOOST_SYSTEM_LIB) AM_CXXFLAGS += -I$(top_srcdir) TEST_BIN = \ + test/alloc/stack \ + test/alloc/linear \ test/backtrace \ test/bezier \ test/bitwise \ diff --git a/alloc/affix.cpp b/alloc/affix.cpp new file mode 100644 index 00000000..87f37ba4 --- /dev/null +++ b/alloc/affix.cpp @@ -0,0 +1,22 @@ +/* + * 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 2015 Danny Robson + */ + +#include "./affix.hpp" + +using util::alloc::affix; + + +/////////////////////////////////////////////////////////////////////////////// diff --git a/alloc/affix.hpp b/alloc/affix.hpp new file mode 100644 index 00000000..6cbf3602 --- /dev/null +++ b/alloc/affix.hpp @@ -0,0 +1,32 @@ +/* + * 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 2015 Danny Robson + */ + +#ifndef __UTIL_ALLOC_AFFIX_HPP +#define __UTIL_ALLOC_AFFIX_HPP + +#include + +namespace util { namespace alloc { + template + class affix { + void* allocate (size_t bytes, size_t align = alignof (std::max_align_t)); + void deallocate (void *ptr, size_t bytes, size_t align = alignof (std::max_align_t)); + }; +} } + +#include "./affix.hpp" + +#endif diff --git a/alloc/allocator.cpp b/alloc/allocator.cpp new file mode 100644 index 00000000..0747499b --- /dev/null +++ b/alloc/allocator.cpp @@ -0,0 +1 @@ +#include "./allocator.hpp" diff --git a/alloc/allocator.hpp b/alloc/allocator.hpp new file mode 100644 index 00000000..dd861851 --- /dev/null +++ b/alloc/allocator.hpp @@ -0,0 +1,44 @@ +/* + * 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 2015 Danny Robson + */ + +#ifndef __UTIL_ALLOC_ALLOCATOR_HPP +#define __UTIL_ALLOC_ALLOCATOR_HPP + +#include +#include + +// C++11 allocator concept conformant allocator adaptor, going from our +// allocator interface to that of the STL and friends. +namespace util { namespace alloc { + template + class allocator { + public: + typedef T value_type; + + template + allocator (Args&& ...args); + + T* allocate (std::size_t count); + void deallocate (T*, std::size_t count); + + private: + B &m_backing; + }; +} } + +#include "./allocator.ipp" + +#endif diff --git a/alloc/allocator.ipp b/alloc/allocator.ipp new file mode 100644 index 00000000..41e52d82 --- /dev/null +++ b/alloc/allocator.ipp @@ -0,0 +1,48 @@ +/* + * 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 2015 Danny Robson + */ + + +#ifdef __UTIL_ALLOC_ALLOCATOR_IPP +#error +#endif + +#define __UTIL_ALLOC_ALLOCATOR_IPP + + +/////////////////////////////////////////////////////////////////////////////// +template +template +util::alloc::allocator::allocator (Args&& ...args): + m_backing (std::forward (args)...) +{ ; } + + +/////////////////////////////////////////////////////////////////////////////// +template +T* +util::alloc::allocator::allocate (std::size_t count) +{ + return m_backing.template allocate (count); +} + + +//----------------------------------------------------------------------------- +template +void +util::alloc::allocator::deallocate (T *t, std::size_t count) +{ + return m_backing.template deallocate (t, count); +} diff --git a/alloc/arena.cpp b/alloc/arena.cpp new file mode 100644 index 00000000..ca065a58 --- /dev/null +++ b/alloc/arena.cpp @@ -0,0 +1 @@ +#include "./arena.hpp" diff --git a/alloc/arena.hpp b/alloc/arena.hpp new file mode 100644 index 00000000..28a812ac --- /dev/null +++ b/alloc/arena.hpp @@ -0,0 +1,44 @@ +/* + * 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 2015 Danny Robson + */ + +#ifndef __UTIL_ALLOC_ARENA_HPP +#define __UTIL_ALLOC_ARENA_HPP + +#include + +namespace util { namespace alloc { + template + class arena { + public: + arena (T &store); + + template + U* acquire (void); + + template + std::unique_ptr unique (void); + + template + void release (U*); + + private: + T &m_store; + }; +} } + +#include "./arena.hpp" + +#endif diff --git a/alloc/arena.ipp b/alloc/arena.ipp new file mode 100644 index 00000000..0fa7f2ec --- /dev/null +++ b/alloc/arena.ipp @@ -0,0 +1,68 @@ +/* + * 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 2015 Danny Robson + */ + +#ifdef __UTIL_ALLOC_ARENA_IPP +#error +#endif + +#define __UTIL_ALLOC_ARENA_IPP + + +/////////////////////////////////////////////////////////////////////////////// +template +util::alloc::arena::arena (T &store): + m_store (store) +{ ; } + + +/////////////////////////////////////////////////////////////////////////////// +template +template +U* +util::alloc::arena::acquire (Args&& ...args) +{ + U *data = m_store.allocate (sizeof (U), alignof (U)); + + try { + new (data) U (std::forward (args)...); + } catch (...) { + m_store.deallocate (data, sizeof (U)); + throw; + } +} + + +//----------------------------------------------------------------------------- +template +template +std::unique_ptr +util::alloc::arena::unique (Args&& ...args) +{ + return std::unique_ptr (acquire (std::forward (args)...)); +} + + +//----------------------------------------------------------------------------- +template +template +void +util::alloc::arena::release (U *u) +{ + u->~U (); + m_store.deallocate (u); +} + + diff --git a/alloc/fallback.cpp b/alloc/fallback.cpp new file mode 100644 index 00000000..76435d99 --- /dev/null +++ b/alloc/fallback.cpp @@ -0,0 +1 @@ +#include "./fallback.hpp" diff --git a/alloc/fallback.hpp b/alloc/fallback.hpp new file mode 100644 index 00000000..f9f1efab --- /dev/null +++ b/alloc/fallback.hpp @@ -0,0 +1,33 @@ +/* + * 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 2015 Danny Robson + */ + +#ifndef __UTIL_ALLOC_FALLBACK_HPP +#define __UTIL_ALLOC_FALLBACK_HPP + +#include + +namespace util { namespace alloc { + template + class fallback { + public: + fallback (A&, B&); + + void* allocate (size_t bytes, size_t align = alignof (std::max_align_t)); + void deallocate (void *ptr, size_t bytes, size_t align = alignof (std::max_align_t)); + }; +} } + +#endif diff --git a/alloc/linear.cpp b/alloc/linear.cpp new file mode 100644 index 00000000..8e1fdc41 --- /dev/null +++ b/alloc/linear.cpp @@ -0,0 +1,63 @@ +/* + * 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 2015 Danny Robson + */ + +#include "./linear.hpp" + +#include "../pointer.hpp" +#include "../debug.hpp" + +using util::alloc::linear; + + +/////////////////////////////////////////////////////////////////////////////// +linear::linear (void *begin, void *end): + m_begin (reinterpret_cast (begin)), + m_end (reinterpret_cast (end)), + m_cursor (reinterpret_cast (begin)) +{ + CHECK_LE (begin, end); +} + + +/////////////////////////////////////////////////////////////////////////////// +void* +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; +} + + +//----------------------------------------------------------------------------- +void +linear::deallocate (void *ptr, size_t bytes, size_t alignment) +{ + (void)ptr; + (void)bytes; + (void)alignment; +} + + +/////////////////////////////////////////////////////////////////////////////// +void +linear::reset (void) +{ + m_cursor = m_begin; +} diff --git a/alloc/linear.hpp b/alloc/linear.hpp new file mode 100644 index 00000000..2a0e9606 --- /dev/null +++ b/alloc/linear.hpp @@ -0,0 +1,48 @@ +/* + * 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 2015 Danny Robson + */ + +#ifndef __UTIL_ALLOC_LINEAR_HPP +#define __UTIL_ALLOC_LINEAR_HPP + +#include + + +namespace util { namespace alloc { + class linear { + public: + linear (const linear&) = delete; + linear (linear&&) = delete; + linear& operator= (const linear&) = delete; + linear& operator= (linear&&) = delete; + + linear (void *begin, void *end); + + void* allocate (size_t bytes, size_t alignment = alignof (std::max_align_t)); + void deallocate (void *ptr, size_t bytes, size_t alignment = alignof (std::max_align_t)); + + void reset (void); + + size_t capacity (void) const; + size_t size (void) const; + + protected: + char *m_begin, *m_end, *m_cursor; + }; +} } + +#include "./linear.hpp" + +#endif diff --git a/alloc/malloc.cpp b/alloc/malloc.cpp new file mode 100644 index 00000000..180a4ca7 --- /dev/null +++ b/alloc/malloc.cpp @@ -0,0 +1,45 @@ +/* + * 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 2015 Danny Robson + */ + +#include "./malloc.hpp" + +#include "../debug.hpp" + +#include + +using util::alloc::malloc; + + +/////////////////////////////////////////////////////////////////////////////// +void* +malloc::allocate (size_t bytes, size_t align) +{ + // C malloc guarantees maximal alignment + (void)align; + + return ::malloc (bytes); +} + + +//----------------------------------------------------------------------------- +void +malloc::deallocate (void *ptr, size_t bytes, size_t align) +{ + (void)bytes; + (void)align; + + ::free (ptr); +} diff --git a/alloc/malloc.hpp b/alloc/malloc.hpp new file mode 100644 index 00000000..bd6e7c6d --- /dev/null +++ b/alloc/malloc.hpp @@ -0,0 +1,32 @@ +/* + * 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 2015 Danny Robson + */ + +#ifndef __UTIL_ALLOC_MALLOC_HPP +#define __UTIL_ALLOC_MALLOC_HPP + +#include + + +namespace util { namespace alloc { + class malloc { + public: + void* allocate (size_t bytes, size_t align = alignof (std::max_align_t)); + void deallocate (void *ptr, size_t bytes, size_t align = alignof (std::max_align_t)); + }; +} } + + +#endif diff --git a/alloc/null.cpp b/alloc/null.cpp new file mode 100644 index 00000000..0bbc9413 --- /dev/null +++ b/alloc/null.cpp @@ -0,0 +1,50 @@ +/* + * 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 2015 Danny Robson + */ + + +#include "./null.hpp" + +#include "../debug.hpp" + +#include + +using util::alloc::null; + + +/////////////////////////////////////////////////////////////////////////////// +void* +null::allocate (size_t bytes, size_t align) +{ + (void)bytes; + (void)align; + + throw std::bad_alloc (); +} + + +//----------------------------------------------------------------------------- +// 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 machinary in the assertion + CHECK_EQ (ptr, (void*)nullptr); +} diff --git a/alloc/null.hpp b/alloc/null.hpp new file mode 100644 index 00000000..551e0540 --- /dev/null +++ b/alloc/null.hpp @@ -0,0 +1,34 @@ +/* + * 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 2015 Danny Robson + */ + +#ifndef __UTIL_ALLOC_NULL_HPP +#define __UTIL_ALLOC_NULL_HPP + +#include + + +namespace util { namespace alloc { + // allocator that always fails, throwing bad_alloc. deallocate will + // succeed with nullptr as with delete, but is undefined with other values + // (it is likely to at least assert). + class null { + public: + void* allocate (size_t bytes, size_t align = alignof (std::max_align_t)); + void deallocate (void *ptr, size_t bytes, size_t align = alignof (std::max_align_t)); + }; +} } + +#endif diff --git a/alloc/stack.cpp b/alloc/stack.cpp new file mode 100644 index 00000000..fa74737e --- /dev/null +++ b/alloc/stack.cpp @@ -0,0 +1,101 @@ +/* + * 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 2015 Danny Robson + */ + +#include "./stack.hpp" + +#include "../debug.hpp" +#include "../pointer.hpp" + +using util::alloc::stack; + + +/////////////////////////////////////////////////////////////////////////////// +stack::stack (void *begin, void *end): + m_begin (reinterpret_cast (begin)), + m_end (reinterpret_cast (end)), + m_cursor (reinterpret_cast (begin)) +{ + CHECK_LE (m_begin, m_end); +} + + +/////////////////////////////////////////////////////////////////////////////// +union record { + using offset_t = uint32_t; + + char *as_bytes; + offset_t *as_uint32; +}; + + +//----------------------------------------------------------------------------- +constexpr auto MIN_ALIGNMENT = sizeof (record::offset_t); + + +/////////////////////////////////////////////////////////////////////////////// +void* +stack::allocate (size_t bytes, size_t alignment) +{ + alignment = util::max (MIN_ALIGNMENT, 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 + ptr = align (ptr, alignment); + + // ensure we haven't overrun our allocated segment + if (ptr + bytes > m_end) + throw std::bad_alloc (); + + // store the total size and record the new stack head + record record; + record.as_bytes = ptr - sizeof (record::offset_t); + *record.as_uint32 = ptr - m_cursor; + + m_cursor = ptr + bytes; + + return ptr; +} + + +//----------------------------------------------------------------------------- +void +stack::deallocate (void *_ptr, size_t bytes, size_t alignment) +{ + (void)bytes; + + alignment = util::max (MIN_ALIGNMENT, alignment); + + auto ptr = reinterpret_cast (_ptr); + + record record; + record.as_bytes = ptr - sizeof (record::offset_t); + + CHECK_LE (bytes, *record.as_uint32); + CHECK_GE (m_cursor - *record.as_uint32, m_begin); + + m_cursor -= *record.as_uint32 + bytes; +} + + +//----------------------------------------------------------------------------- +void +stack::reset (void) +{ + m_cursor = m_begin; +} diff --git a/alloc/stack.hpp b/alloc/stack.hpp new file mode 100644 index 00000000..742a016a --- /dev/null +++ b/alloc/stack.hpp @@ -0,0 +1,45 @@ +/* + * 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 2015 Danny Robson + */ + +#ifndef __UTIL_ALLOC_STACK_HPP +#define __UTIL_ALLOC_STACK_HPP + +#include +#include + + +namespace util { namespace alloc { + class stack { + public: + stack (const stack&) = delete; + stack (stack&&) = delete; + stack& operator= (const stack&) = delete; + stack& operator= (stack&&) = delete; + + stack (void *begin, void *end); + + //[[gnu::alloc_align (2), gnu::alloc_size (1), gnu::returns_nonnull, gnu::warn_unused_result] + void *allocate (size_t bytes, size_t alignment = alignof (std::max_align_t)); + void deallocate (void *ptr, size_t bytes, size_t alignment = alignof (std::max_align_t)); + + void reset (void); + + private: + char *m_begin, *m_end, *m_cursor; + }; +} } + +#endif diff --git a/alloc/stack.ipp b/alloc/stack.ipp new file mode 100644 index 00000000..c779ae58 --- /dev/null +++ b/alloc/stack.ipp @@ -0,0 +1,23 @@ +/* + * 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 2015 Danny Robson + */ + +#ifdef __UTIL_ALLOC_STACK_IPP +#error +#endif + +#define __UTIL_ALLOC_STACK_IPP + + diff --git a/test/alloc/linear.cpp b/test/alloc/linear.cpp new file mode 100644 index 00000000..ec95ada1 --- /dev/null +++ b/test/alloc/linear.cpp @@ -0,0 +1,38 @@ +#include "tap.hpp" +#include "alloc/linear.hpp" + + +int +main (void) +{ + util::TAP::logger tap; + + constexpr size_t BUFFER_SIZE = 1024; + + alignas (std::max_align_t) char memory[BUFFER_SIZE]; + util::alloc::linear store (std::begin (memory), std::end (memory)); + + tap.expect_throw ( + [&] (void) { store.allocate (BUFFER_SIZE + 1, 1); }, + "excessive allocation throws bad_alloc" + ); + + tap.expect_nothrow ( + [&] (void) { store.allocate (BUFFER_SIZE); }, + "maximum allocation succeeds" + ); + + tap.expect_throw ( + [&] (void) { store.allocate (1, 1); }, + "minimum allocation fails after exhaustion" + ); + + store.reset (); + + tap.expect_nothrow ( + [&] (void) { store.allocate (1, 1); }, + "minimum allocation succeeds after reset" + ); + + return tap.status (); +} diff --git a/test/alloc/stack.cpp b/test/alloc/stack.cpp new file mode 100644 index 00000000..24cca3a4 --- /dev/null +++ b/test/alloc/stack.cpp @@ -0,0 +1,68 @@ +#include "tap.hpp" +#include "alloc/stack.hpp" + + +/////////////////////////////////////////////////////////////////////////////// +void +n_allocations (util::alloc::stack &store, + unsigned count, + size_t bytes, + size_t alignment = alignof (std::max_align_t)) +{ + for (unsigned i = 0; i < count; ++i) { + auto ptr = store.allocate (bytes, alignment); + store.deallocate (ptr, bytes, alignment); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +int +main (void) +{ + util::TAP::logger tap; + + constexpr size_t BUFFER_AVAILABLE = 32; + constexpr size_t BUFFER_REQUEST = BUFFER_AVAILABLE - alignof (std::max_align_t); + constexpr size_t BUFFER_PAD = 32; + constexpr size_t BUFFER_SIZE = BUFFER_AVAILABLE + BUFFER_PAD; + + // alignment is kinda important, so make it a little easier and ensure + // something suitable right off the bat. + alignas (std::max_align_t) char memory[BUFFER_SIZE]; + std::fill (std::begin (memory), std::end (memory), 0); + + util::alloc::stack store (memory, memory + BUFFER_AVAILABLE); + + // larger than total allocations should throw + tap.expect_throw ( + [&store] (void) { store.allocate (BUFFER_AVAILABLE + 1, 1); }, + "excessive allocation throws bad_alloc" + ); + + // try a large number of allocations so we exercise the frame handling and + // alignment routines. + tap.expect_nothrow ( + [&store] (void) { n_allocations (store, BUFFER_AVAILABLE, BUFFER_REQUEST); }, + "repeated allocation/deallocation" + ); + + // perform two near maximum allocations and check for exhaustion through + // bad_alloc + auto ptr = store.allocate (BUFFER_REQUEST); + (void)ptr; + + tap.expect_throw ( + [&store] (void) { store.allocate (BUFFER_REQUEST); }, + "bad_alloc thrown on exhaustion" + ); + + // try many allocations again after resetting the allocator to zero usage + store.reset (); + tap.expect_nothrow ( + [&store] (void) { n_allocations (store, BUFFER_AVAILABLE, BUFFER_REQUEST); }, + "no bad_alloc after reset" + ); + + return tap.status (); +}