Click here to Skip to main content
15,891,981 members
Articles / Programming Languages / C#

Circular Buffer

Rate me:
Please Sign up or sign in to vote.
4.50/5 (20 votes)
11 Sep 20023 min read 245.7K   10.3K   84  
C# implementation of a Circular Buffer
//
// Copyright (C) 2002 Robert Hinrichs. All rights reserved.
// bobh@inav.net
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
// EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED 
// WARRANTIES OF MERCHANTIBILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
//
using System;
using System.Collections;
using System.Threading;
using System.Reflection;

namespace CircularBuffer
{
	/// <summary>
	/// Summary description for CircularBuffer.
	/// <newpara>Implements a <b>Circular Buffer</b>. </newpara>
	/// The Circular Buffer is a memory queue where memory locations
	/// are reused when the data producer (writer) overwrites (modulo
	/// the buffer size) previously used locations.
	/// The Circular Buffer operates in two modes of operation: the
	/// synchronous mode and the asychronous mode. The synchronous mode is 
	/// useful where one process, either the producer (writer) or the consumer
	/// (reader), operates much faster than the other. The faster process 
	/// would otherwise waste time waiting for the slower process. 
	/// Instead the faster process can "burst" off the data into the 
	/// Circular Buffer and continue. The slower process will read the 
	/// data at its own rate. However, in the synchronous mode,
	/// the producer and consumer of the Circular Buffer must access the queue 
	/// at the same average rate over time or an overflow or underflow of 
	/// data will occur.
	/// In the asychronous mode, we are only interested in the last data 
	/// items written by the producer--earlier data will be lost. This is useful 
	/// in debug tracing where debug information is continuously written into 
	/// the Circular Buffer over a period of time until the error 
	/// condition is detected. The Circular Buffer is then examined for the 
	/// sequence of states, commands, etc. written just before the error 
	/// was detected to help isolate the bug. 
	/// The actual capacity of the Circular Buffer is one less (N-1) than the
	/// total length of the Buffer(N) so that a full buffer and an empty 
	/// buffer can be differentianted. The default length is N=256 for a 
	/// default capacity of 255.
	/// A client process can be notified that the Circular Buffer has reached
	/// a specified count (WaterMark) using the CBEventHandler delegate
	/// <newpara>
	/// // Example 
	/// </newpara>
	/// <newpara>
	///  <code>
	/// theCB.SetWaterMark(8); // notify every time cb is at count 8 
	///  </code>
	/// </newpara>
	/// <newpara>
	///  <code>
	/// theCB.WaterMarkNotify += new QueueCB.CBEventHandler(OnWaterMarkEvent);
	///  </code>
	/// </newpara>
	/// </summary>
	public class QueueCB : IEnumerable // ...able! not ...ator!
	{		
		/////////////////////////////////////////////////////////////////////

		/// <summary>
		/// Delegate for the WaterMark event.
		/// </summary>
		public delegate void CBEventHandler(object sender, System.EventArgs e);
		/// <summary>
		/// WaterMark event
		/// </summary>
		public event CBEventHandler WaterMarkNotify;
		 
		/////////////////////////////////////////////////////////////////////
		private object[] buffer;
		private System.Int32 nrp, nwp, Capacity, CountValue;
		private System.Int32 add;
		private System.Int32 len;
		private System.Int32 WaterMarkValue;
		private System.Boolean WaterMarkEnableFlag;
		private System.Boolean SynchronousMode;
		private System.Int32 NumberToAdd = 0;
		private  CountEvent countevent;
		/////////////////////////////////////////////////////////////////////
		
		/// <summary>
		/// Returns current number of items in the Circular Buffer.
		/// In the asynchronous mode, count will staturate at N-1 if an 
		/// overflow conditions exists. The read index will then follow the 
		/// write index so that the lastest items are always available.
		/// </summary>
		public System.Int32 Count
		{
			get
			{
				return CountValue;
			}
		}
		/// <summary>
		/// Returns next Read Index
		/// </summary>
		public System.Int32 ReadIndex
		{
			get
			{
				return nrp;
			}
		}
		/// <summary>
		/// Returns next Write Index
		/// </summary>
		public System.Int32 WriteIndex
		{
			get
			{
				return nwp;
			}
		}

