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 { 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*)p + index); } } } } internal class ConcurrentMask { internal static long AtomicOr(ref long destination, long source) { var readValue = Interlocked.Read(ref destination); long oldReadValue, writtenValue; do { writtenValue = readValue | source; oldReadValue = readValue; readValue = Interlocked.CompareExchange(ref destination, writtenValue, oldReadValue); } while(readValue != oldReadValue); return writtenValue; } internal static long AtomicAnd(ref long destination, long source) { var readValue = Interlocked.Read(ref destination); long oldReadValue, writtenValue; do { writtenValue = readValue & source; oldReadValue = readValue; readValue = Interlocked.CompareExchange(ref destination, writtenValue, oldReadValue); } while(readValue != oldReadValue); return writtenValue; } 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 ErrorAllocationCrossesWordBoundary = -3; 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(ref T t, int offset, int bits) where T : IIndexable { var wordOffset = offset >> 6; var bitOffset = offset & 63; if(bitOffset + bits > 64) return ErrorAllocationCrossesWordBoundary; return TryAllocate(ref t.ElementAt(wordOffset), bitOffset, bits); } internal static int TryFree(ref T t, int offset, int bits) where T : IIndexable { var wordOffset = offset >> 6; var bitOffset = offset & 63; return TryFree(ref t.ElementAt(wordOffset), bitOffset, bits); } internal static int TryAllocate(ref T t, out int offset, int begin, int end, int bits) where T : IIndexable { var wordOffset = begin; for(; wordOffset < end; ++wordOffset) if(t.ElementAt(wordOffset) != ~0L) break; for(; 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(ref T t, out int offset, int begin, int bits) where T : IIndexable { var error = TryAllocate(ref t, out offset, begin, t.Length, bits); if(Succeeded(error)) return error; return TryAllocate(ref t, out offset, 0, begin, bits); } internal static int TryAllocate(ref T t, out int offset, int bits) where T : IIndexable { return TryAllocate(ref t, out offset, 0, t.Length, bits); } } }