├── .npmignore
├── CHANGELOG.md
├── CHANGELOG.md.meta
├── JacksonDunstanNativeCollections.meta
├── JacksonDunstanNativeCollections
├── IJobParallelForRanged.cs
├── IJobParallelForRanged.cs.meta
├── JacksonDunstan.NativeCollections.asmdef
├── JacksonDunstan.NativeCollections.asmdef.meta
├── NativeArray2D.cs
├── NativeArray2D.cs.meta
├── NativeChunkedList.cs
├── NativeChunkedList.cs.meta
├── NativeHashSet.cs
├── NativeHashSet.cs.meta
├── NativeIntPtr.cs
├── NativeIntPtr.cs.meta
├── NativeLinkedList.cs
├── NativeLinkedList.cs.meta
├── NativeLongPtr.cs
├── NativeLongPtr.cs.meta
├── NativePerJobThreadIntPtr.cs
├── NativePerJobThreadIntPtr.cs.meta
├── NativePerJobThreadLongPtr.cs
├── NativePerJobThreadLongPtr.cs.meta
├── SharedDisposable.cs
├── SharedDisposable.cs.meta
├── Tests.meta
└── Tests
│ ├── JacksonDunstan.NativeCollections.Tests.asmdef
│ ├── JacksonDunstan.NativeCollections.Tests.asmdef.meta
│ ├── TestNativeArray2D.cs
│ ├── TestNativeArray2D.cs.meta
│ ├── TestNativeChunkedList.cs
│ ├── TestNativeChunkedList.cs.meta
│ ├── TestNativeHashSet.cs
│ ├── TestNativeHashSet.cs.meta
│ ├── TestNativeIntPtr.cs
│ ├── TestNativeIntPtr.cs.meta
│ ├── TestNativeLinkedList.cs
│ ├── TestNativeLinkedList.cs.meta
│ ├── TestNativeLongPtr.cs
│ ├── TestNativeLongPtr.cs.meta
│ ├── TestNativePerJobThreadIntPtr.cs
│ ├── TestNativePerJobThreadIntPtr.cs.meta
│ ├── TestNativePerJobThreadLongPtr.cs
│ ├── TestNativePerJobThreadLongPtr.cs.meta
│ ├── TestSharedDisposable.cs
│ └── TestSharedDisposable.cs.meta
├── LICENSE.md
├── LICENSE.md.meta
├── PerformanceTests.meta
├── PerformanceTests
├── PerformanceTest.cs
├── PerformanceTest.cs.meta
├── PerformanceTestScene.unity
└── PerformanceTestScene.unity.meta
├── README.md
├── README.md.meta
├── package.json
└── package.json.meta
/.npmignore:
--------------------------------------------------------------------------------
1 | PerformanceTests/
2 | PerformanceTests.meta
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [1.0.0] - 2019-08-03
4 |
5 | - Release stable version
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 061e067cc9294e24192bcf702fbc95fa
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 4dfa25768880c4a06aa6470e21395173
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/IJobParallelForRanged.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (c) Jackson Dunstan. See LICENSE.md.
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System;
8 | using Unity.Jobs.LowLevel.Unsafe;
9 | using Unity.Jobs;
10 | using Unity.Collections.LowLevel.Unsafe;
11 |
12 | namespace JacksonDunstan.NativeCollections
13 | {
14 | ///
15 | /// A ParallelFor job type that executes on ranges of indices
16 | ///
17 | [JobProducerType(typeof(IJobParallelForRangedExtensions.ParallelForJobStruct<>))]
18 | public interface IJobParallelForRanged
19 | {
20 | ///
21 | /// Execute on the given range of indices, inclusive of the start and
22 | /// exclusive of the end
23 | ///
24 | ///
25 | ///
26 | /// First index to execute on
27 | ///
28 | ///
29 | ///
30 | /// One greater than the last index to execute on
31 | ///
32 | void Execute(int startIndex, int endIndex);
33 | }
34 |
35 | ///
36 | /// Supporting functionality for
37 | ///
38 | public static class IJobParallelForRangedExtensions
39 | {
40 | ///
41 | /// Supporting functionality for
42 | ///
43 | internal struct ParallelForJobStruct
44 | where TJob : struct, IJobParallelForRanged
45 | {
46 | ///
47 | /// Cached job type reflection data
48 | ///
49 | public static IntPtr jobReflectionData;
50 |
51 | ///
52 | /// Initialize the job type
53 | ///
54 | ///
55 | ///
56 | /// Reflection data for the job type
57 | ///
58 | public static IntPtr Initialize()
59 | {
60 | if (jobReflectionData == IntPtr.Zero)
61 | {
62 | jobReflectionData = JobsUtility.CreateJobReflectionData(
63 | typeof(TJob),
64 | #if UNITY_2020_2_OR_NEWER
65 | // Parameter removed in 2020.2
66 | #else
67 | JobType.ParallelFor,
68 | #endif
69 | (ExecuteJobFunction)Execute);
70 | }
71 | return jobReflectionData;
72 | }
73 |
74 | ///
75 | /// Delegate type for
76 | ///
77 | public delegate void ExecuteJobFunction(
78 | ref TJob data,
79 | IntPtr additionalPtr,
80 | IntPtr bufferRangePatchData,
81 | ref JobRanges ranges,
82 | int jobIndex);
83 |
84 | ///
85 | /// Execute the job until there are no more work stealing ranges
86 | /// available to execute
87 | ///
88 | ///
89 | ///
90 | /// The job to execute
91 | ///
92 | ///
93 | ///
94 | /// TBD. Unused.
95 | ///
96 | ///
97 | ///
98 | /// TBD. Unused.
99 | ///
100 | ///
101 | ///
102 | /// Work stealing ranges to execute from
103 | ///
104 | ///
105 | ///
106 | /// Index of this job
107 | ///
108 | public static unsafe void Execute(
109 | ref TJob jobData,
110 | IntPtr additionalPtr,
111 | IntPtr bufferRangePatchData,
112 | ref JobRanges ranges,
113 | int jobIndex)
114 | {
115 | int startIndex;
116 | int endIndex;
117 | while (JobsUtility.GetWorkStealingRange(
118 | ref ranges,
119 | jobIndex,
120 | out startIndex,
121 | out endIndex))
122 | {
123 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
124 | JobsUtility.PatchBufferMinMaxRanges(
125 | bufferRangePatchData,
126 | UnsafeUtility.AddressOf(ref jobData),
127 | startIndex,
128 | endIndex - startIndex);
129 | #endif
130 | jobData.Execute(startIndex, endIndex);
131 | }
132 | }
133 | }
134 |
135 | ///
136 | /// Run a job asynchronously
137 | ///
138 | ///
139 | ///
140 | /// Job to run
141 | ///
142 | ///
143 | ///
144 | /// Length of the values to execute on.
145 | ///
146 | ///
147 | ///
148 | /// Number of job executions per batch
149 | ///
150 | ///
151 | ///
152 | /// Handle of the job that must be run before this job
153 | ///
154 | ///
155 | ///
156 | /// A handle to the created job
157 | ///
158 | ///
159 | ///
160 | /// Type of job to run
161 | ///
162 | unsafe public static JobHandle ScheduleRanged(
163 | this T jobData,
164 | int valuesLength,
165 | int innerloopBatchCount,
166 | JobHandle dependsOn = new JobHandle())
167 | where T : struct, IJobParallelForRanged
168 | {
169 | var scheduleParams = new JobsUtility.JobScheduleParameters(
170 | UnsafeUtility.AddressOf(ref jobData),
171 | ParallelForJobStruct.Initialize(),
172 | dependsOn,
173 | #if UNITY_2020_2_OR_NEWER
174 | // Parameter renamed in 2020.2
175 | ScheduleMode.Parallel
176 | #else
177 | ScheduleMode.Batched
178 | #endif
179 | );
180 | return JobsUtility.ScheduleParallelFor(
181 | ref scheduleParams,
182 | valuesLength,
183 | innerloopBatchCount);
184 | }
185 |
186 | ///
187 | /// Run a job synchronously
188 | ///
189 | ///
190 | ///
191 | /// Job to run
192 | ///
193 | ///
194 | ///
195 | /// Length of the values to execute on.
196 | ///
197 | ///
198 | ///
199 | /// Type of job to run
200 | ///
201 | public static unsafe void RunRanged(
202 | this T jobData,
203 | int valuesLength)
204 | where T : struct, IJobParallelForRanged
205 | {
206 | var scheduleParams = new JobsUtility.JobScheduleParameters(
207 | UnsafeUtility.AddressOf(ref jobData),
208 | ParallelForJobStruct.Initialize(),
209 | new JobHandle(),
210 | ScheduleMode.Run);
211 | JobsUtility.ScheduleParallelFor(
212 | ref scheduleParams,
213 | valuesLength,
214 | valuesLength);
215 | }
216 | }
217 | }
218 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/IJobParallelForRanged.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e3904ef7c32334b2097c2a177c96c116
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/JacksonDunstan.NativeCollections.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "JacksonDunstan.NativeCollections",
3 | "references": [],
4 | "optionalUnityReferences": [],
5 | "includePlatforms": [],
6 | "excludePlatforms": [],
7 | "allowUnsafeCode": true
8 | }
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/JacksonDunstan.NativeCollections.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0098724e988b8b14d8a97f54d663a398
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/NativeArray2D.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (c) Jackson Dunstan. See LICENSE.md.
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System;
8 | using System.Collections;
9 | using System.Collections.Generic;
10 | using System.Diagnostics;
11 | using Unity.Collections;
12 | using Unity.Collections.LowLevel.Unsafe;
13 | using UnityEngine.Internal;
14 |
15 | namespace JacksonDunstan.NativeCollections
16 | {
17 | ///
18 | /// A two-dimensional array stored in native memory
19 | ///
20 | ///
21 | ///
22 | /// Type of elements in the array
23 | ///
24 | [DebuggerDisplay("Length0 = {Length0}, Length1 = {Length1}")]
25 | [DebuggerTypeProxy(typeof(NativeArray2DDebugView<>))]
26 | [NativeContainer]
27 | [NativeContainerSupportsDeallocateOnJobCompletion]
28 | public unsafe struct NativeArray2D
29 | : IDisposable
30 | , IEnumerable
31 | , IEquatable>
32 | #if CSHARP_7_3_OR_NEWER
33 | where T : unmanaged
34 | #else
35 | where T : struct
36 | #endif
37 | {
38 | ///
39 | /// An enumerator for this type of array. It enumerates from (0,0) to
40 | /// (Length0-1,Length1-1) in rows of the first dimension then the second
41 | /// dimension. For example, an array with Length0=2 and Length1=3 is
42 | /// enumerated as follows:
43 | /// (0, 0)
44 | /// (1, 0)
45 | /// (0, 1)
46 | /// (1, 1)
47 | /// (0, 2)
48 | /// (1, 3)
49 | ///
50 | [ExcludeFromDocs]
51 | public struct Enumerator : IEnumerator
52 | {
53 | ///
54 | /// Array to enumerate
55 | ///
56 | private NativeArray2D m_Array;
57 |
58 | ///
59 | /// Current index in the first dimension
60 | ///
61 | private int m_Index0;
62 |
63 | ///
64 | /// Current index in the second dimension
65 | ///
66 | private int m_Index1;
67 |
68 | ///
69 | /// Create the enumerator. It's initially just before the first
70 | /// element of both dimensions.
71 | ///
72 | ///
73 | ///
74 | /// Array to enumerate
75 | ///
76 | public Enumerator(ref NativeArray2D array)
77 | {
78 | m_Array = array;
79 | m_Index0 = -1;
80 | m_Index1 = 0;
81 | }
82 |
83 | ///
84 | /// Dispose of the enumerator. This is a no-op.
85 | ///
86 | public void Dispose()
87 | {
88 | }
89 |
90 | ///
91 | /// Move to the next element of the array. This moves along the
92 | /// first dimension until the end is hit, at which time the first
93 | /// dimension index is reset to zero and the second dimension index
94 | /// is incremented.
95 | ///
96 | ///
97 | ///
98 | /// If the new indices are within the bounds of the array.
99 | ///
100 | public bool MoveNext()
101 | {
102 | m_Index0++;
103 | if (m_Index0 >= m_Array.Length0)
104 | {
105 | m_Index0 = 0;
106 | m_Index1++;
107 | return m_Index1 < m_Array.Length1;
108 | }
109 | return true;
110 | }
111 |
112 | ///
113 | /// Reset to just before the first element in both dimensions
114 | ///
115 | public void Reset()
116 | {
117 | m_Index0 = -1;
118 | m_Index1 = 0;
119 | }
120 |
121 | ///
122 | /// Get the currently-enumerated element
123 | ///
124 | public T Current
125 | {
126 | get
127 | {
128 | return m_Array[m_Index0, m_Index1];
129 | }
130 | }
131 |
132 | ///
133 | /// Get the currently-enumerated element
134 | ///
135 | object IEnumerator.Current
136 | {
137 | get
138 | {
139 | return Current;
140 | }
141 | }
142 | }
143 |
144 | ///
145 | /// Pointer to the memory the array is stored in.
146 | ///
147 | [NativeDisableUnsafePtrRestriction]
148 | private void* m_Buffer;
149 |
150 | ///
151 | /// Length of the array's first dimension
152 | ///
153 | private int m_Length0;
154 |
155 | ///
156 | /// Length of the array's second dimension
157 | ///
158 | private int m_Length1;
159 |
160 | // These fields are all required when safety checks are enabled
161 | // They must have these exact types, names, and order
162 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
163 | ///
164 | /// A handle to information about what operations can be safely
165 | /// performed on the list at any given time.
166 | ///
167 | private AtomicSafetyHandle m_Safety;
168 |
169 | ///
170 | /// A handle that can be used to tell if the list has been disposed yet
171 | /// or not, which allows for error-checking double disposal.
172 | ///
173 | [NativeSetClassTypeToNullOnSchedule]
174 | private DisposeSentinel m_DisposeSentinel;
175 | #endif
176 |
177 | ///
178 | /// Allocator used to create .
179 | ///
180 | internal Allocator m_Allocator;
181 |
182 | ///
183 | /// Create the array and optionally clear it
184 | ///
185 | ///
186 | ///
187 | /// Length of the array's first dimension. Must be positive.
188 | ///
189 | ///
190 | ///
191 | /// Length of the array's second dimension. Must be positive.
192 | ///
193 | ///
194 | ///
195 | /// Allocator to allocate native memory with. Must be valid as defined
196 | /// by .
197 | ///
198 | ///
199 | ///
200 | /// Whether the array should be cleared or not
201 | ///
202 | public NativeArray2D(
203 | int length0,
204 | int length1,
205 | Allocator allocator,
206 | NativeArrayOptions options = NativeArrayOptions.ClearMemory)
207 | {
208 | Allocate(length0, length1, allocator, out this);
209 | if ((options & NativeArrayOptions.ClearMemory)
210 | == NativeArrayOptions.ClearMemory)
211 | {
212 | UnsafeUtility.MemClear(
213 | m_Buffer,
214 | Length * (long)UnsafeUtility.SizeOf());
215 | }
216 | }
217 |
218 | ///
219 | /// Create a copy of the given managed array
220 | ///
221 | ///
222 | ///
223 | /// Managed array to copy. Must not be null.
224 | ///
225 | ///
226 | ///
227 | /// Allocator to allocate native memory with. Must be valid as defined
228 | /// by .
229 | ///
230 | public NativeArray2D(T[,] array, Allocator allocator)
231 | {
232 | int length0 = array.GetLength(0);
233 | int length1 = array.GetLength(1);
234 | Allocate(length0, length1, allocator, out this);
235 | Copy(array, this);
236 | }
237 |
238 | ///
239 | /// Create a copy of the given native array
240 | ///
241 | ///
242 | ///
243 | /// Native array to copy
244 | ///
245 | ///
246 | ///
247 | /// Allocator to allocate native memory with. Must be valid as defined
248 | /// by .
249 | ///
250 | public NativeArray2D(NativeArray2D array, Allocator allocator)
251 | {
252 | Allocate(array.Length0, array.Length1, allocator, out this);
253 | Copy(array, this);
254 | }
255 |
256 | ///
257 | /// Get the total number of elements in the array
258 | ///
259 | public int Length
260 | {
261 | get
262 | {
263 | return m_Length0 * m_Length1;
264 | }
265 | }
266 |
267 | ///
268 | /// Get the length of the array's first dimension
269 | ///
270 | public int Length0
271 | {
272 | get
273 | {
274 | return m_Length0;
275 | }
276 | }
277 |
278 | ///
279 | /// Get the length of the array's second dimension
280 | ///
281 | public int Length1
282 | {
283 | get
284 | {
285 | return m_Length1;
286 | }
287 | }
288 |
289 | ///
290 | /// Throw an exception if the array isn't readable
291 | ///
292 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
293 | private void RequireReadAccess()
294 | {
295 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
296 | AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
297 | #endif
298 | }
299 |
300 | ///
301 | /// Throw an exception if the list isn't writable
302 | ///
303 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
304 | private void RequireWriteAccess()
305 | {
306 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
307 | AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
308 | #endif
309 | }
310 |
311 | ///
312 | /// Throw an exception if an index is out of bounds
313 | ///
314 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
315 | private void RequireIndexInBounds(int index0, int index1)
316 | {
317 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
318 | if (index0 < 0 || index0 >= m_Length0)
319 | {
320 | throw new IndexOutOfRangeException();
321 | }
322 | if (index1 < 0 || index1 >= m_Length1)
323 | {
324 | throw new IndexOutOfRangeException();
325 | }
326 | #endif
327 | }
328 |
329 | ///
330 | /// Throw an exception when the given allocator is invalid as defined
331 | /// by .
332 | ///
333 | ///
334 | ///
335 | /// Allocator to check.
336 | ///
337 | ///
338 | ///
339 | /// If the given allocator is invalid.
340 | ///
341 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
342 | private static void RequireValidAllocator(Allocator allocator)
343 | {
344 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
345 | if (!UnsafeUtility.IsValidAllocator(allocator))
346 | {
347 | throw new InvalidOperationException(
348 | "The NativeArray2D can not be Disposed because it was " +
349 | "not allocated with a valid allocator.");
350 | }
351 | #endif
352 | }
353 |
354 | ///
355 | /// Index into the array to read or write an element
356 | ///
357 | ///
358 | ///
359 | /// Index of the first dimension
360 | ///
361 | ///
362 | ///
363 | /// Index of the second dimension
364 | ///
365 | public T this[int index0, int index1]
366 | {
367 | get
368 | {
369 | RequireReadAccess();
370 | RequireIndexInBounds(index0, index1);
371 |
372 | int index = index1 * m_Length0 + index0;
373 | return UnsafeUtility.ReadArrayElement(m_Buffer, index);
374 | }
375 |
376 | [WriteAccessRequired]
377 | set
378 | {
379 | RequireWriteAccess();
380 | RequireIndexInBounds(index0, index1);
381 |
382 | int index = index1 * m_Length0 + index0;
383 | UnsafeUtility.WriteArrayElement(m_Buffer, index, value);
384 | }
385 | }
386 |
387 | ///
388 | /// Check if the underlying unmanaged memory has been created and not
389 | /// freed via a call to .
390 | ///
391 | /// This operation has no access requirements.
392 | ///
393 | /// This operation is O(1).
394 | ///
395 | ///
396 | ///
397 | /// Initially true when a non-default constructor is called but
398 | /// initially false when the default constructor is used. After
399 | /// is called, this becomes false. Note that
400 | /// calling on one copy of this object doesn't
401 | /// result in this becoming false for all copies if it was true before.
402 | /// This property should not be used to check whether the object
403 | /// is usable, only to check whether it was ever usable.
404 | ///
405 | public bool IsCreated
406 | {
407 | get
408 | {
409 | return (IntPtr)m_Buffer != IntPtr.Zero;
410 | }
411 | }
412 |
413 | ///
414 | /// Release the object's unmanaged memory. Do not use it after this. Do
415 | /// not call on copies of the object either.
416 | ///
417 | /// This operation requires write access.
418 | ///
419 | /// This complexity of this operation is O(1) plus the allocator's
420 | /// deallocation complexity.
421 | ///
422 | [WriteAccessRequired]
423 | public void Dispose()
424 | {
425 | RequireWriteAccess();
426 | RequireValidAllocator(m_Allocator);
427 |
428 | // Make sure we're not double-disposing
429 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
430 | #if UNITY_2018_3_OR_NEWER
431 | DisposeSentinel.Dispose(ref m_Safety, ref m_DisposeSentinel);
432 | #else
433 | DisposeSentinel.Dispose(m_Safety, ref m_DisposeSentinel);
434 | #endif
435 | #endif
436 |
437 | UnsafeUtility.Free(m_Buffer, m_Allocator);
438 | m_Buffer = null;
439 | m_Length0 = 0;
440 | m_Length1 = 0;
441 | }
442 |
443 | ///
444 | /// Copy the elements of a managed array to this array
445 | ///
446 | ///
447 | ///
448 | /// Array to copy from. Must not be null. Must have the same dimensions
449 | /// as this array.
450 | ///
451 | [WriteAccessRequired]
452 | public void CopyFrom(T[,] array)
453 | {
454 | Copy(array, this);
455 | }
456 |
457 | ///
458 | /// Copy the elements of a native array to this array
459 | ///
460 | ///
461 | ///
462 | /// Array to copy from. Must have the same dimensions as this array.
463 | ///
464 | [WriteAccessRequired]
465 | public void CopyFrom(NativeArray2D array)
466 | {
467 | Copy(array, this);
468 | }
469 |
470 | ///
471 | /// Copy the elements of this array to a managed array
472 | ///
473 | ///
474 | ///
475 | /// Array to copy to. Must not be null. Must have the same dimensions
476 | /// as this array.
477 | ///
478 | public void CopyTo(T[,] array)
479 | {
480 | Copy(this, array);
481 | }
482 |
483 | ///
484 | /// Copy the elements of this array to a native array
485 | ///
486 | ///
487 | ///
488 | /// Array to copy to. Must have the same dimensions
489 | /// as this array.
490 | ///
491 | public void CopyTo(NativeArray2D array)
492 | {
493 | Copy(this, array);
494 | }
495 |
496 | ///
497 | /// Copy the elements of this array to a newly-created managed array
498 | ///
499 | ///
500 | ///
501 | /// A newly-created managed array with the elements of this array.
502 | ///
503 | public T[,] ToArray()
504 | {
505 | T[,] dst = new T[m_Length0, m_Length1];
506 | Copy(this, dst);
507 | return dst;
508 | }
509 |
510 | ///
511 | /// Get an enumerator for this array
512 | ///
513 | ///
514 | ///
515 | /// An enumerator for this array.
516 | ///
517 | public Enumerator GetEnumerator()
518 | {
519 | return new Enumerator(ref this);
520 | }
521 |
522 | ///
523 | /// Get an enumerator for this array
524 | ///
525 | ///
526 | ///
527 | /// An enumerator for this array.
528 | ///
529 | IEnumerator IEnumerable.GetEnumerator()
530 | {
531 | return new Enumerator(ref this);
532 | }
533 |
534 | ///
535 | /// Get an enumerator for this array
536 | ///
537 | ///
538 | ///
539 | /// An enumerator for this array.
540 | ///
541 | IEnumerator IEnumerable.GetEnumerator()
542 | {
543 | return GetEnumerator();
544 | }
545 |
546 | ///
547 | /// Check if this array points to the same native memory as another
548 | /// array.
549 | ///
550 | ///
551 | ///
552 | /// Array to check against.
553 | ///
554 | ///
555 | ///
556 | /// If this array points to the same native memory as the given array.
557 | ///
558 | public bool Equals(NativeArray2D other)
559 | {
560 | return m_Buffer == other.m_Buffer
561 | && m_Length0 == other.m_Length0
562 | && m_Length1 == other.m_Length1;
563 | }
564 |
565 | ///
566 | /// Check if this array points to the same native memory as another
567 | /// array.
568 | ///
569 | ///
570 | ///
571 | /// Array to check against.
572 | ///
573 | ///
574 | ///
575 | /// If this array points to the same native memory as the given array.
576 | ///
577 | public override bool Equals(object other)
578 | {
579 | if (ReferenceEquals(null, other))
580 | {
581 | return false;
582 | }
583 | return other is NativeArray2D && Equals((NativeArray2D)other);
584 | }
585 |
586 | ///
587 | /// Get a hash code for this array
588 | ///
589 | ///
590 | ///
591 | /// A hash code for this array
592 | ///
593 | public override int GetHashCode()
594 | {
595 | int result = (int)m_Buffer;
596 | result = (result * 397) ^ m_Length0;
597 | result = (result * 397) ^ m_Length1;
598 | return result;
599 | }
600 |
601 | ///
602 | /// Check if two arrays point to the same native memory.
603 | ///
604 | ///
605 | ///
606 | /// First array to check
607 | ///
608 | ///
609 | ///
610 | /// Second array to check
611 | ///
612 | ///
613 | ///
614 | /// If the given arrays point to the same native memory.
615 | ///
616 | public static bool operator ==(NativeArray2D a, NativeArray2D b)
617 | {
618 | return a.Equals(b);
619 | }
620 |
621 | ///
622 | /// Check if two arrays don't point to the same native memory.
623 | ///
624 | ///
625 | ///
626 | /// First array to check
627 | ///
628 | ///
629 | ///
630 | /// Second array to check
631 | ///
632 | ///
633 | ///
634 | /// If the given arrays don't point to the same native memory.
635 | ///
636 | public static bool operator !=(NativeArray2D a, NativeArray2D b)
637 | {
638 | return !a.Equals(b);
639 | }
640 |
641 | ///
642 | /// Allocate memory for the array
643 | ///
644 | ///
645 | ///
646 | /// Length of the array's first dimension. Must be positive.
647 | ///
648 | ///
649 | ///
650 | /// Length of the array's second dimension. Must be positive.
651 | ///
652 | ///
653 | ///
654 | /// Allocator to allocate native memory with. Must be valid as defined
655 | /// by .
656 | ///
657 | ///
658 | ///
659 | /// Array to write to once allocated
660 | ///
661 | private static void Allocate(
662 | int length0,
663 | int length1,
664 | Allocator allocator,
665 | out NativeArray2D array)
666 | {
667 | RequireValidAllocator(allocator);
668 |
669 | #if !CSHARP_7_3_OR_NEWER
670 | if (!UnsafeUtility.IsUnmanaged())
671 | {
672 | throw new InvalidOperationException(
673 | "Only unmanaged types are supported");
674 | }
675 | #endif
676 |
677 | int length = length0 * length1;
678 | if (length <= 0)
679 | {
680 | throw new InvalidOperationException(
681 | "Total number of elements must be greater than zero");
682 | }
683 |
684 | array = new NativeArray2D
685 | {
686 | m_Buffer = UnsafeUtility.Malloc(
687 | length * UnsafeUtility.SizeOf(),
688 | UnsafeUtility.AlignOf(),
689 | allocator),
690 | m_Length0 = length0,
691 | m_Length1 = length1,
692 | m_Allocator = allocator
693 | };
694 |
695 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
696 | DisposeSentinel.Create(
697 | out array.m_Safety,
698 | out array.m_DisposeSentinel,
699 | 1,
700 | allocator);
701 | #endif
702 | }
703 |
704 | ///
705 | /// Copy a native array's elements to another native array
706 | ///
707 | ///
708 | ///
709 | /// Array to copy from
710 | ///
711 | ///
712 | ///
713 | /// Array to copy to
714 | ///
715 | ///
716 | ///
717 | /// If the arrays have different sizes
718 | ///
719 | private static void Copy(NativeArray2D src, NativeArray2D dest)
720 | {
721 | src.RequireReadAccess();
722 | dest.RequireWriteAccess();
723 |
724 | if (src.Length0 != dest.Length0
725 | || src.Length1 != dest.Length1)
726 | {
727 | throw new ArgumentException("Arrays must have the same size");
728 | }
729 |
730 | for (int index0 = 0; index0 < src.Length0; ++index0)
731 | {
732 | for (int index1 = 0; index1 < src.Length1; ++index1)
733 | {
734 | dest[index0, index1] = src[index0, index1];
735 | }
736 | }
737 | }
738 |
739 | ///
740 | /// Copy a managed array's elements to a native array
741 | ///
742 | ///
743 | ///
744 | /// Array to copy from
745 | ///
746 | ///
747 | ///
748 | /// Array to copy to
749 | ///
750 | ///
751 | ///
752 | /// If the arrays have different sizes
753 | ///
754 | private static void Copy(T[,] src, NativeArray2D dest)
755 | {
756 | dest.RequireWriteAccess();
757 |
758 | if (src.GetLength(0) != dest.Length0
759 | || src.GetLength(1) != dest.Length1)
760 | {
761 | throw new ArgumentException("Arrays must have the same size");
762 | }
763 |
764 | for (int index0 = 0; index0 < dest.Length0; ++index0)
765 | {
766 | for (int index1 = 0; index1 < dest.Length1; ++index1)
767 | {
768 | dest[index0, index1] = src[index0, index1];
769 | }
770 | }
771 | }
772 |
773 | ///
774 | /// Copy a native array's elements to a managed array
775 | ///
776 | ///
777 | ///
778 | /// Array to copy from
779 | ///
780 | ///
781 | ///
782 | /// Array to copy to
783 | ///
784 | ///
785 | ///
786 | /// If the arrays have different sizes
787 | ///
788 | private static void Copy(NativeArray2D src, T[,] dest)
789 | {
790 | src.RequireReadAccess();
791 |
792 | if (src.Length0 != dest.GetLength(0)
793 | || src.Length1 != dest.GetLength(1))
794 | {
795 | throw new ArgumentException("Arrays must have the same size");
796 | }
797 |
798 | for (int index0 = 0; index0 < src.Length0; ++index0)
799 | {
800 | for (int index1 = 0; index1 < src.Length1; ++index1)
801 | {
802 | dest[index0, index1] = src[index0, index1];
803 | }
804 | }
805 | }
806 | }
807 |
808 | ///
809 | /// A debugger view of the array type
810 | ///
811 | ///
812 | ///
813 | /// Type of elements in the array
814 | ///
815 | internal sealed class NativeArray2DDebugView
816 | #if CSHARP_7_3_OR_NEWER
817 | where T : unmanaged
818 | #else
819 | where T : struct
820 | #endif
821 | {
822 | ///
823 | /// The array to view
824 | ///
825 | private readonly NativeArray2D m_Array;
826 |
827 | ///
828 | /// Create the view
829 | ///
830 | ///
831 | ///
832 | /// The array to view
833 | ///
834 | public NativeArray2DDebugView(NativeArray2D array)
835 | {
836 | m_Array = array;
837 | }
838 |
839 | ///
840 | /// Get the elements of the array as a managed array
841 | ///
842 | public T[,] Items
843 | {
844 | get
845 | {
846 | return m_Array.ToArray();
847 | }
848 | }
849 | }
850 | }
851 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/NativeArray2D.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 280a7cf1ff5454939ad38d4cc7c60790
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/NativeChunkedList.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 7e997a88d82984aaf9a7113d579036c4
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/NativeHashSet.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 361d37df9c7004784a3ef70adb39afcb
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/NativeIntPtr.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (c) Jackson Dunstan. See LICENSE.md.
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System;
8 | using System.Diagnostics;
9 | using System.Runtime.InteropServices;
10 | using System.Threading;
11 | using Unity.Collections;
12 | using Unity.Collections.LowLevel.Unsafe;
13 |
14 | namespace JacksonDunstan.NativeCollections
15 | {
16 | ///
17 | /// A pointer to an int stored in native (i.e. unmanaged) memory
18 | ///
19 | [NativeContainer]
20 | [NativeContainerSupportsDeallocateOnJobCompletion]
21 | [DebuggerTypeProxy(typeof(NativeIntPtrDebugView))]
22 | [DebuggerDisplay("Value = {Value}")]
23 | [StructLayout(LayoutKind.Sequential)]
24 | public unsafe struct NativeIntPtr : IDisposable
25 | {
26 | ///
27 | /// An atomic write-only version of the object suitable for use in a
28 | /// ParallelFor job
29 | ///
30 | [NativeContainer]
31 | [NativeContainerIsAtomicWriteOnly]
32 | public struct Parallel
33 | {
34 | ///
35 | /// Pointer to the value in native memory
36 | ///
37 | [NativeDisableUnsafePtrRestriction]
38 | internal readonly int* m_Buffer;
39 |
40 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
41 | ///
42 | /// A handle to information about what operations can be safely
43 | /// performed on the object at any given time.
44 | ///
45 | internal AtomicSafetyHandle m_Safety;
46 |
47 | ///
48 | /// Create a parallel version of the object
49 | ///
50 | ///
51 | ///
52 | /// Pointer to the value
53 | ///
54 | ///
55 | ///
56 | /// Atomic safety handle for the object
57 | ///
58 | internal Parallel(int* value, AtomicSafetyHandle safety)
59 | {
60 | m_Buffer = value;
61 | m_Safety = safety;
62 | }
63 | #else
64 | ///
65 | /// Create a parallel version of the object
66 | ///
67 | ///
68 | ///
69 | /// Pointer to the value
70 | ///
71 | internal Parallel(int* value)
72 | {
73 | m_Buffer = value;
74 | }
75 | #endif
76 |
77 | ///
78 | /// Increment the stored value
79 | ///
80 | ///
81 | ///
82 | /// This object
83 | ///
84 | [WriteAccessRequired]
85 | public void Increment()
86 | {
87 | RequireWriteAccess();
88 | Interlocked.Increment(ref *m_Buffer);
89 | }
90 |
91 | ///
92 | /// Decrement the stored value
93 | ///
94 | ///
95 | ///
96 | /// This object
97 | ///
98 | [WriteAccessRequired]
99 | public void Decrement()
100 | {
101 | RequireWriteAccess();
102 | Interlocked.Decrement(ref *m_Buffer);
103 | }
104 |
105 | ///
106 | /// Add to the stored value
107 | ///
108 | ///
109 | ///
110 | /// Value to add. Use negative values for subtraction.
111 | ///
112 | ///
113 | ///
114 | /// This object
115 | ///
116 | [WriteAccessRequired]
117 | public void Add(int value)
118 | {
119 | RequireWriteAccess();
120 | Interlocked.Add(ref *m_Buffer, value);
121 | }
122 |
123 | ///
124 | /// Throw an exception if the object isn't writable
125 | ///
126 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
127 | private void RequireWriteAccess()
128 | {
129 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
130 | AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
131 | #endif
132 | }
133 | }
134 |
135 | ///
136 | /// Pointer to the value in native memory. Must be named exactly this
137 | /// way to allow for [NativeContainerSupportsDeallocateOnJobCompletion]
138 | ///
139 | [NativeDisableUnsafePtrRestriction]
140 | internal int* m_Buffer;
141 |
142 | ///
143 | /// Allocator used to create the backing memory
144 | ///
145 | /// This field must be named this way to comply with
146 | /// [NativeContainerSupportsDeallocateOnJobCompletion]
147 | ///
148 | internal readonly Allocator m_AllocatorLabel;
149 |
150 | // These fields are all required when safety checks are enabled
151 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
152 | ///
153 | /// A handle to information about what operations can be safely
154 | /// performed on the object at any given time.
155 | ///
156 | private AtomicSafetyHandle m_Safety;
157 |
158 | ///
159 | /// A handle that can be used to tell if the object has been disposed
160 | /// yet or not, which allows for error-checking double disposal.
161 | ///
162 | [NativeSetClassTypeToNullOnSchedule]
163 | private DisposeSentinel m_DisposeSentinel;
164 | #endif
165 |
166 | ///
167 | /// Allocate memory and set the initial value
168 | ///
169 | ///
170 | ///
171 | /// Allocator to allocate and deallocate with. Must be valid.
172 | ///
173 | ///
174 | ///
175 | /// Initial value of the allocated memory
176 | ///
177 | public NativeIntPtr(Allocator allocator, int initialValue = 0)
178 | {
179 | // Require a valid allocator
180 | if (allocator <= Allocator.None)
181 | {
182 | throw new ArgumentException(
183 | "Allocator must be Temp, TempJob or Persistent",
184 | "allocator");
185 | }
186 |
187 | // Allocate the memory for the value
188 | m_Buffer = (int*)UnsafeUtility.Malloc(
189 | sizeof(int),
190 | UnsafeUtility.AlignOf(),
191 | allocator);
192 |
193 | // Store the allocator to use when deallocating
194 | m_AllocatorLabel = allocator;
195 |
196 | // Create the dispose sentinel
197 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
198 | #if UNITY_2018_3_OR_NEWER
199 | DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 0, allocator);
200 | #else
201 | DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 0);
202 | #endif
203 | #endif
204 |
205 | // Set the initial value
206 | *m_Buffer = initialValue;
207 | }
208 |
209 | ///
210 | /// Get or set the contained value
211 | ///
212 | /// This operation requires read access to the node for 'get' and write
213 | /// access to the node for 'set'.
214 | ///
215 | ///
216 | ///
217 | /// The contained value
218 | ///
219 | public int Value
220 | {
221 | get
222 | {
223 | RequireReadAccess();
224 | return *m_Buffer;
225 | }
226 |
227 | [WriteAccessRequired]
228 | set
229 | {
230 | RequireWriteAccess();
231 | *m_Buffer = value;
232 | }
233 | }
234 |
235 | ///
236 | /// Get a version of this object suitable for use in a ParallelFor job
237 | ///
238 | ///
239 | ///
240 | /// A version of this object suitable for use in a ParallelFor job
241 | ///
242 | public Parallel GetParallel()
243 | {
244 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
245 | Parallel parallel = new Parallel(m_Buffer, m_Safety);
246 | AtomicSafetyHandle.UseSecondaryVersion(ref parallel.m_Safety);
247 | #else
248 | Parallel parallel = new Parallel(m_Buffer);
249 | #endif
250 | return parallel;
251 | }
252 |
253 | ///
254 | /// Check if the underlying unmanaged memory has been created and not
255 | /// freed via a call to .
256 | ///
257 | /// This operation has no access requirements.
258 | ///
259 | /// This operation is O(1).
260 | ///
261 | ///
262 | ///
263 | /// Initially true when a non-default constructor is called but
264 | /// initially false when the default constructor is used. After
265 | /// is called, this becomes false. Note that
266 | /// calling on one copy of this object doesn't
267 | /// result in this becoming false for all copies if it was true before.
268 | /// This property should not be used to check whether the object
269 | /// is usable, only to check whether it was ever usable.
270 | ///
271 | public bool IsCreated
272 | {
273 | get
274 | {
275 | return m_Buffer != null;
276 | }
277 | }
278 |
279 | ///
280 | /// Release the object's unmanaged memory. Do not use it after this. Do
281 | /// not call on copies of the object either.
282 | ///
283 | /// This operation requires write access.
284 | ///
285 | /// This complexity of this operation is O(1) plus the allocator's
286 | /// deallocation complexity.
287 | ///
288 | [WriteAccessRequired]
289 | public void Dispose()
290 | {
291 | RequireWriteAccess();
292 |
293 | // Make sure we're not double-disposing
294 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
295 | #if UNITY_2018_3_OR_NEWER
296 | DisposeSentinel.Dispose(ref m_Safety, ref m_DisposeSentinel);
297 | #else
298 | DisposeSentinel.Dispose(m_Safety, ref m_DisposeSentinel);
299 | #endif
300 | #endif
301 |
302 | UnsafeUtility.Free(m_Buffer, m_AllocatorLabel);
303 | m_Buffer = null;
304 | }
305 |
306 | ///
307 | /// Set whether both read and write access should be allowed. This is
308 | /// used for automated testing purposes only.
309 | ///
310 | ///
311 | ///
312 | /// If both read and write access should be allowed
313 | ///
314 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
315 | public void TestUseOnlySetAllowReadAndWriteAccess(
316 | bool allowReadOrWriteAccess)
317 | {
318 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
319 | AtomicSafetyHandle.SetAllowReadOrWriteAccess(
320 | m_Safety,
321 | allowReadOrWriteAccess);
322 | #endif
323 | }
324 |
325 | ///
326 | /// Throw an exception if the object isn't readable
327 | ///
328 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
329 | private void RequireReadAccess()
330 | {
331 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
332 | AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
333 | #endif
334 | }
335 |
336 | ///
337 | /// Throw an exception if the object isn't writable
338 | ///
339 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
340 | private void RequireWriteAccess()
341 | {
342 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
343 | AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
344 | #endif
345 | }
346 | }
347 |
348 | ///
349 | /// Provides a debugger view of .
350 | ///
351 | internal sealed class NativeIntPtrDebugView
352 | {
353 | ///
354 | /// The object to provide a debugger view for
355 | ///
356 | private NativeIntPtr m_Ptr;
357 |
358 | ///
359 | /// Create the debugger view
360 | ///
361 | ///
362 | ///
363 | /// The object to provide a debugger view for
364 | ///
365 | public NativeIntPtrDebugView(NativeIntPtr ptr)
366 | {
367 | m_Ptr = ptr;
368 | }
369 |
370 | ///
371 | /// Get the viewed object's value
372 | ///
373 | ///
374 | ///
375 | /// The viewed object's value
376 | ///
377 | public int Value
378 | {
379 | get
380 | {
381 | return m_Ptr.Value;
382 | }
383 | }
384 | }
385 | }
386 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/NativeIntPtr.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 840e81eda349647618a0cecc17f5b429
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/NativeLinkedList.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ac88eb2e190c4dfa9d3f36ce8a872f57
3 | timeCreated: 1533422240
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/NativeLongPtr.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (c) Jackson Dunstan. See LICENSE.md.
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System;
8 | using System.Diagnostics;
9 | using System.Runtime.InteropServices;
10 | using System.Threading;
11 | using Unity.Collections;
12 | using Unity.Collections.LowLevel.Unsafe;
13 |
14 | namespace JacksonDunstan.NativeCollections
15 | {
16 | ///
17 | /// A pointer to a long stored in native (i.e. unmanaged) memory
18 | ///
19 | [NativeContainer]
20 | [NativeContainerSupportsDeallocateOnJobCompletion]
21 | [DebuggerTypeProxy(typeof(NativeLongPtrDebugView))]
22 | [DebuggerDisplay("Value = {Value}")]
23 | [StructLayout(LayoutKind.Sequential)]
24 | public unsafe struct NativeLongPtr : IDisposable
25 | {
26 | ///
27 | /// An atomic write-only version of the object suitable for use in a
28 | /// ParallelFor job
29 | ///
30 | [NativeContainer]
31 | [NativeContainerIsAtomicWriteOnly]
32 | public struct Parallel
33 | {
34 | ///
35 | /// Pointer to the value in native memory
36 | ///
37 | [NativeDisableUnsafePtrRestriction]
38 | internal readonly long* m_Buffer;
39 |
40 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
41 | ///
42 | /// A handle to information about what operations can be safely
43 | /// performed on the object at any given time.
44 | ///
45 | internal AtomicSafetyHandle m_Safety;
46 |
47 | ///
48 | /// Create a parallel version of the object
49 | ///
50 | ///
51 | ///
52 | /// Pointer to the value
53 | ///
54 | ///
55 | ///
56 | /// Atomic safety handle for the object
57 | ///
58 | internal Parallel(long* value, AtomicSafetyHandle safety)
59 | {
60 | m_Buffer = value;
61 | m_Safety = safety;
62 | }
63 | #else
64 | ///
65 | /// Create a parallel version of the object
66 | ///
67 | ///
68 | ///
69 | /// Pointer to the value
70 | ///
71 | internal Parallel(long* value)
72 | {
73 | m_Buffer = value;
74 | }
75 | #endif
76 |
77 | ///
78 | /// Increment the stored value
79 | ///
80 | ///
81 | ///
82 | /// This object
83 | ///
84 | [WriteAccessRequired]
85 | public void Increment()
86 | {
87 | RequireWriteAccess();
88 | Interlocked.Increment(ref *m_Buffer);
89 | }
90 |
91 | ///
92 | /// Decrement the stored value
93 | ///
94 | ///
95 | ///
96 | /// This object
97 | ///
98 | [WriteAccessRequired]
99 | public void Decrement()
100 | {
101 | RequireWriteAccess();
102 | Interlocked.Decrement(ref *m_Buffer);
103 | }
104 |
105 | ///
106 | /// Add to the stored value
107 | ///
108 | ///
109 | ///
110 | /// Value to add. Use negative values for subtraction.
111 | ///
112 | ///
113 | ///
114 | /// This object
115 | ///
116 | [WriteAccessRequired]
117 | public void Add(long value)
118 | {
119 | RequireWriteAccess();
120 | Interlocked.Add(ref *m_Buffer, value);
121 | }
122 |
123 | ///
124 | /// Throw an exception if the object isn't writable
125 | ///
126 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
127 | private void RequireWriteAccess()
128 | {
129 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
130 | AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
131 | #endif
132 | }
133 | }
134 |
135 | ///
136 | /// Pointer to the value in native memory. Must be named exactly this
137 | /// way to allow for [NativeContainerSupportsDeallocateOnJobCompletion]
138 | ///
139 | [NativeDisableUnsafePtrRestriction]
140 | internal long* m_Buffer;
141 |
142 | ///
143 | /// Allocator used to create the backing memory
144 | ///
145 | /// This field must be named this way to comply with
146 | /// [NativeContainerSupportsDeallocateOnJobCompletion]
147 | ///
148 | internal readonly Allocator m_AllocatorLabel;
149 |
150 | // These fields are all required when safety checks are enabled
151 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
152 | ///
153 | /// A handle to information about what operations can be safely
154 | /// performed on the object at any given time.
155 | ///
156 | private AtomicSafetyHandle m_Safety;
157 |
158 | ///
159 | /// A handle that can be used to tell if the object has been disposed
160 | /// yet or not, which allows for error-checking double disposal.
161 | ///
162 | [NativeSetClassTypeToNullOnSchedule]
163 | private DisposeSentinel m_DisposeSentinel;
164 | #endif
165 |
166 | ///
167 | /// Allocate memory and set the initial value
168 | ///
169 | ///
170 | ///
171 | /// Allocator to allocate and deallocate with. Must be valid.
172 | ///
173 | ///
174 | ///
175 | /// Initial value of the allocated memory
176 | ///
177 | public NativeLongPtr(Allocator allocator, long initialValue = 0)
178 | {
179 | // Require a valid allocator
180 | if (allocator <= Allocator.None)
181 | {
182 | throw new ArgumentException(
183 | "Allocator must be Temp, TempJob or Persistent",
184 | "allocator");
185 | }
186 |
187 | // Allocate the memory for the value
188 | m_Buffer = (long*)UnsafeUtility.Malloc(
189 | sizeof(long),
190 | UnsafeUtility.AlignOf(),
191 | allocator);
192 |
193 | // Store the allocator to use when deallocating
194 | m_AllocatorLabel = allocator;
195 |
196 | // Create the dispose sentinel
197 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
198 | #if UNITY_2018_3_OR_NEWER
199 | DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 0, allocator);
200 | #else
201 | DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 0);
202 | #endif
203 | #endif
204 |
205 | // Set the initial value
206 | *m_Buffer = initialValue;
207 | }
208 |
209 | ///
210 | /// Get or set the contained value
211 | ///
212 | /// This operation requires read access to the node for 'get' and write
213 | /// access to the node for 'set'.
214 | ///
215 | ///
216 | ///
217 | /// The contained value
218 | ///
219 | public long Value
220 | {
221 | get
222 | {
223 | RequireReadAccess();
224 | return *m_Buffer;
225 | }
226 |
227 | [WriteAccessRequired]
228 | set
229 | {
230 | RequireWriteAccess();
231 | *m_Buffer = value;
232 | }
233 | }
234 |
235 | ///
236 | /// Get a version of this object suitable for use in a ParallelFor job
237 | ///
238 | ///
239 | ///
240 | /// A version of this object suitable for use in a ParallelFor job
241 | ///
242 | public Parallel GetParallel()
243 | {
244 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
245 | Parallel parallel = new Parallel(m_Buffer, m_Safety);
246 | AtomicSafetyHandle.UseSecondaryVersion(ref parallel.m_Safety);
247 | #else
248 | Parallel parallel = new Parallel(m_Buffer);
249 | #endif
250 | return parallel;
251 | }
252 |
253 | ///
254 | /// Check if the underlying unmanaged memory has been created and not
255 | /// freed via a call to .
256 | ///
257 | /// This operation has no access requirements.
258 | ///
259 | /// This operation is O(1).
260 | ///
261 | ///
262 | ///
263 | /// Initially true when a non-default constructor is called but
264 | /// initially false when the default constructor is used. After
265 | /// is called, this becomes false. Note that
266 | /// calling on one copy of this object doesn't
267 | /// result in this becoming false for all copies if it was true before.
268 | /// This property should not be used to check whether the object
269 | /// is usable, only to check whether it was ever usable.
270 | ///
271 | public bool IsCreated
272 | {
273 | get
274 | {
275 | return m_Buffer != null;
276 | }
277 | }
278 |
279 | ///
280 | /// Release the object's unmanaged memory. Do not use it after this. Do
281 | /// not call on copies of the object either.
282 | ///
283 | /// This operation requires write access.
284 | ///
285 | /// This complexity of this operation is O(1) plus the allocator's
286 | /// deallocation complexity.
287 | ///
288 | [WriteAccessRequired]
289 | public void Dispose()
290 | {
291 | RequireWriteAccess();
292 |
293 | // Make sure we're not double-disposing
294 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
295 | #if UNITY_2018_3_OR_NEWER
296 | DisposeSentinel.Dispose(ref m_Safety, ref m_DisposeSentinel);
297 | #else
298 | DisposeSentinel.Dispose(m_Safety, ref m_DisposeSentinel);
299 | #endif
300 | #endif
301 |
302 | UnsafeUtility.Free(m_Buffer, m_AllocatorLabel);
303 | m_Buffer = null;
304 | }
305 |
306 | ///
307 | /// Set whether both read and write access should be allowed. This is
308 | /// used for automated testing purposes only.
309 | ///
310 | ///
311 | ///
312 | /// If both read and write access should be allowed
313 | ///
314 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
315 | public void TestUseOnlySetAllowReadAndWriteAccess(
316 | bool allowReadOrWriteAccess)
317 | {
318 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
319 | AtomicSafetyHandle.SetAllowReadOrWriteAccess(
320 | m_Safety,
321 | allowReadOrWriteAccess);
322 | #endif
323 | }
324 |
325 | ///
326 | /// Throw an exception if the object isn't readable
327 | ///
328 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
329 | private void RequireReadAccess()
330 | {
331 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
332 | AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
333 | #endif
334 | }
335 |
336 | ///
337 | /// Throw an exception if the object isn't writable
338 | ///
339 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
340 | private void RequireWriteAccess()
341 | {
342 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
343 | AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
344 | #endif
345 | }
346 | }
347 |
348 | ///
349 | /// Provides a debugger view of .
350 | ///
351 | internal sealed class NativeLongPtrDebugView
352 | {
353 | ///
354 | /// The object to provide a debugger view for
355 | ///
356 | private NativeLongPtr m_Ptr;
357 |
358 | ///
359 | /// Create the debugger view
360 | ///
361 | ///
362 | ///
363 | /// The object to provide a debugger view for
364 | ///
365 | public NativeLongPtrDebugView(NativeLongPtr ptr)
366 | {
367 | m_Ptr = ptr;
368 | }
369 |
370 | ///
371 | /// Get the viewed object's value
372 | ///
373 | ///
374 | ///
375 | /// The viewed object's value
376 | ///
377 | public long Value
378 | {
379 | get
380 | {
381 | return m_Ptr.Value;
382 | }
383 | }
384 | }
385 | }
386 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/NativeLongPtr.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1712150778a2348629eb002d6eacddf1
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/NativePerJobThreadIntPtr.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (c) Jackson Dunstan. See LICENSE.md.
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System;
8 | using System.Diagnostics;
9 | using System.Runtime.InteropServices;
10 | using Unity.Collections;
11 | using Unity.Collections.LowLevel.Unsafe;
12 | using Unity.Jobs.LowLevel.Unsafe;
13 |
14 | namespace JacksonDunstan.NativeCollections
15 | {
16 | ///
17 | /// A pointer to an int stored in native (i.e. unmanaged) memory. One
18 | /// integer is stored for each of the maximum number of job threads. As of
19 | /// Unity 2018.2, this results in 8 KB of memory usage. The advantage over
20 | /// is that all operations on
21 | /// are faster due to not being atomic. The resulting
22 | /// is collected with a loop. This is therefore a good
23 | /// option when most usage is via and memory usage is
24 | /// not a concern.
25 | ///
26 | [NativeContainer]
27 | [NativeContainerSupportsDeallocateOnJobCompletion]
28 | [DebuggerTypeProxy(typeof(NativePerJobThreadIntPtrDebugView))]
29 | [DebuggerDisplay("Value = {Value}")]
30 | [StructLayout(LayoutKind.Sequential)]
31 | public unsafe struct NativePerJobThreadIntPtr : IDisposable
32 | {
33 | ///
34 | /// An atomic write-only version of the object suitable for use in a
35 | /// ParallelFor job
36 | ///
37 | [NativeContainer]
38 | [NativeContainerIsAtomicWriteOnly]
39 | public struct Parallel
40 | {
41 | ///
42 | /// Pointer to the value in native memory
43 | ///
44 | [NativeDisableUnsafePtrRestriction]
45 | internal readonly int* m_Buffer;
46 |
47 | ///
48 | /// Thread index of the job using this object. This is set by Unity
49 | /// and must have this exact name and type.
50 | ///
51 | [NativeSetThreadIndex]
52 | internal readonly int m_ThreadIndex;
53 |
54 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
55 | ///
56 | /// A handle to information about what operations can be safely
57 | /// performed on the object at any given time.
58 | ///
59 | internal AtomicSafetyHandle m_Safety;
60 |
61 | ///
62 | /// Create a parallel version of the object
63 | ///
64 | ///
65 | ///
66 | /// Pointer to the value
67 | ///
68 | ///
69 | ///
70 | /// Atomic safety handle for the object
71 | ///
72 | internal Parallel(int* value, AtomicSafetyHandle safety)
73 | {
74 | m_Buffer = value;
75 | m_ThreadIndex = 0;
76 | m_Safety = safety;
77 | }
78 | #else
79 | ///
80 | /// Create a parallel version of the object
81 | ///
82 | ///
83 | ///
84 | /// Pointer to the value
85 | ///
86 | internal Parallel(int* value)
87 | {
88 | m_Buffer = value;
89 | m_ThreadIndex = 0;
90 | }
91 | #endif
92 |
93 | ///
94 | /// Increment the stored value
95 | ///
96 | ///
97 | ///
98 | /// This object
99 | ///
100 | [WriteAccessRequired]
101 | public void Increment()
102 | {
103 | RequireWriteAccess();
104 | m_Buffer[IntsPerCacheLine * m_ThreadIndex]++;
105 | }
106 |
107 | ///
108 | /// Decrement the stored value
109 | ///
110 | ///
111 | ///
112 | /// This object
113 | ///
114 | [WriteAccessRequired]
115 | public void Decrement()
116 | {
117 | RequireWriteAccess();
118 | m_Buffer[IntsPerCacheLine * m_ThreadIndex]--;
119 | }
120 |
121 | ///
122 | /// Add to the stored value
123 | ///
124 | ///
125 | ///
126 | /// Value to add. Use negative values for subtraction.
127 | ///
128 | ///
129 | ///
130 | /// This object
131 | ///
132 | [WriteAccessRequired]
133 | public void Add(int value)
134 | {
135 | RequireWriteAccess();
136 | m_Buffer[IntsPerCacheLine * m_ThreadIndex] += value;
137 | }
138 |
139 | ///
140 | /// Throw an exception if the object isn't writable
141 | ///
142 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
143 | private void RequireWriteAccess()
144 | {
145 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
146 | AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
147 | #endif
148 | }
149 | }
150 |
151 | ///
152 | /// Pointer to the value in native memory. Must be named exactly this
153 | /// way to allow for [NativeContainerSupportsDeallocateOnJobCompletion]
154 | ///
155 | [NativeDisableUnsafePtrRestriction]
156 | internal int* m_Buffer;
157 |
158 | ///
159 | /// Allocator used to create the backing memory
160 | ///
161 | /// This field must be named this way to comply with
162 | /// [NativeContainerSupportsDeallocateOnJobCompletion]
163 | ///
164 | internal Allocator m_AllocatorLabel;
165 |
166 | // These fields are all required when safety checks are enabled
167 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
168 | ///
169 | /// A handle to information about what operations can be safely
170 | /// performed on the object at any given time.
171 | ///
172 | private AtomicSafetyHandle m_Safety;
173 |
174 | ///
175 | /// A handle that can be used to tell if the object has been disposed
176 | /// yet or not, which allows for error-checking double disposal.
177 | ///
178 | [NativeSetClassTypeToNullOnSchedule]
179 | private DisposeSentinel m_DisposeSentinel;
180 | #endif
181 |
182 | ///
183 | /// The number of integers that fit into a CPU cache line
184 | ///
185 | private const int IntsPerCacheLine = JobsUtility.CacheLineSize / sizeof(int);
186 |
187 | ///
188 | /// Allocate memory and set the initial value
189 | ///
190 | ///
191 | ///
192 | /// Allocator to allocate and deallocate with. Must be valid.
193 | ///
194 | ///
195 | ///
196 | /// Initial value of the allocated memory
197 | ///
198 | public NativePerJobThreadIntPtr(Allocator allocator, int initialValue = 0)
199 | {
200 | // Require a valid allocator
201 | if (allocator <= Allocator.None)
202 | {
203 | throw new ArgumentException(
204 | "Allocator must be Temp, TempJob or Persistent",
205 | "allocator");
206 | }
207 |
208 | // Allocate the memory for the values
209 | m_Buffer = (int*)UnsafeUtility.Malloc(
210 | JobsUtility.CacheLineSize * JobsUtility.MaxJobThreadCount,
211 | UnsafeUtility.AlignOf(),
212 | allocator);
213 |
214 | // Store the allocator to use when deallocating
215 | m_AllocatorLabel = allocator;
216 |
217 | // Create the dispose sentinel
218 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
219 | #if UNITY_2018_3_OR_NEWER
220 | DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 0, allocator);
221 | #else
222 | DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 0);
223 | #endif
224 | #endif
225 |
226 | // Set the initial value
227 | Value = initialValue;
228 | }
229 |
230 | ///
231 | /// Get or set the contained value
232 | ///
233 | /// This operation requires read access to the node for 'get' and write
234 | /// access to the node for 'set'.
235 | ///
236 | ///
237 | ///
238 | /// The contained value
239 | ///
240 | public int Value
241 | {
242 | get
243 | {
244 | RequireReadAccess();
245 | int value = 0;
246 | for (int i = 0; i < JobsUtility.MaxJobThreadCount; ++i)
247 | {
248 | value += m_Buffer[IntsPerCacheLine * i];
249 | }
250 | return value;
251 | }
252 |
253 | [WriteAccessRequired]
254 | set
255 | {
256 | RequireWriteAccess();
257 | *m_Buffer = value;
258 | for (int i = 1; i < JobsUtility.MaxJobThreadCount; ++i)
259 | {
260 | m_Buffer[IntsPerCacheLine * i] = 0;
261 | }
262 | }
263 | }
264 |
265 | ///
266 | /// Get a version of this object suitable for use in a ParallelFor job
267 | ///
268 | ///
269 | ///
270 | /// A version of this object suitable for use in a ParallelFor job
271 | ///
272 | public Parallel GetParallel()
273 | {
274 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
275 | Parallel parallel = new Parallel(m_Buffer, m_Safety);
276 | AtomicSafetyHandle.UseSecondaryVersion(ref parallel.m_Safety);
277 | #else
278 | Parallel parallel = new Parallel(m_Buffer);
279 | #endif
280 | return parallel;
281 | }
282 |
283 | ///
284 | /// Check if the underlying unmanaged memory has been created and not
285 | /// freed via a call to .
286 | ///
287 | /// This operation has no access requirements.
288 | ///
289 | /// This operation is O(1).
290 | ///
291 | ///
292 | ///
293 | /// Initially true when a non-default constructor is called but
294 | /// initially false when the default constructor is used. After
295 | /// is called, this becomes false. Note that
296 | /// calling on one copy of this object doesn't
297 | /// result in this becoming false for all copies if it was true before.
298 | /// This property should not be used to check whether the object
299 | /// is usable, only to check whether it was ever usable.
300 | ///
301 | public bool IsCreated
302 | {
303 | get
304 | {
305 | return m_Buffer != null;
306 | }
307 | }
308 |
309 | ///
310 | /// Release the object's unmanaged memory. Do not use it after this. Do
311 | /// not call on copies of the object either.
312 | ///
313 | /// This operation requires write access.
314 | ///
315 | /// This complexity of this operation is O(1) plus the allocator's
316 | /// deallocation complexity.
317 | ///
318 | [WriteAccessRequired]
319 | public void Dispose()
320 | {
321 | RequireWriteAccess();
322 |
323 | // Make sure we're not double-disposing
324 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
325 | #if UNITY_2018_3_OR_NEWER
326 | DisposeSentinel.Dispose(ref m_Safety, ref m_DisposeSentinel);
327 | #else
328 | DisposeSentinel.Dispose(m_Safety, ref m_DisposeSentinel);
329 | #endif
330 | #endif
331 |
332 | UnsafeUtility.Free(m_Buffer, m_AllocatorLabel);
333 | m_Buffer = null;
334 | }
335 |
336 | ///
337 | /// Set whether both read and write access should be allowed. This is
338 | /// used for automated testing purposes only.
339 | ///
340 | ///
341 | ///
342 | /// If both read and write access should be allowed
343 | ///
344 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
345 | public void TestUseOnlySetAllowReadAndWriteAccess(
346 | bool allowReadOrWriteAccess)
347 | {
348 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
349 | AtomicSafetyHandle.SetAllowReadOrWriteAccess(
350 | m_Safety,
351 | allowReadOrWriteAccess);
352 | #endif
353 | }
354 |
355 | ///
356 | /// Throw an exception if the object isn't readable
357 | ///
358 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
359 | private void RequireReadAccess()
360 | {
361 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
362 | AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
363 | #endif
364 | }
365 |
366 | ///
367 | /// Throw an exception if the object isn't writable
368 | ///
369 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
370 | private void RequireWriteAccess()
371 | {
372 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
373 | AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
374 | #endif
375 | }
376 | }
377 |
378 | ///
379 | /// Provides a debugger view of .
380 | ///
381 | internal sealed class NativePerJobThreadIntPtrDebugView
382 | {
383 | ///
384 | /// The object to provide a debugger view for
385 | ///
386 | private NativeIntPtr m_Ptr;
387 |
388 | ///
389 | /// Create the debugger view
390 | ///
391 | ///
392 | ///
393 | /// The object to provide a debugger view for
394 | ///
395 | public NativePerJobThreadIntPtrDebugView(NativeIntPtr ptr)
396 | {
397 | m_Ptr = ptr;
398 | }
399 |
400 | ///
401 | /// Get the viewed object's value
402 | ///
403 | ///
404 | ///
405 | /// The viewed object's value
406 | ///
407 | public int Value
408 | {
409 | get
410 | {
411 | return m_Ptr.Value;
412 | }
413 | }
414 | }
415 | }
416 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/NativePerJobThreadIntPtr.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: eea96d2e70fee4eb99acbec8d62d4c8b
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/NativePerJobThreadLongPtr.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (c) Jackson Dunstan. See LICENSE.md.
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System;
8 | using System.Diagnostics;
9 | using System.Runtime.InteropServices;
10 | using Unity.Collections;
11 | using Unity.Collections.LowLevel.Unsafe;
12 | using Unity.Jobs.LowLevel.Unsafe;
13 |
14 | namespace JacksonDunstan.NativeCollections
15 | {
16 | ///
17 | /// A pointer to an long stored in native (i.e. unmanaged) memory. One
18 | /// long is stored for each of the maximum number of job threads. As of
19 | /// Unity 2018.2, this results in 8 KB of memory usage. The advantage over
20 | /// is that all operations on
21 | /// are faster due to not being atomic. The resulting
22 | /// is collected with a loop. This is therefore a good
23 | /// option when most usage is via and memory usage is
24 | /// not a concern.
25 | ///
26 | [NativeContainer]
27 | [NativeContainerSupportsDeallocateOnJobCompletion]
28 | [DebuggerTypeProxy(typeof(NativePerJobThreadLongPtrDebugView))]
29 | [DebuggerDisplay("Value = {Value}")]
30 | [StructLayout(LayoutKind.Sequential)]
31 | public unsafe struct NativePerJobThreadLongPtr : IDisposable
32 | {
33 | ///
34 | /// An atomic write-only version of the object suitable for use in a
35 | /// ParallelFor job
36 | ///
37 | [NativeContainer]
38 | [NativeContainerIsAtomicWriteOnly]
39 | public struct Parallel
40 | {
41 | ///
42 | /// Polonger to the value in native memory
43 | ///
44 | [NativeDisableUnsafePtrRestriction]
45 | internal readonly long* m_Buffer;
46 |
47 | ///
48 | /// Thread index of the job using this object. This is set by Unity
49 | /// and must have this exact name and type.
50 | ///
51 | [NativeSetThreadIndex]
52 | internal readonly int m_ThreadIndex;
53 |
54 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
55 | ///
56 | /// A handle to information about what operations can be safely
57 | /// performed on the object at any given time.
58 | ///
59 | internal AtomicSafetyHandle m_Safety;
60 |
61 | ///
62 | /// Create a parallel version of the object
63 | ///
64 | ///
65 | ///
66 | /// Polonger to the value
67 | ///
68 | ///
69 | ///
70 | /// Atomic safety handle for the object
71 | ///
72 | internal Parallel(long* value, AtomicSafetyHandle safety)
73 | {
74 | m_Buffer = value;
75 | m_ThreadIndex = 0;
76 | m_Safety = safety;
77 | }
78 | #else
79 | ///
80 | /// Create a parallel version of the object
81 | ///
82 | ///
83 | ///
84 | /// Polonger to the value
85 | ///
86 | internal Parallel(long* value)
87 | {
88 | m_Buffer = value;
89 | m_ThreadIndex = 0;
90 | }
91 | #endif
92 |
93 | ///
94 | /// Increment the stored value
95 | ///
96 | ///
97 | ///
98 | /// This object
99 | ///
100 | [WriteAccessRequired]
101 | public void Increment()
102 | {
103 | RequireWriteAccess();
104 | m_Buffer[LongsPerCacheLine * m_ThreadIndex]++;
105 | }
106 |
107 | ///
108 | /// Decrement the stored value
109 | ///
110 | ///
111 | ///
112 | /// This object
113 | ///
114 | [WriteAccessRequired]
115 | public void Decrement()
116 | {
117 | RequireWriteAccess();
118 | m_Buffer[LongsPerCacheLine * m_ThreadIndex]--;
119 | }
120 |
121 | ///
122 | /// Add to the stored value
123 | ///
124 | ///
125 | ///
126 | /// Value to add. Use negative values for subtraction.
127 | ///
128 | ///
129 | ///
130 | /// This object
131 | ///
132 | [WriteAccessRequired]
133 | public void Add(long value)
134 | {
135 | RequireWriteAccess();
136 | m_Buffer[LongsPerCacheLine * m_ThreadIndex] += value;
137 | }
138 |
139 | ///
140 | /// Throw an exception if the object isn't writable
141 | ///
142 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
143 | private void RequireWriteAccess()
144 | {
145 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
146 | AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
147 | #endif
148 | }
149 | }
150 |
151 | ///
152 | /// Polonger to the value in native memory. Must be named exactly this
153 | /// way to allow for [NativeContainerSupportsDeallocateOnJobCompletion]
154 | ///
155 | [NativeDisableUnsafePtrRestriction]
156 | internal long* m_Buffer;
157 |
158 | ///
159 | /// Allocator used to create the backing memory
160 | ///
161 | /// This field must be named this way to comply with
162 | /// [NativeContainerSupportsDeallocateOnJobCompletion]
163 | ///
164 | internal readonly Allocator m_AllocatorLabel;
165 |
166 | // These fields are all required when safety checks are enabled
167 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
168 | ///
169 | /// A handle to information about what operations can be safely
170 | /// performed on the object at any given time.
171 | ///
172 | private AtomicSafetyHandle m_Safety;
173 |
174 | ///
175 | /// A handle that can be used to tell if the object has been disposed
176 | /// yet or not, which allows for error-checking double disposal.
177 | ///
178 | [NativeSetClassTypeToNullOnSchedule]
179 | private DisposeSentinel m_DisposeSentinel;
180 | #endif
181 |
182 | ///
183 | /// The number of longs that fit into a CPU cache line
184 | ///
185 | private const long LongsPerCacheLine = JobsUtility.CacheLineSize / sizeof(long);
186 |
187 | ///
188 | /// Allocate memory and set the initial value
189 | ///
190 | ///
191 | ///
192 | /// Allocator to allocate and deallocate with. Must be valid.
193 | ///
194 | ///
195 | ///
196 | /// Initial value of the allocated memory
197 | ///
198 | public NativePerJobThreadLongPtr(
199 | Allocator allocator,
200 | long initialValue = 0)
201 | {
202 | // Require a valid allocator
203 | if (allocator <= Allocator.None)
204 | {
205 | throw new ArgumentException(
206 | "Allocator must be Temp, TempJob or Persistent",
207 | "allocator");
208 | }
209 |
210 | // Allocate the memory for the values
211 | m_Buffer = (long*)UnsafeUtility.Malloc(
212 | JobsUtility.CacheLineSize * JobsUtility.MaxJobThreadCount,
213 | UnsafeUtility.AlignOf(),
214 | allocator);
215 |
216 | // Store the allocator to use when deallocating
217 | m_AllocatorLabel = allocator;
218 |
219 | // Create the dispose sentinel
220 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
221 | #if UNITY_2018_3_OR_NEWER
222 | DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 0, allocator);
223 | #else
224 | DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 0);
225 | #endif
226 | #endif
227 |
228 | // Set the initial value
229 | Value = initialValue;
230 | }
231 |
232 | ///
233 | /// Get or set the contained value
234 | ///
235 | /// This operation requires read access to the node for 'get' and write
236 | /// access to the node for 'set'.
237 | ///
238 | ///
239 | ///
240 | /// The contained value
241 | ///
242 | public long Value
243 | {
244 | get
245 | {
246 | RequireReadAccess();
247 | long value = 0;
248 | for (long i = 0; i < JobsUtility.MaxJobThreadCount; ++i)
249 | {
250 | value += m_Buffer[LongsPerCacheLine * i];
251 | }
252 | return value;
253 | }
254 |
255 | [WriteAccessRequired]
256 | set
257 | {
258 | RequireWriteAccess();
259 | *m_Buffer = value;
260 | for (long i = 1; i < JobsUtility.MaxJobThreadCount; ++i)
261 | {
262 | m_Buffer[LongsPerCacheLine * i] = 0;
263 | }
264 | }
265 | }
266 |
267 | ///
268 | /// Get a version of this object suitable for use in a ParallelFor job
269 | ///
270 | ///
271 | ///
272 | /// A version of this object suitable for use in a ParallelFor job
273 | ///
274 | public Parallel GetParallel()
275 | {
276 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
277 | Parallel parallel = new Parallel(m_Buffer, m_Safety);
278 | AtomicSafetyHandle.UseSecondaryVersion(ref parallel.m_Safety);
279 | #else
280 | Parallel parallel = new Parallel(m_Buffer);
281 | #endif
282 | return parallel;
283 | }
284 |
285 | ///
286 | /// Check if the underlying unmanaged memory has been created and not
287 | /// freed via a call to .
288 | ///
289 | /// This operation has no access requirements.
290 | ///
291 | /// This operation is O(1).
292 | ///
293 | ///
294 | ///
295 | /// Initially true when a non-default constructor is called but
296 | /// initially false when the default constructor is used. After
297 | /// is called, this becomes false. Note that
298 | /// calling on one copy of this object doesn't
299 | /// result in this becoming false for all copies if it was true before.
300 | /// This property should not be used to check whether the object
301 | /// is usable, only to check whether it was ever usable.
302 | ///
303 | public bool IsCreated
304 | {
305 | get
306 | {
307 | return m_Buffer != null;
308 | }
309 | }
310 |
311 | ///
312 | /// Release the object's unmanaged memory. Do not use it after this. Do
313 | /// not call on copies of the object either.
314 | ///
315 | /// This operation requires write access.
316 | ///
317 | /// This complexity of this operation is O(1) plus the allocator's
318 | /// deallocation complexity.
319 | ///
320 | [WriteAccessRequired]
321 | public void Dispose()
322 | {
323 | RequireWriteAccess();
324 |
325 | // Make sure we're not double-disposing
326 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
327 | #if UNITY_2018_3_OR_NEWER
328 | DisposeSentinel.Dispose(ref m_Safety, ref m_DisposeSentinel);
329 | #else
330 | DisposeSentinel.Dispose(m_Safety, ref m_DisposeSentinel);
331 | #endif
332 | #endif
333 |
334 | UnsafeUtility.Free(m_Buffer, m_AllocatorLabel);
335 | m_Buffer = null;
336 | }
337 |
338 | ///
339 | /// Set whether both read and write access should be allowed. This is
340 | /// used for automated testing purposes only.
341 | ///
342 | ///
343 | ///
344 | /// If both read and write access should be allowed
345 | ///
346 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
347 | public void TestUseOnlySetAllowReadAndWriteAccess(
348 | bool allowReadOrWriteAccess)
349 | {
350 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
351 | AtomicSafetyHandle.SetAllowReadOrWriteAccess(
352 | m_Safety,
353 | allowReadOrWriteAccess);
354 | #endif
355 | }
356 |
357 | ///
358 | /// Throw an exception if the object isn't readable
359 | ///
360 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
361 | private void RequireReadAccess()
362 | {
363 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
364 | AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
365 | #endif
366 | }
367 |
368 | ///
369 | /// Throw an exception if the object isn't writable
370 | ///
371 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
372 | private void RequireWriteAccess()
373 | {
374 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
375 | AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
376 | #endif
377 | }
378 | }
379 |
380 | ///
381 | /// Provides a debugger view of .
382 | ///
383 | internal sealed class NativePerJobThreadLongPtrDebugView
384 | {
385 | ///
386 | /// The object to provide a debugger view for
387 | ///
388 | private NativeLongPtr m_Ptr;
389 |
390 | ///
391 | /// Create the debugger view
392 | ///
393 | ///
394 | ///
395 | /// The object to provide a debugger view for
396 | ///
397 | public NativePerJobThreadLongPtrDebugView(NativeLongPtr ptr)
398 | {
399 | m_Ptr = ptr;
400 | }
401 |
402 | ///
403 | /// Get the viewed object's value
404 | ///
405 | ///
406 | ///
407 | /// The viewed object's value
408 | ///
409 | public long Value
410 | {
411 | get
412 | {
413 | return m_Ptr.Value;
414 | }
415 | }
416 | }
417 | }
418 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/NativePerJobThreadLongPtr.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: a8e8c6b3ce2344575a9ee586d202d340
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/SharedDisposable.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (c) Jackson Dunstan. See LICENSE.md.
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System;
8 | using System.Diagnostics;
9 | using System.Runtime.InteropServices;
10 | using JacksonDunstan.NativeCollections;
11 | using Unity.Collections;
12 | using Unity.Collections.LowLevel.Unsafe;
13 |
14 | namespace JacksonDunstan.NativeCollections
15 | {
16 | ///
17 | /// A reference-counted .
18 | ///
19 | ///
20 | ///
21 | /// Type of disposable that is shared.
22 | ///
23 | [NativeContainer]
24 | [NativeContainerSupportsDeallocateOnJobCompletion]
25 | [DebuggerTypeProxy(typeof(SharedDisposableDebugView<>))]
26 | [DebuggerDisplay("Disposable = {Value}")]
27 | [StructLayout(LayoutKind.Sequential)]
28 | public unsafe struct SharedDisposable : IDisposable
29 | where TDisposable : IDisposable
30 | {
31 | ///
32 | /// Pointer to the ref count in native memory. Must be named exactly
33 | /// this way to allow for
34 | /// [NativeContainerSupportsDeallocateOnJobCompletion]
35 | ///
36 | [NativeDisableUnsafePtrRestriction]
37 | internal int* m_Buffer;
38 |
39 | ///
40 | /// Allocator used to create the backing memory
41 | ///
42 | /// This field must be named this way to comply with
43 | /// [NativeContainerSupportsDeallocateOnJobCompletion]
44 | ///
45 | internal readonly Allocator m_AllocatorLabel;
46 |
47 | ///
48 | /// Disposable that is being shared
49 | ///
50 | private readonly TDisposable m_Disposable;
51 |
52 | // These fields are all required when safety checks are enabled
53 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
54 | ///
55 | /// A handle to information about what operations can be safely
56 | /// performed on the object at any given time.
57 | ///
58 | private AtomicSafetyHandle m_Safety;
59 |
60 | ///
61 | /// A handle that can be used to tell if the object has been disposed
62 | /// yet or not, which allows for error-checking double disposal.
63 | ///
64 | [NativeSetClassTypeToNullOnSchedule]
65 | private DisposeSentinel m_DisposeSentinel;
66 | #endif
67 |
68 | ///
69 | /// Allocate memory and save the disposable
70 | ///
71 | ///
72 | ///
73 | /// Disposable that is being shared
74 | ///
75 | ///
76 | ///
77 | /// Allocator to allocate and deallocate with. Must be valid.
78 | ///
79 | public SharedDisposable(
80 | #if CSHARP_7_3_OR_NEWER
81 | in
82 | #endif
83 | TDisposable disposable,
84 | Allocator allocator)
85 | {
86 | // Require a valid allocator
87 | if (!UnsafeUtility.IsValidAllocator(allocator))
88 | {
89 | throw new ArgumentException(
90 | "Invalid allocator",
91 | "allocator");
92 | }
93 |
94 | // Allocate the memory for the ref count and initialize to 1
95 | m_Buffer = (int*)UnsafeUtility.Malloc(
96 | sizeof(int),
97 | UnsafeUtility.AlignOf(),
98 | allocator);
99 | *m_Buffer = 1;
100 |
101 | // Store the allocator to use when deallocating
102 | m_AllocatorLabel = allocator;
103 |
104 | // Create the dispose sentinel
105 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
106 | #if UNITY_2018_3_OR_NEWER
107 | DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 0, allocator);
108 | #else
109 | DisposeSentinel.Create(out m_Safety, out m_DisposeSentinel, 0);
110 | #endif
111 | #endif
112 |
113 | // Save the disposable
114 | m_Disposable = disposable;
115 | }
116 |
117 | ///
118 | /// Get or set the contained disposable
119 | ///
120 | /// This operation requires read access.
121 | ///
122 | ///
123 | ///
124 | /// The contained disposable
125 | ///
126 | public TDisposable Value
127 | {
128 | get
129 | {
130 | RequireReadAccess();
131 | return m_Disposable;
132 | }
133 | }
134 |
135 | ///
136 | /// Check if the underlying unmanaged memory has been created and not
137 | /// freed via a call to .
138 | ///
139 | /// This operation has no access requirements.
140 | ///
141 | /// This operation is O(1).
142 | ///
143 | ///
144 | ///
145 | /// Initially true when a non-default constructor is called but
146 | /// initially false when the default constructor is used. After
147 | /// is called, this becomes false. Note that
148 | /// calling on one copy of this object doesn't
149 | /// result in this becoming false for all copies if it was true before.
150 | /// This property should not be used to check whether the object
151 | /// is usable, only to check whether it was ever usable.
152 | ///
153 | public bool IsCreated
154 | {
155 | get
156 | {
157 | return m_Buffer != null;
158 | }
159 | }
160 |
161 | ///
162 | /// Increment the reference count.
163 | ///
164 | /// This operation requires write access.
165 | ///
166 | ///
167 | ///
168 | /// A reference to this object.
169 | ///
170 | [WriteAccessRequired]
171 | public SharedDisposable Ref()
172 | {
173 | *m_Buffer = *m_Buffer + 1;
174 | return this;
175 | }
176 |
177 | ///
178 | /// Release the object's unmanaged memory. Do not use it after this. Do
179 | /// not call on copies of the object either.
180 | ///
181 | /// This operation requires write access.
182 | ///
183 | /// This complexity of this operation is O(1) plus the allocator's
184 | /// deallocation complexity.
185 | ///
186 | [WriteAccessRequired]
187 | public void Dispose()
188 | {
189 | RequireWriteAccess();
190 |
191 | int newRefCount = *m_Buffer - 1;
192 | *m_Buffer = newRefCount;
193 | if (newRefCount == 0)
194 | {
195 | // Make sure we're not double-disposing
196 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
197 | #if UNITY_2018_3_OR_NEWER
198 | DisposeSentinel.Dispose(ref m_Safety, ref m_DisposeSentinel);
199 | #else
200 | DisposeSentinel.Dispose(m_Safety, ref m_DisposeSentinel);
201 | #endif
202 | #endif
203 |
204 | UnsafeUtility.Free(m_Buffer, m_AllocatorLabel);
205 | m_Buffer = null;
206 |
207 | m_Disposable.Dispose();
208 | }
209 | }
210 |
211 | ///
212 | /// Set whether both read and write access should be allowed. This is
213 | /// used for automated testing purposes only.
214 | ///
215 | ///
216 | ///
217 | /// If both read and write access should be allowed
218 | ///
219 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
220 | public void TestUseOnlySetAllowReadAndWriteAccess(
221 | bool allowReadOrWriteAccess)
222 | {
223 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
224 | AtomicSafetyHandle.SetAllowReadOrWriteAccess(
225 | m_Safety,
226 | allowReadOrWriteAccess);
227 | #endif
228 | }
229 |
230 | ///
231 | /// Throw an exception if the object isn't readable
232 | ///
233 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
234 | private void RequireReadAccess()
235 | {
236 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
237 | AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
238 | #endif
239 | }
240 |
241 | ///
242 | /// Throw an exception if the object isn't writable
243 | ///
244 | [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
245 | private void RequireWriteAccess()
246 | {
247 | #if ENABLE_UNITY_COLLECTIONS_CHECKS
248 | AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
249 | #endif
250 | }
251 | }
252 |
253 | ///
254 | /// Provides a debugger view of .
255 | ///
256 | ///
257 | ///
258 | /// Type of disposable that is shared.
259 | ///
260 | internal sealed class SharedDisposableDebugView
261 | where TDisposable : IDisposable
262 | {
263 | ///
264 | /// The object to provide a debugger view for
265 | ///
266 | private SharedDisposable m_Ptr;
267 |
268 | ///
269 | /// Create the debugger view
270 | ///
271 | ///
272 | ///
273 | /// The object to provide a debugger view for
274 | ///
275 | public SharedDisposableDebugView(SharedDisposable ptr)
276 | {
277 | m_Ptr = ptr;
278 | }
279 |
280 | ///
281 | /// Get the viewed object's disposable
282 | ///
283 | ///
284 | ///
285 | /// The viewed object's disposable
286 | ///
287 | public TDisposable Disposable
288 | {
289 | get
290 | {
291 | return m_Ptr.Value;
292 | }
293 | }
294 | }
295 | }
296 |
297 | ///
298 | /// Extensions to to support
299 | /// .
300 | ///
301 | public static class IDisposableExtensions
302 | {
303 | ///
304 | /// Allocate memory and save the disposable
305 | ///
306 | ///
307 | ///
308 | /// Disposable that is being shared
309 | ///
310 | ///
311 | ///
312 | /// Allocator to allocate and deallocate with. Must be valid.
313 | ///
314 | public static SharedDisposable Share(
315 | this TDisposable disposable,
316 | Allocator allocator)
317 | where TDisposable : IDisposable
318 | {
319 | return new SharedDisposable(disposable, allocator);
320 | }
321 | }
322 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/SharedDisposable.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: e2c2313cea4814fc59c837b308a4ef3a
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 8b7dc29c216f74b54a311ba9ba6725f3
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/JacksonDunstan.NativeCollections.Tests.asmdef:
--------------------------------------------------------------------------------
1 | {
2 | "name": "JacksonDunstan.NativeCollections.Tests",
3 | "references": [
4 | "JacksonDunstan.NativeCollections"
5 | ],
6 | "optionalUnityReferences": [
7 | "TestAssemblies"
8 | ],
9 | "includePlatforms": [
10 | "Editor"
11 | ],
12 | "excludePlatforms": [],
13 | "allowUnsafeCode": true
14 | }
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/JacksonDunstan.NativeCollections.Tests.asmdef.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 016eb04efadcc124780bb372a2f0b970
3 | AssemblyDefinitionImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestNativeArray2D.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (c) Jackson Dunstan. See LICENSE.md.
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System.Collections;
8 | using NUnit.Framework;
9 | using Unity.Collections;
10 |
11 | namespace JacksonDunstan.NativeCollections.Tests
12 | {
13 | ///
14 | /// Unit tests for and
15 | ///
16 | ///
17 | public class TestNativeArray2D
18 | {
19 | private static NativeArray2D CreateArray(int width, int height)
20 | {
21 | return new NativeArray2D(width, height, Allocator.Temp);
22 | }
23 |
24 | [Test]
25 | public void ConstructorCreatesEmptyArray()
26 | {
27 | using (NativeArray2D array = CreateArray(2, 3))
28 | {
29 | Assert.That(array[0, 0], Is.EqualTo(0));
30 | Assert.That(array[0, 1], Is.EqualTo(0));
31 | Assert.That(array[0, 2], Is.EqualTo(0));
32 | Assert.That(array[1, 0], Is.EqualTo(0));
33 | Assert.That(array[1, 1], Is.EqualTo(0));
34 | Assert.That(array[1, 2], Is.EqualTo(0));
35 | }
36 | }
37 |
38 | [Test]
39 | public void ConstructorThrowsForNonPositiveLength()
40 | {
41 | Assert.That(
42 | () => new NativeArray2D(-2, 3, Allocator.Temp),
43 | Throws.Exception);
44 | Assert.That(
45 | () => new NativeArray2D(2, -3, Allocator.Temp),
46 | Throws.Exception);
47 | Assert.That(
48 | () => new NativeArray2D(0, 3, Allocator.Temp),
49 | Throws.Exception);
50 | Assert.That(
51 | () => new NativeArray2D(2, 0, Allocator.Temp),
52 | Throws.Exception);
53 | }
54 |
55 | [Test]
56 | public void ConstructorThrowsForInvalidAllocator()
57 | {
58 | Assert.That(
59 | () => new NativeArray2D(1, 1, Allocator.None),
60 | Throws.Exception);
61 | }
62 |
63 | [Test]
64 | public void ConstructorCopiesManagedArray()
65 | {
66 | int[,] managed =
67 | {
68 | {100, 200, 300},
69 | {400, 500, 600}
70 | };
71 | using (NativeArray2D native = new NativeArray2D(
72 | managed,
73 | Allocator.Temp))
74 | {
75 | Assert.That(managed[0, 0], Is.EqualTo(native[0, 0]));
76 | Assert.That(managed[0, 1], Is.EqualTo(native[0, 1]));
77 | Assert.That(managed[0, 2], Is.EqualTo(native[0, 2]));
78 | Assert.That(managed[1, 0], Is.EqualTo(native[1, 0]));
79 | Assert.That(managed[1, 1], Is.EqualTo(native[1, 1]));
80 | Assert.That(managed[1, 2], Is.EqualTo(native[1, 2]));
81 | }
82 | }
83 |
84 | [Test]
85 | public void ConstructorCopiesNativeArray()
86 | {
87 | using (NativeArray2D src = CreateArray(2, 3))
88 | {
89 | NativeArray2D srcAlias = src;
90 | srcAlias[0, 0] = 100;
91 | srcAlias[0, 1] = 200;
92 | srcAlias[0, 2] = 300;
93 | srcAlias[1, 0] = 400;
94 | srcAlias[1, 1] = 500;
95 | srcAlias[1, 2] = 600;
96 |
97 | using (NativeArray2D dest = new NativeArray2D(
98 | src,
99 | Allocator.Temp))
100 | {
101 | Assert.That(dest[0, 0], Is.EqualTo(src[0, 0]));
102 | Assert.That(dest[0, 1], Is.EqualTo(src[0, 1]));
103 | Assert.That(dest[0, 2], Is.EqualTo(src[0, 2]));
104 | Assert.That(dest[1, 0], Is.EqualTo(src[1, 0]));
105 | Assert.That(dest[1, 1], Is.EqualTo(src[1, 1]));
106 | Assert.That(dest[1, 2], Is.EqualTo(src[1, 2]));
107 | }
108 | }
109 | }
110 |
111 | [Test]
112 | public void LengthReturnsTotalNumberOfElements()
113 | {
114 | using (NativeArray2D array = CreateArray(2, 3))
115 | {
116 | Assert.That(array.Length, Is.EqualTo(6));
117 | }
118 | }
119 |
120 | [Test]
121 | public void Length0ReturnsTotalNumberOfElements()
122 | {
123 | using (NativeArray2D array = CreateArray(2, 3))
124 | {
125 | Assert.That(array.Length0, Is.EqualTo(2));
126 | }
127 | }
128 |
129 | [Test]
130 | public void Length1ReturnsTotalNumberOfElements()
131 | {
132 | using (NativeArray2D array = CreateArray(2, 3))
133 | {
134 | Assert.That(array.Length1, Is.EqualTo(3));
135 | }
136 | }
137 |
138 | [Test]
139 | public void IndexGetsAndSetsElementAtGivenIndex()
140 | {
141 | using (NativeArray2D array = CreateArray(2, 3))
142 | {
143 | NativeArray2D alias = array;
144 | alias[0, 0] = 100;
145 | alias[0, 1] = 200;
146 | alias[0, 2] = 300;
147 | alias[1, 0] = 400;
148 | alias[1, 1] = 500;
149 | alias[1, 2] = 600;
150 | Assert.That(array[0, 0], Is.EqualTo(100));
151 | Assert.That(array[0, 1], Is.EqualTo(200));
152 | Assert.That(array[0, 2], Is.EqualTo(300));
153 | Assert.That(array[1, 0], Is.EqualTo(400));
154 | Assert.That(array[1, 1], Is.EqualTo(500));
155 | Assert.That(array[1, 2], Is.EqualTo(600));
156 | }
157 | }
158 |
159 | [Test]
160 | public void IndexOutOfBoundsThrows()
161 | {
162 | using (NativeArray2D array = CreateArray(2, 3))
163 | {
164 | Assert.That(
165 | () => array[-1, 0],
166 | Throws.Exception);
167 | Assert.That(
168 | () => array[2, 0],
169 | Throws.Exception);
170 | Assert.That(
171 | () => array[0, 3],
172 | Throws.Exception);
173 | Assert.That(
174 | () => array[0, -1],
175 | Throws.Exception);
176 | }
177 | }
178 |
179 | [Test]
180 | public void IsCreatedReturnsTrueForDefaultStruct()
181 | {
182 | NativeArray2D array = default(NativeArray2D);
183 | Assert.That(array.IsCreated, Is.False);
184 | }
185 |
186 | [Test]
187 | public void IsCreatedReturnsTrueAfterConstructor()
188 | {
189 | using (NativeArray2D array = CreateArray(2, 3))
190 | {
191 | Assert.That(array.IsCreated, Is.True);
192 | }
193 | }
194 |
195 | [Test]
196 | public void DisposeMakesArrayUnusable()
197 | {
198 | NativeArray2D array = CreateArray(2, 3);
199 | array.Dispose();
200 | int val;
201 | Assert.That(() => val = array[0, 0], Throws.Exception);
202 | }
203 |
204 | [Test]
205 | public void CopyFromManagedArrayCopiesElements()
206 | {
207 | using (NativeArray2D dest = CreateArray(2, 3))
208 | {
209 | int[,] src =
210 | {
211 | {100, 200, 300},
212 | {400, 500, 600}
213 | };
214 |
215 | dest.CopyFrom(src);
216 |
217 | Assert.That(src[0, 0], Is.EqualTo(dest[0, 0]));
218 | Assert.That(src[0, 1], Is.EqualTo(dest[0, 1]));
219 | Assert.That(src[0, 2], Is.EqualTo(dest[0, 2]));
220 | Assert.That(src[1, 0], Is.EqualTo(dest[1, 0]));
221 | Assert.That(src[1, 1], Is.EqualTo(dest[1, 1]));
222 | Assert.That(src[1, 2], Is.EqualTo(dest[1, 2]));
223 | }
224 | }
225 |
226 | [Test]
227 | public void CopyFromManagedArrayThrowsWhenDifferentSize()
228 | {
229 | using (NativeArray2D dest = CreateArray(2, 3))
230 | {
231 | int[,] src =
232 | {
233 | {100, 200, 300}
234 | };
235 |
236 | Assert.That(() => dest.CopyFrom(src), Throws.Exception);
237 | }
238 | }
239 |
240 | [Test]
241 | public void CopyFromNativeArrayCopiesElements()
242 | {
243 | using (NativeArray2D src = CreateArray(2, 3))
244 | {
245 | using (NativeArray2D dest = CreateArray(2, 3))
246 | {
247 | NativeArray2D srcAlias = src;
248 | srcAlias[0, 0] = 100;
249 | srcAlias[0, 1] = 200;
250 | srcAlias[0, 2] = 300;
251 | srcAlias[1, 0] = 400;
252 | srcAlias[1, 1] = 500;
253 | srcAlias[1, 2] = 600;
254 |
255 | dest.CopyFrom(src);
256 |
257 | Assert.That(dest[0, 0], Is.EqualTo(src[0, 0]));
258 | Assert.That(dest[0, 1], Is.EqualTo(src[0, 1]));
259 | Assert.That(dest[0, 2], Is.EqualTo(src[0, 2]));
260 | Assert.That(dest[1, 0], Is.EqualTo(src[1, 0]));
261 | Assert.That(dest[1, 1], Is.EqualTo(src[1, 1]));
262 | Assert.That(dest[1, 2], Is.EqualTo(src[1, 2]));
263 | }
264 | }
265 | }
266 |
267 | [Test]
268 | public void CopyFromNativeArrayThrowsWhenDifferentSize()
269 | {
270 | using (NativeArray2D src = CreateArray(2, 3))
271 | {
272 | using (NativeArray2D dest = CreateArray(2, 4))
273 | {
274 | Assert.That(() => dest.CopyFrom(src), Throws.Exception);
275 | }
276 | }
277 | }
278 |
279 | [Test]
280 | public void CopyToManagedArrayCopiesElements()
281 | {
282 | using (NativeArray2D src = CreateArray(2, 3))
283 | {
284 | NativeArray2D srcAlias = src;
285 | srcAlias[0, 0] = 100;
286 | srcAlias[0, 1] = 200;
287 | srcAlias[0, 2] = 300;
288 | srcAlias[1, 0] = 400;
289 | srcAlias[1, 1] = 500;
290 | srcAlias[1, 2] = 600;
291 |
292 | int[,] dest = new int[2, 3];
293 |
294 | src.CopyTo(dest);
295 |
296 | Assert.That(dest[0, 0], Is.EqualTo(src[0, 0]));
297 | Assert.That(dest[0, 1], Is.EqualTo(src[0, 1]));
298 | Assert.That(dest[0, 2], Is.EqualTo(src[0, 2]));
299 | Assert.That(dest[1, 0], Is.EqualTo(src[1, 0]));
300 | Assert.That(dest[1, 1], Is.EqualTo(src[1, 1]));
301 | Assert.That(dest[1, 2], Is.EqualTo(src[1, 2]));
302 | }
303 | }
304 |
305 | [Test]
306 | public void CopyToManagedArrayThrowsWhenDifferentSize()
307 | {
308 | using (NativeArray2D src = CreateArray(2, 3))
309 | {
310 | int[,] dest = new int[2, 4];
311 |
312 | Assert.That(() => src.CopyTo(dest), Throws.Exception);
313 | }
314 | }
315 |
316 | [Test]
317 | public void CopyToNativeArrayCopiesElements()
318 | {
319 | using (NativeArray2D src = CreateArray(2, 3))
320 | {
321 | using (NativeArray2D dest = CreateArray(2, 3))
322 | {
323 | NativeArray2D srcAlias = src;
324 | srcAlias[0, 0] = 100;
325 | srcAlias[0, 1] = 200;
326 | srcAlias[0, 2] = 300;
327 | srcAlias[1, 0] = 400;
328 | srcAlias[1, 1] = 500;
329 | srcAlias[1, 2] = 600;
330 |
331 | src.CopyTo(dest);
332 |
333 | Assert.That(dest[0, 0], Is.EqualTo(src[0, 0]));
334 | Assert.That(dest[0, 1], Is.EqualTo(src[0, 1]));
335 | Assert.That(dest[0, 2], Is.EqualTo(src[0, 2]));
336 | Assert.That(dest[1, 0], Is.EqualTo(src[1, 0]));
337 | Assert.That(dest[1, 1], Is.EqualTo(src[1, 1]));
338 | Assert.That(dest[1, 2], Is.EqualTo(src[1, 2]));
339 | }
340 | }
341 | }
342 |
343 | [Test]
344 | public void CopyToNativeArrayThrowsWhenDifferentSize()
345 | {
346 | using (NativeArray2D src = CreateArray(2, 3))
347 | {
348 | using (NativeArray2D dest = CreateArray(2, 4))
349 | {
350 | Assert.That(() => src.CopyTo(dest), Throws.Exception);
351 | }
352 | }
353 | }
354 |
355 | [Test]
356 | public void ToArrayCreatesArrayWithSameElements()
357 | {
358 | using (NativeArray2D src = CreateArray(2, 3))
359 | {
360 | NativeArray2D srcAlias = src;
361 | srcAlias[0, 0] = 100;
362 | srcAlias[0, 1] = 200;
363 | srcAlias[0, 2] = 300;
364 | srcAlias[1, 0] = 400;
365 | srcAlias[1, 1] = 500;
366 | srcAlias[1, 2] = 600;
367 |
368 | int[,] dest = src.ToArray();
369 |
370 | Assert.That(dest[0, 0], Is.EqualTo(src[0, 0]));
371 | Assert.That(dest[0, 1], Is.EqualTo(src[0, 1]));
372 | Assert.That(dest[0, 2], Is.EqualTo(src[0, 2]));
373 | Assert.That(dest[1, 0], Is.EqualTo(src[1, 0]));
374 | Assert.That(dest[1, 1], Is.EqualTo(src[1, 1]));
375 | Assert.That(dest[1, 2], Is.EqualTo(src[1, 2]));
376 | }
377 | }
378 |
379 | [Test]
380 | public void GetEnumeratorIteratesElementsInCorrectOrder()
381 | {
382 | using (NativeArray2D array = CreateArray(2, 3))
383 | {
384 | NativeArray2D alias = array;
385 | alias[0, 0] = 100;
386 | alias[0, 1] = 200;
387 | alias[0, 2] = 300;
388 | alias[1, 0] = 400;
389 | alias[1, 1] = 500;
390 | alias[1, 2] = 600;
391 |
392 | using (NativeArray2D.Enumerator e = array.GetEnumerator())
393 | {
394 | Assert.That(e.MoveNext(), Is.True);
395 | Assert.That(e.Current, Is.EqualTo(array[0, 0]));
396 | Assert.That(e.MoveNext(), Is.True);
397 | Assert.That(e.Current, Is.EqualTo(array[1, 0]));
398 | Assert.That(e.MoveNext(), Is.True);
399 | Assert.That(e.Current, Is.EqualTo(array[0, 1]));
400 | Assert.That(e.MoveNext(), Is.True);
401 | Assert.That(e.Current, Is.EqualTo(array[1, 1]));
402 | Assert.That(e.MoveNext(), Is.True);
403 | Assert.That(e.Current, Is.EqualTo(array[0, 2]));
404 | Assert.That(e.MoveNext(), Is.True);
405 | Assert.That(e.Current, Is.EqualTo(array[1, 2]));
406 | Assert.That(e.MoveNext(), Is.False);
407 | }
408 | }
409 | }
410 |
411 | [Test]
412 | public void GetEnumeratorNonGenericIteratesElementsInCorrectOrder()
413 | {
414 | using (NativeArray2D array = CreateArray(2, 3))
415 | {
416 | NativeArray2D alias = array;
417 | alias[0, 0] = 100;
418 | alias[0, 1] = 200;
419 | alias[0, 2] = 300;
420 | alias[1, 0] = 400;
421 | alias[1, 1] = 500;
422 | alias[1, 2] = 600;
423 |
424 | IEnumerator e = ((IEnumerable)array).GetEnumerator();
425 | Assert.That(e.MoveNext(), Is.True);
426 | Assert.That(e.Current, Is.EqualTo(array[0, 0]));
427 | Assert.That(e.MoveNext(), Is.True);
428 | Assert.That(e.Current, Is.EqualTo(array[1, 0]));
429 | Assert.That(e.MoveNext(), Is.True);
430 | Assert.That(e.Current, Is.EqualTo(array[0, 1]));
431 | Assert.That(e.MoveNext(), Is.True);
432 | Assert.That(e.Current, Is.EqualTo(array[1, 1]));
433 | Assert.That(e.MoveNext(), Is.True);
434 | Assert.That(e.Current, Is.EqualTo(array[0, 2]));
435 | Assert.That(e.MoveNext(), Is.True);
436 | Assert.That(e.Current, Is.EqualTo(array[1, 2]));
437 | Assert.That(e.MoveNext(), Is.False);
438 | }
439 | }
440 |
441 | [Test]
442 | public void EqualsReturnsTrueOnlyForSameArray()
443 | {
444 | using (NativeArray2D a1 = CreateArray(2, 3))
445 | {
446 | Assert.That(a1.Equals(a1), Is.True);
447 |
448 | using (NativeArray2D a2 = CreateArray(2, 3))
449 | {
450 | Assert.That(a1.Equals(a2), Is.False);
451 | }
452 | }
453 | }
454 |
455 | [Test]
456 | public void EqualsObjectReturnsTrueOnlyForSameArray()
457 | {
458 | using (NativeArray2D a1 = CreateArray(2, 3))
459 | {
460 | Assert.That(a1.Equals((object)a1), Is.True);
461 |
462 | using (NativeArray2D a2 = CreateArray(2, 3))
463 | {
464 | Assert.That(a1.Equals((object)a2), Is.False);
465 | Assert.That(a1.Equals("something else"), Is.False);
466 | }
467 | }
468 | }
469 |
470 | [Test]
471 | public void GetHashCodeReturnsUniqueValue()
472 | {
473 | using (NativeArray2D a1 = CreateArray(2, 3))
474 | {
475 | using (NativeArray2D a2 = CreateArray(2, 3))
476 | {
477 | int hash1 = a1.GetHashCode();
478 | int hash2 = a2.GetHashCode();
479 | Assert.That(hash1, Is.Not.EqualTo(hash2));
480 | }
481 | }
482 | }
483 |
484 | [Test]
485 | public void EqualityOperatorReturnsTrueOnlyForSameArray()
486 | {
487 | using (NativeArray2D a1 = CreateArray(2, 3))
488 | {
489 | // Ignore warning of comparison with self
490 | #pragma warning disable CS1718
491 | Assert.That(a1 == a1, Is.True);
492 | #pragma warning restore CS1718
493 |
494 | using (NativeArray2D a2 = CreateArray(2, 3))
495 | {
496 | Assert.That(a1 == a2, Is.False);
497 | }
498 | }
499 | }
500 |
501 | [Test]
502 | public void InequalityOperatorReturnsTrueOnlyForDifferentArray()
503 | {
504 | using (NativeArray2D a1 = CreateArray(2, 3))
505 | {
506 | // Ignore warning of comparison with self
507 | #pragma warning disable CS1718
508 | Assert.That(a1 != a1, Is.False);
509 | #pragma warning restore CS1718
510 |
511 | using (NativeArray2D a2 = CreateArray(2, 3))
512 | {
513 | Assert.That(a1 != a2, Is.True);
514 | }
515 | }
516 | }
517 |
518 | [Test]
519 | public void EnumeratorResetReturnsToFirstElement()
520 | {
521 | using (NativeArray2D array = CreateArray(2, 3))
522 | {
523 | NativeArray2D alias = array;
524 | alias[0, 0] = 123;
525 | NativeArray2D.Enumerator e = array.GetEnumerator();
526 | e.MoveNext();
527 |
528 | e.Reset();
529 |
530 | Assert.That(e.MoveNext(), Is.True);
531 | Assert.That(e.Current, Is.EqualTo(123));
532 | }
533 | }
534 |
535 | [Test]
536 | public void EnumeratorCurrentReturnsCurrentElementAsObject()
537 | {
538 | using (NativeArray2D array = CreateArray(2, 3))
539 | {
540 | NativeArray2D alias = array;
541 | alias[0, 0] = 123;
542 | NativeArray2D.Enumerator e = array.GetEnumerator();
543 | e.MoveNext();
544 |
545 | Assert.That(((IEnumerator)e).Current, Is.EqualTo(123));
546 | }
547 | }
548 | }
549 | }
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestNativeArray2D.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 2f999fadfa7d43058f471a536f1ab709
3 | timeCreated: 1573436095
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestNativeChunkedList.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 1ff6184a5635346209193b8b9cad4dc9
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestNativeHashSet.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (c) Jackson Dunstan. See LICENSE.md.
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System;
8 | using NUnit.Framework;
9 | using Unity.Collections;
10 | using Unity.Jobs;
11 |
12 | namespace JacksonDunstan.NativeCollections.Tests
13 | {
14 | ///
15 | /// Unit tests for and
16 | /// .
17 | ///
18 | public class TestNativeHashSet
19 | {
20 | private static NativeHashSet CreateEmptySet()
21 | {
22 | return new NativeHashSet(0, Allocator.TempJob);
23 | }
24 |
25 | private static void AssertRequiresReadOrWriteAccess(
26 | NativeHashSet set,
27 | Action action)
28 | {
29 | set.TestUseOnlySetAllowReadAndWriteAccess(false);
30 | try
31 | {
32 | Assert.That(
33 | () => action(),
34 | Throws.TypeOf());
35 | }
36 | finally
37 | {
38 | set.TestUseOnlySetAllowReadAndWriteAccess(true);
39 | }
40 | }
41 |
42 | private static void AssertRequiresReadOrWriteAccess(
43 | NativeHashSet.ParallelWriter writer,
44 | Action action)
45 | {
46 | writer.TestUseOnlySetAllowReadAndWriteAccess(false);
47 | try
48 | {
49 | Assert.That(
50 | () => action(),
51 | Throws.TypeOf());
52 | }
53 | finally
54 | {
55 | writer.TestUseOnlySetAllowReadAndWriteAccess(true);
56 | }
57 | }
58 |
59 | [Test]
60 | public void ConstructorCreatesEmptySet()
61 | {
62 | using (NativeHashSet set = new NativeHashSet(1, Allocator.Temp))
63 | {
64 | Assert.That(set.Length, Is.EqualTo(0));
65 | }
66 | }
67 |
68 | [Test]
69 | public void ConstructorClampsToMinimumCapacity()
70 | {
71 | using (NativeHashSet set = new NativeHashSet(1, Allocator.Temp))
72 | {
73 | Assert.That(set.Capacity, Is.GreaterThan(0));
74 | }
75 | }
76 |
77 | [Test]
78 | public void ConstructorRequiresValidAllocator()
79 | {
80 | Assert.That(
81 | () => new NativeHashSet(1, default(Allocator)),
82 | Throws.Exception);
83 | }
84 |
85 | #if !CSHARP_7_3_OR_NEWER
86 | private struct NonBlittableType
87 | {
88 | public string Str;
89 | }
90 |
91 | [Test]
92 | public void ConstructorRequiresBlittableType()
93 | {
94 | Assert.That(
95 | () => new NativeHashSet(1, Allocator.Temp),
96 | Throws.Exception);
97 | }
98 | #endif
99 |
100 | [Test]
101 | public void GetLengthRequiresReadAccess()
102 | {
103 | using (NativeHashSet set = CreateEmptySet())
104 | {
105 | int len;
106 | AssertRequiresReadOrWriteAccess(
107 | set,
108 | () => len = set.Length);
109 | }
110 | }
111 |
112 | [Test]
113 | public void GetCapacityReturnsSetCapacity()
114 | {
115 | using (NativeHashSet set = new NativeHashSet(
116 | 100,
117 | Allocator.Temp))
118 | {
119 | Assert.That(set.Capacity, Is.EqualTo(100));
120 | }
121 | }
122 |
123 | [Test]
124 | public void GetCapacityRequiresReadAccess()
125 | {
126 | using (NativeHashSet set = CreateEmptySet())
127 | {
128 | int cap;
129 | AssertRequiresReadOrWriteAccess(
130 | set,
131 | () => cap = set.Capacity);
132 | }
133 | }
134 |
135 | [Test]
136 | public void SetCapacityRequiresWriteAccess()
137 | {
138 | NativeHashSet set = CreateEmptySet();
139 | try
140 | {
141 | AssertRequiresReadOrWriteAccess(
142 | set,
143 | () => set.Capacity = 100);
144 | }
145 | finally
146 | {
147 | set.Dispose();
148 | }
149 | }
150 |
151 | [Test]
152 | public void SetCapacityGrowsCapacity()
153 | {
154 | NativeHashSet set = CreateEmptySet();
155 | try
156 | {
157 | set.Capacity = 100;
158 | Assert.That(set.Capacity, Is.EqualTo(100));
159 | }
160 | finally
161 | {
162 | set.Dispose();
163 | }
164 | }
165 |
166 | [Test]
167 | public void SetCapacityCannotShrinkCapacity()
168 | {
169 | NativeHashSet set = new NativeHashSet(10, Allocator.Temp);
170 | try
171 | {
172 | Assert.That(() => set.Capacity = 1, Throws.Exception);
173 | }
174 | finally
175 | {
176 | set.Dispose();
177 | }
178 | }
179 |
180 | [Test]
181 | public void TryAddAddsWhenNotPresent()
182 | {
183 | using (NativeHashSet set = CreateEmptySet())
184 | {
185 | Assert.That(set.TryAdd(1), Is.True);
186 | Assert.That(set.Contains(1), Is.True);
187 | }
188 | }
189 |
190 | [Test]
191 | public void TryAddReturnsFalseWhenPresent()
192 | {
193 | using (NativeHashSet set = CreateEmptySet())
194 | {
195 | set.TryAdd(1);
196 |
197 | Assert.That(set.TryAdd(1), Is.False);
198 | }
199 | }
200 |
201 | [Test]
202 | public void TryAddGrowsWhenAtCapacity()
203 | {
204 | using (NativeHashSet set = CreateEmptySet())
205 | {
206 | int originalCapacity = set.Capacity;
207 | for (int i = 0; i < originalCapacity; ++i)
208 | {
209 | set.TryAdd(i);
210 | }
211 |
212 | Assert.That(set.TryAdd(originalCapacity), Is.True);
213 | Assert.That(set.Capacity, Is.GreaterThan(originalCapacity));
214 | }
215 | }
216 |
217 | [Test]
218 | public void TryAddRequiresWriteAccess()
219 | {
220 | using (NativeHashSet set = CreateEmptySet())
221 | {
222 | AssertRequiresReadOrWriteAccess(
223 | set,
224 | () => set.TryAdd(1));
225 | }
226 | }
227 |
228 | [Test]
229 | public void ClearRemovesAllElements()
230 | {
231 | using (NativeHashSet set = CreateEmptySet())
232 | {
233 | set.TryAdd(1);
234 |
235 | set.Clear();
236 |
237 | Assert.That(set.Length, Is.EqualTo(0));
238 | Assert.That(set.Contains(1), Is.False);
239 | }
240 | }
241 |
242 | [Test]
243 | public void ClearRequiresWriteAccess()
244 | {
245 | using (NativeHashSet set = CreateEmptySet())
246 | {
247 | AssertRequiresReadOrWriteAccess(
248 | set,
249 | () => set.Clear());
250 | }
251 | }
252 |
253 | [Test]
254 | public void RemoveRemovesContainedElement()
255 | {
256 | using (NativeHashSet set = CreateEmptySet())
257 | {
258 | set.TryAdd(1);
259 |
260 | Assert.That(set.Remove(1), Is.True);
261 |
262 | Assert.That(set.Length, Is.EqualTo(0));
263 | Assert.That(set.Contains(1), Is.False);
264 | }
265 | }
266 |
267 | [Test]
268 | public void RemoveReturnsFalseWhenElementIsNotContained()
269 | {
270 | using (NativeHashSet set = CreateEmptySet())
271 | {
272 | set.TryAdd(1);
273 |
274 | Assert.That(set.Remove(2), Is.False);
275 |
276 | Assert.That(set.Length, Is.EqualTo(1));
277 | Assert.That(set.Contains(1), Is.True);
278 | }
279 | }
280 |
281 | [Test]
282 | public void RemoveRequiresWriteAccess()
283 | {
284 | using (NativeHashSet set = CreateEmptySet())
285 | {
286 | AssertRequiresReadOrWriteAccess(
287 | set,
288 | () => set.Remove(0));
289 | }
290 | }
291 |
292 | [Test]
293 | public void ContainsReturnsTrueForContainedElement()
294 | {
295 | using (NativeHashSet set = CreateEmptySet())
296 | {
297 | set.TryAdd(1);
298 |
299 | Assert.That(set.Contains(1), Is.True);
300 | }
301 | }
302 |
303 | [Test]
304 | public void ContainsReturnsFalseForNotContainedElement()
305 | {
306 | using (NativeHashSet set = CreateEmptySet())
307 | {
308 | set.TryAdd(1);
309 |
310 | Assert.That(set.Contains(2), Is.False);
311 | }
312 | }
313 |
314 | [Test]
315 | public void ContainsRequiresReadAccess()
316 | {
317 | using (NativeHashSet set = CreateEmptySet())
318 | {
319 | AssertRequiresReadOrWriteAccess(
320 | set,
321 | () => set.Contains(0));
322 | }
323 | }
324 |
325 | [Test]
326 | public void IsCreatedReturnsTrueForDefaultStruct()
327 | {
328 | NativeHashSet set = default(NativeHashSet);
329 | Assert.That(set.IsCreated, Is.False);
330 | }
331 |
332 | [Test]
333 | public void IsCreatedReturnsTrueAfterConstructor()
334 | {
335 | using (NativeHashSet set = CreateEmptySet())
336 | {
337 | Assert.That(set.IsCreated, Is.True);
338 | }
339 | }
340 |
341 | [Test]
342 | public void OperationsAfterDisposeFail()
343 | {
344 | NativeHashSet set = CreateEmptySet();
345 | set.Dispose();
346 | Assert.That(
347 | () => set.Contains(0),
348 | Throws.Exception);
349 | }
350 |
351 | [Test]
352 | public void IsCreatedReturnsFalseAfterDispose()
353 | {
354 | NativeHashSet set = CreateEmptySet();
355 | set.Dispose();
356 | Assert.That(set.IsCreated, Is.False);
357 | }
358 |
359 | [Test]
360 | public void DisposeRequiresWriteAccess()
361 | {
362 | using (NativeHashSet set = CreateEmptySet())
363 | {
364 | AssertRequiresReadOrWriteAccess(
365 | set,
366 | () => set.Dispose());
367 | }
368 | }
369 |
370 | private struct PreDisposeJob : IJob
371 | {
372 | [WriteOnly] public NativeArray Executed;
373 |
374 | public void Execute()
375 | {
376 | Executed[0] = 1;
377 | }
378 | }
379 |
380 | [Test]
381 | public void DisposeJobDisposesAfterGivenHandle()
382 | {
383 | using (NativeArray executed = new NativeArray(
384 | 1,
385 | Allocator.TempJob))
386 | {
387 | NativeHashSet set = CreateEmptySet();
388 | try
389 | {
390 | PreDisposeJob preDisposeJob = new PreDisposeJob
391 | {
392 | Executed = executed
393 | };
394 | JobHandle preDisposeHandle = preDisposeJob.Schedule();
395 |
396 | JobHandle disposeHandle = set.Dispose(preDisposeHandle);
397 | disposeHandle.Complete();
398 |
399 | Assert.That(set.IsCreated, Is.False);
400 | Assert.That(executed[0], Is.EqualTo(1));
401 | }
402 | finally
403 | {
404 | if (set.IsCreated)
405 | {
406 | set.Dispose();
407 | }
408 | }
409 | }
410 | }
411 |
412 | [Test]
413 | public void DisposeJobRequiresWriteAccess()
414 | {
415 | using (NativeHashSet set = CreateEmptySet())
416 | {
417 | AssertRequiresReadOrWriteAccess(
418 | set,
419 | () => set.Dispose(default(JobHandle)).Complete());
420 | }
421 | }
422 |
423 | [Test]
424 | public void ToNativeArrayCopiesAllElementsToArrayAtGivenIndex()
425 | {
426 | using (NativeArray array = new NativeArray(
427 | 5,
428 | Allocator.TempJob))
429 | {
430 | using (NativeHashSet set = CreateEmptySet())
431 | {
432 | set.TryAdd(1);
433 | set.TryAdd(2);
434 | set.TryAdd(3);
435 |
436 | NativeArray toArray = set.ToNativeArray(array, 1);
437 |
438 | // Didn't overwrite out of given bounds
439 | Assert.That(array[0], Is.EqualTo(0));
440 | Assert.That(array[4], Is.EqualTo(0));
441 |
442 | // Written values are correct
443 | int[] managedArray = {array[1], array[2], array[3]};
444 | Array.Sort(managedArray);
445 | Assert.That(managedArray, Is.EqualTo(new[] {1, 2, 3}));
446 |
447 | // Returned array is the same array
448 | // Check by writing to one and reading from the other
449 | toArray[0] = 4;
450 | Assert.That(array[0], Is.EqualTo(4));
451 | }
452 | }
453 | }
454 |
455 | [Test]
456 | public void ToNativeArrayCopiesAllElementsToNewArrayWhenNotIsCreated()
457 | {
458 | using (NativeHashSet set = CreateEmptySet())
459 | {
460 | set.TryAdd(1);
461 | set.TryAdd(2);
462 | set.TryAdd(3);
463 |
464 | using (NativeArray array = set.ToNativeArray(
465 | default(NativeArray),
466 | 1))
467 | {
468 | // Created enough room
469 | Assert.That(array.Length, Is.EqualTo(4));
470 |
471 | // Didn't overwrite out of given bounds
472 | Assert.That(array[0], Is.EqualTo(0));
473 |
474 | // Written values are correct
475 | int[] managedArray = {array[1], array[2], array[3]};
476 | Array.Sort(managedArray);
477 | Assert.That(managedArray, Is.EqualTo(new[] {1, 2, 3}));
478 | }
479 | }
480 | }
481 |
482 | [Test]
483 | public void ToNativeArrayCopiesAllElementsToNewArrayWhenNotLongEnough()
484 | {
485 | using (NativeArray shortArray = new NativeArray(
486 | 2,
487 | Allocator.TempJob))
488 | {
489 | using (NativeHashSet set = CreateEmptySet())
490 | {
491 | set.TryAdd(1);
492 | set.TryAdd(2);
493 | set.TryAdd(3);
494 |
495 | using (NativeArray toArray = set.ToNativeArray(shortArray, 1))
496 | {
497 | // Created enough room
498 | Assert.That(toArray.Length, Is.EqualTo(4));
499 |
500 | // Didn't overwrite out of given bounds
501 | Assert.That(toArray[0], Is.EqualTo(0));
502 |
503 | // Written values are correct
504 | int[] managedArray = {toArray[1], toArray[2], toArray[3]};
505 | Array.Sort(managedArray);
506 | Assert.That(managedArray, Is.EqualTo(new[] {1, 2, 3}));
507 |
508 | // Returned array is a different array
509 | // Check by writing to one and reading from the other
510 | NativeArray toArrayCopy = toArray;
511 | toArrayCopy[0] = 4;
512 | Assert.That(shortArray[0], Is.Not.EqualTo(4));
513 | }
514 | }
515 | }
516 | }
517 |
518 | [Test]
519 | public void ToNativeArrayRequiresReadAccess()
520 | {
521 | using (NativeArray array = new NativeArray(
522 | 2,
523 | Allocator.TempJob))
524 | {
525 | using (NativeHashSet set = CreateEmptySet())
526 | {
527 | set.TryAdd(1);
528 |
529 | AssertRequiresReadOrWriteAccess(
530 | set,
531 | () => set.ToNativeArray(array, 1));
532 | }
533 | }
534 | }
535 |
536 | [Test]
537 | public void AsParallelWriterReturnsUsableWriter()
538 | {
539 | using (NativeHashSet set = CreateEmptySet())
540 | {
541 | NativeHashSet.ParallelWriter writer = set.AsParallelWriter();
542 |
543 | Assert.That(writer.Capacity, Is.EqualTo(set.Capacity));
544 |
545 | Assert.That(writer.TryAdd(1), Is.True);
546 |
547 | Assert.That(set.Contains(1), Is.True);
548 | }
549 | }
550 |
551 | [Test]
552 | public void ParallelWriterGetCapacityRequiresReadAccess()
553 | {
554 | using (NativeHashSet set = CreateEmptySet())
555 | {
556 | NativeHashSet.ParallelWriter writer = set.AsParallelWriter();
557 |
558 | int cap;
559 | AssertRequiresReadOrWriteAccess(
560 | writer,
561 | () => cap = writer.Capacity);
562 | }
563 | }
564 |
565 | [Test]
566 | public void ParallelWriterTryAddRequiresWriteAccess()
567 | {
568 | using (NativeHashSet set = CreateEmptySet())
569 | {
570 | NativeHashSet.ParallelWriter writer = set.AsParallelWriter();
571 |
572 | AssertRequiresReadOrWriteAccess(
573 | writer,
574 | () => writer.TryAdd(1));
575 | }
576 | }
577 |
578 | struct ParallelWriterJob : IJobParallelFor
579 | {
580 | [ReadOnly] public NativeArray Array;
581 | [WriteOnly] public NativeHashSet.ParallelWriter Writer;
582 |
583 | public void Execute(int index)
584 | {
585 | Writer.TryAdd(Array[index]);
586 | }
587 | }
588 |
589 | [Test]
590 | public void AsParallelWriterReturnsUsableWriterInJob()
591 | {
592 | using (NativeHashSet set = CreateEmptySet())
593 | {
594 | using (NativeArray array = new NativeArray(
595 | 2,
596 | Allocator.TempJob))
597 | {
598 | NativeArray arrayRef = array;
599 | arrayRef[0] = 1;
600 | arrayRef[1] = 2;
601 |
602 | ParallelWriterJob job = new ParallelWriterJob
603 | {
604 | Array = array,
605 | Writer = set.AsParallelWriter()
606 | };
607 |
608 | JobHandle handle = job.Schedule(array.Length, 64);
609 | handle.Complete();
610 |
611 | Assert.That(set.Length, Is.EqualTo(2));
612 | }
613 | }
614 | }
615 | }
616 | }
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestNativeHashSet.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: b722688e34a6473fa86dac967f41581b
3 | timeCreated: 1566774128
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestNativeIntPtr.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (c) Jackson Dunstan. See LICENSE.md.
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System;
8 | using Unity.Collections;
9 | using Unity.Jobs;
10 | using NUnit.Framework;
11 |
12 | namespace JacksonDunstan.NativeCollections.Tests
13 | {
14 | ///
15 | /// Unit tests for
16 | ///
17 | public class TestNativeIntPtr
18 | {
19 | private static void AssertRequiresReadOrWriteAccess(
20 | NativeIntPtr intPtr,
21 | Action action)
22 | {
23 | intPtr.TestUseOnlySetAllowReadAndWriteAccess(false);
24 | try
25 | {
26 | Assert.That(
27 | () => action(),
28 | Throws.TypeOf());
29 | }
30 | finally
31 | {
32 | intPtr.TestUseOnlySetAllowReadAndWriteAccess(true);
33 | }
34 | }
35 |
36 | [Test]
37 | public void ConstructorDefaultsValueToZero()
38 | {
39 | using (NativeIntPtr intPtr = new NativeIntPtr(Allocator.Temp))
40 | {
41 | Assert.That(intPtr.Value, Is.EqualTo(0));
42 | }
43 | }
44 |
45 | [Test]
46 | public void ConstructorSetsInitialValue()
47 | {
48 | using (NativeIntPtr intPtr = new NativeIntPtr(Allocator.Temp, 123))
49 | {
50 | Assert.That(intPtr.Value, Is.EqualTo(123));
51 | }
52 | }
53 |
54 | [Test]
55 | public void ConstructorThrowsExceptionForInvalidAllocator()
56 | {
57 | Assert.That(
58 | () => new NativeIntPtr(Allocator.None),
59 | Throws.TypeOf());
60 | }
61 |
62 | [Test]
63 | public void GetValueReturnsWhatSetValueSets()
64 | {
65 | using (NativeIntPtr intPtr = new NativeIntPtr(Allocator.Temp))
66 | {
67 | NativeIntPtr copy = intPtr;
68 | copy.Value = 123;
69 |
70 | Assert.That(intPtr.Value, Is.EqualTo(123));
71 | }
72 | }
73 |
74 | [Test]
75 | public void GetValueRequiresReadAccess()
76 | {
77 | using (NativeIntPtr intPtr = new NativeIntPtr(Allocator.Temp))
78 | {
79 | int value;
80 | AssertRequiresReadOrWriteAccess(
81 | intPtr,
82 | () => value = intPtr.Value);
83 | }
84 | }
85 |
86 | [Test]
87 | public void SetValueRequiresReadAccess()
88 | {
89 | using (NativeIntPtr intPtr = new NativeIntPtr(Allocator.Temp))
90 | {
91 | NativeIntPtr copy = intPtr;
92 | AssertRequiresReadOrWriteAccess(
93 | intPtr,
94 | () => copy.Value = 123);
95 | }
96 | }
97 |
98 | [Test]
99 | public void ParallelIncrementIncrementsValue()
100 | {
101 | using (NativeIntPtr intPtr = new NativeIntPtr(Allocator.Temp, 123))
102 | {
103 | NativeIntPtr.Parallel parallel = intPtr.GetParallel();
104 | parallel.Increment();
105 |
106 | Assert.That(intPtr.Value, Is.EqualTo(124));
107 | }
108 | }
109 |
110 | [Test]
111 | public void ParallelIncrementRequiresReadAccess()
112 | {
113 | using (NativeIntPtr intPtr = new NativeIntPtr(Allocator.Temp))
114 | {
115 | NativeIntPtr.Parallel parallel = intPtr.GetParallel();
116 | AssertRequiresReadOrWriteAccess(
117 | intPtr,
118 | parallel.Increment);
119 | }
120 | }
121 |
122 | [Test]
123 | public void ParallelDecrementIncrementsValue()
124 | {
125 | using (NativeIntPtr intPtr = new NativeIntPtr(Allocator.Temp, 123))
126 | {
127 | NativeIntPtr.Parallel parallel = intPtr.GetParallel();
128 | parallel.Decrement();
129 |
130 | Assert.That(intPtr.Value, Is.EqualTo(122));
131 | }
132 | }
133 |
134 | [Test]
135 | public void ParallelDecrementRequiresReadAccess()
136 | {
137 | using (NativeIntPtr intPtr = new NativeIntPtr(Allocator.Temp))
138 | {
139 | NativeIntPtr.Parallel parallel = intPtr.GetParallel();
140 | AssertRequiresReadOrWriteAccess(
141 | intPtr,
142 | parallel.Decrement);
143 | }
144 | }
145 |
146 | [Test]
147 | public void ParallelAddOffsetsValue()
148 | {
149 | using (NativeIntPtr intPtr = new NativeIntPtr(Allocator.Temp, 123))
150 | {
151 | NativeIntPtr.Parallel parallel = intPtr.GetParallel();
152 | parallel.Add(5);
153 |
154 | Assert.That(intPtr.Value, Is.EqualTo(128));
155 |
156 | parallel.Add(-15);
157 |
158 | Assert.That(intPtr.Value, Is.EqualTo(113));
159 | }
160 | }
161 |
162 | [Test]
163 | public void ParallelAddRequiresReadAccess()
164 | {
165 | using (NativeIntPtr intPtr = new NativeIntPtr(Allocator.Temp))
166 | {
167 | NativeIntPtr.Parallel parallel = intPtr.GetParallel();
168 | AssertRequiresReadOrWriteAccess(
169 | intPtr,
170 | () => parallel.Add(10));
171 | }
172 | }
173 |
174 | [Test]
175 | public void DisposeMakesUnusable()
176 | {
177 | NativeIntPtr intPtr = new NativeIntPtr(Allocator.Temp);
178 | intPtr.Dispose();
179 | Assert.That(
180 | () => intPtr.Value = 10,
181 | Throws.Exception);
182 | }
183 |
184 | [Test]
185 | public void DisposeRequiresReadAccess()
186 | {
187 | using (NativeIntPtr intPtr = new NativeIntPtr(Allocator.Temp))
188 | {
189 | AssertRequiresReadOrWriteAccess(
190 | intPtr,
191 | intPtr.Dispose);
192 | }
193 | }
194 |
195 | [Test]
196 | public void IsCreatedOnlyReturnsTrueBeforeDispose()
197 | {
198 | NativeIntPtr intPtr = new NativeIntPtr(Allocator.Temp);
199 | Assert.That(intPtr.IsCreated, Is.True);
200 |
201 | intPtr.Dispose();
202 |
203 | Assert.That(intPtr.IsCreated, Is.False);
204 | }
205 |
206 | private struct ParallelForTestJob : IJobParallelFor
207 | {
208 | public NativeArray Array;
209 | public NativeIntPtr.Parallel Sum;
210 |
211 | public void Execute(int index)
212 | {
213 | Sum.Add(Array[index]);
214 | }
215 | }
216 |
217 | [Test]
218 | public void ParallelForJobCanUseParallelPtr()
219 | {
220 | using (NativeArray array = new NativeArray(
221 | 3,
222 | Allocator.TempJob))
223 | {
224 | NativeArray arrayCopy = array;
225 | arrayCopy[0] = 10;
226 | arrayCopy[1] = 20;
227 | arrayCopy[2] = 30;
228 |
229 | using (NativeIntPtr sum = new NativeIntPtr(
230 | Allocator.TempJob))
231 | {
232 | ParallelForTestJob job = new ParallelForTestJob
233 | {
234 | Array = array,
235 | Sum = sum.GetParallel()
236 | };
237 | job.Run(array.Length);
238 |
239 | Assert.That(sum.Value, Is.EqualTo(60));
240 | }
241 | }
242 | }
243 |
244 | private struct DeallocateOnJobCompletionJob : IJob
245 | {
246 | [DeallocateOnJobCompletion]
247 | public NativeIntPtr IntPtr;
248 |
249 | public void Execute()
250 | {
251 | }
252 | }
253 |
254 | [Test]
255 | public void CanDeallocateOnJobCompletion()
256 | {
257 | NativeIntPtr intPtr = new NativeIntPtr(Allocator.TempJob);
258 | var job = new DeallocateOnJobCompletionJob { IntPtr = intPtr };
259 | job.Run();
260 |
261 | Assert.That(
262 | () => intPtr.Value = 10,
263 | Throws.Exception);
264 | }
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestNativeIntPtr.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ef57e2b4fd4614d438ee9832dd522742
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestNativeLinkedList.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 0fa8eeb747ea1413797873e063bdd8dc
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestNativeLongPtr.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (c) Jackson Dunstan. See LICENSE.md.
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System;
8 | using Unity.Collections;
9 | using Unity.Jobs;
10 | using NUnit.Framework;
11 |
12 | namespace JacksonDunstan.NativeCollections.Tests
13 | {
14 | ///
15 | /// Unit tests for
16 | ///
17 | public class TestNativeLongPtr
18 | {
19 | private static void AssertRequiresReadOrWriteAccess(
20 | NativeLongPtr intPtr,
21 | Action action)
22 | {
23 | intPtr.TestUseOnlySetAllowReadAndWriteAccess(false);
24 | try
25 | {
26 | Assert.That(
27 | () => action(),
28 | Throws.TypeOf());
29 | }
30 | finally
31 | {
32 | intPtr.TestUseOnlySetAllowReadAndWriteAccess(true);
33 | }
34 | }
35 |
36 | [Test]
37 | public void ConstructorDefaultsValueToZero()
38 | {
39 | using (NativeLongPtr intPtr = new NativeLongPtr(Allocator.Temp))
40 | {
41 | Assert.That(intPtr.Value, Is.EqualTo(0));
42 | }
43 | }
44 |
45 | [Test]
46 | public void ConstructorSetsInitialValue()
47 | {
48 | using (NativeLongPtr intPtr = new NativeLongPtr(Allocator.Temp, 123))
49 | {
50 | Assert.That(intPtr.Value, Is.EqualTo(123));
51 | }
52 | }
53 |
54 | [Test]
55 | public void ConstructorThrowsExceptionForInvalidAllocator()
56 | {
57 | Assert.That(
58 | () => new NativeLongPtr(Allocator.None),
59 | Throws.TypeOf());
60 | }
61 |
62 | [Test]
63 | public void GetValueReturnsWhatSetValueSets()
64 | {
65 | using (NativeLongPtr intPtr = new NativeLongPtr(Allocator.Temp))
66 | {
67 | NativeLongPtr copy = intPtr;
68 | copy.Value = 123;
69 |
70 | Assert.That(intPtr.Value, Is.EqualTo(123));
71 | }
72 | }
73 |
74 | [Test]
75 | public void GetValueRequiresReadAccess()
76 | {
77 | using (NativeLongPtr intPtr = new NativeLongPtr(Allocator.Temp))
78 | {
79 | long value;
80 | AssertRequiresReadOrWriteAccess(
81 | intPtr,
82 | () => value = intPtr.Value);
83 | }
84 | }
85 |
86 | [Test]
87 | public void SetValueRequiresReadAccess()
88 | {
89 | using (NativeLongPtr intPtr = new NativeLongPtr(Allocator.Temp))
90 | {
91 | NativeLongPtr copy = intPtr;
92 | AssertRequiresReadOrWriteAccess(
93 | intPtr,
94 | () => copy.Value = 123);
95 | }
96 | }
97 |
98 | [Test]
99 | public void ParallelIncrementIncrementsValue()
100 | {
101 | using (NativeLongPtr intPtr = new NativeLongPtr(Allocator.Temp, 123))
102 | {
103 | NativeLongPtr.Parallel parallel = intPtr.GetParallel();
104 | parallel.Increment();
105 |
106 | Assert.That(intPtr.Value, Is.EqualTo(124));
107 | }
108 | }
109 |
110 | [Test]
111 | public void ParallelIncrementRequiresReadAccess()
112 | {
113 | using (NativeLongPtr intPtr = new NativeLongPtr(Allocator.Temp))
114 | {
115 | NativeLongPtr.Parallel parallel = intPtr.GetParallel();
116 | AssertRequiresReadOrWriteAccess(
117 | intPtr,
118 | parallel.Increment);
119 | }
120 | }
121 |
122 | [Test]
123 | public void ParallelDecrementIncrementsValue()
124 | {
125 | using (NativeLongPtr intPtr = new NativeLongPtr(Allocator.Temp, 123))
126 | {
127 | NativeLongPtr.Parallel parallel = intPtr.GetParallel();
128 | parallel.Decrement();
129 |
130 | Assert.That(intPtr.Value, Is.EqualTo(122));
131 | }
132 | }
133 |
134 | [Test]
135 | public void ParallelDecrementRequiresReadAccess()
136 | {
137 | using (NativeLongPtr intPtr = new NativeLongPtr(Allocator.Temp))
138 | {
139 | NativeLongPtr.Parallel parallel = intPtr.GetParallel();
140 | AssertRequiresReadOrWriteAccess(
141 | intPtr,
142 | parallel.Decrement);
143 | }
144 | }
145 |
146 | [Test]
147 | public void ParallelAddOffsetsValue()
148 | {
149 | using (NativeLongPtr intPtr = new NativeLongPtr(Allocator.Temp, 123))
150 | {
151 | NativeLongPtr.Parallel parallel = intPtr.GetParallel();
152 | parallel.Add(5);
153 |
154 | Assert.That(intPtr.Value, Is.EqualTo(128));
155 |
156 | parallel.Add(-15);
157 |
158 | Assert.That(intPtr.Value, Is.EqualTo(113));
159 | }
160 | }
161 |
162 | [Test]
163 | public void ParallelAddRequiresReadAccess()
164 | {
165 | using (NativeLongPtr intPtr = new NativeLongPtr(Allocator.Temp))
166 | {
167 | NativeLongPtr.Parallel parallel = intPtr.GetParallel();
168 | AssertRequiresReadOrWriteAccess(
169 | intPtr,
170 | () => parallel.Add(10));
171 | }
172 | }
173 |
174 | [Test]
175 | public void DisposeMakesUnusable()
176 | {
177 | NativeLongPtr intPtr = new NativeLongPtr(Allocator.Temp);
178 | intPtr.Dispose();
179 | Assert.That(
180 | () => intPtr.Value = 10,
181 | Throws.Exception);
182 | }
183 |
184 | [Test]
185 | public void DisposeRequiresReadAccess()
186 | {
187 | using (NativeLongPtr intPtr = new NativeLongPtr(Allocator.Temp))
188 | {
189 | AssertRequiresReadOrWriteAccess(
190 | intPtr,
191 | intPtr.Dispose);
192 | }
193 | }
194 |
195 | [Test]
196 | public void IsCreatedOnlyReturnsTrueBeforeDispose()
197 | {
198 | NativeLongPtr intPtr = new NativeLongPtr(Allocator.Temp);
199 | Assert.That(intPtr.IsCreated, Is.True);
200 |
201 | intPtr.Dispose();
202 |
203 | Assert.That(intPtr.IsCreated, Is.False);
204 | }
205 |
206 | private struct ParallelForTestJob : IJobParallelFor
207 | {
208 | public NativeArray Array;
209 | public NativeLongPtr.Parallel Sum;
210 |
211 | public void Execute(int index)
212 | {
213 | Sum.Add(Array[index]);
214 | }
215 | }
216 |
217 | [Test]
218 | public void ParallelForJobCanUseParallelPtr()
219 | {
220 | using (NativeArray array = new NativeArray(
221 | 3,
222 | Allocator.TempJob))
223 | {
224 | NativeArray arrayCopy = array;
225 | arrayCopy[0] = 10;
226 | arrayCopy[1] = 20;
227 | arrayCopy[2] = 30;
228 |
229 | using (NativeLongPtr sum = new NativeLongPtr(
230 | Allocator.TempJob))
231 | {
232 | ParallelForTestJob job = new ParallelForTestJob
233 | {
234 | Array = array,
235 | Sum = sum.GetParallel()
236 | };
237 | job.Run(array.Length);
238 |
239 | Assert.That(sum.Value, Is.EqualTo(60));
240 | }
241 | }
242 | }
243 |
244 | private struct DeallocateOnJobCompletionJob : IJob
245 | {
246 | [DeallocateOnJobCompletion]
247 | public NativeLongPtr LongPtr;
248 |
249 | public void Execute()
250 | {
251 | }
252 | }
253 |
254 | [Test]
255 | public void CanDeallocateOnJobCompletion()
256 | {
257 | NativeLongPtr intPtr = new NativeLongPtr(Allocator.TempJob);
258 | var job = new DeallocateOnJobCompletionJob { LongPtr = intPtr };
259 | job.Run();
260 |
261 | Assert.That(
262 | () => intPtr.Value = 10,
263 | Throws.Exception);
264 | }
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestNativeLongPtr.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: adcf3de91cdc1499c8ee8d446b4fd402
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestNativePerJobThreadIntPtr.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (c) Jackson Dunstan. See LICENSE.md.
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System;
8 | using Unity.Collections;
9 | using Unity.Jobs;
10 | using NUnit.Framework;
11 |
12 | namespace JacksonDunstan.NativeCollections.Tests
13 | {
14 | ///
15 | /// Unit tests for
16 | ///
17 | public class TestNativePerJobThreadIntPtr
18 | {
19 | private static void AssertRequiresReadOrWriteAccess(
20 | NativePerJobThreadIntPtr intPtr,
21 | Action action)
22 | {
23 | intPtr.TestUseOnlySetAllowReadAndWriteAccess(false);
24 | try
25 | {
26 | Assert.That(
27 | () => action(),
28 | Throws.TypeOf());
29 | }
30 | finally
31 | {
32 | intPtr.TestUseOnlySetAllowReadAndWriteAccess(true);
33 | }
34 | }
35 |
36 | [Test]
37 | public void ConstructorDefaultsValueToZero()
38 | {
39 | using (NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
40 | Allocator.Temp))
41 | {
42 | Assert.That(intPtr.Value, Is.EqualTo(0));
43 | }
44 | }
45 |
46 | [Test]
47 | public void ConstructorSetsInitialValue()
48 | {
49 | using (NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
50 | Allocator.Temp, 123))
51 | {
52 | Assert.That(intPtr.Value, Is.EqualTo(123));
53 | }
54 | }
55 |
56 | [Test]
57 | public void ConstructorThrowsExceptionForInvalidAllocator()
58 | {
59 | Assert.That(
60 | () => new NativePerJobThreadIntPtr(Allocator.None),
61 | Throws.TypeOf());
62 | }
63 |
64 | [Test]
65 | public void GetValueReturnsWhatSetValueSets()
66 | {
67 | using (NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
68 | Allocator.Temp))
69 | {
70 | NativePerJobThreadIntPtr copy = intPtr;
71 | copy.Value = 123;
72 |
73 | Assert.That(intPtr.Value, Is.EqualTo(123));
74 | }
75 | }
76 |
77 | [Test]
78 | public void GetValueRequiresReadAccess()
79 | {
80 | using (NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
81 | Allocator.Temp))
82 | {
83 | int value;
84 | AssertRequiresReadOrWriteAccess(
85 | intPtr,
86 | () => value = intPtr.Value);
87 | }
88 | }
89 |
90 | [Test]
91 | public void SetValueRequiresReadAccess()
92 | {
93 | using (NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
94 | Allocator.Temp))
95 | {
96 | NativePerJobThreadIntPtr copy = intPtr;
97 | AssertRequiresReadOrWriteAccess(
98 | intPtr,
99 | () => copy.Value = 123);
100 | }
101 | }
102 |
103 | [Test]
104 | public void ParallelIncrementIncrementsValue()
105 | {
106 | using (NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
107 | Allocator.Temp,
108 | 123))
109 | {
110 | NativePerJobThreadIntPtr.Parallel parallel = intPtr.GetParallel();
111 | parallel.Increment();
112 |
113 | Assert.That(intPtr.Value, Is.EqualTo(124));
114 | }
115 | }
116 |
117 | [Test]
118 | public void ParallelIncrementRequiresReadAccess()
119 | {
120 | using (NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
121 | Allocator.Temp))
122 | {
123 | NativePerJobThreadIntPtr.Parallel parallel = intPtr.GetParallel();
124 | AssertRequiresReadOrWriteAccess(
125 | intPtr,
126 | parallel.Increment);
127 | }
128 | }
129 |
130 | [Test]
131 | public void ParallelDecrementIncrementsValue()
132 | {
133 | using (NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
134 | Allocator.Temp,
135 | 123))
136 | {
137 | NativePerJobThreadIntPtr.Parallel parallel = intPtr.GetParallel();
138 | parallel.Decrement();
139 |
140 | Assert.That(intPtr.Value, Is.EqualTo(122));
141 | }
142 | }
143 |
144 | [Test]
145 | public void ParallelDecrementRequiresReadAccess()
146 | {
147 | using (NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
148 | Allocator.Temp))
149 | {
150 | NativePerJobThreadIntPtr.Parallel parallel = intPtr.GetParallel();
151 | AssertRequiresReadOrWriteAccess(
152 | intPtr,
153 | parallel.Decrement);
154 | }
155 | }
156 |
157 | [Test]
158 | public void ParallelAddOffsetsValue()
159 | {
160 | using (NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
161 | Allocator.Temp,
162 | 123))
163 | {
164 | NativePerJobThreadIntPtr.Parallel parallel = intPtr.GetParallel();
165 | parallel.Add(5);
166 |
167 | Assert.That(intPtr.Value, Is.EqualTo(128));
168 |
169 | parallel.Add(-15);
170 |
171 | Assert.That(intPtr.Value, Is.EqualTo(113));
172 | }
173 | }
174 |
175 | [Test]
176 | public void ParallelAddRequiresReadAccess()
177 | {
178 | using (NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
179 | Allocator.Temp))
180 | {
181 | NativePerJobThreadIntPtr.Parallel parallel = intPtr.GetParallel();
182 | AssertRequiresReadOrWriteAccess(
183 | intPtr,
184 | () => parallel.Add(10));
185 | }
186 | }
187 |
188 | [Test]
189 | public void DisposeMakesUnusable()
190 | {
191 | NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
192 | Allocator.Temp);
193 | intPtr.Dispose();
194 | Assert.That(
195 | () => intPtr.Value = 10,
196 | Throws.Exception);
197 | }
198 |
199 | [Test]
200 | public void DisposeRequiresReadAccess()
201 | {
202 | using (NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
203 | Allocator.Temp))
204 | {
205 | AssertRequiresReadOrWriteAccess(
206 | intPtr,
207 | intPtr.Dispose);
208 | }
209 | }
210 |
211 | [Test]
212 | public void IsCreatedOnlyReturnsTrueBeforeDispose()
213 | {
214 | NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
215 | Allocator.Temp);
216 | Assert.That(intPtr.IsCreated, Is.True);
217 |
218 | intPtr.Dispose();
219 |
220 | Assert.That(intPtr.IsCreated, Is.False);
221 | }
222 |
223 | private struct ParallelForTestJob : IJobParallelFor
224 | {
225 | public NativeArray Array;
226 | public NativePerJobThreadIntPtr.Parallel Sum;
227 |
228 | public void Execute(int index)
229 | {
230 | Sum.Add(Array[index]);
231 | }
232 | }
233 |
234 | [Test]
235 | public void ParallelForJobCanUseParallelPtr()
236 | {
237 | using (NativeArray array = new NativeArray(
238 | 3,
239 | Allocator.TempJob))
240 | {
241 | NativeArray arrayCopy = array;
242 | arrayCopy[0] = 10;
243 | arrayCopy[1] = 20;
244 | arrayCopy[2] = 30;
245 |
246 | using (NativePerJobThreadIntPtr sum = new NativePerJobThreadIntPtr(
247 | Allocator.TempJob))
248 | {
249 | ParallelForTestJob job = new ParallelForTestJob
250 | {
251 | Array = array,
252 | Sum = sum.GetParallel()
253 | };
254 | job.Run(array.Length);
255 |
256 | Assert.That(sum.Value, Is.EqualTo(60));
257 | }
258 | }
259 | }
260 |
261 | private struct DeallocateOnJobCompletionJob : IJob
262 | {
263 | [DeallocateOnJobCompletion]
264 | public NativePerJobThreadIntPtr IntPtr;
265 |
266 | public void Execute()
267 | {
268 | }
269 | }
270 |
271 | [Test]
272 | public void CanDeallocateOnJobCompletion()
273 | {
274 | NativePerJobThreadIntPtr intPtr = new NativePerJobThreadIntPtr(
275 | Allocator.TempJob);
276 | var job = new DeallocateOnJobCompletionJob { IntPtr = intPtr };
277 | job.Run();
278 |
279 | Assert.That(
280 | () => intPtr.Value = 10,
281 | Throws.Exception);
282 | }
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestNativePerJobThreadIntPtr.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 3a69b3fed952344689bb907430f8bd40
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestNativePerJobThreadLongPtr.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (c) Jackson Dunstan. See LICENSE.md.
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System;
8 | using Unity.Collections;
9 | using Unity.Jobs;
10 | using NUnit.Framework;
11 |
12 | namespace JacksonDunstan.NativeCollections.Tests
13 | {
14 | ///
15 | /// Unit tests for
16 | ///
17 | public class TestNativePerJobThreadLongPtr
18 | {
19 | private static void AssertRequiresReadOrWriteAccess(
20 | NativePerJobThreadLongPtr longPtr,
21 | Action action)
22 | {
23 | longPtr.TestUseOnlySetAllowReadAndWriteAccess(false);
24 | try
25 | {
26 | Assert.That(
27 | () => action(),
28 | Throws.TypeOf());
29 | }
30 | finally
31 | {
32 | longPtr.TestUseOnlySetAllowReadAndWriteAccess(true);
33 | }
34 | }
35 |
36 | [Test]
37 | public void ConstructorDefaultsValueToZero()
38 | {
39 | using (NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
40 | Allocator.Temp))
41 | {
42 | Assert.That(longPtr.Value, Is.EqualTo(0));
43 | }
44 | }
45 |
46 | [Test]
47 | public void ConstructorSetsInitialValue()
48 | {
49 | using (NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
50 | Allocator.Temp, 123))
51 | {
52 | Assert.That(longPtr.Value, Is.EqualTo(123));
53 | }
54 | }
55 |
56 | [Test]
57 | public void ConstructorThrowsExceptionForInvalidAllocator()
58 | {
59 | Assert.That(
60 | () => new NativePerJobThreadLongPtr(Allocator.None),
61 | Throws.TypeOf());
62 | }
63 |
64 | [Test]
65 | public void GetValueReturnsWhatSetValueSets()
66 | {
67 | using (NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
68 | Allocator.Temp))
69 | {
70 | NativePerJobThreadLongPtr copy = longPtr;
71 | copy.Value = 123;
72 |
73 | Assert.That(longPtr.Value, Is.EqualTo(123));
74 | }
75 | }
76 |
77 | [Test]
78 | public void GetValueRequiresReadAccess()
79 | {
80 | using (NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
81 | Allocator.Temp))
82 | {
83 | long value;
84 | AssertRequiresReadOrWriteAccess(
85 | longPtr,
86 | () => value = longPtr.Value);
87 | }
88 | }
89 |
90 | [Test]
91 | public void SetValueRequiresReadAccess()
92 | {
93 | using (NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
94 | Allocator.Temp))
95 | {
96 | NativePerJobThreadLongPtr copy = longPtr;
97 | AssertRequiresReadOrWriteAccess(
98 | longPtr,
99 | () => copy.Value = 123);
100 | }
101 | }
102 |
103 | [Test]
104 | public void ParallelIncrementIncrementsValue()
105 | {
106 | using (NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
107 | Allocator.Temp,
108 | 123))
109 | {
110 | NativePerJobThreadLongPtr.Parallel parallel = longPtr.GetParallel();
111 | parallel.Increment();
112 |
113 | Assert.That(longPtr.Value, Is.EqualTo(124));
114 | }
115 | }
116 |
117 | [Test]
118 | public void ParallelIncrementRequiresReadAccess()
119 | {
120 | using (NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
121 | Allocator.Temp))
122 | {
123 | NativePerJobThreadLongPtr.Parallel parallel = longPtr.GetParallel();
124 | AssertRequiresReadOrWriteAccess(
125 | longPtr,
126 | parallel.Increment);
127 | }
128 | }
129 |
130 | [Test]
131 | public void ParallelDecrementIncrementsValue()
132 | {
133 | using (NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
134 | Allocator.Temp,
135 | 123))
136 | {
137 | NativePerJobThreadLongPtr.Parallel parallel = longPtr.GetParallel();
138 | parallel.Decrement();
139 |
140 | Assert.That(longPtr.Value, Is.EqualTo(122));
141 | }
142 | }
143 |
144 | [Test]
145 | public void ParallelDecrementRequiresReadAccess()
146 | {
147 | using (NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
148 | Allocator.Temp))
149 | {
150 | NativePerJobThreadLongPtr.Parallel parallel = longPtr.GetParallel();
151 | AssertRequiresReadOrWriteAccess(
152 | longPtr,
153 | parallel.Decrement);
154 | }
155 | }
156 |
157 | [Test]
158 | public void ParallelAddOffsetsValue()
159 | {
160 | using (NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
161 | Allocator.Temp,
162 | 123))
163 | {
164 | NativePerJobThreadLongPtr.Parallel parallel = longPtr.GetParallel();
165 | parallel.Add(5);
166 |
167 | Assert.That(longPtr.Value, Is.EqualTo(128));
168 |
169 | parallel.Add(-15);
170 |
171 | Assert.That(longPtr.Value, Is.EqualTo(113));
172 | }
173 | }
174 |
175 | [Test]
176 | public void ParallelAddRequiresReadAccess()
177 | {
178 | using (NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
179 | Allocator.Temp))
180 | {
181 | NativePerJobThreadLongPtr.Parallel parallel = longPtr.GetParallel();
182 | AssertRequiresReadOrWriteAccess(
183 | longPtr,
184 | () => parallel.Add(10));
185 | }
186 | }
187 |
188 | [Test]
189 | public void DisposeMakesUnusable()
190 | {
191 | NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
192 | Allocator.Temp);
193 | longPtr.Dispose();
194 | Assert.That(
195 | () => longPtr.Value = 10,
196 | Throws.Exception);
197 | }
198 |
199 | [Test]
200 | public void DisposeRequiresReadAccess()
201 | {
202 | using (NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
203 | Allocator.Temp))
204 | {
205 | AssertRequiresReadOrWriteAccess(
206 | longPtr,
207 | longPtr.Dispose);
208 | }
209 | }
210 |
211 | [Test]
212 | public void IsCreatedOnlyReturnsTrueBeforeDispose()
213 | {
214 | NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
215 | Allocator.Temp);
216 | Assert.That(longPtr.IsCreated, Is.True);
217 |
218 | longPtr.Dispose();
219 |
220 | Assert.That(longPtr.IsCreated, Is.False);
221 | }
222 |
223 | private struct ParallelForTestJob : IJobParallelFor
224 | {
225 | public NativeArray Array;
226 | public NativePerJobThreadLongPtr.Parallel Sum;
227 |
228 | public void Execute(int index)
229 | {
230 | Sum.Add(Array[index]);
231 | }
232 | }
233 |
234 | [Test]
235 | public void ParallelForJobCanUseParallelPtr()
236 | {
237 | using (NativeArray array = new NativeArray(
238 | 3,
239 | Allocator.TempJob))
240 | {
241 | NativeArray arrayCopy = array;
242 | arrayCopy[0] = 10;
243 | arrayCopy[1] = 20;
244 | arrayCopy[2] = 30;
245 |
246 | using (NativePerJobThreadLongPtr sum = new NativePerJobThreadLongPtr(
247 | Allocator.TempJob))
248 | {
249 | ParallelForTestJob job = new ParallelForTestJob
250 | {
251 | Array = array,
252 | Sum = sum.GetParallel()
253 | };
254 | job.Run(array.Length);
255 |
256 | Assert.That(sum.Value, Is.EqualTo(60));
257 | }
258 | }
259 | }
260 |
261 | private struct DeallocateOnJobCompletionJob : IJob
262 | {
263 | [DeallocateOnJobCompletion]
264 | public NativePerJobThreadLongPtr LongPtr;
265 |
266 | public void Execute()
267 | {
268 | }
269 | }
270 |
271 | [Test]
272 | public void CanDeallocateOnJobCompletion()
273 | {
274 | NativePerJobThreadLongPtr longPtr = new NativePerJobThreadLongPtr(
275 | Allocator.TempJob);
276 | var job = new DeallocateOnJobCompletionJob { LongPtr = longPtr };
277 | job.Run();
278 |
279 | Assert.That(
280 | () => longPtr.Value = 10,
281 | Throws.Exception);
282 | }
283 | }
284 | }
285 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestNativePerJobThreadLongPtr.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: 5dcf55ce40caa4b3d9fae5f17a75a842
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestSharedDisposable.cs:
--------------------------------------------------------------------------------
1 | //-----------------------------------------------------------------------
2 | //
3 | // Copyright (c) Jackson Dunstan. See LICENSE.md.
4 | //
5 | //-----------------------------------------------------------------------
6 |
7 | using System;
8 | using Unity.Collections;
9 | using Unity.Jobs;
10 | using NUnit.Framework;
11 |
12 | namespace JacksonDunstan.NativeCollections.Tests
13 | {
14 | ///
15 | /// Unit tests for
16 | ///
17 | public class TestSharedDisposable
18 | {
19 | private struct DisposeCallCounter : IDisposable
20 | {
21 | private static int NextId = 1;
22 | public int Id;
23 |
24 | public NativeArray Num;
25 |
26 | public void AssignUniqueId()
27 | {
28 | Id = NextId++;
29 | }
30 |
31 | public void Dispose()
32 | {
33 | Num[0] = Num[0] + 1;
34 | }
35 | }
36 |
37 | private struct TestDisposable : IDisposable
38 | {
39 | public DisposeCallCounter Counter;
40 |
41 | public static TestDisposable Create()
42 | {
43 | return new TestDisposable
44 | {
45 | Counter =
46 | {
47 | Num = new NativeArray(
48 | 1,
49 | Allocator.TempJob)
50 | }
51 | };
52 | }
53 |
54 | public void Dispose()
55 | {
56 | Counter.Num.Dispose();
57 | }
58 | }
59 |
60 | private static void AssertRequiresReadOrWriteAccess(
61 | SharedDisposable shared,
62 | Action action)
63 | {
64 | shared.TestUseOnlySetAllowReadAndWriteAccess(false);
65 | try
66 | {
67 | Assert.That(
68 | () => action(),
69 | Throws.TypeOf());
70 | }
71 | finally
72 | {
73 | shared.TestUseOnlySetAllowReadAndWriteAccess(true);
74 | }
75 | }
76 |
77 | [Test]
78 | public void ConstructorUsesGivenDisposable()
79 | {
80 | using (TestDisposable disposable = TestDisposable.Create())
81 | {
82 | disposable.Counter.AssignUniqueId();
83 | using (var shared = new SharedDisposable(
84 | disposable.Counter,
85 | Allocator.TempJob))
86 | {
87 | Assert.That(shared.Value.Id, Is.EqualTo(disposable.Counter.Id));
88 | }
89 | }
90 | }
91 |
92 | [Test]
93 | public void ShareExtensionUsesGivenDisposable()
94 | {
95 | using (TestDisposable disposable = TestDisposable.Create())
96 | {
97 | disposable.Counter.AssignUniqueId();
98 | using (var shared = disposable.Counter.Share(Allocator.TempJob))
99 | {
100 | Assert.That(shared.Value.Id, Is.EqualTo(disposable.Counter.Id));
101 | }
102 | }
103 | }
104 |
105 | [Test]
106 | public void ConstructorThrowsExceptionForInvalidAllocator()
107 | {
108 | using (TestDisposable disposable = TestDisposable.Create())
109 | {
110 | Assert.That(
111 | () => new SharedDisposable(
112 | disposable.Counter,
113 | Allocator.None),
114 | Throws.TypeOf());
115 | }
116 | }
117 |
118 | [Test]
119 | public void GetValueRequiresReadAccess()
120 | {
121 | using (TestDisposable disposable = TestDisposable.Create())
122 | {
123 | using (var shared = disposable.Counter.Share(Allocator.TempJob))
124 | {
125 | DisposeCallCounter val;
126 | AssertRequiresReadOrWriteAccess(
127 | shared,
128 | () => val = shared.Value);
129 | }
130 | }
131 | }
132 |
133 | [Test]
134 | public void RefIncrementsRefCountAndReturnsCopy()
135 | {
136 | using (TestDisposable disposable = TestDisposable.Create())
137 | {
138 | disposable.Counter.AssignUniqueId();
139 | using (var shared = disposable.Counter.Share(Allocator.TempJob))
140 | {
141 | using (var shared2 = shared.Ref())
142 | {
143 | Assert.That(
144 | shared2.Value.Id,
145 | Is.EqualTo(disposable.Counter.Id));
146 | }
147 | }
148 | Assert.That(disposable.Counter.Num[0], Is.EqualTo(1));
149 | }
150 | }
151 |
152 | [Test]
153 | public void DisposeDisposesDisposableAndMakesUnusable()
154 | {
155 | using (TestDisposable disposable = TestDisposable.Create())
156 | {
157 | var shared = disposable.Counter.Share(Allocator.TempJob);
158 | shared.Dispose();
159 | Assert.That(disposable.Counter.Num[0], Is.EqualTo(1));
160 | DisposeCallCounter val;
161 | Assert.That(
162 | () => val = shared.Value,
163 | Throws.Exception);
164 | }
165 | }
166 |
167 | [Test]
168 | public void DisposeRequiresReadAccess()
169 | {
170 | using (TestDisposable disposable = TestDisposable.Create())
171 | {
172 | using (var shared = disposable.Counter.Share(Allocator.TempJob))
173 | {
174 | AssertRequiresReadOrWriteAccess(
175 | shared,
176 | shared.Dispose);
177 | }
178 | }
179 | }
180 |
181 | [Test]
182 | public void IsCreatedOnlyReturnsTrueBeforeDispose()
183 | {
184 | using (TestDisposable disposable = TestDisposable.Create())
185 | {
186 | var shared = disposable.Counter.Share(Allocator.TempJob);
187 | Assert.That(shared.IsCreated, Is.True);
188 |
189 | shared.Dispose();
190 |
191 | Assert.That(shared.IsCreated, Is.False);
192 | }
193 | }
194 |
195 | private struct DeallocateOnJobCompletionJob : IJob
196 | {
197 | [DeallocateOnJobCompletion]
198 | public SharedDisposable Shared;
199 |
200 | public void Execute()
201 | {
202 | }
203 | }
204 |
205 | [Test]
206 | public void CanDeallocateOnJobCompletion()
207 | {
208 | TestDisposable disposable = TestDisposable.Create();
209 | var shared = disposable.Counter.Share(Allocator.TempJob);
210 | var job = new DeallocateOnJobCompletionJob { Shared = shared };
211 |
212 | job.Run();
213 | Assert.That(
214 | () => disposable.Counter.Num[0],
215 | Throws.Exception);
216 | }
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/JacksonDunstanNativeCollections/Tests/TestSharedDisposable.cs.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: ead27e28daecf48e1a5698a40a538794
3 | MonoImporter:
4 | externalObjects: {}
5 | serializedVersion: 2
6 | defaultReferences: []
7 | executionOrder: 0
8 | icon: {instanceID: 0}
9 | userData:
10 | assetBundleName:
11 | assetBundleVariant:
12 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright 2018 Jackson Dunstan
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/LICENSE.md.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: f6f17df7fb7b12249bc5101c9bd067c3
3 | TextScriptImporter:
4 | externalObjects: {}
5 | userData:
6 | assetBundleName:
7 | assetBundleVariant:
8 |
--------------------------------------------------------------------------------
/PerformanceTests.meta:
--------------------------------------------------------------------------------
1 | fileFormatVersion: 2
2 | guid: dbbb40c0150a04942bb6e3a91a9a720b
3 | folderAsset: yes
4 | DefaultImporter:
5 | externalObjects: {}
6 | userData:
7 | assetBundleName:
8 | assetBundleVariant:
9 |
--------------------------------------------------------------------------------
/PerformanceTests/PerformanceTest.cs:
--------------------------------------------------------------------------------
1 | using UnityEngine;
2 | using Unity.Collections;
3 | using Unity.Jobs;
4 | using Unity.Burst;
5 | using JacksonDunstan.NativeCollections;
6 | using UnityEditor;
7 |
8 | public class PerformanceTest : MonoBehaviour
9 | {
10 | // Add jobs
11 |
12 | [BurstCompile]
13 | struct ListAddJob : IJob
14 | {
15 | public NativeList List;
16 | public int NumElementsToAdd;
17 |
18 | public void Execute()
19 | {
20 | for (int i = 0; i < NumElementsToAdd; ++i)
21 | {
22 | List.Add(i);
23 | }
24 | }
25 | }
26 |
27 | [BurstCompile]
28 | struct LinkedListAddJob : IJob
29 | {
30 | public NativeLinkedList List;
31 | public int NumNodesToInsert;
32 |
33 | public void Execute()
34 | {
35 | NativeLinkedList.Enumerator e = List.Tail;
36 | for (int i = 0; i < NumNodesToInsert; ++i)
37 | {
38 | e = List.InsertAfter(e, i);
39 | }
40 | }
41 | }
42 |
43 | [BurstCompile]
44 | struct ChunkedListAddJob : IJob
45 | {
46 | public NativeChunkedList List;
47 | public int NumElementsToAdd;
48 |
49 | public void Execute()
50 | {
51 | for (int i = 0; i < NumElementsToAdd; ++i)
52 | {
53 | List.Add(i);
54 | }
55 | }
56 | }
57 |
58 | [BurstCompile]
59 | struct HashSetAddJob : IJob
60 | {
61 | public JacksonDunstan.NativeCollections.NativeHashSet Set;
62 | public int NumElementsToAdd;
63 |
64 | public void Execute()
65 | {
66 | for (int i = 0; i < NumElementsToAdd; ++i)
67 | {
68 | Set.TryAdd(i);
69 | }
70 | }
71 | }
72 |
73 | // Iterate jobs
74 |
75 | [BurstCompile]
76 | struct ArrayIterateJob : IJob
77 | {
78 | public NativeArray Array;
79 | public NativeArray Sum;
80 |
81 | public void Execute()
82 | {
83 | for (int i = 0; i < Array.Length; ++i)
84 | {
85 | Sum[0] += Array[i];
86 | }
87 | }
88 | }
89 |
90 | [BurstCompile]
91 | struct ListIterateJob : IJob
92 | {
93 | public NativeList List;
94 | public NativeArray Sum;
95 |
96 | public void Execute()
97 | {
98 | for (int i = 0; i < List.Length; ++i)
99 | {
100 | Sum[0] += List[i];
101 | }
102 | }
103 | }
104 |
105 | [BurstCompile]
106 | struct LinkedListIterateJob : IJob
107 | {
108 | public NativeLinkedList List;
109 | public NativeArray Sum;
110 |
111 | public void Execute()
112 | {
113 | for (int i = 0; i < List.Length; ++i)
114 | {
115 | Sum[0] += List[i];
116 | }
117 | }
118 | }
119 |
120 | [BurstCompile]
121 | struct ChunkedListIterateJob : IJob
122 | {
123 | public NativeChunkedList List;
124 | public NativeArray Sum;
125 |
126 | public void Execute()
127 | {
128 | for (
129 | var chunks = List.Chunks.GetEnumerator();
130 | chunks.MoveNext(); )
131 | {
132 | for (
133 | var chunk = chunks.Current.GetEnumerator();
134 | chunk.MoveNext(); )
135 | {
136 | Sum[0] += chunk.Current;
137 | }
138 | }
139 | }
140 | }
141 |
142 | // Iterate jobs (ParallelFor)
143 |
144 | [BurstCompile]
145 | struct ArrayIterateJobParallelFor : IJobParallelFor
146 | {
147 | public NativeArray Array;
148 | public NativeArray Sum;
149 |
150 | public void Execute(int index)
151 | {
152 | Sum[0] += Array[index];
153 | }
154 | }
155 |
156 | [BurstCompile]
157 | struct LinkedListIterateJobParallelFor : IJobParallelFor
158 | {
159 | public NativeLinkedList List;
160 | public NativeArray Sum;
161 |
162 | public void Execute(int index)
163 | {
164 | Sum[0] += List[index];
165 | }
166 | }
167 |
168 | [BurstCompile]
169 | struct ChunkedListIterateJobParallelFor : IJobParallelForRanged
170 | {
171 | public NativeChunkedList List;
172 | public NativeArray Sum;
173 |
174 | public void Execute(int startIndex, int endIndex)
175 | {
176 | for (
177 | var chunks = List.GetChunksEnumerable(startIndex, endIndex).GetEnumerator();
178 | chunks.MoveNext();)
179 | {
180 | for (
181 | var chunk = chunks.Current.GetEnumerator();
182 | chunk.MoveNext();)
183 | {
184 | Sum[0] += chunk.Current;
185 | }
186 | }
187 | }
188 | }
189 |
190 | // Insert jobs
191 |
192 | [BurstCompile]
193 | struct LinkedListInsertJob : IJob
194 | {
195 | public NativeLinkedList LinkedList;
196 | public int NumElementsToAdd;
197 |
198 | public void Execute()
199 | {
200 | for (int i = 0; i < NumElementsToAdd; ++i)
201 | {
202 | LinkedList.InsertBefore(
203 | LinkedList.GetEnumeratorAtIndex(i / 2),
204 | 1);
205 | }
206 | }
207 | }
208 |
209 | [BurstCompile]
210 | struct ChunkedListInsertJob : IJob
211 | {
212 | public NativeChunkedList List;
213 | public int NumElementsToAdd;
214 |
215 | public void Execute()
216 | {
217 | for (int i = 0; i < NumElementsToAdd; ++i)
218 | {
219 | List.Insert(i / 2, 1);
220 | }
221 | }
222 | }
223 |
224 | // Remove jobs
225 |
226 | [BurstCompile]
227 | struct LinkedListRemoveJob : IJob
228 | {
229 | public NativeLinkedList List;
230 | public int NumElementsToRemove;
231 |
232 | public void Execute()
233 | {
234 | for (int i = List.Length; i > 0; --i)
235 | {
236 | List.Remove(List.GetEnumeratorAtIndex(i / 2));
237 | }
238 | }
239 | }
240 |
241 | [BurstCompile]
242 | struct ChunkedListRemoveJob : IJob
243 | {
244 | public NativeChunkedList List;
245 | public int NumElementsToRemove;
246 |
247 | public void Execute()
248 | {
249 | for (int i = List.Length; i > 0; --i)
250 | {
251 | List.RemoveAt(i / 2);
252 | }
253 | }
254 | }
255 |
256 | [BurstCompile]
257 | struct HashSetRemoveJob : IJob
258 | {
259 | public JacksonDunstan.NativeCollections.NativeHashSet Set;
260 | public int NumElementsToRemove;
261 |
262 | public void Execute()
263 | {
264 | for (int i = 0; i < Set.Length; ++i)
265 | {
266 | Set.Remove(i);
267 | }
268 | }
269 | }
270 |
271 | // NativeIntPtr and NativePerJobThreadIntPtr jobs
272 |
273 | [BurstCompile]
274 | struct NativeIntPtrParallelJob : IJobParallelFor
275 | {
276 | public NativeArray Array;
277 | public NativeIntPtr.Parallel Sum;
278 |
279 | public void Execute(int index)
280 | {
281 | Sum.Add(Array[index]);
282 | }
283 | }
284 |
285 | [BurstCompile]
286 | struct NativePerJobThreadIntPtrParallelJob : IJobParallelFor
287 | {
288 | public NativeArray Array;
289 | public NativePerJobThreadIntPtr.Parallel Sum;
290 |
291 | public void Execute(int index)
292 | {
293 | Sum.Add(Array[index]);
294 | }
295 | }
296 |
297 | // Warm up the job system
298 |
299 | static void WarmUpJobSystem()
300 | {
301 | // Create native collections
302 |
303 | NativeArray sum = new NativeArray(1, Allocator.TempJob);
304 | NativeArray array = new NativeArray(
305 | 4,
306 | Allocator.TempJob);
307 | NativeList list = new NativeList(
308 | 4,
309 | Allocator.TempJob);
310 | NativeLinkedList linkedList = new NativeLinkedList(
311 | 4,
312 | Allocator.TempJob);
313 | NativeChunkedList chunkedList = new NativeChunkedList(
314 | 4,
315 | 4,
316 | Allocator.TempJob);
317 | var hashSet = new JacksonDunstan.NativeCollections.NativeHashSet(
318 | 4,
319 | Allocator.TempJob);
320 | NativeIntPtr nativeIntPtr = new NativeIntPtr(Allocator.TempJob);
321 | NativePerJobThreadIntPtr nativePerJobThreadIntPtr = new NativePerJobThreadIntPtr(
322 | Allocator.TempJob);
323 |
324 | // Create jobs
325 |
326 | ListAddJob listAddJob = new ListAddJob
327 | {
328 | List = list
329 | };
330 | LinkedListAddJob linkedListAddJob = new LinkedListAddJob
331 | {
332 | List = linkedList
333 | };
334 | ChunkedListAddJob chunkedListAddJob = new ChunkedListAddJob
335 | {
336 | List = chunkedList
337 | };
338 | HashSetAddJob hashSetAddJob = new HashSetAddJob
339 | {
340 | Set = hashSet
341 | };
342 | ArrayIterateJob arrayIterateJob = new ArrayIterateJob
343 | {
344 | Array = array,
345 | Sum = sum
346 | };
347 | ListIterateJob listIterateJob = new ListIterateJob
348 | {
349 | List = list,
350 | Sum = sum
351 | };
352 | LinkedListIterateJob linkedListIterateJob = new LinkedListIterateJob
353 | {
354 | List = linkedList,
355 | Sum = sum
356 | };
357 | ChunkedListIterateJob chunkedListIterateJob = new ChunkedListIterateJob
358 | {
359 | List = chunkedList,
360 | Sum = sum
361 | };
362 | ArrayIterateJobParallelFor arrayIterateJobParallelFor = new ArrayIterateJobParallelFor
363 | {
364 | Array = array,
365 | Sum = sum
366 | };
367 | LinkedListIterateJobParallelFor linkedListIterateJobParallelFor = new LinkedListIterateJobParallelFor
368 | {
369 | List = linkedList,
370 | Sum = sum
371 | };
372 | ChunkedListIterateJobParallelFor chunkedListIterateJobParallelFor = new ChunkedListIterateJobParallelFor
373 | {
374 | List = chunkedList,
375 | Sum = sum
376 | };
377 | LinkedListInsertJob linkedListInsertJob = new LinkedListInsertJob
378 | {
379 | LinkedList = linkedList
380 | };
381 | ChunkedListInsertJob chunkedListInsertJob = new ChunkedListInsertJob
382 | {
383 | List = chunkedList
384 | };
385 | LinkedListRemoveJob linkedListRemoveJob = new LinkedListRemoveJob
386 | {
387 | List = linkedList
388 | };
389 | ChunkedListRemoveJob chunkedListRemoveJob = new ChunkedListRemoveJob
390 | {
391 | List = chunkedList
392 | };
393 | HashSetRemoveJob hashSetRemoveJob = new HashSetRemoveJob
394 | {
395 | Set = hashSet
396 | };
397 | NativeIntPtrParallelJob nativeIntPtrParallelJob = new NativeIntPtrParallelJob
398 | {
399 | Array = array,
400 | Sum = nativeIntPtr.GetParallel()
401 | };
402 | NativePerJobThreadIntPtrParallelJob nativePerJobThreadIntPtrParallelJob = new NativePerJobThreadIntPtrParallelJob
403 | {
404 | Array = array,
405 | Sum = nativePerJobThreadIntPtr.GetParallel()
406 | };
407 |
408 | // Run jobs
409 |
410 | listAddJob.Run();
411 | linkedListAddJob.Run();
412 | chunkedListAddJob.Run();
413 | hashSetAddJob.Run();
414 | arrayIterateJob.Run();
415 | listIterateJob.Run();
416 | linkedListIterateJob.Run();
417 | chunkedListIterateJob.Run();
418 | arrayIterateJobParallelFor.Run(array.Length);
419 | linkedListIterateJobParallelFor.Run(linkedList.Length);
420 | chunkedListIterateJobParallelFor.RunRanged(chunkedList.Length);
421 | list.Clear();
422 | linkedList.Clear();
423 | chunkedList.Clear();
424 | hashSet.Clear();
425 | linkedListInsertJob.Run();
426 | chunkedListInsertJob.Run();
427 | linkedListRemoveJob.Run();
428 | chunkedListRemoveJob.Run();
429 | hashSetRemoveJob.Run();
430 | nativeIntPtrParallelJob.Run(array.Length);
431 | nativePerJobThreadIntPtrParallelJob.Run(array.Length);
432 |
433 | // Dispose native collections
434 |
435 | sum.Dispose();
436 | array.Dispose();
437 | list.Dispose();
438 | linkedList.Dispose();
439 | chunkedList.Dispose();
440 | hashSet.Dispose();
441 | nativeIntPtr.Dispose();
442 | nativePerJobThreadIntPtr.Dispose();
443 | }
444 |
445 | // Run the test
446 |
447 | void Start()
448 | {
449 | WarmUpJobSystem();
450 |
451 | const int size =
452 | #if UNITY_EDITOR
453 | 1000
454 | #else
455 | 10000
456 | #endif
457 | ;
458 |
459 | const int chunkSize = 1024;
460 | const int numElementsPerChunk = chunkSize / sizeof(int);
461 |
462 | // Create native collections
463 |
464 | NativeArray sum = new NativeArray(1, Allocator.TempJob);
465 | NativeArray array = new NativeArray