		/// <remarks>
		/// 1> Synchronous Mode -- In the synchronous mode the emptying of the 
		/// Circular Buffer must occur at the same average rate as the filling 
		/// of the Circular Buffer so that the overrun condition never occurs. 
		/// That is, as the tail index chases the head index around the circle 
		/// of the buffer, the head index always stays ahead (in a modulo N sense)
		/// of the tail index. No data is lost. An exception will be thrown when 
		/// a buffer overrun condition occurs.
		/// <newpara>
		/// 2> Asynchronous Mode -- In the asynchronous mode, data will be lost 
		/// as the Circular Buffer is overwritten. Usually in this mode, only 
		/// the last item written are of interest. When the Write index 
		/// (head index) catches the Read Index (tail index) the Read Index is 
		/// automatically incremented and the Circular Buffer count will indicate 
		/// N-1 items in the Buffer.
		/// The synchronous mode is pretty straight forward as most of the 
		/// responsibility for avoiding buffer overflow is a system issue for the 
		/// producer and consumer of the data. The circular buffer just checks 
		/// for error conditions.
		/// The asynchronous mode is more difficult as buffer overruns are allowed.
		/// So things operate as normal until the maximum occupancy is reached in 
		/// the asynchronous mode. From then on, while the buffer is still at 
		/// maximum occupancy, the read index follows the write index. This 
		/// simulates synchronous operation. I call this the saturation
		/// mode. While saturation exists, an additional complication must be 
		/// handled when the data is read. The read index is stored ahead of valid 
		/// data (write index so an adjustment must be made before it is used to 
		/// output data from the circular buffer. 
		/// Capacity is N-1
		/// </newpara>
		/// </remarks>		
		/// <summary>
		/// Used to set synchronous or asynchronous modes of operation
		/// Modes differ in how they handle the buffer overflow 
		/// condition.
		/// </summary>
		/// <value>Property <c>Synchronousmode</c>set to true of false</value>
		public System.Boolean Synchronousmode
		{
			get
			{
				return SynchronousMode;
			}
			set
			{
				lock(this)
				{
					SynchronousMode = value;
				}
			}
		}
		/// <summary>
		/// Sets the level (queue count) at which the WaterMarkNotify event
		/// will fire. 
		/// </summary>
		/// <value>Property <c>Watermark</c>Pos. integer at which event fires</value>
		public System.Int32 Watermark
		{
			get
			{
				return WaterMarkValue;
			}
			set
			{
				lock(this)
				{
					WaterMarkValue = value;
				}
			}
		}

		/// <summary>
		/// Enables WaterMark checking
		/// </summary>
		/// <value>Property <c>Watermarkflag</c>bool enables watermark checking when true</value>
		public System.Boolean Watermarkflag
		{
			get
			{
				return WaterMarkEnableFlag;
			}
			set
			{
				lock(this)
				{
					WaterMarkEnableFlag = value;
				}
			}
		}

		/// <summary>
		/// Returns assembly version string
		/// </summary>
		/// <return>Version string "n.n.n.n"</return>
		public String GetVersion
		{
			get
			{
				Assembly asm = Assembly.GetExecutingAssembly();
				AssemblyName asmn = asm.GetName();
				Version ver = asmn.Version;
				return ver.ToString();
			}
		}
		//////////////////////////////////////////////////////////////////////

		// Client must set through method.
		private System.Int32 capacity
		{
			get
			{
				return Capacity;
			}
			set
			{
				Capacity = value;
				add = 1-capacity;
				len = capacity;
				this.Clear();
			}
		}

		/// <summary>
		/// Default Constructor creates N=256 element
		/// Circular Buffer.
		/// </summary>
		public QueueCB()
		{
			buffer = new object[256];
			capacity = 256;
			add = 1-capacity;
			len = capacity;
			this.Clear();
			Synchronousmode = true;
			Watermarkflag = true;
			CountEvent countevent = new CountEvent();
		}

		/// <summary>
		/// Constructor creates N=256 element 
		/// Circular Buffer with type specified
		/// </summary>
		/// <c>QueueCB theCB = new QueueCB(typeof(string));</c>
		/// <param name="o">Generic Object</param>
		public QueueCB(object o)
		{
			buffer = new object[256];
			capacity = 256;
			add = 1-capacity;
			len = capacity;
			this.Clear();
			Synchronousmode = true;
			Watermarkflag = true;
			countevent = new CountEvent();
		}

