blob: f0f9bad6d235365296fbf44ed5ece844e13d981a [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_UI_LIB_ESCHER_UTIL_STACK_ALLOCATOR_H_
#define SRC_UI_LIB_ESCHER_UTIL_STACK_ALLOCATOR_H_
#include <algorithm>
#include <type_traits>
#include "src/ui/lib/escher/util/align.h"
namespace escher {
// A simple, fast allocator that allocates from a fixed-size region within
// itself. It can return either pointers to uninitialized memory, or memory
// filled with a specified value. Resetting the allocator for reuse is
// extremely fast: the amount of used memory is simply set to zero. No
// destructors are called when the allocator is reset/destroyed; for safety,
// only trivially-destructible types can be allocated.
template <typename T, size_t N>
class StackAllocator {
public:
StackAllocator() : base_(NextAlignedPtr<T>(&buffer_[0])) {}
// Return a pointer to the specified number of T objects. Return nullptr if
// the requested number is zero, or if there is not enough space available.
// The contents of the pointed-to memory are undefined.
T* Allocate(size_t count = 1) {
if (count == 0)
return nullptr;
size_t to_be_used = used_ + count;
if (to_be_used > N)
return nullptr;
T* ptr = base_ + used_;
used_ = to_be_used;
return ptr;
}
// Return a pointer to the specified number of T objects. Return nullptr if
// the requested number is zero, or if there is not enough space available.
// Each T item in the pointed-to memory is initialized to the specified value,
// or a default value if none is specified.
T* AllocateFilled(size_t count = 1, const T& fill_value = T()) {
T* ptr = Allocate(count);
if (ptr) {
std::fill(ptr, ptr + count, fill_value);
}
return ptr;
}
// Reset the allocator so that its memory can be reallocated. Any pointers
// previously obtained from Allocate() and AllocateFilled() are now invalid,
// and should not be used.
void Reset() { used_ = 0; }
private:
static_assert(std::is_trivially_destructible<T>::value, "T is not trivially destructable");
size_t used_ = 0;
// |buffer_| is not necessarily aligned to alignof(T), so we initialize
// |base_| to point to the first properly-aligned address within |buffer_|.
// Adding "alignof(T)" to the size of |buffer_| guarantees that "base_[N-1]"
// fits within it.
T* base_;
uint8_t buffer_[N * sizeof(T) + alignof(T)];
};
} // namespace escher
#endif // SRC_UI_LIB_ESCHER_UTIL_STACK_ALLOCATOR_H_