blob: 1d1a5f744276d8e033d5a19ba3e0d1bed2177265 [file] [log] [blame]
// Copyright 2019 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.
import 'dart:math' show min;
import 'package:meta/meta.dart';
import 'block.dart';
import 'vmo_fields.dart';
import 'vmo_holder.dart';
import 'vmo_writer.dart';
// All sizes are in bytes.
const int _pageSizeBytes = 4096;
/// With this allocator, all allocated blocks in the VMO will be 32 bytes.
/// (Indexes 0, 1, and 2 are reserved and not allocated.)
const int defaultBlockOrder = 1;
/// The base class for which log writers will inherit from.
///
/// (The current implementation is a barely-MVP heap: a 32-byte-block slab
/// allocator.)
class Heap {
/// Size in bytes of the touched / visited subset of the VMO incorporated in
/// the data structure.
int _currentSizeBytes;
final VmoHolder _vmo;
/// Index of the first block on the freelist.
int _freelistHead = invalidIndex;
/// Construct VMO with max available size; initialize startingSize of it.
Heap(this._vmo) {
_currentSizeBytes = min(_pageSizeBytes, _vmo.size);
_addFreelistBlocks(
fromBytes: heapStartIndex * bytesPerIndex, toBytes: _currentSizeBytes);
}
/// Gets a block from the freelist, or null if none available.
Block allocateBlock() {
if (_freelistHead == invalidIndex) {
// Grow one page at a time to save RAM.
_growHeap(_currentSizeBytes + _pageSizeBytes);
}
if (_freelistHead == invalidIndex) {
return null;
}
var block = Block.read(_vmo, _freelistHead);
_freelistHead = block.nextFree;
block.becomeReserved();
return block;
}
/// Returns a [block] to the freelist.
void freeBlock(Block block) {
if (block.type == BlockType.header || block.type == BlockType.free) {
throw ArgumentError("I shouldn't be trying to free this type "
'(index ${block.index}, type ${block.type})');
}
if (block.index < heapStartIndex ||
block.index * bytesPerIndex >= _currentSizeBytes) {
throw ArgumentError('Tried to free bad index ${block.index}');
}
block.becomeFree(_freelistHead); // Do we want to zero its contents?
_freelistHead = block.index;
}
void _addFreelistBlocks({@required int fromBytes, @required int toBytes}) {
for (int i = fromBytes ~/ bytesPerIndex;
i < toBytes ~/ bytesPerIndex;
i += 2) {
Block.create(_vmo, i).becomeFree(_freelistHead);
_freelistHead = i;
}
}
/// Expands the heap to occupy more of the VMO.
///
/// Touches memory, causing actual RAM to be used; that's why the heap
/// starts small and grows as needed.
void _growHeap(int desiredSizeBytes) {
if (_currentSizeBytes == _vmo.size) {
return; // Fail silently.
}
int newSize = desiredSizeBytes;
if (newSize > _vmo.size) {
newSize = _vmo.size;
}
_addFreelistBlocks(fromBytes: _currentSizeBytes, toBytes: newSize);
_currentSizeBytes = newSize;
}
}