		/// <summary>
		/// Constructor creates N=len element 
		/// Circular Buffer with type specified
		/// </summary>
		/// <param name="o">Generic Object</param>
		/// <param name="len">Circular Buffer Length (# of elements)</param>
		public QueueCB(object o, System.Int32 len)
		{
			buffer = new object[len];
			capacity = len;
			add = 1-capacity;
			len = capacity;
			this.Clear();
			Synchronousmode = true;
			Watermarkflag = true;
			countevent = new CountEvent();
		}

		/// <summary>
		/// Constructor creates N=len element 
		/// Circular Buffer with type unspecified
		/// </summary>
		/// <param name="len">Circular Buffer Length (# of elements)</param>
		public QueueCB(System.Int32 len)
		{
			buffer = new object[len];
			capacity = len;
			add = 1-capacity;
			len = capacity;
			this.Clear();
			Synchronousmode = true;
			Watermarkflag = true;
			countevent = new CountEvent();
		}

		/// <summary>
		/// Clears the Circular Buffer and initializes with null
		/// </summary>
		public void Clear()
		{
			nrp = 0;
			nwp = 0;
			CountValue = 0;
			Array.Clear(buffer,0,capacity);
		}

		/// <summary>
		/// Enable WaterMark (queue count) checking
		/// </summary>
		public void WaterMarkEnable()
		{
			WaterMarkEnableFlag = true;
		}

		/// <summary>
		/// Disable WaterMark (queue count) checking
		/// </summary>
		public void WaterMarkDisable()
		{
			WaterMarkEnableFlag = false;
		}

		/// <summary>
		/// Sets Asynchronous Mode of Circular Buffer operation--
		/// see class summary
		/// </summary>
		public void SetAsynchronousMode()
		{
			lock(this)
			{
				Synchronousmode = false;
			}
		}

		/// <summary>
		/// Sets Synchronous Mode of Circular Buffer operation--
		/// see class summary
		/// </summary>
		public void SetSynchronousMode()
		{
			lock(this)
			{
				Synchronousmode = true;
			}
		}

		/// <summary>
		/// Sets WaterMark (queue count) value for
		/// WaterMark event operations
		/// </summary>
		/// <param name="mark">Positive Integer WaterMark event value</param>
		public void SetWaterMark(System.Int32 mark)
		{
			if( mark <= (capacity-1) | (mark < 0))
				lock(this)
				{
					Watermark = mark;
				}
			else
				throw new IndexOutOfRangeException("watermark out of range"); 
		}

		/// <summary>
		/// Returns item at read index without modifing the queue
		/// <exception> throws exception if queue empty</exception>
		/// </summary>
		/// <returns>Returns the object at the read index 
		/// <c>without</c> removing it (modifing the index)</returns>
		public object Peek()
		{
			lock(this)
			{
				object temp;

				if(Count >= 1)
				{
					temp = buffer[nrp];
					return(temp);
				}
				else
					throw new IndexOutOfRangeException
						      ("Too few items in circular buffer"); 

			} // unlock
		}
	
		/// <summary>
		/// Add single item to the Circular Buffer
		/// </summary>
		/// <param name="o">Generic Object </param>
		public void Enqueue(object o)
		{
			if((Count+1 > capacity-1) && (Synchronousmode==true))
				throw new IndexOutOfRangeException
					("Circular Buffer capacity exceeded--Synchronous Mode");
			else // Async mode
			{
				NumberToAdd = 1;
				loadcb(o);
			}
		}

		/// <summary>
		/// Add entire Array[] to the Circular Buffer
		/// </summary>
		/// <param name="a">Array[]</param>
		public void Enqueue(Array a)
		{

			if((Count+a.Length > capacity-1) && (Synchronousmode==true))
				throw new IndexOutOfRangeException
					("Circular Buffer capacity exceeded--Synchronous Mode");
			else // Async mode
			{
				NumberToAdd = a.Length;
				loadcbArray(a);
			}
			
		}

