Skip to content

Instantly share code, notes, and snippets.

@pCYSl5EDgo
Last active July 24, 2019 10:14
Show Gist options
  • Save pCYSl5EDgo/90704920994fd8dfba4ec53b07593e90 to your computer and use it in GitHub Desktop.
Save pCYSl5EDgo/90704920994fd8dfba4ec53b07593e90 to your computer and use it in GitHub Desktop.
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