aboutsummaryrefslogtreecommitdiffstats
path: root/common/almalloc.h
diff options
context:
space:
mode:
Diffstat (limited to 'common/almalloc.h')
-rw-r--r--common/almalloc.h310
1 files changed, 295 insertions, 15 deletions
diff --git a/common/almalloc.h b/common/almalloc.h
index a4297cf5..15ec600d 100644
--- a/common/almalloc.h
+++ b/common/almalloc.h
@@ -1,30 +1,310 @@
#ifndef AL_MALLOC_H
#define AL_MALLOC_H
-#include <stddef.h>
+#include <algorithm>
+#include <cstddef>
+#include <iterator>
+#include <limits>
+#include <memory>
+#include <new>
+#include <type_traits>
+#include <utility>
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "pragmadefs.h"
-/* Minimum alignment required by posix_memalign. */
-#define DEF_ALIGN sizeof(void*)
void *al_malloc(size_t alignment, size_t size);
void *al_calloc(size_t alignment, size_t size);
-void al_free(void *ptr);
+void al_free(void *ptr) noexcept;
+
+
+#define DEF_NEWDEL(T) \
+ void *operator new(size_t size) \
+ { \
+ void *ret = al_malloc(alignof(T), size); \
+ if(!ret) throw std::bad_alloc(); \
+ return ret; \
+ } \
+ void operator delete(void *block) noexcept { al_free(block); }
+
+#define DEF_PLACE_NEWDEL() \
+ void *operator new(size_t /*size*/, void *ptr) noexcept { return ptr; } \
+ void operator delete(void *block, void*) noexcept { al_free(block); } \
+ void operator delete(void *block) noexcept { al_free(block); }
+
+struct FamCount { size_t mCount; };
+
+#define DEF_FAM_NEWDEL(T, FamMem) \
+ static constexpr size_t Sizeof(size_t count) noexcept \
+ { return decltype(FamMem)::Sizeof(count, offsetof(T, FamMem)); } \
+ \
+ void *operator new(size_t /*size*/, FamCount fam) \
+ { \
+ if(void *ret{al_malloc(alignof(T), T::Sizeof(fam.mCount))}) \
+ return ret; \
+ throw std::bad_alloc(); \
+ } \
+ void operator delete(void *block, FamCount) { al_free(block); } \
+ void operator delete(void *block) noexcept { al_free(block); }
+
+
+namespace al {
+
+#define REQUIRES(...) typename std::enable_if<(__VA_ARGS__),int>::type = 0
+
+template<typename T, std::size_t alignment=alignof(T)>
+struct allocator {
+ using value_type = T;
+ using reference = T&;
+ using const_reference = const T&;
+ using pointer = T*;
+ using const_pointer = const T*;
+ using size_type = std::size_t;
+ using difference_type = std::ptrdiff_t;
+ using is_always_equal = std::true_type;
+
+ template<typename U>
+ struct rebind {
+ using other = allocator<U, (alignment<alignof(U))?alignof(U):alignment>;
+ };
+
+ allocator() = default;
+ template<typename U, std::size_t N>
+ constexpr allocator(const allocator<U,N>&) noexcept { }
+
+ T *allocate(std::size_t n)
+ {
+ if(n > std::numeric_limits<std::size_t>::max()/sizeof(T)) throw std::bad_alloc();
+ if(auto p = static_cast<T*>(al_malloc(alignment, n*sizeof(T)))) return p;
+ throw std::bad_alloc();
+ }
+ void deallocate(T *p, std::size_t) noexcept { al_free(p); }
+};
+template<typename T, std::size_t N, typename U, std::size_t M>
+bool operator==(const allocator<T,N>&, const allocator<U,M>&) noexcept { return true; }
+template<typename T, std::size_t N, typename U, std::size_t M>
+bool operator!=(const allocator<T,N>&, const allocator<U,M>&) noexcept { return false; }
-size_t al_get_page_size(void);
+template<size_t alignment, typename T>
+inline T* assume_aligned(T *ptr) noexcept
+{
+ static_assert((alignment & (alignment-1)) == 0, "alignment must be a power of 2");
+#ifdef __GNUC__
+ return static_cast<T*>(__builtin_assume_aligned(ptr, alignment));
+#elif defined(_MSC_VER)
+ auto ptrval = reinterpret_cast<uintptr_t>(ptr);
+ if((ptrval&(alignment-1)) != 0) __assume(0);
+ return reinterpret_cast<T*>(ptrval);
+#else
+ return ptr;
+#endif
+}
-/**
- * Returns non-0 if the allocation function has direct alignment handling.
- * Otherwise, the standard malloc is used with an over-allocation and pointer
- * offset strategy.
+/* At least VS 2015 complains that 'ptr' is unused when the given type's
+ * destructor is trivial (a no-op). So disable that warning for this call.
*/
-int al_is_sane_alignment_allocator(void);
+DIAGNOSTIC_PUSH
+msc_pragma(warning(disable : 4100))
+template<typename T>
+inline void destroy_at(T *ptr) { ptr->~T(); }
+DIAGNOSTIC_POP
-#ifdef __cplusplus
+template<typename T>
+inline void destroy(T first, const T end)
+{
+ while(first != end)
+ {
+ al::destroy_at(std::addressof(*first));
+ ++first;
+ }
}
-#endif
+
+template<typename T, typename N, REQUIRES(std::is_integral<N>::value)>
+inline T destroy_n(T first, N count)
+{
+ if(count != 0)
+ {
+ do {
+ al::destroy_at(std::addressof(*first));
+ ++first;
+ } while(--count);
+ }
+ return first;
+}
+
+
+template<typename T>
+inline void uninitialized_default_construct(T first, const T last)
+{
+ using ValueT = typename std::iterator_traits<T>::value_type;
+ T current{first};
+ try {
+ while(current != last)
+ {
+ ::new (static_cast<void*>(std::addressof(*current))) ValueT;
+ ++current;
+ }
+ }
+ catch(...) {
+ destroy(first, current);
+ throw;
+ }
+}
+
+template<typename T, typename N, REQUIRES(std::is_integral<N>::value)>
+inline T uninitialized_default_construct_n(T first, N count)
+{
+ using ValueT = typename std::iterator_traits<T>::value_type;
+ T current{first};
+ if(count != 0)
+ {
+ try {
+ do {
+ ::new (static_cast<void*>(std::addressof(*current))) ValueT;
+ ++current;
+ } while(--count);
+ }
+ catch(...) {
+ destroy(first, current);
+ throw;
+ }
+ }
+ return current;
+}
+
+
+template<typename T0, typename T1>
+inline T1 uninitialized_move(T0 first, const T0 last, const T1 output)
+{
+ using ValueT = typename std::iterator_traits<T1>::value_type;
+ T1 current{output};
+ try {
+ while(first != last)
+ {
+ ::new (static_cast<void*>(std::addressof(*current))) ValueT{std::move(*first)};
+ ++current;
+ ++first;
+ }
+ }
+ catch(...) {
+ destroy(output, current);
+ throw;
+ }
+ return current;
+}
+
+template<typename T0, typename N, typename T1, REQUIRES(std::is_integral<N>::value)>
+inline T1 uninitialized_move_n(T0 first, N count, const T1 output)
+{
+ using ValueT = typename std::iterator_traits<T1>::value_type;
+ T1 current{output};
+ if(count != 0)
+ {
+ try {
+ do {
+ ::new (static_cast<void*>(std::addressof(*current))) ValueT{std::move(*first)};
+ ++current;
+ ++first;
+ } while(--count);
+ }
+ catch(...) {
+ destroy(output, current);
+ throw;
+ }
+ }
+ return current;
+}
+
+
+/* std::make_unique was added with C++14, so until we rely on that, make our
+ * own version.
+ */
+template<typename T, typename ...ArgsT>
+std::unique_ptr<T> make_unique(ArgsT&&...args)
+{ return std::unique_ptr<T>{new T{std::forward<ArgsT>(args)...}}; }
+
+
+/* A flexible array type. Used either standalone or at the end of a parent
+ * struct, with placement new, to have a run-time-sized array that's embedded
+ * with its size.
+ */
+template<typename T, size_t alignment=alignof(T)>
+struct FlexArray {
+ using element_type = T;
+ using value_type = typename std::remove_cv<T>::type;
+ using index_type = size_t;
+ using difference_type = ptrdiff_t;
+
+ using pointer = T*;
+ using const_pointer = const T*;
+ using reference = T&;
+ using const_reference = const T&;
+
+ using iterator = pointer;
+ using const_iterator = const_pointer;
+ using reverse_iterator = std::reverse_iterator<iterator>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+
+ const index_type mSize;
+DIAGNOSTIC_PUSH
+std_pragma("GCC diagnostic ignored \"-Wpedantic\"")
+msc_pragma(warning(disable : 4200))
+ alignas(alignment) element_type mArray[0];
+DIAGNOSTIC_POP
+
+ static std::unique_ptr<FlexArray> Create(index_type count)
+ {
+ void *ptr{al_calloc(alignof(FlexArray), Sizeof(count))};
+ return std::unique_ptr<FlexArray>{new (ptr) FlexArray{count}};
+ }
+ static constexpr index_type Sizeof(index_type count, index_type base=0u) noexcept
+ {
+ return base +
+ std::max<index_type>(offsetof(FlexArray, mArray) + sizeof(T)*count, sizeof(FlexArray));
+ }
+
+ FlexArray(index_type size) : mSize{size}
+ { uninitialized_default_construct_n(mArray, mSize); }
+ ~FlexArray() { destroy_n(mArray, mSize); }
+
+ FlexArray(const FlexArray&) = delete;
+ FlexArray& operator=(const FlexArray&) = delete;
+
+ index_type size() const noexcept { return mSize; }
+ bool empty() const noexcept { return mSize == 0; }
+
+ pointer data() noexcept { return mArray; }
+ const_pointer data() const noexcept { return mArray; }
+
+ reference operator[](index_type i) noexcept { return mArray[i]; }
+ const_reference operator[](index_type i) const noexcept { return mArray[i]; }
+
+ reference front() noexcept { return mArray[0]; }
+ const_reference front() const noexcept { return mArray[0]; }
+
+ reference back() noexcept { return mArray[mSize-1]; }
+ const_reference back() const noexcept { return mArray[mSize-1]; }
+
+ iterator begin() noexcept { return mArray; }
+ const_iterator begin() const noexcept { return mArray; }
+ const_iterator cbegin() const noexcept { return mArray; }
+ iterator end() noexcept { return mArray + mSize; }
+ const_iterator end() const noexcept { return mArray + mSize; }
+ const_iterator cend() const noexcept { return mArray + mSize; }
+
+ reverse_iterator rbegin() noexcept { return end(); }
+ const_reverse_iterator rbegin() const noexcept { return end(); }
+ const_reverse_iterator crbegin() const noexcept { return cend(); }
+ reverse_iterator rend() noexcept { return begin(); }
+ const_reverse_iterator rend() const noexcept { return begin(); }
+ const_reverse_iterator crend() const noexcept { return cbegin(); }
+
+ DEF_PLACE_NEWDEL()
+};
+
+#undef REQUIRES
+
+} // namespace al
#endif /* AL_MALLOC_H */