		/// <summary>
		/// Add a single item to the Circular Buffer but block if no room.
		/// Used with synchronous mode only.
		/// </summary>
		/// <param name="o">Generic Object</param>
		public void EnqueueBlocking(object o)
		{
			if(Synchronousmode==true)
			{   
				if(Count <= (capacity-1)) // if room is available
					loadcb(o);            // write the item
				else
				{   // wait for room to be available
					countevent.WaitLoad(1);
					loadcb(o);            // write the item
				}

                countevent.SetCopy(1);// in case waiting for items
			}

			else
				throw new ArgumentException
					("must be in synchronous mode"); 
		}

		/// <summary>
		/// Add entire Array[] to the Circular Buffer but block if no room.
		/// Used with synchronous mode only.
		/// </summary>
		/// <param name="a">Array[]</param>
		public void EnqueueBlocking(Array a)
		{
			if(Synchronousmode==true)
			{
				if( (Count+a.Length) <= (capacity-1)) // if room is available
				{
					loadcbArray(a); // write the item
				}
				else
				{   // wait for room to be available
					countevent.WaitLoad(a.Length);
					loadcbArray(a); // write the item
				}

				countevent.SetCopy(a.Length);// in case waiting for items
			}
			else
				throw new ArgumentException
					("must be in synchronous mode"); 
		}

		/// <summary>
		/// Return/Remove single item from the Circular Buffer
		/// Will throw exception if no items are in the Circular Buffer 
		/// regardless of mode.
		/// This and CopyTo(Array, index, number) are the only queue 
		/// item removal methods that will check for underflow and throw an 
		/// exception. Others will either block or return the number 
		/// of items they were able to successfully remove.
		/// </summary>
		/// <returns>returns/removes a single object from the 
		/// Circular Buffer</returns>
		public object Dequeue()
		{
			if(Count>0)
				return copycb(buffer);
			else
			{
				throw new IndexOutOfRangeException
					("No items in circular buffer"); 
			}
		}

		/// <summary>
		/// Return/Remove single item from the Circular Buffer but block
		/// if empty. Must be in synchronous mode.
		/// </summary>
		/// <returns>returns/removes a single object from the
		/// Circular Buffer</returns>
		public object DequeueBlocking()
		{
			object tempo;

			if(Synchronousmode==true)
			{
				if(Count<=0)
					countevent.WaitCopy(1);

				tempo = copycb(buffer);
                countevent.SetLoad(1); // this is for a blocked enqueue
				return tempo;
			}
			else
			{
				throw new ArgumentException
					("must be in synchronous mode"); 
			}
		}

		/// <summary>
		/// Copy/Remove items from entire Circular Buffer to an Array[] 
		/// with Array offset of index.
		/// </summary>
		/// <param name="a">Target Array[]</param>
		/// <param name="index">Target Array[] offset</param>
		/// <returns>Number of items copied/removed from Circular Buffer
		/// </returns>
		public System.Int32 CopyTo(Array a, System.Int32 index)
		{
			System.Int32 NumberOfItems;

			lock(this)
			{
				NumberOfItems = Count;
				if(a.Length < NumberOfItems)
				{
					throw new IndexOutOfRangeException
						("too many items for destination array");
				}
				else
				{
					copycbArray(a, index, a.Length);
					return NumberOfItems;
				}
			}
		}

		/// <summary>
		/// Copy/Remove num items from Circular Buffer to an Array[] 
		/// with Array offset of index.
		/// This and Dequeue() are the only queue 
		/// item removal methods that will check for underflow and throw an 
		/// exception. Others will either block or return the number 
		/// of items they were able to successfully remove.
		/// </summary>
		/// <param name="a">Target Array[]</param>
		/// <param name="index">Target Array[] offset</param>
		/// <param name="num">Number of items to copy/remove</param>
		/// <returns>Number of items copied/removed from Circular Buffer
		/// </returns>
		public System.Int32 CopyTo(Array a, System.Int32 index, 
			                       System.Int32 num)
		{
			System.Int32 NumberOfItems;

			lock(this)
			{
				NumberOfItems = Count;
				if(NumberOfItems >= num)
				{
					copycbArray(a, index, num);
				}
				else
				{
					throw new IndexOutOfRangeException
						("Not enough items in circular buffer"); 
				}
				return NumberOfItems;
			}
		}

		/// <summary>
		/// Copy/Remove items from entire Circular Buffer to an Array[]
		/// </summary>
		/// <param name="a">Target Array[]</param>
		/// <returns>Number if items copied/removed from Circular Buffer
		/// </returns>
		public System.Int32 CopyTo(Array a)
		{
			System.Int32 NumberOfItems;

			lock(this)
			{
				NumberOfItems = Count;
				if(a.Length < NumberOfItems)
				{
					throw new IndexOutOfRangeException
						("too many items for destination array");
				}
				else
				{				
					copycbArray(a, 0, NumberOfItems);
					return NumberOfItems;
				}
			}
		}

		/// <summary>
		/// Copy/Remove items from entire Circular Buffer to an Array[] 
		/// with Array offset of index but block if empty. Must be in
		/// synchronous mode.
		/// </summary>
		/// <param name="a">Target Array[]</param>
		/// <param name="index">Target Array[] offset</param>
		/// <returns>Number if items copied/removed from Circular Buffer
		/// </returns>
		public System.Int32 CopyToBlocking(Array a, System.Int32 index)
		{			
			System.Int32 NumberOfItems;

			if(Synchronousmode==true)
			{
				// returns all available items from the circular buffer 
				// to caller's array
				lock(this)
				{
					NumberOfItems = Count;
				}
				if(a.Length < NumberOfItems)
				{
					throw new IndexOutOfRangeException
						("too many items for destination array");
				}
				else
				{
					if(NumberOfItems > 0)
					{   // we have at least this many
						copycbArray(a, index, NumberOfItems);
						// this is the only reason method is called blocking
						// this if for a blocked enqueue
						countevent.SetLoad(NumberOfItems);
					}
				}
				return NumberOfItems;
			}
			else
			{
				throw new ArgumentException
					("must be in synchronous mode"); 
			}
		}

		/// <summary>
		/// Copy/Remove items from entire Circular Buffer to an Array[] 
		/// with Array offset of index but block if empty. Must be in
		/// synchronous mode.
		/// </summary>
		/// <param name="a">Target Array[]</param>
		/// <param name="index">Target Array[] offset</param>
		/// <param name="numbertocopy">number to copy</param>
		/// <returns>Number if items copied/removed from Circular Buffer
		/// </returns>
		public System.Int32 CopyToBlocking(Array a, 
			System.Int32 index, System.Int32 numbertocopy)
		{			
			System.Int32 NumberOfItems;

			if(Synchronousmode==true)
			{
				if(Count < numbertocopy)
				{   // this is the only CopyToBlocking that will block 
					// since a specific number was asked for.
					countevent.WaitCopy(numbertocopy);
					NumberOfItems = numbertocopy;
					copycbArray(a, index, numbertocopy);
				}
				else
				{
					NumberOfItems = numbertocopy;
					copycbArray(a, index, numbertocopy);
				}

				// this if for a blocked enqueue
				countevent.SetLoad(numbertocopy);
				return NumberOfItems;
			}
			else
			{
				throw new ArgumentException
					("must be in synchronous mode"); 
			}
		}


		/// <summary>
		/// Copy/Remove items from entire Circular Buffer to an Array[] 
		/// with Array offset of zero but block if empty. Must be in
		/// synchronous mode.
		/// </summary>
		/// <param name="a">Target Array[]</param>
		/// <returns>Number if items copied/removed from Circular Buffer
		/// </returns>
		public System.Int32 CopyToBlocking(Array a)
		{			

			System.Int32 NumberOfItems;

			if(Synchronousmode==true)
			{
				// returns all available items from the circular buffer 
				// to caller's array
					NumberOfItems = Count;
					if(a.Length < NumberOfItems)
					{
						throw new IndexOutOfRangeException
							("too many items for destination array");
					}
					else
					{
						if(NumberOfItems > 0)
						{
							copycbArray(a, 0, a.Length);
							// this is the only reason method is 
							// called "blocking"
							// this if for a blocked enqueue
							countevent.SetLoad(NumberOfItems);
						}
					}

				return NumberOfItems;
			}
			else
			{
				throw new ArgumentException
					("must be in synchronous mode"); 
			}			
		}
		/// <summary>
		/// Displays Circular Buffer contents and or state information.
		/// Overridable.
		/// </summary>
		public virtual void DumpCircularBuffer()
		{

		}
		//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
		// raise the event by invoking the delegates
		/// <summary>
		/// Raise the watermark event by invoking the delegates
		/// </summary>
		protected virtual void OnWaterMarkNotify()
		{
			if(WaterMarkNotify != null) // if someone has signed up
				// fire the generic event
				WaterMarkNotify(this, System.EventArgs.Empty); 
		}
		//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
        
