1 // 2 // Copyright (c) 2010-2023 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using System.Collections.Generic; 10 using System.Collections; 11 12 namespace Antmicro.Renode.Utilities.Collections 13 { 14 public class CircularBuffer<T> : IEnumerable<T> 15 { CircularBuffer(int size)16 public CircularBuffer(int size) 17 { 18 buffer = new T[size]; 19 Clear(); 20 } 21 Clear()22 public void Clear() 23 { 24 IsWrapped = false; 25 LastPosition = buffer.Length - 1; 26 FirstPosition = 0; 27 IsEmpty = true; 28 } 29 Enqueue(T element)30 public bool Enqueue(T element) 31 { 32 bool overflow = false; 33 34 if(!IsEmpty && ((LastPosition + 1) % buffer.Length) == FirstPosition) 35 { 36 MoveFirst(); 37 overflow = true; //buffer overflow 38 } 39 MoveLast(); 40 buffer[LastPosition] = element; 41 IsEmpty = false; 42 43 return !overflow; 44 } 45 TryDequeue(out T result)46 public bool TryDequeue(out T result) 47 { 48 result = default(T); 49 if(IsEmpty) 50 { 51 return false; 52 } 53 result = buffer[FirstPosition]; 54 if(FirstPosition == LastPosition) 55 { 56 IsEmpty = true; 57 } 58 MoveFirst(); 59 return true; 60 } 61 DequeueAll()62 public IEnumerable<T> DequeueAll() 63 { 64 T value; 65 while(TryDequeue(out value)) 66 { 67 yield return value; 68 } 69 } 70 TryPeek(out T result)71 public bool TryPeek(out T result) 72 { 73 if(IsEmpty) 74 { 75 result = default(T); 76 return false; 77 } 78 result = buffer[FirstPosition]; 79 return true; 80 } 81 CopyTo(T[] array, int arrayIndex)82 public void CopyTo(T[] array, int arrayIndex) 83 { 84 if(IsEmpty) 85 { 86 return; 87 } 88 if(!IsWrapped) 89 { 90 Array.Copy(buffer, FirstPosition, array, arrayIndex, LastPosition + 1 - FirstPosition); 91 return; 92 } 93 var start = FirstPosition; 94 var rightSideLength = buffer.Length - start; 95 Array.Copy(buffer, start, array, arrayIndex, rightSideLength); 96 Array.Copy(buffer, 0, array, arrayIndex + rightSideLength, LastPosition + 1); 97 } 98 GetEnumerator()99 public IEnumerator<T> GetEnumerator() 100 { 101 if(IsEmpty) 102 { 103 yield break; 104 } 105 var end = LastPosition; 106 var currentYield = FirstPosition; 107 do 108 { 109 yield return buffer[currentYield]; 110 currentYield++; 111 if(currentYield == buffer.Length) 112 { 113 currentYield = 0; 114 } 115 } 116 while(currentYield != (end + 1) % buffer.Length); 117 } 118 119 public T this[int i] 120 { 121 get 122 { 123 return buffer[i]; 124 } 125 set 126 { 127 buffer[i] = value; 128 } 129 } 130 131 private int LastPosition { get; set; } 132 private int FirstPosition { get; set; } 133 private bool IsEmpty { get; set; } 134 public bool IsWrapped { get; set; } 135 public int Capacity => buffer.Length; 136 public int Count => IsEmpty ? 0 137 : (IsWrapped 138 ? LastPosition + buffer.Length + 1 - FirstPosition 139 : LastPosition + 1 - FirstPosition); 140 IEnumerable.GetEnumerator()141 IEnumerator IEnumerable.GetEnumerator() 142 { 143 return GetEnumerator(); 144 } 145 UpdateIndex()146 private void UpdateIndex() 147 { 148 } 149 MoveFirst()150 private void MoveFirst() 151 { 152 FirstPosition++; 153 if(FirstPosition == buffer.Length) 154 { 155 IsWrapped = false; 156 FirstPosition = 0; 157 } 158 } MoveLast()159 private void MoveLast() 160 { 161 LastPosition++; 162 if(LastPosition == buffer.Length) 163 { 164 IsWrapped = !IsEmpty; // usually we should isWrapped=true here, unless there is nothing in the collection. Then lastPosition should jump to 0, but there is no wrapping 165 LastPosition = 0; 166 } 167 } 168 169 private readonly T[] buffer; 170 } 171 } 172