A game about forced loneliness, made by TACStudios
1using System;
2using System.Diagnostics;
3using System.Runtime.CompilerServices;
4using System.Runtime.InteropServices;
5using Unity.Burst;
6using Unity.Collections.LowLevel.Unsafe;
7using Unity.Jobs;
8using static Unity.Collections.AllocatorManager;
9
10namespace Unity.Collections
11{
12 /// <summary>
13 /// An arbitrarily-sized array of bits.
14 /// </summary>
15 /// <remarks>
16 /// The number of allocated bytes is always a multiple of 8. For example, a 65-bit array could fit in 9 bytes, but its allocation is actually 16 bytes.
17 /// </remarks>
18 [StructLayout(LayoutKind.Sequential)]
19 [NativeContainer]
20 [DebuggerDisplay("Length = {Length}, IsCreated = {IsCreated}")]
21 [GenerateTestsForBurstCompatibility]
22 public unsafe struct NativeBitArray
23 : INativeDisposable
24 {
25#if ENABLE_UNITY_COLLECTIONS_CHECKS
26 internal AtomicSafetyHandle m_Safety;
27 static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<NativeBitArray>();
28#endif
29 [NativeDisableUnsafePtrRestriction]
30 internal UnsafeBitArray* m_BitArray;
31 internal AllocatorHandle m_Allocator;
32
33 /// <summary>
34 /// Initializes and returns an instance of NativeBitArray.
35 /// </summary>
36 /// <param name="numBits">The number of bits.</param>
37 /// <param name="allocator">The allocator to use.</param>
38 /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
39 public NativeBitArray(int numBits, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory)
40 {
41 CollectionHelper.CheckAllocator(allocator);
42#if ENABLE_UNITY_COLLECTIONS_CHECKS
43 m_Safety = CollectionHelper.CreateSafetyHandle(allocator);
44 CollectionHelper.SetStaticSafetyId(ref m_Safety, ref s_staticSafetyId.Data, "Unity.Collections.NativeBitArray");
45#endif
46 m_BitArray = UnsafeBitArray.Alloc(allocator);
47 m_Allocator = allocator;
48 * m_BitArray = new UnsafeBitArray(numBits, allocator, options);
49 }
50
51 /// <summary>
52 /// Whether this array has been allocated (and not yet deallocated).
53 /// </summary>
54 /// <value>True if this array has been allocated (and not yet deallocated).</value>
55 public readonly bool IsCreated => m_BitArray != null && m_BitArray->IsCreated;
56
57 /// <summary>
58 /// Whether the container is empty.
59 /// </summary>
60 /// <value>True if the container is empty or the container has not been constructed.</value>
61 public readonly bool IsEmpty => !IsCreated || Length == 0;
62
63 /// <summary>
64 /// Sets the length, expanding the capacity if necessary.
65 /// </summary>
66 /// <param name="numBits">The new length in bits.</param>
67 /// <param name="options">Whether newly allocated data should be zeroed out.</param>
68 public void Resize(int numBits, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
69 {
70#if ENABLE_UNITY_COLLECTIONS_CHECKS
71 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
72#endif
73 m_BitArray->Resize(numBits, options);
74 }
75
76 /// <summary>
77 /// Sets the capacity.
78 /// </summary>
79 /// <param name="capacityInBits">The new capacity.</param>
80 public void SetCapacity(int capacityInBits)
81 {
82#if ENABLE_UNITY_COLLECTIONS_CHECKS
83 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
84#endif
85 m_BitArray->SetCapacity(capacityInBits);
86 }
87
88 /// <summary>
89 /// Sets the capacity to match what it would be if it had been originally initialized with all its entries.
90 /// </summary>
91 public void TrimExcess()
92 {
93#if ENABLE_UNITY_COLLECTIONS_CHECKS
94 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
95#endif
96 m_BitArray->TrimExcess();
97 }
98
99 /// <summary>
100 /// Releases all resources (memory and safety handles).
101 /// </summary>
102 public void Dispose()
103 {
104#if ENABLE_UNITY_COLLECTIONS_CHECKS
105 if (!AtomicSafetyHandle.IsDefaultValue(m_Safety))
106 {
107 AtomicSafetyHandle.CheckExistsAndThrow(m_Safety);
108 }
109#endif
110 if (!IsCreated)
111 {
112 return;
113 }
114
115#if ENABLE_UNITY_COLLECTIONS_CHECKS
116 CollectionHelper.DisposeSafetyHandle(ref m_Safety);
117#endif
118 UnsafeBitArray.Free(m_BitArray, m_Allocator);
119 m_BitArray = null;
120 m_Allocator = Invalid;
121 }
122
123 /// <summary>
124 /// Creates and schedules a job that will dispose this array.
125 /// </summary>
126 /// <param name="inputDeps">The handle of a job which the new job will depend upon.</param>
127 /// <returns>The handle of a new job that will dispose this array. The new job depends upon inputDeps.</returns>
128 public JobHandle Dispose(JobHandle inputDeps)
129 {
130#if ENABLE_UNITY_COLLECTIONS_CHECKS
131 if (!AtomicSafetyHandle.IsDefaultValue(m_Safety))
132 {
133 AtomicSafetyHandle.CheckExistsAndThrow(m_Safety);
134 }
135#endif
136 if (!IsCreated)
137 {
138 return inputDeps;
139 }
140
141#if ENABLE_UNITY_COLLECTIONS_CHECKS
142 var jobHandle = new NativeBitArrayDisposeJob { Data = new NativeBitArrayDispose { m_BitArrayData = m_BitArray, m_Allocator = m_Allocator, m_Safety = m_Safety } }.Schedule(inputDeps);
143 AtomicSafetyHandle.Release(m_Safety);
144#else
145 var jobHandle = new NativeBitArrayDisposeJob { Data = new NativeBitArrayDispose { m_BitArrayData = m_BitArray, m_Allocator = m_Allocator } }.Schedule(inputDeps);
146#endif
147 m_BitArray = null;
148 m_Allocator = Invalid;
149
150 return jobHandle;
151
152 }
153
154 /// <summary>
155 /// Returns the number of bits.
156 /// </summary>
157 /// <value>The number of bits.</value>
158 public readonly int Length
159 {
160 [MethodImpl(MethodImplOptions.AggressiveInlining)]
161 get
162 {
163 CheckRead();
164 return CollectionHelper.AssumePositive(m_BitArray->Length);
165 }
166 }
167
168 /// <summary>
169 /// Returns the capacity number of bits.
170 /// </summary>
171 /// <value>The capacity number of bits.</value>
172 public readonly int Capacity
173 {
174 [MethodImpl(MethodImplOptions.AggressiveInlining)]
175 get
176 {
177 CheckRead();
178 return CollectionHelper.AssumePositive(m_BitArray->Capacity);
179 }
180 }
181
182 /// <summary>
183 /// Sets all the bits to 0.
184 /// </summary>
185 public void Clear()
186 {
187 CheckWrite();
188 m_BitArray->Clear();
189 }
190
191 /// <summary>
192 /// Returns a native array that aliases the content of this array.
193 /// </summary>
194 /// <typeparam name="T">The type of elements in the aliased array.</typeparam>
195 /// <exception cref="InvalidOperationException">Thrown if the number of bits in this array
196 /// is not evenly divisible by the size of T in bits (`sizeof(T) * 8`).</exception>
197 /// <returns>A native array that aliases the content of this array.</returns>
198 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
199 public NativeArray<T> AsNativeArray<T>() where T : unmanaged
200 {
201 CheckReadBounds<T>();
202
203 var bitsPerElement = UnsafeUtility.SizeOf<T>() * 8;
204 var length = m_BitArray->Length / bitsPerElement;
205
206 var array = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T>(m_BitArray->Ptr, length, Allocator.None);
207#if ENABLE_UNITY_COLLECTIONS_CHECKS
208 AtomicSafetyHandle.UseSecondaryVersion(ref m_Safety);
209 NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref array, m_Safety);
210#endif
211 return array;
212 }
213
214 /// <summary>
215 /// Sets the bit at an index to 0 or 1.
216 /// </summary>
217 /// <param name="pos">Index of the bit to set.</param>
218 /// <param name="value">True for 1, false for 0.</param>
219 public void Set(int pos, bool value)
220 {
221 CheckWrite();
222 m_BitArray->Set(pos, value);
223 }
224
225 /// <summary>
226 /// Sets a range of bits to 0 or 1.
227 /// </summary>
228 /// <remarks>
229 /// The range runs from index `pos` up to (but not including) `pos + numBits`.
230 /// No exception is thrown if `pos + numBits` exceeds the length.
231 /// </remarks>
232 /// <param name="pos">Index of the first bit to set.</param>
233 /// <param name="value">True for 1, false for 0.</param>
234 /// <param name="numBits">Number of bits to set.</param>
235 /// <exception cref="ArgumentException">Thrown if pos is out of bounds or if numBits is less than 1.</exception>
236 public void SetBits(int pos, bool value, int numBits)
237 {
238 CheckWrite();
239 m_BitArray->SetBits(pos, value, numBits);
240 }
241
242 /// <summary>
243 /// Copies bits of a ulong to bits in this array.
244 /// </summary>
245 /// <remarks>
246 /// The destination bits in this array run from index pos up to (but not including) `pos + numBits`.
247 /// No exception is thrown if `pos + numBits` exceeds the length.
248 ///
249 /// The lowest bit of the ulong is copied to the first destination bit; the second-lowest bit of the ulong is
250 /// copied to the second destination bit; and so forth.
251 /// </remarks>
252 /// <param name="pos">Index of the first bit to set.</param>
253 /// <param name="value">Unsigned long from which to copy bits.</param>
254 /// <param name="numBits">Number of bits to set (must be between 1 and 64).</param>
255 /// <exception cref="ArgumentException">Thrown if pos is out of bounds or if numBits is not between 1 and 64.</exception>
256 public void SetBits(int pos, ulong value, int numBits = 1)
257 {
258 CheckWrite();
259 m_BitArray->SetBits(pos, value, numBits);
260 }
261
262 /// <summary>
263 /// Returns a ulong which has bits copied from this array.
264 /// </summary>
265 /// <remarks>
266 /// The source bits in this array run from index pos up to (but not including) `pos + numBits`.
267 /// No exception is thrown if `pos + numBits` exceeds the length.
268 ///
269 /// The first source bit is copied to the lowest bit of the ulong; the second source bit is copied to the second-lowest bit of the ulong; and so forth. Any remaining bits in the ulong will be 0.
270 /// </remarks>
271 /// <param name="pos">Index of the first bit to get.</param>
272 /// <param name="numBits">Number of bits to get (must be between 1 and 64).</param>
273 /// <exception cref="ArgumentException">Thrown if pos is out of bounds or if numBits is not between 1 and 64.</exception>
274 /// <returns>A ulong which has bits copied from this array.</returns>
275 public ulong GetBits(int pos, int numBits = 1)
276 {
277 CheckRead();
278 return m_BitArray->GetBits(pos, numBits);
279 }
280
281 /// <summary>
282 /// Returns true if the bit at an index is 1.
283 /// </summary>
284 /// <param name="pos">Index of the bit to test.</param>
285 /// <returns>True if the bit at the index is 1.</returns>
286 /// <exception cref="ArgumentException">Thrown if `pos` is out of bounds.</exception>
287 public bool IsSet(int pos)
288 {
289 CheckRead();
290 return m_BitArray->IsSet(pos);
291 }
292
293 /// <summary>
294 /// Copies a range of bits from this array to another range in this array.
295 /// </summary>
296 /// <remarks>
297 /// The bits to copy run from index `srcPos` up to (but not including) `srcPos + numBits`.
298 /// The bits to set run from index `dstPos` up to (but not including) `dstPos + numBits`.
299 ///
300 /// The ranges may overlap, but the result in the overlapping region is undefined.
301 /// </remarks>
302 /// <param name="dstPos">Index of the first bit to set.</param>
303 /// <param name="srcPos">Index of the first bit to copy.</param>
304 /// <param name="numBits">Number of bits to copy.</param>
305 /// <exception cref="ArgumentException">Thrown if either `dstPos + numBits` or `srcPos + numBits` exceed the length of this array.</exception>
306 public void Copy(int dstPos, int srcPos, int numBits)
307 {
308 CheckWrite();
309 m_BitArray->Copy(dstPos, srcPos, numBits);
310 }
311
312 /// <summary>
313 /// Copies a range of bits from an array to a range of bits in this array.
314 /// </summary>
315 /// <remarks>
316 /// The bits to copy in the source array run from index srcPos up to (but not including) `srcPos + numBits`.
317 /// The bits to set in the destination array run from index dstPos up to (but not including) `dstPos + numBits`.
318 ///
319 /// When the source and destination are the same array, the ranges may still overlap, but the result in the overlapping region is undefined.
320 /// </remarks>
321 /// <param name="dstPos">Index of the first bit to set.</param>
322 /// <param name="srcBitArray">The source array.</param>
323 /// <param name="srcPos">Index of the first bit to copy.</param>
324 /// <param name="numBits">The number of bits to copy.</param>
325 /// <exception cref="ArgumentException">Thrown if either `dstPos + numBits` or `srcBitArray + numBits` exceed the length of this array.</exception>
326 public void Copy(int dstPos, ref NativeBitArray srcBitArray, int srcPos, int numBits)
327 {
328#if ENABLE_UNITY_COLLECTIONS_CHECKS
329 AtomicSafetyHandle.CheckReadAndThrow(srcBitArray.m_Safety);
330#endif
331 CheckWrite();
332 m_BitArray->Copy(dstPos, ref *srcBitArray.m_BitArray, srcPos, numBits);
333 }
334
335 /// <summary>
336 /// Finds the first length-*N* contiguous sequence of 0 bits in this bit array.
337 /// </summary>
338 /// <param name="pos">Index at which to start searching.</param>
339 /// <param name="numBits">Number of contiguous 0 bits to look for.</param>
340 /// <returns>The index in this array where the sequence is found. The index will be greater than or equal to `pos`.
341 /// Returns -1 if no occurrence is found.</returns>
342 public int Find(int pos, int numBits)
343 {
344 CheckRead();
345 return m_BitArray->Find(pos, numBits);
346 }
347
348 /// <summary>
349 /// Finds the first length-*N* contiguous sequence of 0 bits in this bit array. Searches only a subsection.
350 /// </summary>
351 /// <param name="pos">Index at which to start searching.</param>
352 /// <param name="numBits">Number of contiguous 0 bits to look for.</param>
353 /// <param name="count">Number of bits to search.</param>
354 /// <returns>The index in this array where the sequence is found. The index will be greater than or equal to `pos` but less than `pos + count`.
355 /// Returns -1 if no occurrence is found.</returns>
356 public int Find(int pos, int count, int numBits)
357 {
358 CheckRead();
359 return m_BitArray->Find(pos, count, numBits);
360 }
361
362 /// <summary>
363 /// Returns true if none of the bits in a range are 1 (*i.e.* all bits in the range are 0).
364 /// </summary>
365 /// <param name="pos">Index of the bit at which to start searching.</param>
366 /// <param name="numBits">Number of bits to test. Defaults to 1.</param>
367 /// <returns>Returns true if none of the bits in range `pos` up to (but not including) `pos + numBits` are 1.</returns>
368 /// <exception cref="ArgumentException">Thrown if `pos` is out of bounds or `numBits` is less than 1.</exception>
369 public bool TestNone(int pos, int numBits = 1)
370 {
371 CheckRead();
372 return m_BitArray->TestNone(pos, numBits);
373 }
374
375 /// <summary>
376 /// Returns true if at least one of the bits in a range is 1.
377 /// </summary>
378 /// <param name="pos">Index of the bit at which to start searching.</param>
379 /// <param name="numBits">Number of bits to test. Defaults to 1.</param>
380 /// <returns>True if one ore more of the bits in range `pos` up to (but not including) `pos + numBits` are 1.</returns>
381 /// <exception cref="ArgumentException">Thrown if `pos` is out of bounds or `numBits` is less than 1.</exception>
382 public bool TestAny(int pos, int numBits = 1)
383 {
384 CheckRead();
385 return m_BitArray->TestAny(pos, numBits);
386 }
387
388 /// <summary>
389 /// Returns true if all of the bits in a range are 1.
390 /// </summary>
391 /// <param name="pos">Index of the bit at which to start searching.</param>
392 /// <param name="numBits">Number of bits to test. Defaults to 1.</param>
393 /// <returns>True if all of the bits in range `pos` up to (but not including) `pos + numBits` are 1.</returns>
394 /// <exception cref="ArgumentException">Thrown if `pos` is out of bounds or `numBits` is less than 1.</exception>
395 public bool TestAll(int pos, int numBits = 1)
396 {
397 CheckRead();
398 return m_BitArray->TestAll(pos, numBits);
399 }
400
401 /// <summary>
402 /// Returns the number of bits in a range that are 1.
403 /// </summary>
404 /// <param name="pos">Index of the bit at which to start searching.</param>
405 /// <param name="numBits">Number of bits to test. Defaults to 1.</param>
406 /// <returns>The number of bits in a range of bits that are 1.</returns>
407 /// <exception cref="ArgumentException">Thrown if `pos` is out of bounds or `numBits` is less than 1.</exception>
408 public int CountBits(int pos, int numBits = 1)
409 {
410 CheckRead();
411 return m_BitArray->CountBits(pos, numBits);
412 }
413
414 /// <summary>
415 /// Returns a readonly version of this NativeBitArray instance.
416 /// </summary>
417 /// <remarks>ReadOnly containers point to the same underlying data as the NativeBitArray it is made from.</remarks>
418 /// <returns>ReadOnly instance for this.</returns>
419 public ReadOnly AsReadOnly()
420 {
421 return new ReadOnly(ref this);
422 }
423
424 /// <summary>
425 /// A read-only alias for the value of a UnsafeBitArray. Does not have its own allocated storage.
426 /// </summary>
427 [NativeContainer]
428 [NativeContainerIsReadOnly]
429 public struct ReadOnly
430 {
431#if ENABLE_UNITY_COLLECTIONS_CHECKS
432 AtomicSafetyHandle m_Safety;
433 internal static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<ReadOnly>();
434#endif
435 [NativeDisableUnsafePtrRestriction]
436 internal UnsafeBitArray.ReadOnly m_BitArray;
437
438 /// <summary>
439 /// Whether this array has been allocated (and not yet deallocated).
440 /// </summary>
441 /// <value>True if this array has been allocated (and not yet deallocated).</value>
442 public readonly bool IsCreated => m_BitArray.IsCreated;
443
444 /// <summary>
445 /// Whether the container is empty.
446 /// </summary>
447 /// <value>True if the container is empty or the container has not been constructed.</value>
448 public readonly bool IsEmpty => m_BitArray.IsEmpty;
449
450 internal ReadOnly(ref NativeBitArray data)
451 {
452 m_BitArray = data.m_BitArray->AsReadOnly();
453#if ENABLE_UNITY_COLLECTIONS_CHECKS
454 m_Safety = data.m_Safety;
455 CollectionHelper.SetStaticSafetyId<ReadOnly>(ref m_Safety, ref s_staticSafetyId.Data);
456#endif
457 }
458
459 /// <summary>
460 /// Returns the number of bits.
461 /// </summary>
462 /// <value>The number of bits.</value>
463 public readonly int Length
464 {
465 get
466 {
467 CheckRead();
468 return CollectionHelper.AssumePositive(m_BitArray.Length);
469 }
470 }
471
472 /// <summary>
473 /// Returns a ulong which has bits copied from this array.
474 /// </summary>
475 /// <remarks>
476 /// The source bits in this array run from index pos up to (but not including) `pos + numBits`.
477 /// No exception is thrown if `pos + numBits` exceeds the length.
478 ///
479 /// The first source bit is copied to the lowest bit of the ulong; the second source bit is copied to the second-lowest bit of the ulong; and so forth. Any remaining bits in the ulong will be 0.
480 /// </remarks>
481 /// <param name="pos">Index of the first bit to get.</param>
482 /// <param name="numBits">Number of bits to get (must be between 1 and 64).</param>
483 /// <exception cref="ArgumentException">Thrown if pos is out of bounds or if numBits is not between 1 and 64.</exception>
484 /// <returns>A ulong which has bits copied from this array.</returns>
485 public readonly ulong GetBits(int pos, int numBits = 1)
486 {
487 CheckRead();
488 return m_BitArray.GetBits(pos, numBits);
489 }
490
491 /// <summary>
492 /// Returns true if the bit at an index is 1.
493 /// </summary>
494 /// <param name="pos">Index of the bit to test.</param>
495 /// <returns>True if the bit at the index is 1.</returns>
496 /// <exception cref="ArgumentException">Thrown if `pos` is out of bounds.</exception>
497 public readonly bool IsSet(int pos)
498 {
499 CheckRead();
500 return m_BitArray.IsSet(pos);
501 }
502
503 /// <summary>
504 /// Finds the first length-*N* contiguous sequence of 0 bits in this bit array.
505 /// </summary>
506 /// <param name="pos">Index at which to start searching.</param>
507 /// <param name="numBits">Number of contiguous 0 bits to look for.</param>
508 /// <returns>The index in this array where the sequence is found. The index will be greater than or equal to `pos`.
509 /// Returns -1 if no occurrence is found.</returns>
510 public readonly int Find(int pos, int numBits)
511 {
512 CheckRead();
513 return m_BitArray.Find(pos, numBits);
514 }
515
516 /// <summary>
517 /// Finds the first length-*N* contiguous sequence of 0 bits in this bit array. Searches only a subsection.
518 /// </summary>
519 /// <param name="pos">Index at which to start searching.</param>
520 /// <param name="numBits">Number of contiguous 0 bits to look for.</param>
521 /// <param name="count">Number of bits to search.</param>
522 /// <returns>The index in this array where the sequence is found. The index will be greater than or equal to `pos` but less than `pos + count`.
523 /// Returns -1 if no occurrence is found.</returns>
524 public readonly int Find(int pos, int count, int numBits)
525 {
526 CheckRead();
527 return m_BitArray.Find(pos, count, numBits);
528 }
529
530 /// <summary>
531 /// Returns true if none of the bits in a range are 1 (*i.e.* all bits in the range are 0).
532 /// </summary>
533 /// <param name="pos">Index of the bit at which to start searching.</param>
534 /// <param name="numBits">Number of bits to test. Defaults to 1.</param>
535 /// <returns>Returns true if none of the bits in range `pos` up to (but not including) `pos + numBits` are 1.</returns>
536 /// <exception cref="ArgumentException">Thrown if `pos` is out of bounds or `numBits` is less than 1.</exception>
537 public readonly bool TestNone(int pos, int numBits = 1)
538 {
539 CheckRead();
540 return m_BitArray.TestNone(pos, numBits);
541 }
542
543 /// <summary>
544 /// Returns true if at least one of the bits in a range is 1.
545 /// </summary>
546 /// <param name="pos">Index of the bit at which to start searching.</param>
547 /// <param name="numBits">Number of bits to test. Defaults to 1.</param>
548 /// <returns>True if one ore more of the bits in range `pos` up to (but not including) `pos + numBits` are 1.</returns>
549 /// <exception cref="ArgumentException">Thrown if `pos` is out of bounds or `numBits` is less than 1.</exception>
550 public readonly bool TestAny(int pos, int numBits = 1)
551 {
552 CheckRead();
553 return m_BitArray.TestAny(pos, numBits);
554 }
555
556 /// <summary>
557 /// Returns true if all of the bits in a range are 1.
558 /// </summary>
559 /// <param name="pos">Index of the bit at which to start searching.</param>
560 /// <param name="numBits">Number of bits to test. Defaults to 1.</param>
561 /// <returns>True if all of the bits in range `pos` up to (but not including) `pos + numBits` are 1.</returns>
562 /// <exception cref="ArgumentException">Thrown if `pos` is out of bounds or `numBits` is less than 1.</exception>
563 public readonly bool TestAll(int pos, int numBits = 1)
564 {
565 CheckRead();
566 return m_BitArray.TestAll(pos, numBits);
567 }
568
569 /// <summary>
570 /// Returns the number of bits in a range that are 1.
571 /// </summary>
572 /// <param name="pos">Index of the bit at which to start searching.</param>
573 /// <param name="numBits">Number of bits to test. Defaults to 1.</param>
574 /// <returns>The number of bits in a range of bits that are 1.</returns>
575 /// <exception cref="ArgumentException">Thrown if `pos` is out of bounds or `numBits` is less than 1.</exception>
576 public readonly int CountBits(int pos, int numBits = 1)
577 {
578 CheckRead();
579 return m_BitArray.CountBits(pos, numBits);
580 }
581
582 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
583 readonly void CheckRead()
584 {
585#if ENABLE_UNITY_COLLECTIONS_CHECKS
586 AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
587#endif
588 }
589 }
590
591 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
592 readonly void CheckRead()
593 {
594#if ENABLE_UNITY_COLLECTIONS_CHECKS
595 AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
596#endif
597 }
598
599 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
600 void CheckReadBounds<T>() where T : unmanaged
601 {
602 CheckRead();
603
604 var bitsPerElement = UnsafeUtility.SizeOf<T>() * 8;
605 var length = m_BitArray->Length / bitsPerElement;
606
607 if (length == 0)
608 {
609 throw new InvalidOperationException($"Number of bits in the NativeBitArray {m_BitArray->Length} is not sufficient to cast to NativeArray<T> {UnsafeUtility.SizeOf<T>() * 8}.");
610 }
611 else if (m_BitArray->Length != bitsPerElement* length)
612 {
613 throw new InvalidOperationException($"Number of bits in the NativeBitArray {m_BitArray->Length} couldn't hold multiple of T {UnsafeUtility.SizeOf<T>()}. Output array would be truncated.");
614 }
615 }
616
617 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
618 void CheckWrite()
619 {
620#if ENABLE_UNITY_COLLECTIONS_CHECKS
621 AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
622#endif
623 }
624 }
625
626 [NativeContainer]
627 [GenerateTestsForBurstCompatibility]
628 internal unsafe struct NativeBitArrayDispose
629 {
630 [NativeDisableUnsafePtrRestriction]
631 public UnsafeBitArray* m_BitArrayData;
632 public AllocatorHandle m_Allocator;
633
634#if ENABLE_UNITY_COLLECTIONS_CHECKS
635 public AtomicSafetyHandle m_Safety;
636#endif
637
638 public void Dispose()
639 {
640 UnsafeBitArray.Free(m_BitArrayData, m_Allocator);
641 }
642 }
643
644 [BurstCompile]
645 internal unsafe struct NativeBitArrayDisposeJob : IJob
646 {
647 public NativeBitArrayDispose Data;
648
649 public void Execute()
650 {
651 Data.Dispose();
652 }
653 }
654}
655
656namespace Unity.Collections.LowLevel.Unsafe
657{
658 /// <summary>
659 /// Unsafe helper methods for NativeBitArray.
660 /// </summary>
661 [GenerateTestsForBurstCompatibility]
662 public static class NativeBitArrayUnsafeUtility
663 {
664#if ENABLE_UNITY_COLLECTIONS_CHECKS
665 /// <summary>
666 /// Returns an array's atomic safety handle.
667 /// </summary>
668 /// <param name="container">Array from which to get an AtomicSafetyHandle.</param>
669 /// <returns>This array's atomic safety handle.</returns>
670 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS", CompileTarget = GenerateTestsForBurstCompatibilityAttribute.BurstCompatibleCompileTarget.Editor)]
671 public static AtomicSafetyHandle GetAtomicSafetyHandle(in NativeBitArray container)
672 {
673 return container.m_Safety;
674 }
675
676 /// <summary>
677 /// Sets an array's atomic safety handle.
678 /// </summary>
679 /// <param name="container">Array which the AtomicSafetyHandle is for.</param>
680 /// <param name="safety">Atomic safety handle for this array.</param>
681 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS", CompileTarget = GenerateTestsForBurstCompatibilityAttribute.BurstCompatibleCompileTarget.Editor)]
682 public static void SetAtomicSafetyHandle(ref NativeBitArray container, AtomicSafetyHandle safety)
683 {
684 container.m_Safety = safety;
685 }
686#endif
687
688 /// <summary>
689 /// Returns a bit array with content aliasing a buffer.
690 /// </summary>
691 /// <param name="ptr">A buffer.</param>
692 /// <param name="sizeInBytes">Size of the buffer in bytes. Must be a multiple of 8.</param>
693 /// <param name="allocator">The allocator that was used to create the buffer.</param>
694 /// <returns>A bit array with content aliasing a buffer.</returns>
695 public static unsafe NativeBitArray ConvertExistingDataToNativeBitArray(void* ptr, int sizeInBytes, AllocatorManager.AllocatorHandle allocator)
696 {
697 var bitArray = UnsafeBitArray.Alloc(Allocator.Persistent);
698 *bitArray = new UnsafeBitArray(ptr, sizeInBytes, allocator);
699
700 return new NativeBitArray
701 {
702 m_BitArray = bitArray,
703 m_Allocator = Allocator.Persistent,
704 };
705 }
706 }
707}