		#region Private Support Functions
        //////////////////////////////////////////////////////////////////////
		////////////////////////////PRIVATE///////////////////////////////////
		////////////////////////Support Functions/////////////////////////////
		//////////////////////////////////////////////////////////////////////
		private void loadcb(object o)
		{
			lock(this)
			{
				System.Int32 i = nwp;
				System.Int32 tempCount;

				
				buffer[i] = o;
				// for speed, we only update the count after the operation
				// but if watermark check is on, we must do it here also
				// but don't bother if nobody has registered for the event
				if((Watermarkflag == true) && (WaterMarkNotify != null))
				{
					tempCount = i-nrp;
					if(tempCount < 0)
						tempCount += Capacity; // modulo buffer size

					if(tempCount==Watermark)
						OnWaterMarkNotify();
				}

				i += add;
				if(i<0) i+= len;

				nwp = i;
				updateCount();
			} // unlock
		}

		private void loadcbArray(Array a)
		{
			lock(this)
			{
				System.Int32 i = nwp;
				System.Int32 n = a.Length;
				System.Int32 offset = 0;
				System.Int32 tempCount;


				while(n-- > 0)
				{					
					buffer[i] = a.GetValue(offset++);

					// for speed, we only update the count after the operation
					// but if watermark check is on, we must do it here also
					// but don't bother if nobody has registered for the event
					if((Watermarkflag == true) && (WaterMarkNotify != null))
					{
						tempCount = i-nrp;
						if(tempCount < 0)
							tempCount += Capacity; // modulo buffer size

						if(tempCount==Watermark)
							OnWaterMarkNotify();
					}

					i += add;
					if(i<0) i+= len;
				}
				nwp = i;
				updateCount();
			} // unlock
		}

		private object copycb(object o)
		{

			lock(this)
			{
				System.Int32 i = nrp;
				object temp;
				System.Int32 tempCount;

				// A modification to the read index may be required
				// if we have been operating in asynchronous saturated mode
				if(Synchronousmode==false)
					if(Count >= (capacity-1))
						i = (nrp-1)%capacity;
				
				temp = buffer[i];

				// for speed, we only update the count after the operation
				// but if watermark check is on, we must do it here also
				// but don't bother if nobody has registered for the event
				if((Watermarkflag == true) && (WaterMarkNotify != null))
				{
					tempCount = nrp-i; // change
					if(tempCount < 0)
						tempCount += Capacity; // modulo buffer size

					if(tempCount==Watermark)
						OnWaterMarkNotify();
				}

				i += add;
				if(i<0) i+= len;
				nrp = i;
				updateCount();
				return(temp);
			} // unlock
		}

		private void copycbArray(Array a, System.Int32 index, System.Int32 alen)
		{

			lock(this)
			{
				System.Int32 i = nrp;
				System.Int32 n = alen - index; 
				System.Int32 offset = index;
				System.Int32 tempCount;

				// A modification to the read index may be required
				// if we have been operating in asynchronous saturated mode
				//if(Synchronousmode==false)
				//	if(Count >= (capacity-1))
				//		i = (nrp-1)%capacity;

				
				if(Count >= n)
				{
					while(n-- > 0)
					{
						a.SetValue(buffer[i], offset++);
						// for speed, we only update the count after the 
						// operation but if watermark check is on, we must 
						// do it here also but don't bother if nobody has
						// registered for the event
						if((Watermarkflag == true) && (WaterMarkNotify 
							                         != null))
						{
							tempCount = nrp-i; // change
							if(tempCount < 0)
								tempCount += Capacity; // modulo buffer size

							if(tempCount==Watermark)
								OnWaterMarkNotify();
						}
						i += add;
						if(i<0) i+= len;
					}
				}
				else
					throw new IndexOutOfRangeException
						 ("Too few items in circular buffer"); 
				nrp = i;
				updateCount();
			} // unlock
		}

		private void updateCount()
		{
			if(Synchronousmode==true)
			{
				CountValue = nwp-nrp;
				if(Count < 0)
					CountValue += Capacity; // modulo buffer size
			}  			  
				// if asynchronousmode, adjust the read index to follow the 
				// write index if buffer is full (saturation)
			else
			{
                // check to see if we are in saturation
				if(CountValue+NumberToAdd >= (capacity-1))
				{ // if saturated, read index follows write index
					nrp = (nwp+NumberToAdd)%capacity;
					// and Count remains at max capacity
					CountValue = capacity-1;
				}
				else
				{
					CountValue = nwp-nrp;
					if(Count < 0)
						CountValue += Capacity; // modulo buffer size
				}
			}

		}
		#endregion

		#region IEnumerator Support
		/// <summary>
		/// IEnumerator support
		/// </summary>
		/// <returns></returns>
		public CBEnumerator GetEnumerator()
		{
			return new QueueCB.CBEnumerator(this);
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return this.GetEnumerator();
		}
	
		// a helper class
	   	/// <summary>
	   	/// A helper class
	   	/// </summary>
		public class CBEnumerator : IEnumerator // ...ator! ...ator! ...ator!
		{
			QueueCB cb;

			internal CBEnumerator(QueueCB cb)
			{
				this.cb = cb;
			}

			// IEnumerator support
			/// <summary>
			/// IEnumerator support
			/// </summary>
			/// <returns></returns>
			public bool MoveNext()
			{
				lock(this)
				{
					if(cb.Count == 0)
						return false;
					else
						return true;
				}
			}

			/// <summary>
			/// IEnumerator support
			/// </summary>
			public void Reset()
			{
			}

			// Client must set through method.
			/// <summary>
			/// IEnumerator support
			/// </summary>
			public object Current
			{
				get
				{
					lock(this)
					{
						System.Int32 i = cb.nrp;
						object temp;

						// A modification to the read index may be required
						// if we have been operating in asynchronous 
						// saturated mode
						if(cb.Synchronousmode==false)
							if(cb.Count >= (cb.capacity-1))
								i = (cb.nrp-1)%cb.capacity;
				
						temp = cb.buffer[i];
						i += cb.add;
						if(i<0) i+= cb.len;
						cb.nrp = i;
						cb.updateCount();
						return(temp);
					} // unlock
				}
			}

		} // end class CBEnumerator
		#endregion

	} // end class QueueCB 
	#region Blocking Support
	internal class CountEvent
	{
		private AutoResetEvent loadautoevent;
		private AutoResetEvent copyautoevent;

		private System.Int32 LoadEventCount = 0;
		private System.Int32 CopyEventCount = 0;

		// constructor
		internal CountEvent()
		{
			loadautoevent = new AutoResetEvent(false);
			copyautoevent = new AutoResetEvent(false);
			CopyEventCount = 0;
			LoadEventCount = 0;
		}

///////////////////////////////////////////////////////////////////
// WaitLoad() and SetLoad() are part of a blocked enqueue operation.
// WaitLoad requests to continue when the number specified can be written. 
// SetLoad updates the count as items are removed from the circular buffer.
		internal void WaitLoad(System.Int32 cnt)
		{
			lock(this)
		    {
				LoadEventCount = cnt; // wait for count to reach this value
		    }            
			loadautoevent.WaitOne();
		}

		internal void SetLoad(System.Int32 cnt)
		{
			lock(this)
			{
				if(LoadEventCount > 0)
				{
					LoadEventCount -= cnt; // queue has been emptied by this amount
					if(LoadEventCount <= 0)
					{
						loadautoevent.Set();
					}
				}
			}
		}
///////////////////////////////////////////////////////////////////
// WaitCopy() and SetCopy() are part of a blocked dequeue operation.
// WaitCopy requests to continue when the number specified can be read. 
// SetCopy updates the count as items are written into the circular buffer.
		internal void WaitCopy(System.Int32 cnt)
		{
			lock(this)
			{
				CopyEventCount = cnt; // countdown this value
			}
            
			copyautoevent.WaitOne();
		}

		internal void SetCopy(System.Int32 cnt)
		{
			lock(this)
			{
				if(CopyEventCount > 0)
				{
					CopyEventCount -= cnt; // queue has been emptied by this amount
					if(CopyEventCount <= 0)
					{
						copyautoevent.Set();
					}
				}
			}
		}


	} // end class CountEvent

	#endregion
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions