using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Unity.Burst;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
namespace Unity.Collections
{
///
/// An unmanaged single value.
///
/// The functional equivalent of an array of length 1.
/// When you need just one value, NativeReference can be preferable to an array because it better conveys the intent.
/// The type of value.
[StructLayout(LayoutKind.Sequential)]
[NativeContainer]
[BurstCompatible(GenericTypeArguments = new [] { typeof(int) })]
public unsafe struct NativeReference
: INativeDisposable
, IEquatable>
where T : unmanaged
{
[NativeDisableUnsafePtrRestriction]
internal void* m_Data;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
internal AtomicSafetyHandle m_Safety;
static readonly SharedStatic s_SafetyId = SharedStatic.GetOrCreate>();
#if REMOVE_DISPOSE_SENTINEL
#else
[NativeSetClassTypeToNullOnSchedule]
DisposeSentinel m_DisposeSentinel;
#endif
#endif
internal AllocatorManager.AllocatorHandle m_AllocatorLabel;
///
/// Initializes and returns an instance of NativeReference.
///
/// The allocator to use.
/// Whether newly allocated bytes should be zeroed out.
public NativeReference(AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory)
{
Allocate(allocator, out this);
if (options == NativeArrayOptions.ClearMemory)
{
UnsafeUtility.MemClear(m_Data, UnsafeUtility.SizeOf());
}
}
///
/// Initializes and returns an instance of NativeReference.
///
/// The allocator to use.
/// The initial value.
public NativeReference(T value, AllocatorManager.AllocatorHandle allocator)
{
Allocate(allocator, out this);
*(T*)m_Data = value;
}
static void Allocate(AllocatorManager.AllocatorHandle allocator, out NativeReference reference)
{
CollectionHelper.CheckAllocator(allocator);
reference = default;
reference.m_Data = Memory.Unmanaged.Allocate(UnsafeUtility.SizeOf(), UnsafeUtility.AlignOf(), allocator);
reference.m_AllocatorLabel = allocator;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
#if REMOVE_DISPOSE_SENTINEL
reference.m_Safety = CollectionHelper.CreateSafetyHandle(allocator);
#else
if (allocator.IsCustomAllocator)
{
reference.m_Safety = AtomicSafetyHandle.Create();
reference.m_DisposeSentinel = null;
}
else
{
DisposeSentinel.Create(out reference.m_Safety, out reference.m_DisposeSentinel, 1, allocator.ToAllocator);
}
#endif
CollectionHelper.SetStaticSafetyId>(ref reference.m_Safety, ref s_SafetyId.Data);
#endif
}
///
/// The value stored in this reference.
///
/// The new value to store in this reference.
/// The value stored in this reference.
public T Value
{
get
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
#endif
return *(T*)m_Data;
}
set
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
#endif
*(T*)m_Data = value;
}
}
///
/// Whether this reference has been allocated (and not yet deallocated).
///
/// True if this reference has been allocated (and not yet deallocated).
public bool IsCreated => m_Data != null;
///
/// Releases all resources (memory and safety handles).
///
public void Dispose()
{
CheckNotDisposed();
if (CollectionHelper.ShouldDeallocate(m_AllocatorLabel))
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
#if REMOVE_DISPOSE_SENTINEL
CollectionHelper.DisposeSafetyHandle(ref m_Safety);
#else
DisposeSentinel.Dispose(ref m_Safety, ref m_DisposeSentinel);
#endif
#endif
Memory.Unmanaged.Free(m_Data, m_AllocatorLabel);
m_AllocatorLabel = Allocator.Invalid;
}
m_Data = null;
}
///
/// Creates and schedules a job that will release all resources (memory and safety handles) of this reference.
///
/// A job handle. The newly scheduled job will depend upon this handle.
/// The handle of a new job that will release all resources (memory and safety handles) of this reference.
[NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
public JobHandle Dispose(JobHandle inputDeps)
{
CheckNotDisposed();
if (CollectionHelper.ShouldDeallocate(m_AllocatorLabel))
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
#if REMOVE_DISPOSE_SENTINEL
#else
// [DeallocateOnJobCompletion] is not supported, but we want the deallocation
// to happen in a thread. DisposeSentinel needs to be cleared on main thread.
// AtomicSafetyHandle can be destroyed after the job was scheduled (Job scheduling
// will check that no jobs are writing to the container).
DisposeSentinel.Clear(ref m_DisposeSentinel);
#endif
#endif
var jobHandle = new NativeReferenceDisposeJob
{
Data = new NativeReferenceDispose
{
m_Data = m_Data,
m_AllocatorLabel = m_AllocatorLabel,
#if ENABLE_UNITY_COLLECTIONS_CHECKS
m_Safety = m_Safety
#endif
}
}.Schedule(inputDeps);
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.Release(m_Safety);
#endif
m_Data = null;
m_AllocatorLabel = Allocator.Invalid;
return jobHandle;
}
m_Data = null;
return inputDeps;
}
///
/// Copy the value of another reference to this reference.
///
/// The reference to copy from.
public void CopyFrom(NativeReference reference)
{
Copy(this, reference);
}
///
/// Copy the value of this reference to another reference.
///
/// The reference to copy to.
public void CopyTo(NativeReference reference)
{
Copy(reference, this);
}
///
/// Returns true if the value stored in this reference is equal to the value stored in another reference.
///
/// A reference to compare with.
/// True if the value stored in this reference is equal to the value stored in another reference.
[NotBurstCompatible]
public bool Equals(NativeReference other)
{
return Value.Equals(other.Value);
}
///
/// Returns true if the value stored in this reference is equal to an object.
///
/// Can only be equal if the object is itself a NativeReference.
/// An object to compare with.
/// True if the value stored in this reference is equal to the object.
[NotBurstCompatible]
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
return obj is NativeReference && Equals((NativeReference)obj);
}
///
/// Returns the hash code of this reference.
///
/// The hash code of this reference.
public override int GetHashCode()
{
return Value.GetHashCode();
}
///
/// Returns true if the values stored in two references are equal.
///
/// A reference.
/// Another reference.
/// True if the two values are equal.
public static bool operator ==(NativeReference left, NativeReference right)
{
return left.Equals(right);
}
///
/// Returns true if the values stored in two references are unequal.
///
/// A reference.
/// Another reference.
/// True if the two values are unequal.
public static bool operator !=(NativeReference left, NativeReference right)
{
return !left.Equals(right);
}
///
/// Copies the value of a reference to another reference.
///
/// The destination reference.
/// The source reference.
public static void Copy(NativeReference dst, NativeReference src)
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckReadAndThrow(src.m_Safety);
AtomicSafetyHandle.CheckWriteAndThrow(dst.m_Safety);
#endif
UnsafeUtility.MemCpy(dst.m_Data, src.m_Data, UnsafeUtility.SizeOf());
}
///
/// Returns a read-only reference aliasing the value of this reference.
///
/// A read-only reference aliasing the value of this reference.
public ReadOnly AsReadOnly()
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
return new ReadOnly(m_Data, ref m_Safety);
#else
return new ReadOnly(m_Data);
#endif
}
///
/// A read-only alias for the value of a NativeReference. Does not have its own allocated storage.
///
[NativeContainer]
[NativeContainerIsReadOnly]
[BurstCompatible(GenericTypeArguments = new [] { typeof(int) })]
public unsafe struct ReadOnly
{
[NativeDisableUnsafePtrRestriction]
readonly void* m_Data;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle m_Safety;
internal static readonly SharedStatic s_staticSafetyId = SharedStatic.GetOrCreate();
[BurstCompatible(CompileTarget = BurstCompatibleAttribute.BurstCompatibleCompileTarget.Editor)]
internal ReadOnly(void* data, ref AtomicSafetyHandle safety)
{
m_Data = data;
m_Safety = safety;
CollectionHelper.SetStaticSafetyId(ref m_Safety, ref s_staticSafetyId.Data);
}
#else
internal ReadOnly(void* data)
{
m_Data = data;
}
#endif
///
/// The value aliased by this reference.
///
/// The value aliased by the reference.
public T Value
{
get
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
#endif
return *(T*)m_Data;
}
}
}
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
void CheckNotDisposed()
{
if (m_Data == null)
throw new ObjectDisposedException("The NativeReference is already disposed.");
}
}
[NativeContainer]
unsafe struct NativeReferenceDispose
{
[NativeDisableUnsafePtrRestriction]
internal void* m_Data;
internal AllocatorManager.AllocatorHandle m_AllocatorLabel;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
internal AtomicSafetyHandle m_Safety;
#endif
public void Dispose()
{
Memory.Unmanaged.Free(m_Data, m_AllocatorLabel);
}
}
[BurstCompile]
struct NativeReferenceDisposeJob : IJob
{
internal NativeReferenceDispose Data;
public void Execute()
{
Data.Dispose();
}
}
}
namespace Unity.Collections.LowLevel.Unsafe
{
///
/// Provides extension methods for NativeReference.
///
[BurstCompatible]
public static class NativeReferenceUnsafeUtility
{
///
/// Returns a pointer to this reference's stored value.
///
/// Performs a job safety check for read-write access.
/// The type of the value.
/// The reference.
/// A pointer to this reference's stored value.
[BurstCompatible(GenericTypeArguments = new [] { typeof(int) })]
public static unsafe void* GetUnsafePtr(this NativeReference reference)
where T : unmanaged
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckWriteAndThrow(reference.m_Safety);
#endif
return reference.m_Data;
}
///
/// Returns a pointer to this reference's stored value.
///
/// Performs a job safety check for read-only access.
/// The type of the value.
/// The reference.
/// A pointer to this reference's stored value.
[BurstCompatible(GenericTypeArguments = new [] { typeof(int) })]
public static unsafe void* GetUnsafeReadOnlyPtr(this NativeReference reference)
where T : unmanaged
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
AtomicSafetyHandle.CheckReadAndThrow(reference.m_Safety);
#endif
return reference.m_Data;
}
///
/// Returns a pointer to this reference's stored value.
///
/// Performs no job safety checks.
/// The type of the value.
/// The reference.
/// A pointer to this reference's stored value.
[BurstCompatible(GenericTypeArguments = new [] { typeof(int) })]
public static unsafe void* GetUnsafePtrWithoutChecks(this NativeReference reference)
where T : unmanaged
{
return reference.m_Data;
}
}
}