#ifndef AL_MALLOC_H #define AL_MALLOC_H #include #include #include #include #include #include #include #include #ifdef _MSC_VER #define DIAGNOSTIC_PUSH __pragma(warning(push)) #define GNUDIAGNOSTIC(x) #define MVSDIAGNOSTIC(...) __pragma(__VA_ARGS__) #define DIAGNOSTIC_POP __pragma(warning(pop)) #elif defined(__GNUC__) || defined(__clang__) #define DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") #define GNUDIAGNOSTIC(x) _Pragma(x) #define MVSDIAGNOSTIC(...) #define DIAGNOSTIC_POP _Pragma("GCC diagnostic push") #else #define DIAGNOSTIC_PUSH #define GNUDIAGNOSTIC(x) #define MVSDIAGNOSTIC(...) #define DIAGNOSTIC_POP #endif void *al_malloc(size_t alignment, size_t size); void *al_calloc(size_t alignment, size_t size); 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 struct allocator { using value_type = T; using is_always_equal = std::true_type; template struct rebind { using other = allocator; }; allocator() = default; template constexpr allocator(const allocator&) noexcept { } T *allocate(std::size_t n) { if(n > std::numeric_limits::max()/sizeof(T)) throw std::bad_alloc(); if(auto p = static_cast(al_malloc(alignment, n*sizeof(T)))) return p; throw std::bad_alloc(); } void deallocate(T *p, std::size_t) noexcept { al_free(p); } }; template bool operator==(const allocator&, const allocator&) noexcept { return true; } template bool operator!=(const allocator&, const allocator&) noexcept { return false; } template 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(__builtin_assume_aligned(ptr, alignment)); #elif defined(_MSC_VER) auto ptrval = reinterpret_cast(ptr); if((ptrval&(alignment-1)) != 0) __assume(0); return reinterpret_cast(ptrval); #else return ptr; #endif } /* 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. */ DIAGNOSTIC_PUSH MVSDIAGNOSTIC(warning(disable : 4100)) template inline void destroy_at(T *ptr) { ptr->~T(); } DIAGNOSTIC_POP template inline void destroy(T first, const T end) { while(first != end) { al::destroy_at(std::addressof(*first)); ++first; } } template::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 inline void uninitialized_default_construct(T first, const T last) { using ValueT = typename std::iterator_traits::value_type; T current{first}; try { while(current != last) { ::new (static_cast(std::addressof(*current))) ValueT; ++current; } } catch(...) { destroy(first, current); throw; } } template::value)> inline T uninitialized_default_construct_n(T first, N count) { using ValueT = typename std::iterator_traits::value_type; T current{first}; if(count != 0) { try { do { ::new (static_cast(std::addressof(*current))) ValueT; ++current; } while(--count); } catch(...) { destroy(first, current); throw; } } return current; } template inline T1 uninitialized_move(T0 first, const T0 last, const T1 output) { using ValueT = typename std::iterator_traits::value_type; T1 current{output}; try { while(first != last) { ::new (static_cast(std::addressof(*current))) ValueT{std::move(*first)}; ++current; ++first; } } catch(...) { destroy(output, current); throw; } return current; } template::value)> inline T1 uninitialized_move_n(T0 first, N count, const T1 output) { using ValueT = typename std::iterator_traits::value_type; T1 current{output}; if(count != 0) { try { do { ::new (static_cast(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 std::unique_ptr make_unique(ArgsT&&...args) { return std::unique_ptr{new T{std::forward(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 struct FlexArray { using element_type = T; using value_type = typename std::remove_cv::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; using const_reverse_iterator = std::reverse_iterator; const index_type mSize; DIAGNOSTIC_PUSH GNUDIAGNOSTIC("GCC diagnostic ignored \"-Wpedantic\"") MVSDIAGNOSTIC(warning(disable : 4200)) alignas(alignment) element_type mArray[0]; DIAGNOSTIC_POP static std::unique_ptr Create(index_type count) { void *ptr{al_calloc(alignof(FlexArray), Sizeof(count))}; return std::unique_ptr{new (ptr) FlexArray{count}}; } static constexpr index_type Sizeof(index_type count, index_type base=0u) noexcept { return base + std::max(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 */