using System;
using System.IO;
namespace Org.BouncyCastle.Tls
{
/// A queue for bytes. This file could be more optimized.
public sealed class ByteQueue
{
/// The smallest number which can be written as 2^x which is bigger than i.
public static int NextTwoPow(int i)
{
/*
* This code is based of a lot of code I found on the Internet which mostly
* referenced a book called "Hacking delight".
*/
i |= i >> 1;
i |= i >> 2;
i |= i >> 4;
i |= i >> 8;
i |= i >> 16;
return i + 1;
}
/// The buffer where we store our data.
private byte[] m_databuf;
/// How many bytes at the beginning of the buffer are skipped.
private int m_skipped = 0;
/// How many bytes in the buffer are valid data.
private int m_available = 0;
private bool m_readOnlyBuf = false;
public ByteQueue()
: this(0)
{
}
public ByteQueue(int capacity)
{
this.m_databuf = capacity == 0 ? TlsUtilities.EmptyBytes : new byte[capacity];
}
public ByteQueue(byte[] buf, int off, int len)
{
this.m_databuf = buf;
this.m_skipped = off;
this.m_available = len;
this.m_readOnlyBuf = true;
}
/// Add some data to our buffer.
/// A byte-array to read data from.
/// How many bytes to skip at the beginning of the array.
/// How many bytes to read from the array.
public void AddData(byte[] buf, int off, int len)
{
if (m_readOnlyBuf)
throw new InvalidOperationException("Cannot add data to read-only buffer");
if ((m_skipped + m_available + len) > m_databuf.Length)
{
int desiredSize = ByteQueue.NextTwoPow(m_available + len);
if (desiredSize > m_databuf.Length)
{
byte[] tmp = new byte[desiredSize];
Array.Copy(m_databuf, m_skipped, tmp, 0, m_available);
m_databuf = tmp;
}
else
{
Array.Copy(m_databuf, m_skipped, m_databuf, 0, m_available);
}
m_skipped = 0;
}
Array.Copy(buf, off, m_databuf, m_skipped + m_available, len);
m_available += len;
}
/// The number of bytes which are available in this buffer.
public int Available
{
get { return m_available; }
}
/// Copy some bytes from the beginning of the data to the provided .
/// The to copy the bytes to.
/// How many bytes to copy.
public void CopyTo(Stream output, int length)
{
if (length > m_available)
throw new InvalidOperationException("Cannot copy " + length + " bytes, only got " + m_available);
output.Write(m_databuf, m_skipped, length);
}
/// Read data from the buffer.
/// The buffer where the read data will be copied to.
/// How many bytes to skip at the beginning of buf.
/// How many bytes to read at all.
/// How many bytes from our data to skip.
public void Read(byte[] buf, int offset, int len, int skip)
{
if ((buf.Length - offset) < len)
{
throw new ArgumentException("Buffer size of " + buf.Length
+ " is too small for a read of " + len + " bytes");
}
if ((m_available - skip) < len)
{
throw new InvalidOperationException("Not enough data to read");
}
Array.Copy(m_databuf, m_skipped + skip, buf, offset, len);
}
/// Return a over some bytes at the beginning of the data.
///
/// How many bytes will be readable.
/// A over the data.
internal HandshakeMessageInput ReadHandshakeMessage(int length)
{
if (length > m_available)
throw new InvalidOperationException("Cannot read " + length + " bytes, only got " + m_available);
int position = m_skipped;
m_available -= length;
m_skipped += length;
return new HandshakeMessageInput(m_databuf, position, length);
}
public int ReadInt32()
{
if (m_available < 4)
throw new InvalidOperationException("Not enough data to read");
return TlsUtilities.ReadInt32(m_databuf, m_skipped);
}
/// Remove some bytes from our data from the beginning.
/// How many bytes to remove.
public void RemoveData(int i)
{
if (i > m_available)
throw new InvalidOperationException("Cannot remove " + i + " bytes, only got " + m_available);
/*
* Skip the data.
*/
m_available -= i;
m_skipped += i;
}
/// Remove data from the buffer.
/// The buffer where the removed data will be copied to.
/// How many bytes to skip at the beginning of buf.
/// How many bytes to read at all.
/// How many bytes from our data to skip.
public void RemoveData(byte[] buf, int off, int len, int skip)
{
Read(buf, off, len, skip);
RemoveData(skip + len);
}
public byte[] RemoveData(int len, int skip)
{
byte[] buf = new byte[len];
RemoveData(buf, 0, len, skip);
return buf;
}
public void Shrink()
{
if (m_available == 0)
{
m_databuf = TlsUtilities.EmptyBytes;
m_skipped = 0;
}
else
{
int desiredSize = ByteQueue.NextTwoPow(m_available);
if (desiredSize < m_databuf.Length)
{
byte[] tmp = new byte[desiredSize];
Array.Copy(m_databuf, m_skipped, tmp, 0, m_available);
m_databuf = tmp;
m_skipped = 0;
}
}
}
}
}