blob: e5aebd5d997cfb22d85d13f1c95bbbc9e0c4d91b [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.
#include "lib/escher/util/block_allocator.h"
#include "lib/escher/util/align.h"
#include "lib/fxl/logging.h"
namespace escher {
BlockAllocator::BlockAllocator(size_t fixed_size_block_size)
: fixed_size_block_size_(fixed_size_block_size),
current_fixed_size_block_(fixed_size_blocks_.emplace(
fixed_size_blocks_.end(), fixed_size_block_size_)) {}
void* BlockAllocator::Allocate(size_t size, size_t alignment) {
// Any allocation bigger than 1/4 of the fixed-block size is treated as a
// large block allocation. This guarantees that no more than 1/4 of a block's
// space is wasted, without having undesirably small "large blocks".
if (size > fixed_size_block_size_ / 4) {
auto it = InsertLargeBlock(size, alignment);
void* result = AllocateFromBlock(it, size, alignment);
FXL_DCHECK(result);
return result;
} else if (void* result = AllocateFromBlock(current_fixed_size_block_, size,
alignment)) {
return result;
} else {
result = AllocateFromBlock(ObtainNextFixedSizeBlock(), size, alignment);
FXL_DCHECK(result);
return result;
}
}
void BlockAllocator::Reset() {
large_blocks_.clear();
for (auto& b : fixed_size_blocks_) {
b.current_ptr = b.start;
}
current_fixed_size_block_ = fixed_size_blocks_.begin();
}
BlockAllocator::BlockList::iterator BlockAllocator::InsertLargeBlock(
size_t size, size_t alignment) {
// TODO(ES-89): Is there a standard way to find/specify alignment of data in
// std::vector?. If we had this we wouldn't need to overallocate in many
// cases. Another approach would be to not use vectors for large block data;
// maybe simple malloced ptrs?
const size_t aligned_size = size + alignment;
return large_blocks_.emplace(large_blocks_.end(), aligned_size);
}
BlockAllocator::BlockList::iterator BlockAllocator::ObtainNextFixedSizeBlock() {
FXL_DCHECK(current_fixed_size_block_ != fixed_size_blocks_.end());
if (++current_fixed_size_block_ == fixed_size_blocks_.end()) {
// No next block was available, so allocate another one.
current_fixed_size_block_ = fixed_size_blocks_.emplace(
current_fixed_size_block_, fixed_size_block_size_);
}
return current_fixed_size_block_;
}
void* BlockAllocator::AllocateFromBlock(BlockList::iterator it, size_t size,
size_t alignment) {
Block& block = *it;
uint8_t* next = AlignedToNext(block.current_ptr, alignment);
uint8_t* end_of_next = next + size;
if (end_of_next <= block.end) {
// Enough free space for allocation.
block.current_ptr = end_of_next;
return next;
}
// Not enough free space for allocation;
return nullptr;
}
BlockAllocator::Block::Block(size_t size)
: bytes(size),
current_ptr(bytes.data()),
start(current_ptr),
end(start + size) {}
} // namespace escher