#region File Header
//
// FiniteQueue.cs - A generic finite queue (ring/circular buffer) with index [] operator.
//
// Copyright (C) Javier Valcarce. BSD License
#endregion
#region Using Statements
using System;
using System.Threading;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
#endregion
namespace SolarPanel
{
///
/// FinitQueueBA (BA = Blocking API)
/// This class implements a thread-safe generic finite queue (also called circular buffer,
/// ring buffer or bounded buffer) with *blocking* Enqueue()/Dequeue() operations. It has
/// also a index [] operator to examine and change the data inside the queue.
///
public class FiniteQueueBA : IFiniteQueue
{
#region Private Members
int head;
int tail;
T[] buff;
int count;
int space;
object sync = new object();
#endregion
#region Constructor
///
/// Creates a ring buffer of n elements of type T.
///
/// Queue's capacity
public FiniteQueueBA(int n)
{
if (n < 1)
throw new ArgumentException();
buff = new T[n];
Clear();
}
///
/// Creates a ring buffer using a T array as the underlaying storage.
///
/// Array of T elements
public FiniteQueueBA(T[] v)
{
if (v.Length < 1)
throw new ArgumentException();
buff = v;
Clear();
}
#endregion
#region Public Methods
///
/// Removes all objects from the FiniteQueue
///
public void Clear()
{
lock (sync)
{
head = 0;
tail = 0;
count = 0;
space = buff.Length;
}
}
///
/// Enqueue a single object to the end of the FiniteQueue
///
///
public int Enqueue(T token)
{
lock (sync)
{
if (space == 0)
Monitor.Wait(sync);
buff[tail] = token;
tail++;
if (tail == buff.Length)
tail = 0;
count++;
space--;
Monitor.Pulse(sync);
return 1;
}
}
///
/// Enqueue an array of objects to the end of the RingBuffer]]>
///
///
public int Enqueue(T[] src, int offset, int count)
{
lock (sync)
{
if (this.space < count)
Monitor.Wait(sync);
for (int i = 0; i < count; i++)
{
buff[tail] = src[offset + i];
tail++;
if (tail == buff.Length)
tail = 0;
}
this.count += count;
this.space -= count;
Monitor.Pulse(sync);
return count;
}
}
///
/// Removes and returns the token at the beginning of the FiniteQueue
///
/// The number of tokens dequeued, always 1
public int Dequeue(out T token)
{
lock (sync)
{
if (count == 0)
Monitor.Wait(sync);
token = buff[head];
head++;
if (head == buff.Length)
head = 0;
count--;
space++;
Monitor.Pulse(sync);
return 1;
}
}
///
/// Removes and returns the first count tokens at the beginning of the FiniteQueue
///
/// Destination array to store the dequeued tokens
/// Offset in the dst array
/// Number of tokens to be dequeued
///
public int Dequeue(T[] dst, int offset, int count)
{
lock (sync)
{
if (this.count < count)
Monitor.Wait(sync);
for (int i = 0; i < count; i++)
{
dst[offset + i] = buff[head];
head++;
if (head == buff.Length)
head = 0;
}
this.count -= count;
this.space += count;
Monitor.Pulse(sync);
return count;
}
}
#endregion
#region Properties
///
/// Index operator.
/// Warning: this operator use MOD (%) to compute the index, its performance is not
/// very good. Use it sporadically.
///
/// Index in the queue
/// The indexed element
public T this[int index]
{
get
{
lock (sync)
{
return buff[(head + index) % buff.Length];
}
}
set
{
lock (sync)
{
buff[(head + index) % buff.Length] = value;
}
}
}
public int Capacity
{
get
{
return buff.Length;
}
}
///
/// Number of tokens in the queue
///
public int Count
{
get
{
lock (sync)
{
return count;
}
}
}
///
/// Available free space in the queue
///
public int Space
{
get
{
lock (sync)
{
return space;
}
}
}
#endregion
}
}