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); } }
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; } }
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); } }
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); } }
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); } }
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); }
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); }
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); }
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); }
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); }