< Summary

Class:GDX.Collections.Generic.ConcurrentCircularBuffer[T]
Assembly:GDX
File(s):./Packages/com.dotbunny.gdx/GDX/Collections/Generic/ConcurrentCircularBuffer.cs
Covered lines:29
Uncovered lines:126
Coverable lines:155
Total lines:341
Line coverage:18.7% (29 of 155)
Covered branches:0
Total branches:0
Covered methods:4
Total methods:15
Method coverage:26.6% (4 of 15)

Coverage History

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
ConcurrentCircularBuffer(...)0%4.14081.82%
ConcurrentCircularBuffer(...)0%42600%
Add(...)0%110100%
Clear()0%12300%
GetBack()0%12300%
GetFront()0%2100%
IsEmpty()0%2100%
IsFull()0%2100%
PopBack()0%12300%
PopFront()0%12300%
PushBack(...)0%5.515072.73%
PushFront(...)0%30500%
ToArray()0%20400%

File(s)

./Packages/com.dotbunny.gdx/GDX/Collections/Generic/ConcurrentCircularBuffer.cs

#LineLine coverage
 1// Copyright (c) 2020-2024 dotBunny Inc.
 2// dotBunny licenses this file to you under the BSL-1.0 license.
 3// See the LICENSE file in the project root for more information.
 4
 5using System;
 6using System.Runtime.CompilerServices;
 7
 8namespace GDX.Collections.Generic
 9{
 10    /// <summary>
 11    ///     A concurrent sized buffer which loops back over itself as elements are used.
 12    /// </summary>
 13    /// <typeparam name="T">The type of <see cref="object" />s contained within.</typeparam>
 14    [VisualScriptingCompatible(1)]
 15    public struct ConcurrentCircularBuffer<T>
 16    {
 17        /// <summary>
 18        ///     Lock pattern to ensure we aren't stepping on our toes across threads.
 19        /// </summary>
 20        readonly object m_Lock;
 21
 22        /// <summary>
 23        ///     Internal array of backed data for the <see cref="ConcurrentCircularBuffer{T}" />.
 24        /// </summary>
 25        public readonly T[] Array;
 26
 27        /// <summary>
 28        ///     The cached array length for <see cref="Array" />.
 29        /// </summary>
 30        public readonly int Capacity;
 31
 32        /// <summary>
 33        ///     The current size of occupied elements in the <see cref="ConcurrentCircularBuffer{T}" />.
 34        /// </summary>
 35        /// <remarks>CAUTION! Changing this will alter the understanding of the data.</remarks>
 36        public int Count;
 37
 38        /// <summary>
 39        ///     The index of the last item in <see cref="Array" />.
 40        /// </summary>
 41        /// <remarks>CAUTION! Changing this will alter the understanding of the data.</remarks>
 42        public int EndIndex;
 43
 44        /// <summary>
 45        ///     The index of the first item in <see cref="Array" />.
 46        /// </summary>
 47        /// <remarks>CAUTION! Changing this will alter the understanding of the data.</remarks>
 48        public int StartIndex;
 49
 50        /// <summary>
 51        ///     Create a <see cref="ConcurrentCircularBuffer{T}" /> with a <paramref name="capacity" />.
 52        /// </summary>
 53        /// <param name="capacity">The maximum number of items allowed in the <see cref="ConcurrentCircularBuffer{T}" />
 54        public ConcurrentCircularBuffer(int capacity)
 1455        {
 1456            if (capacity < 1)
 057            {
 058                throw new ArgumentException("Circular buffer cannot have negative or zero capacity.", nameof(capacity));
 59            }
 60
 1461            Array = new T[capacity];
 1462            Capacity = capacity;
 63
 1464            Count = 0;
 65
 1466            StartIndex = 0;
 1467            EndIndex = Count == capacity ? 0 : Count;
 1468            m_Lock = new object();
 1469        }
 70
 71        /// <summary>
 72        ///     Create a <see cref="ConcurrentCircularBuffer{T}" /> with a <paramref name="capacity" />, filling with
 73        ///     <paramref name="targetItems" />.
 74        /// </summary>
 75        /// <param name="capacity">The maximum number of items allowed in the <see cref="ConcurrentCircularBuffer{T}" />
 76        /// <param name="targetItems">An array of values to fill the <see cref="ConcurrentCircularBuffer{T}" /> with.</p
 77        /// <exception cref="ArgumentException">
 78        ///     Invalid number of entries provided to the <see cref="ConcurrentCircularBuffer{T}" />
 79        ///     constructor.
 80        /// </exception>
 81        /// <exception cref="ArgumentNullException">
 82        ///     No items were provided to the <see cref="ConcurrentCircularBuffer{T}" />
 83        ///     constructor.
 84        /// </exception>
 85        public ConcurrentCircularBuffer(int capacity, T[] targetItems)
 086        {
 087            if (capacity < 1)
 088            {
 089                throw new ArgumentException("Circular buffer cannot have negative or zero capacity.", nameof(capacity));
 90            }
 91
 092            if (targetItems == null)
 093            {
 094                throw new ArgumentNullException(nameof(targetItems));
 95            }
 96
 097            if (targetItems.Length > capacity)
 098            {
 099                throw new ArgumentException("Too many items to fit circular buffer", nameof(targetItems));
 100            }
 101
 0102            Array = new T[capacity];
 0103            Capacity = capacity;
 104
 0105            System.Array.Copy(targetItems, Array, targetItems.Length);
 0106            Count = targetItems.Length;
 107
 0108            StartIndex = 0;
 0109            EndIndex = Count == capacity ? 0 : Count;
 0110            m_Lock = new object();
 0111        }
 112
 113        /// <summary>
 114        ///     Access item at <paramref name="pseudoIndex" />.
 115        /// </summary>
 116        /// <param name="pseudoIndex"></param>
 117        /// <exception cref="IndexOutOfRangeException">Provided index is out of buffers range.</exception>
 118        public T this[int pseudoIndex]
 119        {
 120            get =>
 2121                Array[
 122                    StartIndex +
 123                    (pseudoIndex < Capacity - StartIndex ? pseudoIndex : pseudoIndex - Capacity)];
 124            set
 0125            {
 0126                lock (m_Lock)
 0127                {
 0128                    Array[
 129                        StartIndex +
 130                        (pseudoIndex < Capacity - StartIndex ? pseudoIndex : pseudoIndex - Capacity)] = value;
 0131                }
 0132            }
 133        }
 134
 135        /// <summary>
 136        ///     Add an <paramref name="item" /> to the <see cref="Array" />.
 137        /// </summary>
 138        /// <param name="item">The typed <see cref="object" /> to add.</param>
 139        public void Add(T item)
 13140        {
 13141            PushBack(item);
 13142        }
 143
 144        /// <summary>
 145        ///     Clear all values of the <see cref="Array" />.
 146        /// </summary>
 147        public void Clear()
 0148        {
 0149            lock (m_Lock)
 0150            {
 0151                for (int i = 0; i < Array.Length; i++)
 0152                {
 0153                    Array[i] = default;
 0154                }
 155
 0156                Count = 0;
 0157                StartIndex = 0;
 0158                EndIndex = 0;
 0159            }
 0160        }
 161
 162        /// <summary>
 163        ///     Get the last item in the <see cref="Array" />.
 164        /// </summary>
 165        /// <returns>The last typed object in <see cref="Array" />.</returns>
 166        public T GetBack()
 0167        {
 0168            return Array[(EndIndex != 0 ? EndIndex : Capacity) - 1];
 0169        }
 170
 171        /// <summary>
 172        ///     Get the first item in the <see cref="Array" />.
 173        /// </summary>
 174        /// <returns>The first typed object in <see cref="Array" />.</returns>
 175        public T GetFront()
 0176        {
 0177            return Array[StartIndex];
 0178        }
 179
 180
 181        /// <summary>
 182        ///     Does the <see cref="Array" /> have any items in it?
 183        /// </summary>
 184        /// <returns>true/false</returns>
 185        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 186        public bool IsEmpty()
 0187        {
 0188            return Count == 0;
 0189        }
 190
 191        /// <summary>
 192        ///     Is the <see cref="Array" /> at capacity?
 193        /// </summary>
 194        /// <returns>true/false</returns>
 195        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 196        public bool IsFull()
 0197        {
 0198            return Count == Capacity;
 0199        }
 200
 201        /// <summary>
 202        ///     Remove an item from the end of the <see cref="Array" />.
 203        /// </summary>
 204        public void PopBack()
 0205        {
 0206            lock (m_Lock)
 0207            {
 0208                if (EndIndex == 0)
 0209                {
 0210                    EndIndex = Capacity;
 0211                }
 212
 0213                EndIndex--;
 0214                Array[EndIndex] = default;
 0215                --Count;
 0216            }
 0217        }
 218
 219        /// <summary>
 220        ///     Remove an item from the start of the <see cref="Array" />.
 221        /// </summary>
 222        public void PopFront()
 0223        {
 0224            lock (m_Lock)
 0225            {
 0226                Array[StartIndex] = default;
 0227                if (++StartIndex == Capacity)
 0228                {
 0229                    StartIndex = 0;
 0230                }
 231
 0232                --Count;
 0233            }
 0234        }
 235
 236        /// <summary>
 237        ///     Add an item to the end of the <see cref="Array" />.
 238        /// </summary>
 239        /// <param name="targetItem">The item to add to the end of <see cref="Array" />.</param>
 240        public void PushBack(T targetItem)
 13241        {
 13242            lock (m_Lock)
 13243            {
 13244                if (Count == Capacity)
 2245                {
 2246                    Array[EndIndex] = targetItem;
 2247                    if (++EndIndex == Capacity)
 0248                    {
 0249                        EndIndex = 0;
 0250                    }
 251
 2252                    StartIndex = EndIndex;
 2253                }
 254                else
 11255                {
 11256                    Array[EndIndex] = targetItem;
 11257                    if (++EndIndex == Capacity)
 0258                    {
 0259                        EndIndex = 0;
 0260                    }
 261
 11262                    ++Count;
 11263                }
 13264            }
 13265        }
 266
 267        /// <summary>
 268        ///     Add an item to the start of the <see cref="Array" />.
 269        /// </summary>
 270        /// <param name="targetItem">The item to add to the start of <see cref="Array" />.</param>
 271        public void PushFront(T targetItem)
 0272        {
 0273            lock (m_Lock)
 0274            {
 0275                if (Count == Capacity)
 0276                {
 0277                    if (StartIndex == 0)
 0278                    {
 0279                        StartIndex = Capacity;
 0280                    }
 281
 0282                    StartIndex--;
 0283                    EndIndex = StartIndex;
 0284                    Array[StartIndex] = targetItem;
 0285                }
 286                else
 0287                {
 0288                    if (StartIndex == 0)
 0289                    {
 0290                        StartIndex = Capacity;
 0291                    }
 292
 0293                    StartIndex--;
 0294                    Array[StartIndex] = targetItem;
 0295                    ++Count;
 0296                }
 0297            }
 0298        }
 299
 300        /// <summary>
 301        ///     Copy <see cref="Array" /> to an array of the same type.
 302        /// </summary>
 303        /// <returns>A copied version of the <see cref="Array" /> as an array.</returns>
 304        public T[] ToArray()
 0305        {
 0306            T[] newArray = new T[Count];
 307
 308            // We dont need to fill anything as its empty.
 0309            if (Count <= 0)
 0310            {
 0311                return newArray;
 312            }
 313
 314            int length;
 315
 316            // First Part
 0317            if (StartIndex < EndIndex)
 0318            {
 0319                length = EndIndex - StartIndex;
 0320                System.Array.Copy(Array, StartIndex, newArray, 0, length);
 0321            }
 322            else
 0323            {
 0324                length = Array.Length - StartIndex;
 0325                System.Array.Copy(Array, StartIndex, newArray, 0, length);
 0326            }
 327
 328            // Second Part
 0329            if (StartIndex > EndIndex)
 0330            {
 0331                System.Array.Copy(Array, EndIndex, newArray, length, 0);
 0332            }
 333            else
 0334            {
 0335                System.Array.Copy(Array, 0, newArray, length, EndIndex);
 0336            }
 337
 0338            return newArray;
 0339        }
 340    }
 341}

Coverage by test methods










Methods/Properties

ConcurrentCircularBuffer(System.Int32)
ConcurrentCircularBuffer(System.Int32, )
Item(System.Int32)
Item(System.Int32, T)
Add(T)
Clear()
GetBack()
GetFront()
IsEmpty()
IsFull()
PopBack()
PopFront()
PushBack(T)
PushFront(T)
ToArray()