diff options
Diffstat (limited to 'src/arena.h')
-rw-r--r-- | src/arena.h | 273 |
1 files changed, 0 insertions, 273 deletions
diff --git a/src/arena.h b/src/arena.h deleted file mode 100644 index 569833ca..00000000 --- a/src/arena.h +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2019 Roman Perepelitsa. -// -// This file is part of GitStatus. -// -// GitStatus is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// GitStatus is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with GitStatus. If not, see <https://www.gnu.org/licenses/>. - -#ifndef ROMKATV_GITSTATUS_ARENA_H_ -#define ROMKATV_GITSTATUS_ARENA_H_ - -#include <cassert> -#include <cstddef> -#include <cstdint> -#include <cstring> -#include <limits> -#include <new> -#include <type_traits> -#include <vector> - -#include "string_view.h" - -namespace gitstatus { - -// Thread-compatible. Very fast and very flexible w.r.t. allocation size and alignment. -// -// Natural API extensions: -// -// // Donates a block to the arena. When the time comes, it'll be freed with -// // free(p, size, userdata). -// void Donate(void* p, size_t size, void* userdata, void(*free)(void*, size_t, void*)); -class Arena { - public: - struct Options { - // The first call to Allocate() will allocate a block of this size. There is one exception when - // the first requested allocation size is larger than this limit. Subsequent blocks will be - // twice as large as the last until they saturate at max_block_size. - size_t min_block_size = 64; - - // Allocate blocks at most this large. There is one exception when the requested allocation - // size is larger than this limit. - size_t max_block_size = 8 << 10; - - // When the size of the first allocation in a block is larger than this threshold, the block - // size will be equal to the allocation size. This is meant to reduce memory waste when making - // many allocations with sizes slightly over max_block_size / 2. With max_alloc_threshold equal - // to max_block_size / N, the upper bound on wasted memory when making many equally-sized - // allocations is 100.0 / (N + 1) percent. When making allocations of different sizes, the upper - // bound on wasted memory is 50%. - size_t max_alloc_threshold = 1 << 10; - - // Natural extensions: - // - // void* userdata; - // void (*alloc)(size_t size, size_t alignment, void* userdata); - // void (*free)(void* p, size_t size, void* userdata); - }; - - // Requires: opt.min_block_size <= opt.max_block_size. - // - // Doesn't allocate any memory. - Arena(Options opt); - Arena() : Arena(Options()) {} - Arena(Arena&&); - ~Arena(); - - Arena& operator=(Arena&& other); - - // Requires: alignment is a power of 2. - // - // Result is never null and always aligned. If size is zero, the result may be equal to the last. - // Alignment above alignof(std::max_align_t) is supported. There is no requirement for alignment - // to be less than size or to divide it. - inline void* Allocate(size_t size, size_t alignment) { - assert(alignment && !(alignment & (alignment - 1))); - uintptr_t p = Align(top_->tip, alignment); - uintptr_t e = p + size; - if (e <= top_->end) { - top_->tip = e; - return reinterpret_cast<void*>(p); - } - return AllocateSlow(size, alignment); - } - - template <class T> - inline T* Allocate(size_t n) { - static_assert(!std::is_reference<T>(), ""); - return static_cast<T*>(Allocate(n * sizeof(T), alignof(T))); - } - - template <class T> - inline T* Allocate() { - return Allocate<T>(1); - } - - inline char* MemDup(const char* p, size_t len) { - char* res = Allocate<char>(len); - std::memcpy(res, p, len); - return res; - } - - // Copies the null-terminated string (including the trailing null character) to the arena and - // returns a pointer to the copy. - inline char* StrDup(const char* s) { - size_t len = std::strlen(s); - return MemDup(s, len + 1); - } - - // Guarantees: !StrDup(p, len)[len]. - inline char* StrDup(const char* p, size_t len) { - char* res = Allocate<char>(len + 1); - std::memcpy(res, p, len); - res[len] = 0; - return res; - } - - // Guarantees: !StrDup(s)[s.len]. - inline char* StrDup(StringView s) { - return StrDup(s.ptr, s.len); - } - - template <class... Ts> - inline char* StrCat(const Ts&... ts) { - return [&](std::initializer_list<StringView> ss) { - size_t len = 0; - for (StringView s : ss) len += s.len; - char* p = Allocate<char>(len + 1); - for (StringView s : ss) { - std::memcpy(p, s.ptr, s.len); - p += s.len; - } - *p = 0; - return p - len; - }({ts...}); - } - - // Copies/moves `val` to the arena and returns a pointer to it. - template <class T> - inline std::remove_const_t<std::remove_reference_t<T>>* Dup(T&& val) { - return DirectInit<std::remove_const_t<std::remove_reference_t<T>>>(std::forward<T>(val)); - } - - // The same as `new T{args...}` but on the arena. - template <class T, class... Args> - inline T* DirectInit(Args&&... args) { - T* res = Allocate<T>(); - ::new (const_cast<void*>(static_cast<const void*>(res))) T(std::forward<Args>(args)...); - return res; - } - - // The same as `new T(args...)` but on the arena. - template <class T, class... Args> - inline T* BraceInit(Args&&... args) { - T* res = Allocate<T>(); - ::new (const_cast<void*>(static_cast<const void*>(res))) T{std::forward<Args>(args)...}; - return res; - } - - // Tip() and TipSize() allow you to allocate the remainder of the current block. They can be - // useful if you are flexible w.r.t. the allocation size. - // - // Invariant: - // - // const void* tip = Tip(); - // void* p = Allocate(TipSize(), 1); // grab the remainder of the current block - // assert(p == tip); - const void* Tip() const { return reinterpret_cast<const void*>(top_->tip); } - size_t TipSize() const { return top_->end - top_->tip; } - - // Invalidates all allocations (without running destructors of allocated objects) and frees all - // blocks except at most the specified number of blocks. The retained blocks will be used to - // fulfil future allocation requests. - void Reuse(size_t num_blocks = std::numeric_limits<size_t>::max()); - - private: - struct Block { - size_t size() const { return end - start; } - uintptr_t start; - uintptr_t tip; - uintptr_t end; - }; - - inline static size_t Align(size_t n, size_t m) { return (n + m - 1) & ~(m - 1); }; - - void AddBlock(size_t size, size_t alignment); - bool ReuseBlock(size_t size, size_t alignment); - - __attribute__((noinline)) void* AllocateSlow(size_t size, size_t alignment); - - Options opt_; - std::vector<Block> blocks_; - // Invariant: !blocks_.empty() <= reusable_ && reusable_ <= blocks_.size(). - size_t reusable_ = 0; - // Invariant: (top_ == &g_empty_block) == blocks_.empty(). - // Invariant: blocks_.empty() || top_ == &blocks_.back() || top_ < blocks_.data() + reusable_. - Block* top_; - - static Block g_empty_block; -}; - -// Copies of ArenaAllocator use the same thread-compatible Arena without synchronization. -template <class T> -class ArenaAllocator { - public: - using value_type = T; - using pointer = T*; - using const_pointer = const T*; - using reference = T&; - using const_reference = const T&; - using size_type = size_t; - using difference_type = ptrdiff_t; - using propagate_on_container_move_assignment = std::true_type; - template <class U> - struct rebind { - using other = ArenaAllocator<U>; - }; - using is_always_equal = std::false_type; - - ArenaAllocator(Arena* arena = nullptr) : arena_(*arena) {} - - Arena& arena() const { return arena_; } - - pointer address(reference x) const { return &x; } - const_pointer address(const_reference x) const { return &x; } - pointer allocate(size_type n, const void* hint = nullptr) { return arena_.Allocate<T>(n); } - void deallocate(T* p, std::size_t n) {} - size_type max_size() const { return std::numeric_limits<size_type>::max() / sizeof(value_type); } - - template <class U, class... Args> - void construct(U* p, Args&&... args) { - ::new (const_cast<void*>(static_cast<const void*>(p))) U(std::forward<Args>(args)...); - } - - template <class U> - void destroy(U* p) { - p->~U(); - } - - bool operator==(const ArenaAllocator& other) const { return &arena_ == &other.arena_; } - bool operator!=(const ArenaAllocator& other) const { return &arena_ != &other.arena_; } - - private: - Arena& arena_; -}; - -template <class C> -struct LazyWithArena; - -template <template <class, class> class C, class T1, class A> -struct LazyWithArena<C<T1, A>> { - using type = C<T1, ArenaAllocator<typename C<T1, A>::value_type>>; -}; - -template <template <class, class, class> class C, class T1, class T2, class A> -struct LazyWithArena<C<T1, T2, A>> { - using type = C<T1, T2, ArenaAllocator<typename C<T1, T2, A>::value_type>>; -}; - -template <class C> -using WithArena = typename LazyWithArena<C>::type; - -} // namespace gitstatus - -#endif // ROMKATV_GITSTATUS_DIR_H_ |