A game about forced loneliness, made by TACStudios
1using System;
2using System.Diagnostics;
3using System.Runtime.InteropServices;
4using Unity.Burst;
5using Unity.Collections.LowLevel.Unsafe;
6using Unity.Jobs;
7
8
9namespace Unity.Collections
10{
11 /// <summary>
12 /// An unmanaged single value.
13 /// </summary>
14 /// <remarks>The functional equivalent of an array of length 1.
15 /// When you need just one value, NativeReference can be preferable to an array because it better conveys the intent.</remarks>
16 /// <typeparam name="T">The type of value.</typeparam>
17 [StructLayout(LayoutKind.Sequential)]
18 [NativeContainer]
19 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
20 public unsafe struct NativeReference<T>
21 : INativeDisposable
22 , IEquatable<NativeReference<T>>
23 where T : unmanaged
24 {
25 [NativeDisableUnsafePtrRestriction]
26 internal void* m_Data;
27
28#if ENABLE_UNITY_COLLECTIONS_CHECKS
29 internal AtomicSafetyHandle m_Safety;
30
31 static readonly SharedStatic<int> s_SafetyId = SharedStatic<int>.GetOrCreate<NativeReference<T>>();
32#endif
33
34 internal AllocatorManager.AllocatorHandle m_AllocatorLabel;
35
36 /// <summary>
37 /// Initializes and returns an instance of NativeReference.
38 /// </summary>
39 /// <param name="allocator">The allocator to use.</param>
40 /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
41 public NativeReference(AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory)
42 {
43 Allocate(allocator, out this);
44 if (options == NativeArrayOptions.ClearMemory)
45 {
46 UnsafeUtility.MemClear(m_Data, UnsafeUtility.SizeOf<T>());
47 }
48 }
49
50 /// <summary>
51 /// Initializes and returns an instance of NativeReference.
52 /// </summary>
53 /// <param name="allocator">The allocator to use.</param>
54 /// <param name="value">The initial value.</param>
55 public NativeReference(T value, AllocatorManager.AllocatorHandle allocator)
56 {
57 Allocate(allocator, out this);
58 *(T*)m_Data = value;
59 }
60
61 static void Allocate(AllocatorManager.AllocatorHandle allocator, out NativeReference<T> reference)
62 {
63 CollectionHelper.CheckAllocator(allocator);
64
65 reference = default;
66 reference.m_Data = Memory.Unmanaged.Allocate(UnsafeUtility.SizeOf<T>(), UnsafeUtility.AlignOf<T>(), allocator);
67 reference.m_AllocatorLabel = allocator;
68
69#if ENABLE_UNITY_COLLECTIONS_CHECKS
70 reference.m_Safety = CollectionHelper.CreateSafetyHandle(allocator);
71
72 CollectionHelper.InitNativeContainer<T>(reference.m_Safety);
73 CollectionHelper.SetStaticSafetyId<NativeQueue<T>>(ref reference.m_Safety, ref s_SafetyId.Data);
74#endif
75 }
76
77 /// <summary>
78 /// The value stored in this reference.
79 /// </summary>
80 /// <param name="value">The new value to store in this reference.</param>
81 /// <value>The value stored in this reference.</value>
82 public T Value
83 {
84 get
85 {
86#if ENABLE_UNITY_COLLECTIONS_CHECKS
87 AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
88#endif
89 return *(T*)m_Data;
90 }
91
92 set
93 {
94#if ENABLE_UNITY_COLLECTIONS_CHECKS
95 AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
96#endif
97 *(T*)m_Data = value;
98 }
99 }
100
101 /// <summary>
102 /// Whether this reference has been allocated (and not yet deallocated).
103 /// </summary>
104 /// <value>True if this reference has been allocated (and not yet deallocated).</value>
105 public readonly bool IsCreated => m_Data != null;
106
107 /// <summary>
108 /// Releases all resources (memory and safety handles).
109 /// </summary>
110 public void Dispose()
111 {
112#if ENABLE_UNITY_COLLECTIONS_CHECKS
113 if (!AtomicSafetyHandle.IsDefaultValue(m_Safety))
114 {
115 AtomicSafetyHandle.CheckExistsAndThrow(m_Safety);
116 }
117#endif
118 if (!IsCreated)
119 {
120 return;
121 }
122
123 if (CollectionHelper.ShouldDeallocate(m_AllocatorLabel))
124 {
125#if ENABLE_UNITY_COLLECTIONS_CHECKS
126 CollectionHelper.DisposeSafetyHandle(ref m_Safety);
127#endif
128 Memory.Unmanaged.Free(m_Data, m_AllocatorLabel);
129 m_AllocatorLabel = Allocator.Invalid;
130 }
131
132 m_Data = null;
133 }
134
135 /// <summary>
136 /// Creates and schedules a job that will release all resources (memory and safety handles) of this reference.
137 /// </summary>
138 /// <param name="inputDeps">A job handle. The newly scheduled job will depend upon this handle.</param>
139 /// <returns>The handle of a new job that will release all resources (memory and safety handles) of this reference.</returns>
140 public JobHandle Dispose(JobHandle inputDeps)
141 {
142#if ENABLE_UNITY_COLLECTIONS_CHECKS
143 if (!AtomicSafetyHandle.IsDefaultValue(m_Safety))
144 {
145 AtomicSafetyHandle.CheckExistsAndThrow(m_Safety);
146 }
147#endif
148 if (!IsCreated)
149 {
150 return inputDeps;
151 }
152
153 if (CollectionHelper.ShouldDeallocate(m_AllocatorLabel))
154 {
155 var jobHandle = new NativeReferenceDisposeJob
156 {
157 Data = new NativeReferenceDispose
158 {
159 m_Data = m_Data,
160 m_AllocatorLabel = m_AllocatorLabel,
161#if ENABLE_UNITY_COLLECTIONS_CHECKS
162 m_Safety = m_Safety
163#endif
164 }
165 }.Schedule(inputDeps);
166
167#if ENABLE_UNITY_COLLECTIONS_CHECKS
168 AtomicSafetyHandle.Release(m_Safety);
169#endif
170
171 m_Data = null;
172 m_AllocatorLabel = Allocator.Invalid;
173
174 return jobHandle;
175 }
176
177 m_Data = null;
178
179 return inputDeps;
180 }
181
182 /// <summary>
183 /// Copy the value of another reference to this reference.
184 /// </summary>
185 /// <param name="reference">The reference to copy from.</param>
186 public void CopyFrom(NativeReference<T> reference)
187 {
188 Copy(this, reference);
189 }
190
191 /// <summary>
192 /// Copy the value of this reference to another reference.
193 /// </summary>
194 /// <param name="reference">The reference to copy to.</param>
195 public void CopyTo(NativeReference<T> reference)
196 {
197 Copy(reference, this);
198 }
199
200 /// <summary>
201 /// Returns true if the value stored in this reference is equal to the value stored in another reference.
202 /// </summary>
203 /// <param name="other">A reference to compare with.</param>
204 /// <returns>True if the value stored in this reference is equal to the value stored in another reference.</returns>
205 [ExcludeFromBurstCompatTesting("Equals boxes because Value does not implement IEquatable<T>")]
206 public bool Equals(NativeReference<T> other)
207 {
208 return Value.Equals(other.Value);
209 }
210
211 /// <summary>
212 /// Returns true if the value stored in this reference is equal to an object.
213 /// </summary>
214 /// <remarks>Can only be equal if the object is itself a NativeReference.</remarks>
215 /// <param name="obj">An object to compare with.</param>
216 /// <returns>True if the value stored in this reference is equal to the object.</returns>
217 [ExcludeFromBurstCompatTesting("Takes managed object")]
218 public override bool Equals(object obj)
219 {
220 if (ReferenceEquals(null, obj))
221 {
222 return false;
223 }
224 return obj is NativeReference<T> && Equals((NativeReference<T>)obj);
225 }
226
227 /// <summary>
228 /// Returns the hash code of this reference.
229 /// </summary>
230 /// <returns>The hash code of this reference.</returns>
231 public override int GetHashCode()
232 {
233 return Value.GetHashCode();
234 }
235
236
237 /// <summary>
238 /// Returns true if the values stored in two references are equal.
239 /// </summary>
240 /// <param name="left">A reference.</param>
241 /// <param name="right">Another reference.</param>
242 /// <returns>True if the two values are equal.</returns>
243 public static bool operator ==(NativeReference<T> left, NativeReference<T> right)
244 {
245 return left.Equals(right);
246 }
247
248 /// <summary>
249 /// Returns true if the values stored in two references are unequal.
250 /// </summary>
251 /// <param name="left">A reference.</param>
252 /// <param name="right">Another reference.</param>
253 /// <returns>True if the two values are unequal.</returns>
254 public static bool operator !=(NativeReference<T> left, NativeReference<T> right)
255 {
256 return !left.Equals(right);
257 }
258
259 /// <summary>
260 /// Copies the value of a reference to another reference.
261 /// </summary>
262 /// <param name="dst">The destination reference.</param>
263 /// <param name="src">The source reference.</param>
264 public static void Copy(NativeReference<T> dst, NativeReference<T> src)
265 {
266#if ENABLE_UNITY_COLLECTIONS_CHECKS
267 AtomicSafetyHandle.CheckReadAndThrow(src.m_Safety);
268 AtomicSafetyHandle.CheckWriteAndThrow(dst.m_Safety);
269#endif
270 UnsafeUtility.MemCpy(dst.m_Data, src.m_Data, UnsafeUtility.SizeOf<T>());
271 }
272
273 /// <summary>
274 /// Returns a read-only reference aliasing the value of this reference.
275 /// </summary>
276 /// <returns>A read-only reference aliasing the value of this reference.</returns>
277 public ReadOnly AsReadOnly()
278 {
279#if ENABLE_UNITY_COLLECTIONS_CHECKS
280 return new ReadOnly(m_Data, ref m_Safety);
281#else
282 return new ReadOnly(m_Data);
283#endif
284 }
285
286 /// <summary>
287 /// Returns a read-only native reference that aliases the content of a native reference.
288 /// </summary>
289 /// <param name="nativeReference">NativeReference to alias.</param>
290 /// <returns>A read-only native reference that aliases the content of a native reference.</returns>
291 public static implicit operator ReadOnly(NativeReference<T> nativeReference)
292 {
293 return nativeReference.AsReadOnly();
294 }
295 /// <summary>
296 /// A read-only alias for the value of a NativeReference. Does not have its own allocated storage.
297 /// </summary>
298 [NativeContainer]
299 [NativeContainerIsReadOnly]
300 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
301 public unsafe struct ReadOnly
302 {
303 [NativeDisableUnsafePtrRestriction]
304 readonly void* m_Data;
305
306#if ENABLE_UNITY_COLLECTIONS_CHECKS
307 AtomicSafetyHandle m_Safety;
308 internal static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<ReadOnly>();
309
310 [GenerateTestsForBurstCompatibility(CompileTarget = GenerateTestsForBurstCompatibilityAttribute.BurstCompatibleCompileTarget.Editor)]
311 internal ReadOnly(void* data, ref AtomicSafetyHandle safety)
312 {
313 m_Data = data;
314 m_Safety = safety;
315 CollectionHelper.SetStaticSafetyId<ReadOnly>(ref m_Safety, ref s_staticSafetyId.Data);
316 }
317#else
318 internal ReadOnly(void* data)
319 {
320 m_Data = data;
321 }
322#endif
323
324 /// <summary>
325 /// The value aliased by this reference.
326 /// </summary>
327 /// <value>The value aliased by the reference.</value>
328 public T Value
329 {
330 get
331 {
332#if ENABLE_UNITY_COLLECTIONS_CHECKS
333 AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
334#endif
335 return *(T*)m_Data;
336 }
337 }
338 }
339 }
340
341 [NativeContainer]
342 unsafe struct NativeReferenceDispose
343 {
344 [NativeDisableUnsafePtrRestriction]
345 internal void* m_Data;
346
347 internal AllocatorManager.AllocatorHandle m_AllocatorLabel;
348
349#if ENABLE_UNITY_COLLECTIONS_CHECKS
350 internal AtomicSafetyHandle m_Safety;
351#endif
352
353 public void Dispose()
354 {
355 Memory.Unmanaged.Free(m_Data, m_AllocatorLabel);
356 }
357 }
358
359 [BurstCompile]
360 struct NativeReferenceDisposeJob : IJob
361 {
362 internal NativeReferenceDispose Data;
363
364 public void Execute()
365 {
366 Data.Dispose();
367 }
368 }
369}
370
371namespace Unity.Collections.LowLevel.Unsafe
372{
373 /// <summary>
374 /// Provides extension methods for NativeReference.
375 /// </summary>
376 [GenerateTestsForBurstCompatibility]
377 public static class NativeReferenceUnsafeUtility
378 {
379 /// <summary>
380 /// Returns a pointer to this reference's stored value.
381 /// </summary>
382 /// <remarks>Performs a job safety check for read-write access.</remarks>
383 /// <typeparam name="T">The type of the value.</typeparam>
384 /// <param name="reference">The reference.</param>
385 /// <returns>A pointer to this reference's stored value.</returns>
386 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
387 public static unsafe T* GetUnsafePtr<T>(this NativeReference<T> reference)
388 where T : unmanaged
389 {
390#if ENABLE_UNITY_COLLECTIONS_CHECKS
391 AtomicSafetyHandle.CheckWriteAndThrow(reference.m_Safety);
392#endif
393 return (T*)reference.m_Data;
394 }
395
396 /// <summary>
397 /// Returns a pointer to this reference's stored value.
398 /// </summary>
399 /// <remarks>Performs a job safety check for read-only access.</remarks>
400 /// <typeparam name="T">The type of the value.</typeparam>
401 /// <param name="reference">The reference.</param>
402 /// <returns>A pointer to this reference's stored value.</returns>
403 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
404 public static unsafe T* GetUnsafeReadOnlyPtr<T>(this NativeReference<T> reference)
405 where T : unmanaged
406 {
407#if ENABLE_UNITY_COLLECTIONS_CHECKS
408 AtomicSafetyHandle.CheckReadAndThrow(reference.m_Safety);
409#endif
410 return (T*)reference.m_Data;
411 }
412
413 /// <summary>
414 /// Returns a pointer to this reference's stored value.
415 /// </summary>
416 /// <remarks>Performs no job safety checks.</remarks>
417 /// <typeparam name="T">The type of the value.</typeparam>
418 /// <param name="reference">The reference.</param>
419 /// <returns>A pointer to this reference's stored value.</returns>
420 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
421 public static unsafe T* GetUnsafePtrWithoutChecks<T>(this NativeReference<T> reference)
422 where T : unmanaged
423 {
424 return (T*)reference.m_Data;
425 }
426 }
427}