Example #1
0
        public void SetFreeListEntryIndex(int headBlockIndex)
        {
            var freeList = new AllocationTableEntry {
                Next = headBlockIndex
            };

            WriteEntry(FreeListEntryIndex, freeList);
        }
Example #2
0
        /// <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));
        }
Example #3
0
        /// <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);
        }
Example #4
0
        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);
        }
Example #5
0
        public int GetFreeListEntryIndex()
        {
            AllocationTableEntry freeList = ReadEntry(FreeListEntryIndex);

            return(freeList.GetNext());
        }
Example #6
0
        private void WriteEntry(int entryIndex, AllocationTableEntry entry)
        {
            Span <byte> bytes  = stackalloc byte[EntrySize];
            int         offset = entryIndex * EntrySize;

            ref AllocationTableEntry newEntry = ref GetEntryFromBytes(bytes);
Example #7
0
        /// <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);
        }