public void SetFreeListEntryIndex(int headBlockIndex) { var freeList = new AllocationTableEntry { Next = headBlockIndex }; WriteEntry(FreeListEntryIndex, freeList); }
/// <summary> /// Trims an existing list to the specified length and returns the excess blocks as a new list. /// </summary> /// <param name="listHeadBlockIndex">The starting block of the list to trim.</param> /// <param name="newListLength">The length in blocks that the list will be shortened to.</param> /// <returns>The index of the head node of the removed blocks.</returns> public int Trim(int listHeadBlockIndex, int newListLength) { int blocksRemaining = newListLength; int nextEntry = BlockToEntryIndex(listHeadBlockIndex); int listAIndex = -1; int listBIndex = -1; while (blocksRemaining > 0) { if (nextEntry == 0) { return(-1); } int currentEntryIndex = nextEntry; ReadEntry(EntryIndexToBlock(currentEntryIndex), out int nextBlock, out int _, out int segmentLength); nextEntry = BlockToEntryIndex(nextBlock); if (segmentLength == blocksRemaining) { listAIndex = currentEntryIndex; listBIndex = nextEntry; } else if (segmentLength > blocksRemaining) { Split(EntryIndexToBlock(currentEntryIndex), blocksRemaining); listAIndex = currentEntryIndex; listBIndex = currentEntryIndex + blocksRemaining; } blocksRemaining -= segmentLength; } if (listAIndex == -1 || listBIndex == -1) { return(-1); } AllocationTableEntry listANode = ReadEntry(listAIndex); AllocationTableEntry listBNode = ReadEntry(listBIndex); listANode.SetNext(0); listBNode.MakeListStart(); WriteEntry(listAIndex, listANode); WriteEntry(listBIndex, listBNode); return(EntryIndexToBlock(listBIndex)); }
/// <summary> /// Combines 2 lists into one list. The second list will be attached to the end of the first list. /// </summary> /// <param name="frontListBlockIndex">The index of the start block of the first list.</param> /// <param name="backListBlockIndex">The index of the start block of the second list.</param> public void Join(int frontListBlockIndex, int backListBlockIndex) { int frontEntryIndex = BlockToEntryIndex(frontListBlockIndex); int backEntryIndex = BlockToEntryIndex(backListBlockIndex); int frontTailIndex = GetListTail(frontEntryIndex); AllocationTableEntry frontTail = ReadEntry(frontTailIndex); AllocationTableEntry backHead = ReadEntry(backEntryIndex); frontTail.SetNext(backEntryIndex); backHead.SetPrev(frontTailIndex); WriteEntry(frontTailIndex, frontTail); WriteEntry(backEntryIndex, backHead); }
public void Free(int listBlockIndex) { int listEntryIndex = BlockToEntryIndex(listBlockIndex); AllocationTableEntry listEntry = ReadEntry(listEntryIndex); if (!listEntry.IsListStart()) { throw new ArgumentOutOfRangeException(nameof(listBlockIndex), "The block to free must be the start of a list."); } int freeListIndex = GetFreeListEntryIndex(); // Free list is empty if (freeListIndex == 0) { SetFreeListEntryIndex(listEntryIndex); return; } Join(listBlockIndex, EntryIndexToBlock(freeListIndex)); SetFreeListBlockIndex(listBlockIndex); }
public int GetFreeListEntryIndex() { AllocationTableEntry freeList = ReadEntry(FreeListEntryIndex); return(freeList.GetNext()); }
private void WriteEntry(int entryIndex, AllocationTableEntry entry) { Span <byte> bytes = stackalloc byte[EntrySize]; int offset = entryIndex * EntrySize; ref AllocationTableEntry newEntry = ref GetEntryFromBytes(bytes);
/// <summary> /// Splits a single list segment into 2 segments. The sequence of blocks in the full list will remain the same. /// </summary> /// <param name="segmentBlockIndex">The block index of the segment to split.</param> /// <param name="firstSubSegmentLength">The length of the first subsegment.</param> public void Split(int segmentBlockIndex, int firstSubSegmentLength) { Debug.Assert(firstSubSegmentLength > 0); int segAIndex = BlockToEntryIndex(segmentBlockIndex); AllocationTableEntry segA = ReadEntry(segAIndex); if (!segA.IsMultiBlockSegment()) { throw new ArgumentException("Cannot split a single-entry segment."); } AllocationTableEntry segARange = ReadEntry(segAIndex + 1); int originalLength = segARange.GetNext() - segARange.GetPrev() + 1; if (firstSubSegmentLength >= originalLength) { throw new ArgumentOutOfRangeException(nameof(firstSubSegmentLength), $"Requested sub-segment length ({firstSubSegmentLength}) must be less than the full segment length ({originalLength})"); } int segBIndex = segAIndex + firstSubSegmentLength; int segALength = firstSubSegmentLength; int segBLength = originalLength - segALength; var segB = new AllocationTableEntry(); // Insert segment B between segments A and C segB.SetPrev(segAIndex); segB.SetNext(segA.GetNext()); segA.SetNext(segBIndex); if (!segB.IsListEnd()) { AllocationTableEntry segC = ReadEntry(segB.GetNext()); segC.SetPrev(segBIndex); WriteEntry(segB.GetNext(), segC); } // Write the new range entries if needed if (segBLength > 1) { segB.MakeMultiBlockSegment(); var segBRange = new AllocationTableEntry(); segBRange.SetRange(segBIndex, segBIndex + segBLength - 1); WriteEntry(segBIndex + 1, segBRange); WriteEntry(segBIndex + segBLength - 1, segBRange); } WriteEntry(segBIndex, segB); if (segALength == 1) { segA.MakeSingleBlockSegment(); } else { segARange.SetRange(segAIndex, segAIndex + segALength - 1); WriteEntry(segAIndex + 1, segARange); WriteEntry(segAIndex + segALength - 1, segARange); } WriteEntry(segAIndex, segA); }