Last active
July 24, 2019 10:14
-
-
Save pCYSl5EDgo/90704920994fd8dfba4ec53b07593e90 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Runtime.CompilerServices; | |
using Unity.Collections; | |
using Unity.Collections.LowLevel.Unsafe; | |
namespace UniNativeLinq | |
{ | |
public unsafe struct | |
NativeEnumerable<T> : IEnumerable<T> | |
where T : unmanaged | |
{ | |
public readonly T* Ptr; | |
public readonly long Length; | |
public ref T this[long index] => ref Ptr[index]; | |
public NativeEnumerable(NativeArray<T> array) | |
{ | |
if (array.IsCreated) | |
{ | |
Ptr = (T*) NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array); | |
Length = array.Length; | |
} | |
else | |
{ | |
Ptr = null; | |
Length = 0; | |
} | |
} | |
public NativeEnumerable(NativeArray<T> array, long offset, long length) | |
{ | |
if (array.IsCreated && length > 0) | |
{ | |
if (offset >= 0) | |
{ | |
Ptr = (T*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array); | |
Ptr += offset; | |
Length = length; | |
} | |
else | |
{ | |
Ptr = (T*) NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array); | |
Length = length + offset; | |
} | |
} | |
else | |
{ | |
Ptr = null; | |
Length = 0; | |
} | |
} | |
public NativeEnumerable(T* ptr, long length) | |
{ | |
if (length <= 0 || ptr == null) | |
{ | |
this = default; | |
return; | |
} | |
Ptr = ptr; | |
Length = length; | |
} | |
public void Dispose(Allocator allocator) | |
{ | |
if (Ptr != null) | |
UnsafeUtility.Free(Ptr, allocator); | |
this = default; | |
} | |
public Enumerator GetEnumerator() => new Enumerator(this); | |
public ReverseEnumerator GetReverseEnumerator() => new ReverseEnumerator(this); | |
public struct Enumerator : IEnumerator<T> | |
{ | |
internal readonly T* Ptr; | |
internal readonly long Length; | |
private long index; | |
public ref T Current => ref Ptr[index]; | |
T IEnumerator<T>.Current => Current; | |
object IEnumerator.Current => Current; | |
internal Enumerator(in NativeEnumerable<T> parent) | |
{ | |
index = -1; | |
Ptr = parent.Ptr; | |
Length = parent.Length; | |
} | |
public void Dispose() => this = default; | |
public bool MoveNext() => ++index < Length; | |
public void Reset() => index = -1; | |
public ref T TryGetNext(out bool success) | |
{ | |
success = ++index < Length; | |
if (success) | |
return ref Ptr[index]; | |
index = Length; | |
T* ptr = stackalloc T[1]; | |
return ref *ptr; | |
} | |
public bool TryMoveNext(out T value) | |
{ | |
if (++index < Length) | |
{ | |
value = Ptr[index]; | |
return true; | |
} | |
else | |
{ | |
value = default; | |
index = Length; | |
return false; | |
} | |
} | |
} | |
public struct ReverseEnumerator : IEnumerator<T> | |
{ | |
internal readonly T* Ptr; | |
private readonly long length; | |
private long index; | |
public ref T Current => ref Ptr[index]; | |
T IEnumerator<T>.Current => Current; | |
object IEnumerator.Current => Current; | |
internal ReverseEnumerator(in NativeEnumerable<T> parent) | |
{ | |
index = parent.Length; | |
Ptr = parent.Ptr; | |
length = parent.Length; | |
} | |
public void Dispose() => this = default; | |
public bool MoveNext() => --index >= 0; | |
public void Reset() => index = length; | |
public ref T TryGetNext(out bool success) | |
{ | |
success = --index >= 0; | |
if (success) | |
return ref Ptr[index]; | |
index = 0; | |
T* ptr = stackalloc T[1]; | |
return ref *ptr; | |
} | |
public bool TryMoveNext(out T value) | |
{ | |
if (--index >= 0) | |
{ | |
value = Ptr[index]; | |
return true; | |
} | |
else | |
{ | |
value = default; | |
index = 0; | |
return false; | |
} | |
} | |
} | |
#region Interface Implementation | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
IEnumerator<T> IEnuemrable<T>.GetEnumerator() => GetEnumerator(); | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public bool CanFastCount() => true; | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public bool Any() => Length != 0; | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public int Count() => (int)Length; | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public long LongCount() => Length; | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public readonly void CopyTo(T* dest) => UnsafeUtility.MemCpy(dest, Ptr, sizeof(T) * Length); | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public T[] ToArray() | |
{ | |
var count = LongCount(); | |
if (count == 0) return Array.Empty<T>(); | |
var answer = new T[count]; | |
fixed(T* ptr = &answer[0]) | |
CopyTo(ptr); | |
return answer; | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public NativeEnumerable<T> ToNativeEnumerable(Allocator allocator) | |
{ | |
var count = LongCount(); | |
var ptr = (T*) UnsafeUtility.Malloc(count * sizeof(T), 4, allocator); | |
CopyTo(ptr); | |
return new NativeEnumerable<T>(ptr, count); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public NativeArray<T> ToNativeArray(Allocator allocator) | |
{ | |
var count = Count(); | |
if (count == 0) return default; | |
var answer = new NativeArray<T>(count, allocator, NativeArrayOptions.UninitializedMemory); | |
CopyTo((T*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(answer)); | |
return answer; | |
} | |
#endregion | |
public NativeEnumerable<T> | |
Skip(long count) | |
=> new NativeEnumerable<T>(Ptr + count, Length - count); | |
public NativeEnumerable<T> | |
SkipLast(long count) | |
=> new NativeEnumerable<T>(Ptr, Length - count); | |
public NativeEnumerable<T> | |
Take(long count) | |
{ | |
if (count >= Length) return this; | |
if (count <= 0) return default; | |
return new NativeEnumerable<T>(Ptr, count); | |
} | |
public NativeEnumerable<T> | |
TakeLast(long count) | |
{ | |
if (count >= Length) return this; | |
if (Ptr == null || count <= 0) return default; | |
return new NativeEnumerable<T>(Ptr + Length - count, count); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
public bool TryGetLast(out T value) | |
{ | |
var answer = Length != 0; | |
value = Ptr[Length - 1]; | |
return answer; | |
} | |
public static implicit operator NativeArray<T>(NativeEnumerable<T> enumerable) | |
{ | |
var aanswer = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T>(enumerable.Ptr, (int)enumerable.Length, Allocator.None); | |
#if ENABLE_UNITY_COLLECTIONS_CHECKS | |
NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref answer, AtomicSafetyHandle.GetTempUnsafePtrSliceHandle()); | |
#endif | |
return answer; | |
} | |
public static implicit operator NativeEnumerable<T>(NativeArray<T> array) | |
=> new NativeEnumerable<T>(array); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment