| // 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 |