< Summary

Information
Class: CounterpointCollective.Collections.StructQueue<T>
Assembly: CounterpointCollective.CoalescingQueue
File(s): /builds/counterpointcollective/prestoprimitives/CoalescingQueue/Collections/StructQueue.cs
Line coverage
93%
Covered lines: 80
Uncovered lines: 6
Coverable lines: 86
Total lines: 212
Line coverage: 93%
Branch coverage
88%
Covered branches: 30
Total branches: 34
Branch coverage: 88.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
get_Count()100%11100%
Enqueue(...)75%4488.88%
TryDequeue(...)83.33%7675%
Dequeue(...)50%2266.66%
TryPeek(...)100%22100%
get_Next()100%11100%
get_State()100%11100%
.ctor(...)100%11100%
GetRef()100%11100%
Dispose()100%11100%
get_IsEmpty()100%11100%
.ctor(...)100%22100%
TryEnqueue(...)100%66100%
TryDequeue(...)75%4477.77%
TryPeek(...)100%22100%
Free(...)100%66100%
GetRef(...)100%11100%

File(s)

/builds/counterpointcollective/prestoprimitives/CoalescingQueue/Collections/StructQueue.cs

#LineLine coverage
 1namespace CounterpointCollective.Collections
 2{
 3
 4    public class StructQueue<T> where T : struct
 5    {
 156        private int nextSegmentSize = 8;
 157        private readonly Queue<Segment> _segments = [];
 8        private Segment lastSegment;
 9
 1029917210        public int Count { get; private set; }
 11
 1512        public StructQueue()
 13        {
 1514            lastSegment = new(nextSegmentSize);
 1515            nextSegmentSize *= 2;
 1516            _segments.Enqueue(lastSegment);
 1517        }
 18
 19        public void Enqueue(out Handle handle)
 20        {
 224989021            if (!lastSegment.TryEnqueue(out handle))
 22            {
 2323                var newSegment = new Segment(nextSegmentSize);
 2324                nextSegmentSize *= 2;
 2325                _segments.Enqueue(newSegment);
 2326                lastSegment = newSegment;
 2327                if (!lastSegment.TryEnqueue(out handle))
 28                {
 029                    throw new InvalidOperationException("Failed to enqueue item. This cannot happen.");
 30                }
 31            }
 224989032            Count++;
 224989033        }
 34
 35        public bool TryDequeue(out Handle handle)
 36        {
 224977937            var s = _segments.Peek();
 38
 224977939            if (s.TryDequeue(out handle))
 40            {
 224977941                if (s.IsEmpty && _segments.Count > 1)
 42                {
 43                    // remove empty segment, we have a next one.
 2344                    _segments.Dequeue();
 45                }
 224977946                Count--;
 224977947                return true;
 48            }
 49
 050            handle = default;
 051            return false;
 52        }
 53
 54        public void Dequeue(out Handle handle)
 55        {
 224977856            if (!TryDequeue(out handle))
 57            {
 058                throw new InvalidOperationException("Queue is empty.");
 59            }
 224977860        }
 61
 62        public bool TryPeek(out Handle handle)
 63        {
 129903364            var s = _segments.Peek();
 65
 129903366            if (s.TryPeek(out handle))
 67            {
 129903268                return true;
 69            }
 70
 171            handle = default;
 172            return false;
 73        }
 74
 75        internal struct Slot
 76        {
 77            public const byte State_Free = 0;
 78            public const byte State_Enqueued = 1;
 79            public const byte State_Dequeued = 2;
 80            public T Item;
 1119747081            public int Next { get; set; }
 899922982            public byte State { get; set; }
 83        }
 84
 85        public readonly struct Handle : IDisposable
 86        {
 87            private readonly Segment _segment;
 88            private readonly int _index;
 89
 90            internal Handle(Segment segment, int index)
 91            {
 579870192                _segment = segment;
 579870193                _index = index;
 579870194            }
 95
 899855196            public ref T GetRef() => ref _segment.GetRef(_index).Item;
 97
 98            public void Dispose()
 224978199                => _segment.Free(_index);
 100        }
 101
 102        internal class Segment
 103        {
 104            private readonly Slot[] Slots;
 105
 106            private int freeTop;
 107
 108            private int queueStart;
 109            private int queueEnd;
 110
 2249779111            public bool IsEmpty => queueStart == -1;
 112
 113
 38114            public Segment(int size)
 115            {
 38116                Slots = new Slot[size];
 117
 38118                queueStart = -1;
 38119                queueEnd = -1;
 120
 121                // init free slots links
 2098448122                for (int i = 0; i < size - 1; i++)
 1049186123                    Slots[i].Next = i + 1;
 38124                Slots[size - 1].Next = -1;
 125
 38126                freeTop = 0;
 38127            }
 128
 129            public bool TryEnqueue(out Handle handle)
 130            {
 131                int idx;
 132                do
 133                {
 2249913134                    idx = freeTop;
 2249959135                    if (idx < 0) { handle = default; return false; }
 2249890136                } while (Interlocked.CompareExchange(ref freeTop, Slots[idx].Next, idx) != idx);
 137
 138
 2249890139                ref var slot = ref Slots[idx];
 2249890140                slot.State = Slot.State_Enqueued;
 2249890141                slot.Next = -1;
 142
 2249890143                if (queueStart == -1)
 144                {
 1100982145                    queueStart = idx;
 146                } else
 147                {
 1148908148                    Slots[queueEnd].Next = idx;
 149                }
 2249890150                queueEnd = idx;
 151
 2249890152                handle = new Handle(this, idx);
 2249890153                return true;
 154            }
 155
 156            public bool TryDequeue(out Handle handle)
 157            {
 2249779158                if (queueStart == -1)
 159                {
 0160                    handle = default;
 0161                    return false;
 162                }
 163
 2249779164                Slots[queueStart].State = Slot.State_Dequeued;
 2249779165                handle = new Handle(this, queueStart);
 2249779166                queueStart = Slots[queueStart].Next;
 2249779167                if (queueStart == -1)
 168                {
 1100978169                    queueEnd = -1;
 170                }
 171
 2249779172                return true;
 173            }
 174
 175            public bool TryPeek(out Handle handle)
 176            {
 1299033177                if (queueStart == -1)
 178                {
 1179                    handle = default;
 1180                    return false;
 181                }
 1299032182                handle = new Handle(this, queueStart);
 1299032183                return true;
 184            }
 185
 186            public void Free(int index)
 187            {
 2249781188                ref var slot = ref Slots[index];
 2249781189                switch(slot.State)
 190                {
 191                    case Slot.State_Enqueued:
 1192                        throw new InvalidOperationException("Cannot dispose an enqueued item.");
 193                    case Slot.State_Dequeued:
 2249779194                        slot.State = Slot.State_Free;
 2249779195                        slot.Item = default;
 196
 197                        int oldTop;
 198                        do
 199                        {
 2249779200                            oldTop = freeTop;
 2249779201                            slot.Next = oldTop;
 2249779202                        } while (Interlocked.CompareExchange(ref freeTop, index, oldTop) != oldTop);
 203                        break;
 204                    default:
 205                        break;
 206                }
 2249779207            }
 208
 8998551209            public ref Slot GetRef(int index) => ref Slots[index];
 210        }
 211    }
 212}