169 lines
6.1 KiB
C#
169 lines
6.1 KiB
C#
|
using System;
|
||
|
using System.Threading;
|
||
|
using Unity.Collections.LowLevel.Unsafe;
|
||
|
using Unity.Mathematics;
|
||
|
|
||
|
#pragma warning disable 0649
|
||
|
|
||
|
namespace Unity.Collections
|
||
|
{
|
||
|
internal struct Long8
|
||
|
{
|
||
|
internal long f0,f1,f2,f3,f4,f5,f6,f7;
|
||
|
}
|
||
|
|
||
|
internal struct Long64
|
||
|
{
|
||
|
internal Long8 f0,f1,f2,f3,f4,f5,f6,f7;
|
||
|
}
|
||
|
|
||
|
internal struct Long512
|
||
|
{
|
||
|
internal Long64 f0,f1,f2,f3,f4,f5,f6,f7;
|
||
|
}
|
||
|
|
||
|
internal struct Long1024 : IIndexable<long>
|
||
|
{
|
||
|
internal Long512 f0,f1;
|
||
|
public int Length { get { return 1024;} set {} }
|
||
|
public ref long ElementAt(int index)
|
||
|
{
|
||
|
unsafe { fixed(Long512* p = &f0) {
|
||
|
return ref UnsafeUtility.AsRef<long>((long*)p + index);
|
||
|
} }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal class ConcurrentMask
|
||
|
{
|
||
|
internal static void longestConsecutiveOnes(long value, out int offset, out int count)
|
||
|
{
|
||
|
count = 0;
|
||
|
var newvalue = value;
|
||
|
while(newvalue != 0)
|
||
|
{
|
||
|
value = newvalue;
|
||
|
newvalue = value & (long)((ulong)value >> 1);
|
||
|
++count;
|
||
|
}
|
||
|
offset = math.tzcnt(value);
|
||
|
}
|
||
|
|
||
|
internal static bool foundAtLeastThisManyConsecutiveOnes(long value, int minimum, out int offset, out int count)
|
||
|
{
|
||
|
if(minimum == 1)
|
||
|
{
|
||
|
offset = math.tzcnt(value); // find offset of first 1 bit
|
||
|
count = 1;
|
||
|
return offset != 64;
|
||
|
}
|
||
|
longestConsecutiveOnes(value, out offset, out count);
|
||
|
return count >= minimum;
|
||
|
}
|
||
|
|
||
|
internal static bool foundAtLeastThisManyConsecutiveZeroes(long value, int minimum, out int offset, out int count)
|
||
|
{
|
||
|
return foundAtLeastThisManyConsecutiveOnes(~value, minimum, out offset, out count);
|
||
|
}
|
||
|
|
||
|
internal const int ErrorFailedToFree = -1;
|
||
|
internal const int ErrorFailedToAllocate = -2;
|
||
|
internal const int EmptyBeforeAllocation = 0;
|
||
|
internal const int EmptyAfterFree = 0;
|
||
|
|
||
|
internal static bool Succeeded(int error)
|
||
|
{
|
||
|
return error >= 0;
|
||
|
}
|
||
|
|
||
|
internal static long MakeMask(int offset, int bits)
|
||
|
{
|
||
|
return (long)(~0UL >> (64-bits)) << offset;
|
||
|
}
|
||
|
|
||
|
internal static int TryAllocate(ref long l, int offset, int bits)
|
||
|
{
|
||
|
var mask = MakeMask(offset, bits);
|
||
|
var readValue = Interlocked.Read(ref l);
|
||
|
long oldReadValue, writtenValue;
|
||
|
do
|
||
|
{
|
||
|
if((readValue & mask) != 0)
|
||
|
return ErrorFailedToAllocate;
|
||
|
writtenValue = readValue | mask;
|
||
|
oldReadValue = readValue;
|
||
|
readValue = Interlocked.CompareExchange(ref l, writtenValue, oldReadValue);
|
||
|
} while(readValue != oldReadValue);
|
||
|
return math.countbits(readValue); // how many bits were set, before i allocated? sometimes if 0, do something special (allocate chunk?)
|
||
|
}
|
||
|
|
||
|
internal static int TryFree(ref long l, int offset, int bits)
|
||
|
{
|
||
|
var mask = MakeMask(offset, bits);
|
||
|
var readValue = Interlocked.Read(ref l);
|
||
|
long oldReadValue, writtenValue;
|
||
|
do
|
||
|
{
|
||
|
if((readValue & mask) != mask)
|
||
|
return ErrorFailedToFree;
|
||
|
writtenValue = readValue & ~mask;
|
||
|
oldReadValue = readValue;
|
||
|
readValue = Interlocked.CompareExchange(ref l, writtenValue, oldReadValue);
|
||
|
} while(readValue != oldReadValue);
|
||
|
return math.countbits(writtenValue); // how many bits are set, after i freed? sometimes if 0, do something special (free chunk?)
|
||
|
}
|
||
|
|
||
|
internal static int TryAllocate(ref long l, out int offset, int bits)
|
||
|
{
|
||
|
var readValue = Interlocked.Read(ref l);
|
||
|
long oldReadValue, writtenValue;
|
||
|
do
|
||
|
{
|
||
|
if(!foundAtLeastThisManyConsecutiveZeroes(readValue, bits, out offset, out int _))
|
||
|
return ErrorFailedToAllocate;
|
||
|
var mask = MakeMask(offset, bits);
|
||
|
writtenValue = readValue | mask;
|
||
|
oldReadValue = readValue;
|
||
|
readValue = Interlocked.CompareExchange(ref l, writtenValue, oldReadValue);
|
||
|
} while(readValue != oldReadValue);
|
||
|
return math.countbits(readValue); // how many bits were set, before i allocated? sometimes if 0, do something special (allocate chunk?)
|
||
|
}
|
||
|
|
||
|
internal static int TryAllocate<T>(ref T t, int offset, int bits) where T : IIndexable<long>
|
||
|
{
|
||
|
var wordOffset = offset >> 6;
|
||
|
var bitOffset = offset & 63;
|
||
|
return TryAllocate(ref t.ElementAt(wordOffset), bitOffset, bits);
|
||
|
}
|
||
|
|
||
|
internal static int TryFree<T>(ref T t, int offset, int bits) where T : IIndexable<long>
|
||
|
{
|
||
|
var wordOffset = offset >> 6;
|
||
|
var bitOffset = offset & 63;
|
||
|
return TryFree(ref t.ElementAt(wordOffset), bitOffset, bits);
|
||
|
}
|
||
|
|
||
|
internal static int TryAllocate<T>(ref T t, out int offset, int begin, int end, int bits) where T : IIndexable<long>
|
||
|
{
|
||
|
for(var wordOffset = begin; wordOffset < end; ++wordOffset)
|
||
|
{
|
||
|
int error, bitOffset;
|
||
|
error = TryAllocate(ref t.ElementAt(wordOffset), out bitOffset, bits);
|
||
|
if(Succeeded(error))
|
||
|
{
|
||
|
offset = wordOffset * 64 + bitOffset;
|
||
|
return error;
|
||
|
}
|
||
|
}
|
||
|
offset = -1;
|
||
|
return ErrorFailedToAllocate;
|
||
|
}
|
||
|
|
||
|
internal static int TryAllocate<T>(ref T t, out int offset, int bits) where T : IIndexable<long>
|
||
|
{
|
||
|
return TryAllocate(ref t, out offset, 0, t.Length, bits);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|