summary refs log tree commit diff
path: root/crypto/src/tls/ByteQueue.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/tls/ByteQueue.cs')
-rw-r--r--crypto/src/tls/ByteQueue.cs195
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;
+                }
+            }
+        }
+    }
+}