/
githubmirror
/
shadowsocks-windows
Обзор
Документация
Войти
/
githubmirror
/
shadowsocks-windows
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
v4
shadowsocks-csharp/Encryption/CircularBuffer/ByteCircularBuffer.cs
510 строк
23 KB
Syrone Wong
Replace circular buffer with the blockcopy variant
19 май 2017, 10:56
19 май 2017, 10:56
2eeebc2
Код
Авторство
О чём код?
#region Original License //New BSD License(BSD) // //Copyright(c) 2014-2015 Cyotek Ltd //Copyright(c) 2012, Alex Regueiro //All rights reserved. // //Redistribution and use in source and binary forms, with or without //modification, are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // * Neither the name of Cyotek nor the // names of its contributors may be used to endorse or promote products // derived from this software without specific prior written permission. // //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND //ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED //WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE //DISCLAIMED.IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY //DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND //ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS //SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endregion using System; using System.Collections.Generic; namespace Shadowsocks.Encryption.CircularBuffer { /// <summary> /// Represents a first-in, first-out collection of objects using a fixed buffer. /// </summary> /// <remarks> /// <para>The capacity of a <see cref="ByteCircularBuffer" /> is the number of elements the <see cref="ByteCircularBuffer"/> can hold. </para> /// <para>ByteCircularBuffer accepts <c>null</c> as a valid value for reference types and allows duplicate elements.</para> /// <para>The <see cref="Get()"/> methods will remove the items that are returned from the ByteCircularBuffer. To view the contents of the ByteCircularBuffer without removing items, use the <see cref="Peek()"/> or <see cref="PeekLast"/> methods.</para> /// </remarks> public class ByteCircularBuffer { // based on http://circularbuffer.codeplex.com/ // http://en.wikipedia.org/wiki/Circular_buffer // modified from https://github.com/cyotek/Cyotek.Collections.Generic.CircularBuffer // some code taken from https://github.com/xorxornop/RingBuffer // and https://github.com/xorxornop/PerfCopy #region Instance Fields private byte[] _buffer; private int _capacity; #endregion #region Public Constructors /// <summary> /// Initializes a new instance of the <see cref="ByteCircularBuffer"/> class that is empty and has the specified initial capacity. /// </summary> /// <param name="capacity">The maximum capcity of the buffer.</param> /// <exception cref="System.ArgumentException">Thown if the <paramref name="capacity"/> is less than zero.</exception> public ByteCircularBuffer(int capacity) { if (capacity < 0) { throw new ArgumentException("The buffer capacity must be greater than or equal to zero.", nameof(capacity)); } _buffer = new byte[capacity]; this.Capacity = capacity; this.Size = 0; this.Head = 0; this.Tail = 0; } #endregion #region Public Properties /// <summary> /// Gets or sets the total number of elements the internal data structure can hold. /// </summary> /// <value>The total number of elements that the <see cref="ByteCircularBuffer"/> can contain.</value> /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the specified new capacity is smaller than the current contents of the buffer.</exception> public int Capacity { get { return _capacity; } set { if (value != _capacity) { if (value < this.Size) { throw new ArgumentOutOfRangeException(nameof(value), value, "The new capacity must be greater than or equal to the buffer size."); } var newBuffer = new byte[value]; if (this.Size > 0) { this.CopyTo(newBuffer); } _buffer = newBuffer; _capacity = value; } } } /// <summary> /// Gets the index of the beginning of the buffer data. /// </summary> /// <value>The index of the first element in the buffer.</value> public int Head { get; protected set; } /// <summary> /// Gets a value indicating whether the buffer is empty. /// </summary> /// <value><c>true</c> if buffer is empty; otherwise, <c>false</c>.</value> public virtual bool IsEmpty => this.Size == 0; /// <summary> /// Gets a value indicating whether the buffer is full. /// </summary> /// <value><c>true</c> if the buffer is full; otherwise, <c>false</c>.</value> /// <remarks>The <see cref="IsFull"/> property always returns <c>false</c> if the <see cref="AllowOverwrite"/> property is set to <c>true</c>.</remarks> public virtual bool IsFull => this.Size == this.Capacity; /// <summary> /// Gets the number of elements contained in the <see cref="ByteCircularBuffer"/>. /// </summary> /// <value>The number of elements contained in the <see cref="ByteCircularBuffer"/>.</value> public int Size { get; protected set; } /// <summary> /// Gets the index of the end of the buffer data. /// </summary> /// <value>The index of the last element in the buffer.</value> public int Tail { get; protected set; } #endregion #region Public Members /// <summary> /// Removes all items from the <see cref="ByteCircularBuffer" />. /// </summary> public void Clear() { this.Size = 0; this.Head = 0; this.Tail = 0; _buffer = new byte[this.Capacity]; } /// <summary> /// Determines whether the <see cref="ByteCircularBuffer" /> contains a specific value. /// </summary> /// <param name="item">The object to locate in the <see cref="ByteCircularBuffer" />.</param> /// <returns><c>true</c> if <paramref name="item" /> is found in the <see cref="ByteCircularBuffer" />; otherwise, <c>false</c>.</returns> public bool Contains(byte item) { var bufferIndex = this.Head; var comparer = EqualityComparer<byte>.Default; var result = false; for (int i = 0; i < this.Size; i++, bufferIndex++) { if (bufferIndex == this.Capacity) { bufferIndex = 0; } if (comparer.Equals(_buffer[bufferIndex], item)) { result = true; break; } } return result; } /// <summary> /// Copies the entire <see cref="ByteCircularBuffer"/> to a compatible one-dimensional array, starting at the beginning of the target array. /// </summary> /// <param name="array">The one-dimensional <see cref="Array"/> that is the destination of the elements copied from <see cref="ByteCircularBuffer"/>. The <see cref="Array"/> must have zero-based indexing.</param> public void CopyTo(byte[] array) { this.CopyTo(array, 0); } /// <summary> /// Copies the entire <see cref="ByteCircularBuffer"/> to a compatible one-dimensional array, starting at the specified index of the target array. /// </summary> /// <param name="array">The one-dimensional <see cref="Array"/> that is the destination of the elements copied from <see cref="ByteCircularBuffer"/>. The <see cref="Array"/> must have zero-based indexing.</param> /// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param> public void CopyTo(byte[] array, int arrayIndex) { this.CopyTo(this.Head, array, arrayIndex, Math.Min(this.Size, array.Length - arrayIndex)); } /// <summary> /// Copies a range of elements from the <see cref="ByteCircularBuffer"/> to a compatible one-dimensional array, starting at the specified index of the target array. /// </summary> /// <param name="index">The zero-based index in the source <see cref="ByteCircularBuffer"/> at which copying begins.</param> /// <param name="array">The one-dimensional <see cref="Array"/> that is the destination of the elements copied from <see cref="ByteCircularBuffer"/>. The <see cref="Array"/> must have zero-based indexing.</param> /// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param> /// <param name="count">The number of elements to copy.</param> public virtual void CopyTo(int index, byte[] array, int arrayIndex, int count) { if (count > this.Size) { throw new ArgumentOutOfRangeException(nameof(count), count, "The read count cannot be greater than the buffer size."); } var startAnchor = index; var dstIndex = arrayIndex; while (count > 0) { int chunk = Math.Min(Capacity - startAnchor, count); Buffer.BlockCopy(_buffer, startAnchor, array, dstIndex, chunk); startAnchor = (startAnchor + chunk == Capacity) ? 0 : startAnchor + chunk; dstIndex += chunk; count -= chunk; } } /// <summary> /// Removes and returns the specified number of objects from the beginning of the <see cref="ByteCircularBuffer"/>. /// </summary> /// <param name="count">The number of elements to remove and return from the <see cref="ByteCircularBuffer"/>.</param> /// <returns>The objects that are removed from the beginning of the <see cref="ByteCircularBuffer"/>.</returns> public byte[] Get(int count) { if (count <= 0) throw new ArgumentOutOfRangeException("should greater than 0"); var result = new byte[count]; this.Get(result); return result; } /// <summary> /// Copies and removes the specified number elements from the <see cref="ByteCircularBuffer"/> to a compatible one-dimensional array, starting at the beginning of the target array. /// </summary> /// <param name="array">The one-dimensional <see cref="Array"/> that is the destination of the elements copied from <see cref="ByteCircularBuffer"/>. The <see cref="Array"/> must have zero-based indexing.</param> /// <returns>The actual number of elements copied into <paramref name="array"/>.</returns> public int Get(byte[] array) { if (array.Length <= 0) throw new ArgumentOutOfRangeException("should greater than 0"); return this.Get(array, 0, array.Length); } /// <summary> /// Copies and removes the specified number elements from the <see cref="ByteCircularBuffer"/> to a compatible one-dimensional array, starting at the specified index of the target array. /// </summary> /// <param name="array">The one-dimensional <see cref="Array"/> that is the destination of the elements copied from <see cref="ByteCircularBuffer"/>. The <see cref="Array"/> must have zero-based indexing.</param> /// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param> /// <param name="count">The number of elements to copy.</param> /// <returns>The actual number of elements copied into <paramref name="array"/>.</returns> public virtual int Get(byte[] array, int arrayIndex, int count) { if (arrayIndex < 0) { throw new ArgumentOutOfRangeException(nameof(arrayIndex), "Negative offset specified. Offsets must be positive."); } if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count), "Negative count specified. Count must be positive."); } if (count > this.Size) { throw new ArgumentException("Ringbuffer contents insufficient for take/read operation.", nameof(count)); } if (array.Length < arrayIndex + count) { throw new ArgumentException("Destination array too small for requested output."); } var bytesCopied = 0; var dstIndex = arrayIndex; while (count > 0) { int chunk = Math.Min(Capacity - this.Head, count); Buffer.BlockCopy(_buffer, this.Head, array, dstIndex, chunk); this.Head = (this.Head + chunk == Capacity) ? 0 : this.Head + chunk; this.Size -= chunk; dstIndex += chunk; bytesCopied += chunk; count -= chunk; } return bytesCopied; } /// <summary> /// Removes and returns the object at the beginning of the <see cref="ByteCircularBuffer"/>. /// </summary> /// <returns>The object that is removed from the beginning of the <see cref="ByteCircularBuffer"/>.</returns> /// <exception cref="System.InvalidOperationException">Thrown if the buffer is empty.</exception> /// <remarks>This method is similar to the <see cref="Peek()"/> method, but <c>Peek</c> does not modify the <see cref="ByteCircularBuffer"/>.</remarks> public virtual byte Get() { if (this.IsEmpty) { throw new InvalidOperationException("The buffer is empty."); } var item = _buffer[this.Head]; if (++this.Head == this.Capacity) { this.Head = 0; } this.Size--; return item; } /// <summary> /// Returns the object at the beginning of the <see cref="ByteCircularBuffer"/> without removing it. /// </summary> /// <returns>The object at the beginning of the <see cref="ByteCircularBuffer"/>.</returns> /// <exception cref="System.InvalidOperationException">Thrown if the buffer is empty.</exception> public virtual byte Peek() { if (this.IsEmpty) { throw new InvalidOperationException("The buffer is empty."); } var item = _buffer[this.Head]; return item; } /// <summary> /// Returns the specified number of objects from the beginning of the <see cref="ByteCircularBuffer"/>. /// </summary> /// <param name="count">The number of elements to return from the <see cref="ByteCircularBuffer"/>.</param> /// <returns>The objects that from the beginning of the <see cref="ByteCircularBuffer"/>.</returns> /// <exception cref="System.InvalidOperationException">Thrown if the buffer is empty.</exception> public virtual byte[] Peek(int count) { if (this.IsEmpty) { throw new InvalidOperationException("The buffer is empty."); } var items = new byte[count]; this.CopyTo(items); return items; } /// <summary> /// Returns the object at the end of the <see cref="ByteCircularBuffer"/> without removing it. /// </summary> /// <returns>The object at the end of the <see cref="ByteCircularBuffer"/>.</returns> /// <exception cref="System.InvalidOperationException">Thrown if the buffer is empty.</exception> public virtual byte PeekLast() { int bufferIndex; if (this.IsEmpty) { throw new InvalidOperationException("The buffer is empty."); } if (this.Tail == 0) { bufferIndex = this.Size - 1; } else { bufferIndex = this.Tail - 1; } var item = _buffer[bufferIndex]; return item; } /// <summary> /// Copies an entire compatible one-dimensional array to the <see cref="ByteCircularBuffer"/>. /// </summary> /// <param name="array">The one-dimensional <see cref="Array"/> that is the source of the elements copied to <see cref="ByteCircularBuffer"/>. The <see cref="Array"/> must have zero-based indexing.</param> /// <exception cref="System.InvalidOperationException">Thrown if buffer does not have sufficient capacity to put in new items.</exception> /// <remarks>If <see cref="Size"/> plus the size of <paramref name="array"/> exceeds the capacity of the <see cref="ByteCircularBuffer"/> and the <see cref="AllowOverwrite"/> property is <c>true</c>, the oldest items in the <see cref="ByteCircularBuffer"/> are overwritten with <paramref name="array"/>.</remarks> public int Put(byte[] array) { return this.Put(array, 0, array.Length); } /// <summary> /// Copies a range of elements from a compatible one-dimensional array to the <see cref="ByteCircularBuffer"/>. /// </summary> /// <param name="array">The one-dimensional <see cref="Array"/> that is the source of the elements copied to <see cref="ByteCircularBuffer"/>. The <see cref="Array"/> must have zero-based indexing.</param> /// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param> /// <param name="count">The number of elements to copy.</param> /// <exception cref="System.InvalidOperationException">Thrown if buffer does not have sufficient capacity to put in new items.</exception> /// <remarks>If <see cref="Size"/> plus <paramref name="count"/> exceeds the capacity of the <see cref="ByteCircularBuffer"/> and the <see cref="AllowOverwrite"/> property is <c>true</c>, the oldest items in the <see cref="ByteCircularBuffer"/> are overwritten with <paramref name="array"/>.</remarks> public virtual int Put(byte[] array, int arrayIndex, int count) { if (count <= 0) throw new ArgumentOutOfRangeException(nameof(count), "Count must be positive."); if (this.Size + count > this.Capacity) { throw new InvalidOperationException("The buffer does not have sufficient capacity to put new items."); } if (array.Length < arrayIndex + count) { throw new ArgumentException("Source array too small for requested input."); } var srcIndex = arrayIndex; var bytesToProcess = count; while (bytesToProcess > 0) { int chunk = Math.Min(Capacity - Tail, bytesToProcess); Buffer.BlockCopy(array, srcIndex, _buffer, Tail, chunk); Tail = (Tail + chunk == Capacity) ? 0 : Tail + chunk; this.Size += chunk; srcIndex += chunk; bytesToProcess -= chunk; } return count; } /// <summary> /// Adds a byte to the end of the <see cref="ByteCircularBuffer"/>. /// </summary> /// <param name="item">The byte to add to the <see cref="ByteCircularBuffer"/>. </param> /// <exception cref="System.InvalidOperationException">Thrown if buffer does not have sufficient capacity to put in new items.</exception> public virtual void Put(byte item) { if (IsFull) { throw new InvalidOperationException("The buffer does not have sufficient capacity to put new items."); } _buffer[this.Tail] = item; this.Tail++; if (this.Size == this.Capacity) { this.Head++; if (this.Head >= this.Capacity) { this.Head -= this.Capacity; } } if (this.Tail == this.Capacity) { this.Tail = 0; } if (this.Size != this.Capacity) { this.Size++; } } /// <summary> /// Increments the starting index of the data buffer in the <see cref="ByteCircularBuffer"/>. /// </summary> /// <param name="count">The number of elements to increment the data buffer start index by.</param> public void Skip(int count) { if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count), "Negative count specified. Count must be positive."); } if (count > this.Size) { throw new ArgumentException("Ringbuffer contents insufficient for operation.", nameof(count)); } // Modular division gives new offset position this.Head = (this.Head + count) % Capacity; this.Size -= count; } /// <summary> /// Copies the <see cref="ByteCircularBuffer"/> elements to a new array. /// </summary> /// <returns>A new array containing elements copied from the <see cref="ByteCircularBuffer"/>.</returns> /// <remarks>The <see cref="ByteCircularBuffer"/> is not modified. The order of the elements in the new array is the same as the order of the elements from the beginning of the <see cref="ByteCircularBuffer"/> to its end.</remarks> public byte[] ToArray() { var result = new byte[this.Size]; this.CopyTo(result); return result; } #endregion } }