233 lines
7.8 KiB
C#
233 lines
7.8 KiB
C#
|
using System;
|
||
|
using Unity.Collections;
|
||
|
using System.Diagnostics;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using Unity.Collections.LowLevel.Unsafe;
|
||
|
|
||
|
namespace UnityEngine.U2D.Common.UTess
|
||
|
{
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential)]
|
||
|
[DebuggerDisplay("Length = {Length}")]
|
||
|
[DebuggerTypeProxy(typeof(ArraySliceDebugView<>))]
|
||
|
internal unsafe struct ArraySlice<T> : System.IEquatable<ArraySlice<T>> where T : struct
|
||
|
{
|
||
|
[NativeDisableUnsafePtrRestriction] internal byte* m_Buffer;
|
||
|
internal int m_Stride;
|
||
|
internal int m_Length;
|
||
|
|
||
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||
|
internal int m_MinIndex;
|
||
|
internal int m_MaxIndex;
|
||
|
internal AtomicSafetyHandle m_Safety;
|
||
|
#endif
|
||
|
|
||
|
public ArraySlice(NativeArray<T> array, int start, int length)
|
||
|
{
|
||
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||
|
if (start < 0)
|
||
|
throw new ArgumentOutOfRangeException(nameof(start), $"Slice start {start} < 0.");
|
||
|
if (length < 0)
|
||
|
throw new ArgumentOutOfRangeException(nameof(length), $"Slice length {length} < 0.");
|
||
|
if (start + length > array.Length)
|
||
|
throw new ArgumentException(
|
||
|
$"Slice start + length ({start + length}) range must be <= array.Length ({array.Length})");
|
||
|
m_MinIndex = 0;
|
||
|
m_MaxIndex = length - 1;
|
||
|
m_Safety = Unity.Collections.LowLevel.Unsafe.NativeArrayUnsafeUtility.GetAtomicSafetyHandle(array);
|
||
|
#endif
|
||
|
|
||
|
m_Stride = UnsafeUtility.SizeOf<T>();
|
||
|
var ptr = (byte*)array.GetUnsafePtr() + m_Stride * start;
|
||
|
m_Buffer = ptr;
|
||
|
m_Length = length;
|
||
|
}
|
||
|
|
||
|
public ArraySlice(Array<T> array, int start, int length)
|
||
|
{
|
||
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||
|
if (start < 0)
|
||
|
throw new ArgumentOutOfRangeException(nameof(start), $"Slice start {start} < 0.");
|
||
|
if (length < 0)
|
||
|
throw new ArgumentOutOfRangeException(nameof(length), $"Slice length {length} < 0.");
|
||
|
if (start + length > array.Length)
|
||
|
throw new ArgumentException(
|
||
|
$"Slice start + length ({start + length}) range must be <= array.Length ({array.Length})");
|
||
|
m_MinIndex = 0;
|
||
|
m_MaxIndex = length - 1;
|
||
|
m_Safety = Unity.Collections.LowLevel.Unsafe.NativeArrayUnsafeUtility.GetAtomicSafetyHandle(array.m_Array);
|
||
|
#endif
|
||
|
|
||
|
m_Stride = UnsafeUtility.SizeOf<T>();
|
||
|
var ptr = (byte*)array.UnsafePtr + m_Stride * start;
|
||
|
m_Buffer = ptr;
|
||
|
m_Length = length;
|
||
|
}
|
||
|
|
||
|
public bool Equals(ArraySlice<T> other)
|
||
|
{
|
||
|
return m_Buffer == other.m_Buffer && m_Stride == other.m_Stride && m_Length == other.m_Length;
|
||
|
}
|
||
|
|
||
|
public override bool Equals(object obj)
|
||
|
{
|
||
|
if (ReferenceEquals(null, obj)) return false;
|
||
|
return obj is ArraySlice<T> && Equals((ArraySlice<T>)obj);
|
||
|
}
|
||
|
|
||
|
public override int GetHashCode()
|
||
|
{
|
||
|
unchecked
|
||
|
{
|
||
|
var hashCode = (int)m_Buffer;
|
||
|
hashCode = (hashCode * 397) ^ m_Stride;
|
||
|
hashCode = (hashCode * 397) ^ m_Length;
|
||
|
return hashCode;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static bool operator ==(ArraySlice<T> left, ArraySlice<T> right)
|
||
|
{
|
||
|
return left.Equals(right);
|
||
|
}
|
||
|
|
||
|
public static bool operator !=(ArraySlice<T> left, ArraySlice<T> right)
|
||
|
{
|
||
|
return !left.Equals(right);
|
||
|
}
|
||
|
|
||
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||
|
// These are double-whammy excluded to we can elide bounds checks in the Burst disassembly view
|
||
|
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||
|
void CheckReadIndex(int index)
|
||
|
{
|
||
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||
|
if (index < m_MinIndex || index > m_MaxIndex)
|
||
|
FailOutOfRangeError(index);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||
|
void CheckWriteIndex(int index)
|
||
|
{
|
||
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||
|
if (index < m_MinIndex || index > m_MaxIndex)
|
||
|
FailOutOfRangeError(index);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
||
|
private void FailOutOfRangeError(int index)
|
||
|
{
|
||
|
if (index < Length && (m_MinIndex != 0 || m_MaxIndex != Length - 1))
|
||
|
throw new System.IndexOutOfRangeException(
|
||
|
$"Index {index} is out of restricted IJobParallelFor range [{m_MinIndex}...{m_MaxIndex}] in ReadWriteBuffer.\n" +
|
||
|
"ReadWriteBuffers are restricted to only read & write the element at the job index. " +
|
||
|
"You can use double buffering strategies to avoid race conditions due to " +
|
||
|
"reading & writing in parallel to the same elements from a job.");
|
||
|
|
||
|
throw new System.IndexOutOfRangeException($"Index {index} is out of range of '{Length}' Length.");
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
public static unsafe ArraySlice<T> ConvertExistingDataToArraySlice(void* dataPointer, int stride, int length)
|
||
|
{
|
||
|
if (length < 0)
|
||
|
throw new System.ArgumentException($"Invalid length of '{length}'. It must be greater than 0.",
|
||
|
nameof(length));
|
||
|
if (stride < 0)
|
||
|
throw new System.ArgumentException($"Invalid stride '{stride}'. It must be greater than 0.",
|
||
|
nameof(stride));
|
||
|
|
||
|
var newSlice = new ArraySlice<T>
|
||
|
{
|
||
|
m_Stride = stride,
|
||
|
m_Buffer = (byte*)dataPointer,
|
||
|
m_Length = length,
|
||
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||
|
m_MinIndex = 0,
|
||
|
m_MaxIndex = length - 1,
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
return newSlice;
|
||
|
}
|
||
|
|
||
|
public T this[int index]
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||
|
CheckReadIndex(index);
|
||
|
#endif
|
||
|
return UnsafeUtility.ReadArrayElementWithStride<T>(m_Buffer, index, m_Stride);
|
||
|
}
|
||
|
|
||
|
[WriteAccessRequired]
|
||
|
set
|
||
|
{
|
||
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||
|
CheckWriteIndex(index);
|
||
|
#endif
|
||
|
UnsafeUtility.WriteArrayElementWithStride(m_Buffer, index, m_Stride, value);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal unsafe void* GetUnsafeReadOnlyPtr()
|
||
|
{
|
||
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||
|
AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
|
||
|
#endif
|
||
|
return m_Buffer;
|
||
|
}
|
||
|
|
||
|
internal void CopyTo(T[] array)
|
||
|
{
|
||
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
||
|
if (Length != array.Length)
|
||
|
throw new ArgumentException($"array.Length ({array.Length}) does not match the Length of this instance ({Length}).", nameof(array));
|
||
|
#endif
|
||
|
unsafe
|
||
|
{
|
||
|
GCHandle handle = GCHandle.Alloc(array, GCHandleType.Pinned);
|
||
|
IntPtr addr = handle.AddrOfPinnedObject();
|
||
|
|
||
|
var sizeOf = UnsafeUtility.SizeOf<T>();
|
||
|
UnsafeUtility.MemCpyStride((byte*)addr, sizeOf, this.GetUnsafeReadOnlyPtr(), Stride, sizeOf, m_Length);
|
||
|
|
||
|
handle.Free();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal T[] ToArray()
|
||
|
{
|
||
|
var array = new T[Length];
|
||
|
CopyTo(array);
|
||
|
return array;
|
||
|
}
|
||
|
|
||
|
public int Stride => m_Stride;
|
||
|
public int Length => m_Length;
|
||
|
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// DebuggerTypeProxy for <see cref="ArraySlice{T}"/>
|
||
|
/// </summary>
|
||
|
internal sealed class ArraySliceDebugView<T> where T : struct
|
||
|
{
|
||
|
ArraySlice<T> m_Slice;
|
||
|
|
||
|
public ArraySliceDebugView(ArraySlice<T> slice)
|
||
|
{
|
||
|
m_Slice = slice;
|
||
|
}
|
||
|
|
||
|
public T[] Items
|
||
|
{
|
||
|
get { return m_Slice.ToArray(); }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|