diff options
Diffstat (limited to 'crypto/src/tls/ByteQueue.cs')
-rw-r--r-- | crypto/src/tls/ByteQueue.cs | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/crypto/src/tls/ByteQueue.cs b/crypto/src/tls/ByteQueue.cs new file mode 100644 index 000000000..b0241972e --- /dev/null +++ b/crypto/src/tls/ByteQueue.cs @@ -0,0 +1,195 @@ +using System; +using System.IO; + +namespace Org.BouncyCastle.Tls +{ + /// <summary>A queue for bytes. This file could be more optimized.</summary> + public sealed class ByteQueue + { + /// <returns>The smallest number which can be written as 2^x which is bigger than i.</returns> + 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; + } + + /// <summary>The buffer where we store our data.</summary> + private byte[] m_databuf; + + /// <summary>How many bytes at the beginning of the buffer are skipped.</summary> + private int m_skipped = 0; + + /// <summary>How many bytes in the buffer are valid data.</summary> + 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; + } + + /// <summary>Add some data to our buffer.</summary> + /// <param name="buf">A byte-array to read data from.</param> + /// <param name="off">How many bytes to skip at the beginning of the array.</param> + /// <param name="len">How many bytes to read from the array.</param> + 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; + } + + /// <returns>The number of bytes which are available in this buffer.</returns> + public int Available + { + get { return m_available; } + } + + /// <summary>Copy some bytes from the beginning of the data to the provided <see cref="Stream"/>.</summary> + /// <param name="output">The <see cref="Stream"/> to copy the bytes to.</param> + /// <param name="length">How many bytes to copy.</param> + 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); + } + + /// <summary>Read data from the buffer.</summary> + /// <param name="buf">The buffer where the read data will be copied to.</param> + /// <param name="offset">How many bytes to skip at the beginning of buf.</param> + /// <param name="len">How many bytes to read at all.</param> + /// <param name="skip">How many bytes from our data to skip.</param> + 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); + } + + /// <summary>Return a <see cref="HandshakeMessageInput"/> over some bytes at the beginning of the data. + /// </summary> + /// <param name="length">How many bytes will be readable.</param> + /// <returns>A <see cref="HandshakeMessageInput"/> over the data.</returns> + 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); + } + + /// <summary>Remove some bytes from our data from the beginning.</summary> + /// <param name="i">How many bytes to remove.</param> + 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; + } + + /// <summary>Remove data from the buffer.</summary> + /// <param name="buf">The buffer where the removed data will be copied to.</param> + /// <param name="off">How many bytes to skip at the beginning of buf.</param> + /// <param name="len">How many bytes to read at all.</param> + /// <param name="skip">How many bytes from our data to skip.</param> + 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; + } + } + } + } +} |