public unsafe void ResizeToAtLeast(ref RawBuffer buffer, int targetSize, int copyCount) { Debug.Assert(copyCount <= buffer.Length, "Can't copy more than the capacity of the buffer."); Debug.Assert(copyCount <= targetSize, "Can't copy more than the target size."); //Only do anything if the new size is actually different from the current size. targetSize = 1 << (SpanHelper.GetContainingPowerOf2(targetSize)); if (buffer.Allocated) { DecomposeId(buffer.Id, out var powerIndex, out var slotIndex); var currentSize = 1 << powerIndex; if (currentSize != targetSize || pools[powerIndex].GetStartPointerForSlot(slotIndex) != buffer.Memory) { TakeAtLeast(targetSize, out var newBuffer); Buffer.MemoryCopy(buffer.Memory, newBuffer.Memory, buffer.Length, copyCount); pools[powerIndex].Return(slotIndex); buffer = newBuffer; } else { //While the allocation size is equal to the target size, the buffer might not be. //Fortunately, if the allocation stays the same size and the buffer start is at its original location, we can skip doing any work. //(With more work, you could expand *backwards*, we just didn't bother since this is exceptionally rare anyway. //The typed codepath doesn't bother doing this at all, and that's fine.) buffer.Length = targetSize; } } else { //Nothing to return or copy. TakeAtLeast(targetSize, out buffer); } }
public static int GetCapacityForCount <T>(int count) { if (count == 0) { count = 1; } return((1 << SpanHelper.GetContainingPowerOf2(count * Unsafe.SizeOf <T>())) / Unsafe.SizeOf <T>()); }
public PowerPool(int power, int minimumBlockSize, int expectedPooledCount) { Power = power; SuballocationSize = 1 << power; BlockSize = Math.Max(SuballocationSize, minimumBlockSize); Slots = new ManagedIdPool(expectedPooledCount); SuballocationsPerBlock = BlockSize / SuballocationSize; SuballocationsPerBlockShift = SpanHelper.GetContainingPowerOf2(SuballocationsPerBlock); SuballocationsPerBlockMask = (1 << SuballocationsPerBlockShift) - 1; Blocks = new Block[1]; BlockCount = 0; #if DEBUG outstandingIds = new HashSet <int>(); #if LEAKDEBUG outstandingAllocators = new Dictionary <string, HashSet <int> >(); #endif #endif }
public unsafe void Take(out RawBuffer buffer) { var slot = Slots.Take(); var blockIndex = slot >> SuballocationsPerBlockShift; if (blockIndex >= Blocks.Length) { Array.Resize(ref Blocks, 1 << SpanHelper.GetContainingPowerOf2(blockIndex + 1)); } if (blockIndex >= BlockCount) { #if DEBUG for (int i = 0; i < blockIndex; ++i) { Debug.Assert(Blocks[i].Allocated, "If a block index is found to exceed the current block count, then every block preceding the index should be allocated."); } #endif BlockCount = blockIndex + 1; Debug.Assert(!Blocks[blockIndex].Allocated); Blocks[blockIndex] = new Block(BlockSize); } var indexInBlock = slot & SuballocationsPerBlockMask; buffer = new RawBuffer(Blocks[blockIndex].Allocate(indexInBlock, SuballocationSize), SuballocationSize, (Power << IdPowerShift) | slot); Debug.Assert(buffer.Id >= 0 && Power >= 0 && Power < 32, "Slot/power should be safely encoded in a 32 bit integer."); #if DEBUG const int maximumOutstandingCount = 1 << 20; Debug.Assert(outstandingIds.Count <= maximumOutstandingCount, $"Do you actually truly really need to have {maximumOutstandingCount} allocations taken from this power pool, or is this a memory leak?"); Debug.Assert(outstandingIds.Add(slot), "Should not be able to request the same slot twice."); #if LEAKDEBUG var allocator = new StackTrace().ToString(); if (!outstandingAllocators.TryGetValue(allocator, out var idsForAllocator)) { idsForAllocator = new HashSet <int>(); outstandingAllocators.Add(allocator, idsForAllocator); } Debug.Assert(idsForAllocator.Count < (1 << 25), "Do you actually have that many allocations for this one allocator?"); idsForAllocator.Add(slot); #endif #endif }
public void TakeAtLeast(int count, out RawBuffer buffer) { TakeForPower(SpanHelper.GetContainingPowerOf2(count), out buffer); }
/// <summary> /// Gets the capacity allocated for a power. /// </summary> /// <param name="power">Power to check.</param> /// <returns>Allocated capacity for the given power.</returns> public int GetCapacityForPower(int power) { SpanHelper.ValidatePower(power); return(pools[power].BlockCount * pools[power].BlockSize); }
/// <summary> /// Ensures that the pool associated with a given power has at least a certain amount of capacity, measured in bytes. /// </summary> /// <param name="byteCount">Minimum number of bytes to require for the power pool.</param> /// <param name="power">Power associated with the pool to check.</param> public void EnsureCapacityForPower(int byteCount, int power) { SpanHelper.ValidatePower(power); ValidatePinnedState(true); pools[power].EnsureCapacity(byteCount); }