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