1+ using System ;
2+ using System . Collections ;
3+ using System . Collections . Generic ;
4+ using System . Linq ;
5+
6+ namespace DataStructures . Lists
7+ {
8+ public class CircularBuffer < T > : IEnumerable < T > , ICollection < T > where T : IComparable < T >
9+ {
10+ private T [ ] _circularBuffer ;
11+ private int _end ;
12+ private int _start ;
13+ private static readonly int _defaultBufferLength = 10 ;
14+
15+ /// <summary>
16+ /// Returns the length of the buffer
17+ /// </summary>
18+ public int Length
19+ {
20+ get
21+ {
22+ return _circularBuffer . Length - 1 ;
23+ }
24+ }
25+
26+ /// <summary>
27+ /// Checks if no element is inserted into the buffer
28+ /// </summary>
29+ public bool IsEmpty
30+ {
31+ get
32+ {
33+ return _count == 0 ;
34+ }
35+ }
36+
37+ /// <summary>
38+ /// Checks if the buffer is filled up
39+ /// </summary>
40+ public bool IsFilledUp
41+ {
42+ get
43+ {
44+ return ( ( _end + 1 ) % _circularBuffer . Length == _start ) && ! _circularBuffer [ _start ] . Equals ( _circularBuffer [ _end ] ) ;
45+ }
46+ }
47+
48+ /// <summary>
49+ /// Controls whether data should be overridden when it is continously inserted without reading
50+ /// </summary>
51+ public bool CanOverride
52+ {
53+ get ;
54+ }
55+
56+ /// <summary>
57+ /// Initializes a circular buffer with initial length of 10
58+ /// </summary>
59+ public CircularBuffer ( bool canOverride = true ) : this ( _defaultBufferLength , canOverride )
60+ {
61+ }
62+
63+ /// <summary>
64+ /// Initializes a circular buffer with given length
65+ /// </summary>
66+ /// <param name="length">The length of the buffer</param>
67+ public CircularBuffer ( int length , bool canOverride = true )
68+ {
69+ if ( length < 1 )
70+ {
71+ throw new ArgumentOutOfRangeException ( "length can not be zero or negative" ) ;
72+ }
73+ _circularBuffer = new T [ length + 1 ] ;
74+ _end = 0 ;
75+ _start = 0 ;
76+ CanOverride = canOverride ;
77+ }
78+
79+ /// <summary>
80+ /// Writes value to the back of the buffer
81+ /// </summary>
82+ /// <param name="value">value to be added to the buffer</param>
83+ public void Add ( T value )
84+ {
85+ if ( CanOverride == false && IsFilledUp == true )
86+ {
87+ throw new CircularBufferFullException ( $ "Circular Buffer is filled up. { value } can not be inserted") ;
88+ }
89+ innerInsert ( value ) ;
90+ }
91+
92+ // Inserts data into the buffer without checking if it is full
93+ private void innerInsert ( T value )
94+ {
95+ _circularBuffer [ _end ] = value ;
96+ _end = ( _end + 1 ) % _circularBuffer . Length ;
97+ if ( _end == _start )
98+ {
99+ _start = ( _start + 1 ) % _circularBuffer . Length ;
100+ }
101+
102+ // Count should not be greater than the length of the buffer when overriding
103+ _count = _count < Length ? ++ _count : _count ;
104+ }
105+
106+ /// <summary>
107+ /// Reads and removes the value in front of the buffer, and places the next value in front.
108+ /// </summary>
109+ public T Pop ( )
110+ {
111+ var result = _circularBuffer [ _start ] ;
112+ _circularBuffer [ _start ] = _circularBuffer [ _end ] ;
113+ _start = ( _start + 1 ) % _circularBuffer . Length ;
114+ //Count should not go below Zero when poping an empty buffer.
115+ _count = _count > 0 ? -- _count : _count ;
116+ return result ;
117+ }
118+
119+ #region IEnumerable Implementation
120+ public IEnumerator < T > GetEnumerator ( )
121+ {
122+ for ( int i = _start ; i < Count ; i ++ )
123+ {
124+ yield return _circularBuffer [ i ] ;
125+ }
126+ }
127+
128+ IEnumerator IEnumerable . GetEnumerator ( )
129+ {
130+ return GetEnumerator ( ) ;
131+ }
132+ #endregion
133+
134+ #region ICollection Implementation
135+ private int _count ;
136+ /// <summary>
137+ /// Returns the number of elements.
138+ /// </summary>
139+ public int Count
140+ {
141+ get
142+ {
143+ return _count ;
144+ }
145+ }
146+ /// <summary>
147+ /// Checks whether this collection is readonly
148+ /// </summary>
149+ public bool IsReadOnly
150+ {
151+ get
152+ {
153+ return false ;
154+ }
155+ }
156+ /// <summary>
157+ /// Clears this instance
158+ /// </summary>
159+ public void Clear ( )
160+ {
161+ _count = 0 ;
162+ _start = 0 ;
163+ _end = 0 ;
164+ _circularBuffer = new T [ Length + 1 ] ;
165+ }
166+ /// <summary>
167+ /// Checks whether the buffer contains an item
168+ /// </summary>
169+ public bool Contains ( T item )
170+ {
171+ return _circularBuffer . Contains ( item ) ;
172+ }
173+ /// <summary>
174+ /// Copies this buffer to an array
175+ /// </summary>
176+ public void CopyTo ( T [ ] array , int arrayIndex )
177+ {
178+ if ( array == null )
179+ {
180+ throw new ArgumentNullException ( "array can not be null" ) ;
181+ }
182+
183+ if ( array . Length == 0 || arrayIndex >= array . Length || arrayIndex < 0 )
184+ {
185+ throw new IndexOutOfRangeException ( ) ;
186+ }
187+
188+ // Get enumerator
189+ var enumarator = GetEnumerator ( ) ;
190+
191+ // Copy elements if there is any in the buffer and if the index is within the valid range
192+ while ( arrayIndex < array . Length )
193+ {
194+ if ( enumarator . MoveNext ( ) )
195+ {
196+ array [ arrayIndex ] = enumarator . Current ;
197+ arrayIndex ++ ;
198+ }
199+ else
200+ {
201+ break ;
202+ }
203+ }
204+ }
205+ /// <summary>
206+ /// Removes an item from the buffer
207+ /// </summary>
208+ public bool Remove ( T item )
209+ {
210+ if ( ! IsEmpty && Contains ( item ) )
211+ {
212+ var sourceArray = _circularBuffer . Except ( new T [ ] { item } ) . ToArray ( ) ;
213+ _circularBuffer = new T [ Length + 1 ] ;
214+ Array . Copy ( sourceArray , _circularBuffer , sourceArray . Length ) ;
215+
216+ if ( ! Equals ( item , default ( T ) ) )
217+ {
218+ _end = sourceArray . Length - 1 ;
219+ _count = sourceArray . Length - 1 ;
220+ }
221+ else
222+ {
223+ _end = sourceArray . Length ;
224+ _count = sourceArray . Length ;
225+ }
226+
227+ return true ;
228+ }
229+
230+ return false ;
231+ }
232+ #endregion
233+ }
234+
235+ public class CircularBufferFullException : Exception
236+ {
237+ public CircularBufferFullException ( string message ) : base ( message )
238+ {
239+ }
240+ }
241+ }
0 commit comments