forked from mirror/DotRecast
Rented array struct
This commit is contained in:
parent
1315de063f
commit
96ffed87e3
|
@ -1,31 +1,96 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace DotRecast.Core.Buffers
|
namespace DotRecast.Core.Buffers
|
||||||
{
|
{
|
||||||
public static class RcRentedArray
|
public static class RcRentedArray
|
||||||
{
|
{
|
||||||
|
private sealed class RentIdPool
|
||||||
|
{
|
||||||
|
private int[] _generations;
|
||||||
|
private readonly Queue<int> _freeIds;
|
||||||
|
private int _maxId;
|
||||||
|
|
||||||
|
public RentIdPool(int capacity)
|
||||||
|
{
|
||||||
|
_generations = new int[capacity];
|
||||||
|
_freeIds = new Queue<int>(capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal RentIdGen AcquireId()
|
||||||
|
{
|
||||||
|
if (!_freeIds.TryDequeue(out int id))
|
||||||
|
{
|
||||||
|
id = _maxId++;
|
||||||
|
if(_generations.Length <= id)
|
||||||
|
Array.Resize(ref _generations, _generations.Length << 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RentIdGen(id, _generations[id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void ReturnId(int id)
|
||||||
|
{
|
||||||
|
_generations[id]++;
|
||||||
|
_freeIds.Enqueue(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal int GetGeneration(int id)
|
||||||
|
{
|
||||||
|
return _generations.Length <= id ? 0 : _generations[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public const int START_RENT_ID_POOL_CAPACITY = 16;
|
||||||
|
private static readonly ThreadLocal<RentIdPool> _rentPool = new ThreadLocal<RentIdPool>(() => new RentIdPool(START_RENT_ID_POOL_CAPACITY));
|
||||||
|
|
||||||
public static RcRentedArray<T> Rent<T>(int minimumLength)
|
public static RcRentedArray<T> Rent<T>(int minimumLength)
|
||||||
{
|
{
|
||||||
var array = ArrayPool<T>.Shared.Rent(minimumLength);
|
var array = ArrayPool<T>.Shared.Rent(minimumLength);
|
||||||
return new RcRentedArray<T>(ArrayPool<T>.Shared, array, minimumLength);
|
return new RcRentedArray<T>(ArrayPool<T>.Shared, _rentPool.Value.AcquireId(), array, minimumLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static bool IsDisposed(RentIdGen rentIdGen)
|
||||||
|
{
|
||||||
|
return _rentPool.Value.GetGeneration(rentIdGen.Id) != rentIdGen.Gen;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void ReturnId(RentIdGen rentIdGen)
|
||||||
|
{
|
||||||
|
_rentPool.Value.ReturnId(rentIdGen.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RcRentedArray<T> : IDisposable
|
public readonly struct RentIdGen
|
||||||
|
{
|
||||||
|
public readonly int Id;
|
||||||
|
public readonly int Gen;
|
||||||
|
|
||||||
|
public RentIdGen(int id, int gen)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Gen = gen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct RcRentedArray<T> : IDisposable
|
||||||
{
|
{
|
||||||
private ArrayPool<T> _owner;
|
private ArrayPool<T> _owner;
|
||||||
private T[] _array;
|
private T[] _array;
|
||||||
|
private readonly RentIdGen _rentIdGen;
|
||||||
|
|
||||||
public int Length { get; }
|
public int Length { get; }
|
||||||
public bool IsDisposed => null == _owner || null == _array;
|
public bool IsDisposed => null == _owner || null == _array || RcRentedArray.IsDisposed(_rentIdGen);
|
||||||
|
|
||||||
internal RcRentedArray(ArrayPool<T> owner, T[] array, int length)
|
internal RcRentedArray(ArrayPool<T> owner, RentIdGen rentIdGen, T[] array, int length)
|
||||||
{
|
{
|
||||||
_owner = owner;
|
_owner = owner;
|
||||||
_array = array;
|
_array = array;
|
||||||
Length = length;
|
Length = length;
|
||||||
|
_rentIdGen = rentIdGen;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ref T this[int index]
|
public ref T this[int index]
|
||||||
|
@ -34,6 +99,8 @@ namespace DotRecast.Core.Buffers
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length);
|
RcThrowHelper.ThrowExceptionIfIndexOutOfRange(index, Length);
|
||||||
|
if (IsDisposed)
|
||||||
|
throw new NullReferenceException();
|
||||||
return ref _array[index];
|
return ref _array[index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,12 +118,14 @@ namespace DotRecast.Core.Buffers
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (null != _owner && null != _array)
|
if (null != _owner && null != _array && !RcRentedArray.IsDisposed(_rentIdGen))
|
||||||
{
|
{
|
||||||
|
RcRentedArray.ReturnId(_rentIdGen);
|
||||||
_owner.Return(_array, true);
|
_owner.Return(_array, true);
|
||||||
|
}
|
||||||
|
|
||||||
_owner = null;
|
_owner = null;
|
||||||
_array = null;
|
_array = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -103,4 +103,19 @@ public class RcRentedArrayTest
|
||||||
Assert.That(r1.IsDisposed, Is.EqualTo(true));
|
Assert.That(r1.IsDisposed, Is.EqualTo(true));
|
||||||
Assert.That(r1.AsArray(), Is.Null);
|
Assert.That(r1.AsArray(), Is.Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestIdPoolCapacity()
|
||||||
|
{
|
||||||
|
var buffer = new RcRentedArray<int>[RcRentedArray.START_RENT_ID_POOL_CAPACITY + 2];
|
||||||
|
for (int i = 0; i < buffer.Length; i++)
|
||||||
|
{
|
||||||
|
Assert.DoesNotThrow(() => buffer[i] = RcRentedArray.Rent<int>(4));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < buffer.Length; i++)
|
||||||
|
{
|
||||||
|
Assert.DoesNotThrow(() => buffer[i].Dispose());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue