144 lines
5.9 KiB
C#
144 lines
5.9 KiB
C#
// Licensed to the .NET Foundation under one or more agreements.
|
|
// The .NET Foundation licenses this file to you under the MIT license.
|
|
// See the LICENSE file in the project root for more information.
|
|
#if NETCORE
|
|
using System;
|
|
|
|
namespace SharpCompress.Buffers
|
|
{
|
|
internal sealed partial class DefaultArrayPool<T> : ArrayPool<T>
|
|
{
|
|
/// <summary>The default maximum length of each array in the pool (2^20).</summary>
|
|
private const int DefaultMaxArrayLength = 1024 * 1024;
|
|
/// <summary>The default maximum number of arrays per bucket that are available for rent.</summary>
|
|
private const int DefaultMaxNumberOfArraysPerBucket = 50;
|
|
/// <summary>Lazily-allocated empty array used when arrays of length 0 are requested.</summary>
|
|
private static T[] s_emptyArray; // we support contracts earlier than those with Array.Empty<T>()
|
|
|
|
private readonly Bucket[] _buckets;
|
|
|
|
internal DefaultArrayPool() : this(DefaultMaxArrayLength, DefaultMaxNumberOfArraysPerBucket)
|
|
{
|
|
}
|
|
|
|
internal DefaultArrayPool(int maxArrayLength, int maxArraysPerBucket)
|
|
{
|
|
if (maxArrayLength <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(maxArrayLength));
|
|
}
|
|
if (maxArraysPerBucket <= 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(maxArraysPerBucket));
|
|
}
|
|
|
|
// Our bucketing algorithm has a min length of 2^4 and a max length of 2^30.
|
|
// Constrain the actual max used to those values.
|
|
const int MinimumArrayLength = 0x10, MaximumArrayLength = 0x40000000;
|
|
if (maxArrayLength > MaximumArrayLength)
|
|
{
|
|
maxArrayLength = MaximumArrayLength;
|
|
}
|
|
else if (maxArrayLength < MinimumArrayLength)
|
|
{
|
|
maxArrayLength = MinimumArrayLength;
|
|
}
|
|
|
|
// Create the buckets.
|
|
int poolId = Id;
|
|
int maxBuckets = Utilities.SelectBucketIndex(maxArrayLength);
|
|
var buckets = new Bucket[maxBuckets + 1];
|
|
for (int i = 0; i < buckets.Length; i++)
|
|
{
|
|
buckets[i] = new Bucket(Utilities.GetMaxSizeForBucket(i), maxArraysPerBucket, poolId);
|
|
}
|
|
_buckets = buckets;
|
|
}
|
|
|
|
/// <summary>Gets an ID for the pool to use with events.</summary>
|
|
private int Id => GetHashCode();
|
|
|
|
public override T[] Rent(int minimumLength)
|
|
{
|
|
// Arrays can't be smaller than zero. We allow requesting zero-length arrays (even though
|
|
// pooling such an array isn't valuable) as it's a valid length array, and we want the pool
|
|
// to be usable in general instead of using `new`, even for computed lengths.
|
|
if (minimumLength < 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException(nameof(minimumLength));
|
|
}
|
|
else if (minimumLength == 0)
|
|
{
|
|
// No need for events with the empty array. Our pool is effectively infinite
|
|
// and we'll never allocate for rents and never store for returns.
|
|
return s_emptyArray ?? (s_emptyArray = new T[0]);
|
|
}
|
|
|
|
T[] buffer = null;
|
|
|
|
int index = Utilities.SelectBucketIndex(minimumLength);
|
|
if (index < _buckets.Length)
|
|
{
|
|
// Search for an array starting at the 'index' bucket. If the bucket is empty, bump up to the
|
|
// next higher bucket and try that one, but only try at most a few buckets.
|
|
const int MaxBucketsToTry = 2;
|
|
int i = index;
|
|
do
|
|
{
|
|
// Attempt to rent from the bucket. If we get a buffer from it, return it.
|
|
buffer = _buckets[i].Rent();
|
|
if (buffer != null)
|
|
{
|
|
return buffer;
|
|
}
|
|
}
|
|
while (++i < _buckets.Length && i != index + MaxBucketsToTry);
|
|
|
|
// The pool was exhausted for this buffer size. Allocate a new buffer with a size corresponding
|
|
// to the appropriate bucket.
|
|
buffer = new T[_buckets[index]._bufferLength];
|
|
}
|
|
else
|
|
{
|
|
// The request was for a size too large for the pool. Allocate an array of exactly the requested length.
|
|
// When it's returned to the pool, we'll simply throw it away.
|
|
buffer = new T[minimumLength];
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
public override void Return(T[] array, bool clearArray = false)
|
|
{
|
|
if (array == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(array));
|
|
}
|
|
else if (array.Length == 0)
|
|
{
|
|
// Ignore empty arrays. When a zero-length array is rented, we return a singleton
|
|
// rather than actually taking a buffer out of the lowest bucket.
|
|
return;
|
|
}
|
|
|
|
// Determine with what bucket this array length is associated
|
|
int bucket = Utilities.SelectBucketIndex(array.Length);
|
|
|
|
// If we can tell that the buffer was allocated, drop it. Otherwise, check if we have space in the pool
|
|
if (bucket < _buckets.Length)
|
|
{
|
|
// Clear the array if the user requests
|
|
if (clearArray)
|
|
{
|
|
Array.Clear(array, 0, array.Length);
|
|
}
|
|
|
|
// Return the buffer to its bucket. In the future, we might consider having Return return false
|
|
// instead of dropping a bucket, in which case we could try to return to a lower-sized bucket,
|
|
// just as how in Rent we allow renting from a higher-sized bucket.
|
|
_buckets[bucket].Return(array);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif |