Beispiel #1
0
        private PageEntry *GetFreePageEntry(int order)
        {
            Assert.SdkRequiresGreaterEqual(order, 0);
            Assert.SdkRequiresLessEqual(order, GetOrderMax());

            // Try orders from low to high until we find a free page entry.
            for (int curOrder = order; curOrder <= GetOrderMax(); curOrder++)
            {
                ref PageList freeList = ref FreeLists[curOrder];
                if (!freeList.IsEmpty())
                {
                    // The current list isn't empty, so grab an entry from it.
                    PageEntry *pageEntry = freeList.PopFront();
                    Assert.SdkAssert(pageEntry != null);

                    // Update size bookkeeping.
                    TotalFreeSize -= GetBytesFromOrder(curOrder);

                    // If we allocated more memory than needed, free the unneeded portion.
                    DivideBuddies(pageEntry, order, curOrder);
                    Assert.SdkAssert(pageEntry->Next == null);

                    // Return the newly-divided entry.
                    return(pageEntry);
                }
            }
Beispiel #2
0
            public bool Remove(PageEntry *pageEntry)
            {
                Assert.SdkRequires(pageEntry != null);

                // If we're empty, we can't remove the page list.
                if (IsEmpty())
                {
                    return(false);
                }

                // We're going to loop over all pages to find this one, then unlink it.
                PageEntry *prevEntry = null;
                PageEntry *curEntry  = FirstPageEntry;

                while (true)
                {
                    // Check if we found the page.
                    if (curEntry == pageEntry)
                    {
                        if (curEntry == FirstPageEntry)
                        {
                            // If it's the first page, we just set our first.
                            FirstPageEntry = curEntry->Next;
                        }
                        else if (curEntry == LastPageEntry)
                        {
                            // If it's the last page, we set our last.
                            LastPageEntry       = prevEntry;
                            LastPageEntry->Next = null;
                        }
                        else
                        {
                            // If it's in the middle, we just unlink.
                            prevEntry->Next = curEntry->Next;
                        }

                        // Unlink this entry's next.
                        curEntry->Next = null;

                        // Update our entry count.
                        EntryCount--;
                        Assert.SdkGreaterEqual(EntryCount, 0);

                        return(true);
                    }

                    // If we have no next page, we can't remove.
                    if (curEntry->Next == null)
                    {
                        return(false);
                    }

                    // Advance to the next item in the list.
                    prevEntry = curEntry;
                    curEntry  = curEntry->Next;
                }
            }
Beispiel #3
0
        private PageEntry *GetBuddy(PageEntry *pageEntry, int order)
        {
            Assert.SdkRequires(FreeLists != null);
            Assert.SdkRequiresGreaterEqual(order, 0);
            Assert.SdkRequiresLessEqual(order, GetOrderMax());

            nuint address = GetAddressFromPageEntry(pageEntry);
            nuint offset  = (nuint)GetBlockCountFromOrder(order) * GetBlockSize();

            if (IsAlignedToOrder(pageEntry, order + 1))
            {
                // If the page entry is aligned to the next order,
                // return the buddy block to the right of the current entry.
                return(address + offset < HeapStart + HeapSize ? GetPageEntryFromAddress(address + offset) : null);
            }
            else
            {
                // If the page entry isn't aligned, return the buddy block to the left of the current entry.
                return(HeapStart <= address - offset ? GetPageEntryFromAddress(address - offset) : null);
            }
        }
Beispiel #4
0
        private void DivideBuddies(PageEntry *pageEntry, int requiredOrder, int chosenOrder)
        {
            Assert.SdkRequires(FreeLists != null);
            Assert.SdkRequiresGreaterEqual(requiredOrder, 0);
            Assert.SdkRequiresGreaterEqual(chosenOrder, requiredOrder);
            Assert.SdkRequiresLessEqual(chosenOrder, GetOrderMax());

            // Start at the end of the entry.
            nuint address = GetAddressFromPageEntry(pageEntry) + GetBytesFromOrder(chosenOrder);

            for (int order = chosenOrder; order > requiredOrder; order--)
            {
                // For each order, subtract that order's size from the address to get the start of a new block.
                address -= GetBytesFromOrder(order - 1);
                PageEntry *dividedEntry = GetPageEntryFromAddress(address);

                // Push back to the list.
                FreeLists[order - 1].PushBack(dividedEntry);
                TotalFreeSize += GetBytesFromOrder(order - 1);
            }
        }
Beispiel #5
0
        public void *AllocateByOrder(int order)
        {
            Assert.SdkRequires(FreeLists != null);
            Assert.SdkRequiresGreaterEqual(order, 0);
            Assert.SdkRequiresLessEqual(order, GetOrderMax());

            // Get the page entry.
            PageEntry *pageEntry = GetFreePageEntry(order);

            if (pageEntry != null)
            {
                // Ensure we're allocating an unlinked page.
                Assert.SdkAssert(pageEntry->Next == null);

                // Return the address for this entry.
                return((void *)GetAddressFromPageEntry(pageEntry));
            }
            else
            {
                return(null);
            }
        }
Beispiel #6
0
        private void JoinBuddies(PageEntry *pageEntry, int order)
        {
            Assert.SdkRequires(FreeLists != null);
            Assert.SdkRequiresGreaterEqual(order, 0);
            Assert.SdkRequiresLessEqual(order, GetOrderMax());

            PageEntry *curEntry = pageEntry;
            int        curOrder = order;

            while (curOrder < GetOrderMax())
            {
                // Get the buddy page.
                PageEntry *buddyEntry = GetBuddy(curEntry, curOrder);

                // Check whether the buddy is in the relevant free list.
                if (buddyEntry != null && FreeLists[curOrder].Remove(buddyEntry))
                {
                    TotalFreeSize -= GetBytesFromOrder(curOrder);

                    // Ensure we coalesce with the correct buddy when page is aligned
                    if (!IsAlignedToOrder(curEntry, curOrder + 1))
                    {
                        curEntry = buddyEntry;
                    }

                    curOrder++;
                }
                else
                {
                    // Buddy isn't in the free list, so we can't coalesce.
                    break;
                }
            }

            // Insert the coalesced entry into the free list.
            FreeLists[curOrder].PushBack(curEntry);
            TotalFreeSize += GetBytesFromOrder(curOrder);
        }
Beispiel #7
0
            public PageEntry *PopFront()
            {
                Assert.SdkRequires(EntryCount > 0);

                // Get the first entry.
                PageEntry *pageEntry = FirstPageEntry;

                // Advance our list.
                FirstPageEntry  = pageEntry->Next;
                pageEntry->Next = null;

                // Decrement our count.
                EntryCount--;
                Assert.SdkGreaterEqual(EntryCount, 0);

                // If this was our last page, clear our last entry.
                if (EntryCount == 0)
                {
                    LastPageEntry = null;
                }

                return(pageEntry);
            }
Beispiel #8
0
        public void Free(void *pointer, int order)
        {
            Assert.SdkRequires(FreeLists != null);
            Assert.SdkRequiresGreaterEqual(order, 0);
            Assert.SdkRequiresLessEqual(order, GetOrderMax());

            // Allow Free(null)
            if (pointer == null)
            {
                return;
            }

            // Ensure the pointer is block aligned.
            Assert.SdkAligned((nuint)pointer - HeapStart, (int)GetBlockSize());

            // Get the page entry.
            PageEntry *pageEntry = GetPageEntryFromAddress((UIntPtr)pointer);

            Assert.SdkAssert(IsAlignedToOrder(pageEntry, order));

            /* Reinsert into the free lists. */
            JoinBuddies(pageEntry, order);
        }
        public void Free(void *pointer, int order)
        {
            Assert.True(FreeLists != null);
            Assert.True(order >= 0);
            Assert.True(order <= GetOrderMax());

            // Allow Free(null)
            if (pointer == null)
            {
                return;
            }

            // Ensure the pointer is block aligned.
            Assert.True(Alignment.IsAlignedPow2((nuint)pointer - HeapStart, (uint)GetBlockSize()));

            // Get the page entry.
            PageEntry *pageEntry = GetPageEntryFromAddress((UIntPtr)pointer);

            Assert.True(IsAlignedToOrder(pageEntry, order));

            /* Reinsert into the free lists. */
            JoinBuddies(pageEntry, order);
        }
Beispiel #10
0
            public void PushBack(PageEntry *pageEntry)
            {
                Assert.SdkRequires(pageEntry != null);

                // If we're empty, we want to set the first page entry.
                if (IsEmpty())
                {
                    FirstPageEntry = pageEntry;
                }
                else
                {
                    // We're not empty, so push the page to the back.
                    Assert.SdkAssert(LastPageEntry != pageEntry);
                    LastPageEntry->Next = pageEntry;
                }

                // Set our last page entry to be this one, and link it to the list.
                LastPageEntry       = pageEntry;
                LastPageEntry->Next = null;

                // Increment our entry count.
                EntryCount++;
                Assert.SdkGreater(EntryCount, 0);
            }
Beispiel #11
0
        public Result Initialize(UIntPtr address, nuint size, nuint blockSize, int orderMax)
        {
            Assert.SdkRequires(FreeLists == null);
            Assert.SdkRequiresNotEqual(address, UIntPtr.Zero);
            Assert.SdkRequiresAligned(address.ToUInt64(), (int)BufferAlignment);
            Assert.SdkRequiresGreaterEqual(blockSize, BlockSizeMin);
            Assert.SdkRequires(BitUtil.IsPowerOfTwo(blockSize));
            Assert.SdkRequiresGreaterEqual(size, blockSize);
            Assert.SdkRequiresGreater(orderMax, 0);
            Assert.SdkRequiresLess(orderMax, OrderUpperLimit);

            // Set up our basic member variables
            BlockSize = blockSize;
            OrderMax  = orderMax;
            HeapStart = address;
            HeapSize  = size;

            TotalFreeSize = 0;

            // Determine page sizes
            nuint maxPageSize  = BlockSize << OrderMax;
            nuint maxPageCount = (nuint)Alignment.AlignUp(HeapSize, (uint)maxPageSize) / maxPageSize;

            Assert.SdkGreater((int)maxPageCount, 0);

            // Setup the free lists
            if (ExternalFreeLists != null)
            {
                Assert.SdkAssert(InternalFreeLists == null);
                FreeLists = ExternalFreeLists;
            }
            else
            {
                InternalFreeLists = GC.AllocateArray <PageList>(OrderMax + 1, true);
                FreeLists         = (PageList *)Unsafe.AsPointer(ref MemoryMarshal.GetArrayDataReference(InternalFreeLists));
                if (InternalFreeLists == null)
                {
                    return(ResultFs.AllocationMemoryFailedInFileSystemBuddyHeapA.Log());
                }
            }

            // All but the last page region should go to the max order.
            for (nuint i = 0; i < maxPageCount - 1; i++)
            {
                PageEntry *pageEntry = GetPageEntryFromAddress(HeapStart + i * maxPageSize);
                FreeLists[orderMax].PushBack(pageEntry);
            }

            TotalFreeSize += (nuint)FreeLists[orderMax].GetSize() * GetBytesFromOrder(orderMax);

            // Allocate remaining space to smaller orders as possible.
            {
                nuint remaining  = HeapSize - (maxPageCount - 1) * maxPageSize;
                nuint curAddress = HeapStart - (maxPageCount - 1) * maxPageSize;
                Assert.SdkAligned(remaining, (int)BlockSize);

                do
                {
                    // Determine what order we can use.
                    int order = GetOrderFromBytes(remaining + 1);
                    if (order < 0)
                    {
                        Assert.SdkEqual(OrderMax, GetOrderFromBytes(remaining));
                        order = OrderMax + 1;
                    }

                    Assert.SdkGreater(order, 0);
                    Assert.SdkLessEqual(order, OrderMax + 1);

                    // Add to the correct free list.
                    FreeLists[order - 1].PushBack(GetPageEntryFromAddress(curAddress));
                    TotalFreeSize += GetBytesFromOrder(order - 1);

                    // Move on to the next order.
                    nuint pageSize = GetBytesFromOrder(order - 1);
                    curAddress += pageSize;
                    remaining  -= pageSize;
                } while (BlockSize <= remaining);
            }

            return(Result.Success);
        }