memgraph/poc/memory/stack_allocator.hpp
Mislav Bradac 0588de76bb Move unused datastructures to poc
Reviewers: buda

Reviewed By: buda

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D526
2017-07-10 12:03:11 +02:00

122 lines
3.7 KiB
C++

#pragma once
#include <cmath>
#include "memory/block_allocator.hpp"
#include "utils/exceptions.hpp"
#include "utils/likely.hpp"
// http://en.cppreference.com/w/cpp/language/new
namespace utils {
///
/// @brief Raised by @c StackAllocator when it cannot allocate the object.
///
class OutOfMemory : public StacktraceException {
public:
using StacktraceException::StacktraceException;
};
// Useful for allocating memory which can be freed with one call.
// Most performant for data which need to be present to the end.
class StackAllocator {
static constexpr size_t page_size = 64 * 1024;
public:
~StackAllocator() { free(); }
// Allocates memory for object of type T.
// Retruns pointer to memory for it.
template <class T>
inline T *allocate() {
// If size is bigger than pages_size then this do-whil will never end
// until it eats all the memory.
static_assert(sizeof(T) <= page_size,
"Cant allocate objects larger than page_size");
do {
// Mask which has log2(alignof(T)) lower bits setted to 0 and the
// rest to 1.
// example:
// alignof(T)==8 => mask=0xfffffffffffffff8;
// This will be calculated in compile time.
size_t mask = ~(((size_t)alignof(T)) - 1);
// aligned contains ptr aligned to alignof(T).
// There are two types of head ptr:
// a) aligned to alignof(T)
// b) not aligned to alignof(T)
// For a) whe want for aligned to be equal to head, and for b)
// aligned shuold be first aligned ptr greater than head.
//
// head - 1 => turns a) into b) now whe only have to get first
// aligned ptr greater than (head - 1).
//
// (head - 1) & mask => will produce first smaller than head ptr
// aligned to alignof(T).
//
// ( (head - 1) & mask ) + alignof(T) => will produce first grater
// or equal than head ptr aligned to alignof(T).
char *aligned = (char *)(((((size_t)head) - 1) & mask) + alignof(T));
// New head which points to unallocated memory points to first byte
// after space for object T.
char *new_head = aligned + sizeof(T);
// If the new_head is greater than end that means that there isn't
// enough space for object T in current block of memory.
if (LIKELY(new_head <= end)) {
// All is fine, head can become new_head
head = new_head;
// Returns aligned ptr with enough space for object T.
return (T *)aligned;
}
// There isn't enough free space so whe must allocate more.
void *alloc = blocks.acquire();
// Check if there are memory. If not throw exception rather than
// return nullptr.
if (UNLIKELY(alloc == nullptr))
throw new OutOfMemory("BlockAllocator returned nullptr");
// Remember that whee allocated memory so that whe can free-it
// after.
allocated_blocks.push_back(alloc);
// Set new head, the old one isn't needed anymore.
head = (char *)alloc;
// Update end to point to first byte after newly allocated memory.
end = head + page_size;
// After allocating new memory lets try again to "allocate" place
// for T.
} while (true);
}
template <class T, class... Args>
inline T *make(Args &&... args) {
auto ptr = allocate<T>();
new (ptr) T(std::forward<Args>(args)...);
return ptr;
}
// Relases all memory.
void free() {
while (allocated_blocks.size()) {
blocks.release(allocated_blocks.back());
allocated_blocks.pop_back();
}
}
private:
BlockAllocator<page_size> blocks;
std::vector<void *> allocated_blocks;
char *head = {nullptr};
char *end = {nullptr};
};
} // namespace utils