< Summary

Class:GDX.Collections.Generic.ConcurrentCircularBuffer[T]
Assembly:GDX
File(s):./Packages/com.dotbunny.gdx/GDX/Collections/Generic/ConcurrentCircularBuffer.cs
Covered lines:26
Uncovered lines:131
Coverable lines:157
Total lines:339
Line coverage:16.5% (26 of 157)
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%330100%
GetFront()0%2100%
IsEmpty()0%2100%
IsFull()0%2100%
PopBack()0%12300%
PopFront()0%12300%
PushBack(...)0%8.125050%
PushFront(...)0%30500%
ToArray()0%20400%

File(s)

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

#LineLine coverage
 1// Copyright (c) 2020-2023 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="CircularBuffer{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="CircularBuffer{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="CircularBuffer{T}" /> with a <paramref name="capacity" />.
 52        /// </summary>
 53        /// <param name="capacity">The maximum number of items allowed in the <see cref="CircularBuffer{T}" /></param>
 54        public ConcurrentCircularBuffer(int capacity)
 155        {
 156            if (capacity < 1)
 057            {
 058                throw new ArgumentException("Circular buffer cannot have negative or zero capacity.", nameof(capacity));
 59            }
 60
 161            Array = new T[capacity];
 162            Capacity = capacity;
 63
 164            Count = 0;
 65
 166            StartIndex = 0;
 167            EndIndex = Count == capacity ? 0 : Count;
 168            m_Lock = new object();
 169        }
 70
 71        /// <summary>
 72        ///     Create a <see cref="CircularBuffer{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="CircularBuffer{T}" /></param>
 76        /// <param name="targetItems">An array of values to fill the <see cref="CircularBuffer{T}" /> with.</param>
 77        /// <exception cref="ArgumentException">
 78        ///     Invalid number of entries provided to the <see cref="CircularBuffer{T}" />
 79        ///     constructor.
 80        /// </exception>
 81        /// <exception cref="ArgumentNullException">No items were provided to the <see cref="CircularBuffer{T}" /> const
 82        public ConcurrentCircularBuffer(int capacity, T[] targetItems)
 083        {
 084            if (capacity < 1)
 085            {
 086                throw new ArgumentException("Circular buffer cannot have negative or zero capacity.", nameof(capacity));
 87            }
 88
 089            if (targetItems == null)
 090            {
 091                throw new ArgumentNullException(nameof(targetItems));
 92            }
 93
 094            if (targetItems.Length > capacity)
 095            {
 096                throw new ArgumentException("Too many items to fit circular buffer", nameof(targetItems));
 97            }
 98
 099            Array = new T[capacity];
 0100            Capacity = capacity;
 101
 0102            System.Array.Copy(targetItems, Array, targetItems.Length);
 0103            Count = targetItems.Length;
 104
 0105            StartIndex = 0;
 0106            EndIndex = Count == capacity ? 0 : Count;
 0107            m_Lock = new object();
 0108        }
 109
 110        /// <summary>
 111        ///     Access item at <paramref name="pseudoIndex" />.
 112        /// </summary>
 113        /// <param name="pseudoIndex"></param>
 114        /// <exception cref="IndexOutOfRangeException">Provided index is out of buffers range.</exception>
 115        public T this[int pseudoIndex]
 116        {
 117            get
 0118            {
 0119                return Array[
 120                    StartIndex +
 121                    (pseudoIndex < Capacity - StartIndex ? pseudoIndex : pseudoIndex - Capacity)];
 0122            }
 123            set
 0124            {
 0125                lock (m_Lock)
 0126                {
 0127                    Array[
 128                        StartIndex +
 129                        (pseudoIndex < Capacity - StartIndex ? pseudoIndex : pseudoIndex - Capacity)] = value;
 0130                }
 0131            }
 132        }
 133
 134        /// <summary>
 135        ///     Add an <paramref name="item" /> to the <see cref="Array" />.
 136        /// </summary>
 137        /// <param name="item">The typed <see cref="object" /> to add.</param>
 138        public void Add(T item)
 2139        {
 2140            PushBack(item);
 2141        }
 142
 143        /// <summary>
 144        ///     Clear all values of the <see cref="Array" />.
 145        /// </summary>
 146        public void Clear()
 0147        {
 0148            lock (m_Lock)
 0149            {
 0150                for (int i = 0; i < Array.Length; i++)
 0151                {
 0152                    Array[i] = default;
 0153                }
 154
 0155                Count = 0;
 0156                StartIndex = 0;
 0157                EndIndex = 0;
 0158            }
 0159        }
 160
 161        /// <summary>
 162        ///     Get the last item in the <see cref="Array" />.
 163        /// </summary>
 164        /// <returns>The last typed object in <see cref="Array" />.</returns>
 165        public T GetBack()
 2166        {
 2167            return Array[(EndIndex != 0 ? EndIndex : Capacity) - 1];
 2168        }
 169
 170        /// <summary>
 171        ///     Get the first item in the <see cref="Array" />.
 172        /// </summary>
 173        /// <returns>The first typed object in <see cref="Array" />.</returns>
 174        public T GetFront()
 0175        {
 0176            return Array[StartIndex];
 0177        }
 178
 179
 180        /// <summary>
 181        ///     Does the <see cref="Array" /> have any items in it?
 182        /// </summary>
 183        /// <returns>true/false</returns>
 184        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 185        public bool IsEmpty()
 0186        {
 0187            return Count == 0;
 0188        }
 189
 190        /// <summary>
 191        ///     Is the <see cref="Array" /> at capacity?
 192        /// </summary>
 193        /// <returns>true/false</returns>
 194        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 195        public bool IsFull()
 0196        {
 0197            return Count == Capacity;
 0198        }
 199
 200        /// <summary>
 201        ///     Remove an item from the end of the <see cref="Array" />.
 202        /// </summary>
 203        public void PopBack()
 0204        {
 0205            lock (m_Lock)
 0206            {
 0207                if (EndIndex == 0)
 0208                {
 0209                    EndIndex = Capacity;
 0210                }
 211
 0212                EndIndex--;
 0213                Array[EndIndex] = default;
 0214                --Count;
 0215            }
 0216        }
 217
 218        /// <summary>
 219        ///     Remove an item from the start of the <see cref="Array" />.
 220        /// </summary>
 221        public void PopFront()
 0222        {
 0223            lock (m_Lock)
 0224            {
 0225                Array[StartIndex] = default;
 0226                if (++StartIndex == Capacity)
 0227                {
 0228                    StartIndex = 0;
 0229                }
 0230                --Count;
 0231            }
 0232        }
 233
 234        /// <summary>
 235        ///     Add an item to the end of the <see cref="Array" />.
 236        /// </summary>
 237        /// <param name="targetItem">The item to add to the end of <see cref="Array" />.</param>
 238        public void PushBack(T targetItem)
 2239        {
 2240            lock (m_Lock)
 2241            {
 2242                if (Count == Capacity)
 0243                {
 0244                    Array[EndIndex] = targetItem;
 0245                    if (++EndIndex == Capacity)
 0246                    {
 0247                        EndIndex = 0;
 0248                    }
 249
 0250                    StartIndex = EndIndex;
 0251                }
 252                else
 2253                {
 2254                    Array[EndIndex] = targetItem;
 2255                    if (++EndIndex == Capacity)
 0256                    {
 0257                        EndIndex = 0;
 0258                    }
 259
 2260                    ++Count;
 2261                }
 2262            }
 2263        }
 264
 265        /// <summary>
 266        ///     Add an item to the start of the <see cref="Array" />.
 267        /// </summary>
 268        /// <param name="targetItem">The item to add to the start of <see cref="Array" />.</param>
 269        public void PushFront(T targetItem)
 0270        {
 0271            lock (m_Lock)
 0272            {
 0273                if (Count == Capacity)
 0274                {
 0275                    if (StartIndex == 0)
 0276                    {
 0277                        StartIndex = Capacity;
 0278                    }
 279
 0280                    StartIndex--;
 0281                    EndIndex = StartIndex;
 0282                    Array[StartIndex] = targetItem;
 0283                }
 284                else
 0285                {
 0286                    if (StartIndex == 0)
 0287                    {
 0288                        StartIndex = Capacity;
 0289                    }
 290
 0291                    StartIndex--;
 0292                    Array[StartIndex] = targetItem;
 0293                    ++Count;
 0294                }
 0295            }
 0296        }
 297
 298        /// <summary>
 299        ///     Copy <see cref="Array" /> to an array of the same type.
 300        /// </summary>
 301        /// <returns>A copied version of the <see cref="Array" /> as an array.</returns>
 302        public T[] ToArray()
 0303        {
 0304            T[] newArray = new T[Count];
 305
 306            // We dont need to fill anything as its empty.
 0307            if (Count <= 0)
 0308            {
 0309                return newArray;
 310            }
 311
 312            int length;
 313
 314            // First Part
 0315            if (StartIndex < EndIndex)
 0316            {
 0317                length = EndIndex - StartIndex;
 0318                System.Array.Copy(Array, StartIndex, newArray, 0, length);
 0319            }
 320            else
 0321            {
 0322                length = Array.Length - StartIndex;
 0323                System.Array.Copy(Array, StartIndex, newArray, 0, length);
 0324            }
 325
 326            // Second Part
 0327            if (StartIndex > EndIndex)
 0328            {
 0329                System.Array.Copy(Array, EndIndex, newArray, length, 0);
 0330            }
 331            else
 0332            {
 0333                System.Array.Copy(Array, 0, newArray, length, EndIndex);
 0334            }
 335
 0336            return newArray;
 0337        }
 338    }
 339}

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()