DynamicBuffer.cs

/*
 * All intellectual rights of this framework, including this source file belong to Appicacy, René Vaessen.
 * Customers of Appicacy, may copy and change it, as long as this header remains.
 *
 */
using System.Text;
using System.Net.Sockets;
using GenXdev.MemoryManagement;
using GenXdev.Configuration;
using Ninject;
using GenXdev.Buffers;
using System.Diagnostics;
 
namespace GenXdev.Buffers
{
    public delegate void DynamicBufferConversionCallback(byte[] ConversionBuffer, int Index0ByteBatchOffset, int StartPositionOffset, int Length);
 
    [DebuggerDisplay("Count = {Count}, Pos = {Position}}")]
    public class DynamicBuffer : Stream
    {
        public event EventHandler<DynamicBufferAddedEventArgs> OnAdded;
        public event EventHandler<DynamicBufferEmptyEventArgs> OnRemoved;
        public event EventHandler<DynamicBufferEmptyEventArgs> OnEmpty;
 
        void TriggerAdded(int count)
        {
            var handler = OnAdded;
 
            if (handler != null)
            {
                handler(
                    this,
                    new DynamicBufferAddedEventArgs(
                        this,
                        count
                    )
                );
            }
        }
 
        void UpdateCappedPositionAndTriggerEmptyEventIfNeeded(int removed)
        {
            if (removed == 0)
                return;
 
            if (_CappedPosition.HasValue)
                _CappedPosition = _CappedPosition.Value - removed;
 
            var onRemovedHandler = OnRemoved;
 
            if (onRemovedHandler != null)
            {
                onRemovedHandler(
                    this,
                    new DynamicBufferEmptyEventArgs(this)
                );
            }
 
            if (Count > 0)
                return;
 
            var onEmptyHandler = OnEmpty;
 
            if (onEmptyHandler != null)
            {
                onEmptyHandler(
                    this,
                    new DynamicBufferEmptyEventArgs(this)
                );
            }
 
        }
 
        #region Initialization
 
        [Inject]
        public DynamicBuffer(IServiceMemoryManager MemoryManager, IMemoryManagerConfiguration MemoryConfig)
        {
            this.SyncRoot = new object();
            this.MemoryManager = MemoryManager;
            this.InternalFragmentBufferSize = MemoryConfig.DynamicBufferFragmentSize;
            this.Reader = new BinaryReader(this, new UTF8Encoding(false));
            this.Writer = new BinaryWriter(this, new UTF8Encoding(false));
        }
 
        ~DynamicBuffer()
        {
            Dispose(false);
        }
 
        #endregion
 
        #region Fields
 
        IServiceMemoryManager MemoryManager;
        internal List<byte[]> Buffers = new List<byte[]>();
        long _Position;
        int? _CappedPosition;
        internal int ReadOffset;
        internal int WriteOffset;
        protected internal int InternalFragmentBufferSize;
        public object SyncRoot { get; private set; }
 
        #endregion
 
        #region Public
 
        public unsafe bool StartsWithSameContentAs(DynamicBuffer dynBuffer)
        {
            try
            {
                if (dynBuffer == null)
                    return false;
 
                if (Count == dynBuffer.Count)
                {
                    if (Count == 0)
                        return true;
                }
 
                if (Count > dynBuffer.Count)
                {
                    return false;
                }
 
                int length = Count;
                var bufferEnumerator = new DynamicBufferDoubleReadEnumerator(this, 0, dynBuffer, 0, ref length);
 
                foreach (var bufferInfo in bufferEnumerator)
                {
                    fixed (byte* pb1 = Buffers[bufferInfo.Buffer1Index])
                    fixed (byte* pb2 = dynBuffer.Buffers[bufferInfo.Buffer2Index])
                    {
                        var p1 = pb1;
                        var p2 = pb2;
 
                        p1 += bufferInfo.Buffer1Offset;
                        p2 += bufferInfo.Buffer2Offset;
 
                        while (--bufferInfo.Length >= 0)
                        {
                            if (*p1++ != *p2++)
                                return false;
                        }
                    }
                }
 
                return true;
            }
            catch
            {
                return false;
            }
        }
 
        public unsafe bool HasSameContent(DynamicBuffer dynBuffer)
        {
            try
            {
                if (dynBuffer == null)
                    return false;
                if (Count != dynBuffer.Count)
                {
                    return false;
                }
 
                if (Count == 0)
                    return true;
 
                int length = Count;
                var bufferEnumerator = new DynamicBufferDoubleReadEnumerator(this, 0, dynBuffer, 0, ref length);
 
                foreach (var bufferInfo in bufferEnumerator)
                {
                    fixed (byte* pb1 = Buffers[bufferInfo.Buffer1Index])
                    fixed (byte* pb2 = dynBuffer.Buffers[bufferInfo.Buffer2Index])
                    {
                        var p1 = pb1;
                        var p2 = pb2;
 
                        p1 += bufferInfo.Buffer1Offset;
                        p2 += bufferInfo.Buffer2Offset;
 
                        while (--bufferInfo.Length >= 0)
                        {
                            if (*p1++ != *p2++)
                                return false;
                        }
                    }
                }
 
                return true;
            }
            catch
            {
                return false;
            }
        }
 
        public BinaryReader Reader { get; private set; }
        public BinaryWriter Writer { get; private set; }
 
        public int Add(byte[] Source)
        {
            lock (SyncRoot)
            {
                return Add(Source, 0, Source.Length);
            }
        }
 
        public int Add(byte[] source, int sourceOffset, int length)
        {
            lock (SyncRoot)
            {
                // check
                length = Math.Min(length, source.Length - sourceOffset);
                var bytesLeft = length;
                var written = length;
                int bytesDone = 0;
 
                // loop
                while (bytesLeft > 0)
                {
                    // get buffer offset
                    int bufferOffset = WriteOffset % InternalFragmentBufferSize;
 
                    // buffer full?
                    if (bufferOffset == 0)
                    {
                        // add new buffer
                        AddBuffer();
                    }
 
                    // get nr of bytes to put in this buffer
                    int bytesNow = Math.Min(InternalFragmentBufferSize - bufferOffset, bytesLeft);
 
                    // copy bytes
                    Buffer.BlockCopy(source, sourceOffset + bytesDone, Buffers[Buffers.Count - 1], bufferOffset, bytesNow);
 
                    // update offsets
                    WriteOffset += bytesNow;
                    bytesDone += bytesNow;
                    bytesLeft -= bytesNow;
                }
 
                TriggerAdded(written);
                return written;
            }
        }
 
        public void Insert(int offset, byte[] source, int sourceOffset, int length)
        {
            lock (SyncRoot)
            {
                length = Math.Max(0, Math.Min(length, source.Length - sourceOffset));
 
                if (offset > this.Count)
                {
                    throw new InvalidOperationException();
                }
 
                if (offset == this.Count)
                {
                    Add(source, sourceOffset, length);
                    return;
                }
 
                int extraBuffersNeeded = Convert.ToInt32(Math.Ceiling(
 
                    // needed length minus any additional freespace left in lastbuffer
                    (length - (
                       // last buffer full? -> minus nothing
                       WriteOffset % InternalFragmentBufferSize == 0 ? 0 :
 
                       // last buffer not full! -> minus freespace
                       InternalFragmentBufferSize - (WriteOffset % InternalFragmentBufferSize)
                       )
                    ) / Convert.ToDouble(InternalFragmentBufferSize)
                ));
 
                // add buffers
                for (var i = 0; i < extraBuffersNeeded; i++)
                    AddBuffer();
 
                // move data away from insertion point
                int moved = 0;
                var bufferMoveEnumerator = new DynamicBufferMoveDownEnumerator(this, offset, length);
                foreach (var bufferInfo in bufferMoveEnumerator)
                {
                    Buffer.BlockCopy(
                        Buffers[bufferInfo.SourceBufferIndex],
                        bufferInfo.SourceBufferOffset,
 
                        Buffers[bufferInfo.TargetBufferIndex],
                        bufferInfo.TargetBufferOffset,
 
                        bufferInfo.Length
                    );
 
                    moved += bufferInfo.Length;
                }
                WriteOffset += length;
 
                var bufferReadEnumerator = new DynamicBufferReadEnumerator(this, offset, ref length);
 
                int copied = 0;
 
                foreach (var bufferInfo in bufferReadEnumerator)
                {
                    Buffer.BlockCopy(
 
                        source,
                        sourceOffset + copied,
 
                        Buffers[bufferInfo.SourceBufferIndex],
                        bufferInfo.SourceBufferOffset,
 
                        bufferInfo.Length
                    );
 
                    copied += bufferInfo.Length;
                }
 
                TriggerAdded(length);
            }
        }
 
        public void Add(byte Source)
        {
            lock (SyncRoot)
            {
                // not enough buffers?
                if ((WriteOffset++ / InternalFragmentBufferSize) == Buffers.Count)
                    AddBuffer();
 
                // set byte
                this[RealCount - 1] = Source;
 
                TriggerAdded(1);
            }
        }
 
        public int Remove(byte[] target, int targetOffset, int length)
        {
            lock (SyncRoot)
            {
                // get nr of bytes to (re)move
                int nrOfBytes = Math.Min(Math.Min(length, Count), target.Length - targetOffset);
 
                // check
                if (nrOfBytes <= 0)
                    return 0;
 
                // loop
                int bytesProcessed = 0;
 
                while (bytesProcessed < nrOfBytes)
                {
                    // get nr of bytes from first buffer
                    int nrOfBytesFromFirstBuffer = Math.Min(InternalFragmentBufferSize - ReadOffset, nrOfBytes - bytesProcessed);
 
                    // copy bytes
                    Buffer.BlockCopy(Buffers[0], ReadOffset, target, targetOffset + bytesProcessed, nrOfBytesFromFirstBuffer);
 
                    // update cursors
                    bytesProcessed += nrOfBytesFromFirstBuffer;
                    ReadOffset += nrOfBytesFromFirstBuffer;
                    if (ReadOffset >= InternalFragmentBufferSize)
                    {
                        ReadOffset -= InternalFragmentBufferSize;
                        WriteOffset -= InternalFragmentBufferSize;
                        ReturnFirstBuffer();
                    }
                    if (RealCount == 0)
                        Reset();
                }
 
                UpdateCappedPositionAndTriggerEmptyEventIfNeeded(nrOfBytes);
 
                return nrOfBytes;
            }
        }
 
        public int RemoveAt(int offset, byte[] target, int targetOffset, int length)
        {
            lock (SyncRoot)
            {
                if (offset == 0)
                {
                    return Remove(target, targetOffset, length);
                }
 
                var bufferEnumerator = new DynamicBufferReadEnumerator(this, offset, ref length);
                var removed = 0;
 
                foreach (var bufferInfo in bufferEnumerator)
                {
                    Buffer.BlockCopy(
                        Buffers[bufferInfo.SourceBufferIndex],
                        bufferInfo.SourceBufferOffset,
 
                        target,
                        targetOffset + removed,
 
                        bufferInfo.Length
                    );
 
                    removed += bufferInfo.Length;
                }
 
                if (RemoveAt(offset, length) != removed)
                {
                    throw new InvalidOperationException();
                }
 
                UpdateCappedPositionAndTriggerEmptyEventIfNeeded(removed);
 
                return removed;
            }
        }
 
        UTF8Encoding utf8 = new UTF8Encoding(false);
        public string ToString(Encoding encoding = null, long? maxLength = null, int offset = 0)
        {
            try
            {
                lock (SyncRoot)
                {
                    offset = Math.Min(Count, Math.Max(0, offset));
                    var max = Math.Max(0, Math.Min(Count - offset, maxLength.HasValue ? maxLength.Value : 1024));
 
                    if (max == 0) return String.Empty;
 
                    encoding = encoding == null ? (utf8) : encoding;
 
                    var oldPosition = _Position;
                    try
                    {
                        _Position = offset;
 
                        var bytes = MemoryManager.TakeBuffer((int)max);
                        try
                        {
                            Read(bytes, 0, bytes.Length);
 
                            return encoding.GetString(bytes, 0, (int)max) + (
 
                                  max < Count - offset && !maxLength.HasValue ? "..[Truncated]" : String.Empty
                                );
                        }
                        finally
                        {
                            MemoryManager.ReturnBuffer(bytes);
                        }
                    }
                    finally
                    {
                        _Position = oldPosition;
                    }
                }
            }
            catch
            {
                return String.Empty;
            }
        }
 
        public int RemoveAt(int offset, int length)
        {
            lock (SyncRoot)
            {
                if (offset == 0)
                {
                    return Remove(length);
                }
 
                var bufferEnumerator = new DynamicBufferMoveUpEnumerator(this, offset, ref length);
 
                if (length == 0)
                    return 0;
 
                var movedUp = 0;
 
                foreach (var bufferInfo in bufferEnumerator)
                {
                    // move the bytes
                    Buffer.BlockCopy(
                        Buffers[bufferInfo.SourceBufferIndex],
                        bufferInfo.SourceBufferOffset,
 
                        Buffers[bufferInfo.TargetBufferIndex],
                        bufferInfo.TargetBufferOffset,
 
                        bufferInfo.Length
                    );
 
                    movedUp += bufferInfo.Length;
                }
 
                WriteOffset -= length;
 
                ReleaseUnneededBuffers();
 
                UpdateCappedPositionAndTriggerEmptyEventIfNeeded(movedUp);
 
                return length;
            }
        }
 
        public int Remove(int NrOfBytes)
        {
            lock (SyncRoot)
            {
                NrOfBytes = Math.Min(Count, NrOfBytes);
 
                ReadOffset += NrOfBytes;
 
                while (ReadOffset >= InternalFragmentBufferSize)
                {
                    ReadOffset -= InternalFragmentBufferSize;
                    WriteOffset -= InternalFragmentBufferSize;
                    ReturnFirstBuffer();
                }
                if (RealCount == 0)
                    Reset();
 
                UpdateCappedPositionAndTriggerEmptyEventIfNeeded(NrOfBytes);
 
                return NrOfBytes;
            }
        }
 
        public int RemoveAtEnd(int NrOfBytes)
        {
            lock (SyncRoot)
            {
                NrOfBytes = Math.Min(Count, NrOfBytes);
 
                if (_CappedPosition.HasValue)
                {
                    return RemoveAt(Count - NrOfBytes, NrOfBytes);
                }
                else
                {
                    WriteOffset -= NrOfBytes;
 
                    // release unneeded buffers
                    ReleaseUnneededBuffers();
                }
 
                UpdateCappedPositionAndTriggerEmptyEventIfNeeded(NrOfBytes);
 
                return NrOfBytes;
            }
        }
 
        public int IndexOf(byte[] searchSequence, int startPosition = 0, int reducedSearchSeqTailLength = 0)
        {
            lock (SyncRoot)
            {
                startPosition = Math.Max(0, startPosition);
 
                var seqLength = Math.Max(0, searchSequence.Length - reducedSearchSeqTailLength);
 
                if (seqLength == 0)
                    return -1;
 
                if (startPosition > (Count - seqLength))
                {
                    return -1;
                }
 
                int matchCount = 0;
 
                for (var i = startPosition; i < Count; i++)
                {
                    if (this[i] == searchSequence[matchCount])
                    {
                        matchCount++;
 
                        if (matchCount == seqLength)
                            return (i + 1) - seqLength;
                    }
                    else
                    {
                        matchCount = 0;
                        if (this[i] == searchSequence[matchCount])
                        {
                            matchCount++;
 
                            if (matchCount == seqLength)
                                return (i + 1) - seqLength;
                        }
                    }
                }
 
                return -1;
            }
        }
 
        public int IndexOf(ref int startPosition, byte[] searchSequence)
        {
            var result = IndexOf(searchSequence, startPosition);
 
            if (result < 0)
            {
                startPosition = AdvanceSearchStartPosition(startPosition, searchSequence);
            }
            else
            {
                startPosition = result + 1;
            }
 
            return result;
        }
 
        public int IndexOf(ref int startPosition, ref int? count, bool SslStarted, byte[] searchSequence)
        {
            var result = IndexOf(searchSequence, startPosition);
 
            if (result < 0)
            {
                startPosition = AdvanceSearchStartPosition(startPosition, searchSequence);
 
                if (SslStarted)
                {
                    count = Math.Min(searchSequence.Length, Math.Max(1, searchSequence.Length - (Count - startPosition)));
                }
            }
            else
            {
                startPosition = result + 1;
 
                if (SslStarted)
                {
                    count = 1;
                }
            }
 
            return result;
        }
 
        private int AdvanceSearchStartPosition(int startPosition, byte[] searchSequence)
        {
            for (
                // start by expecting that only one byte from the sequence is missing
                startPosition = Math.Max(0, Count - (searchSequence.Length - 1));
 
                // it is possible that we become sure, we have none off the bytes of the search sequence in the buffer
                // by looking at the last meaningfull ones at the end of the buffer
                (startPosition < Count) &&
 
                // keep checking
                (IndexOf(searchSequence, startPosition, searchSequence.Length - (Count - startPosition)) < 0);
 
                // move startposition forward, while we notice that we have no match on the reduced searchSequence
                startPosition++
             ) { }
            return startPosition;
        }
 
        public void WriteToStream(Stream stream)
        {
            lock (SyncRoot)
            {
                int length = Count;
                var bufferEnumerator = new DynamicBufferReadEnumerator(this, 0, ref length);
 
                foreach (var bufferInfo in bufferEnumerator)
                {
                    stream.Write(
                        Buffers[bufferInfo.SourceBufferIndex],
                        bufferInfo.SourceBufferOffset,
                        bufferInfo.Length
                    );
                }
            }
        }
 
        public byte[] RemoveBytes(int? count = null)
        {
            lock (SyncRoot)
            {
                if (!count.HasValue)
                {
                    count = Count;
                }
                else
                {
                    count = Math.Min(Count, count.Value);
                }
 
                var result = new byte[count.Value];
 
                Remove(result, 0, count.Value);
 
                return result;
            }
        }
 
        public void Add(SocketAsyncEventArgs saea)
        {
            Add(saea.Buffer, saea.Offset, saea.BytesTransferred);
        }
 
        public int MoveTo(Stream stream, int MaxLength, DynamicBufferConversionCallback ConversionCallback = null)
        {
            lock (SyncRoot)
            {
                // check
                MaxLength = Math.Min(MaxLength, Count);
 
                // remember
                int NrOfBytesMoved = MaxLength;
 
                while (MaxLength > 0)
                {
                    // get nr of bytes to move from the first buffer
                    int NrOfBytesFromFirstBuffer = Math.Min(MaxLength, InternalFragmentBufferSize - ReadOffset);
 
                    // converting?
                    if (ConversionCallback != null)
                    {
                        // call conversion callback
                        ConversionCallback(Buffers[0], (NrOfBytesMoved - MaxLength) - ReadOffset, ReadOffset, NrOfBytesFromFirstBuffer);
                    }
 
                    // write to stream
                    stream.Write(Buffers[0],
                        // offset
                        ReadOffset,
                        // count
                        NrOfBytesFromFirstBuffer);
 
                    // update cursors
                    MaxLength -= NrOfBytesFromFirstBuffer;
                    ReadOffset += NrOfBytesFromFirstBuffer;
                    if (ReadOffset >= InternalFragmentBufferSize)
                    {
                        ReadOffset -= InternalFragmentBufferSize;
                        WriteOffset -= InternalFragmentBufferSize;
                        ReturnFirstBuffer();
                    }
                    if (RealCount == 0)
                        Reset();
                }
 
                UpdateCappedPositionAndTriggerEmptyEventIfNeeded(NrOfBytesMoved);
 
                return NrOfBytesMoved;
            }
        }
 
        public int IndexOf(byte SearchValue, int offset = 0, int length = -1)
        {
            lock (SyncRoot)
            {
                if (offset < 0) offset = 0;
                if (offset > Count - 1) return -1;
 
                length = (length < 0) ? Count : Math.Min(Count, length - offset);
 
                var bufferEnumerator = new DynamicBufferReadEnumerator(this, offset, ref length);
 
                foreach (var bufferInfo in bufferEnumerator)
                {
                    var idx = Array.IndexOf<byte>(
                        Buffers[bufferInfo.SourceBufferIndex],
                        SearchValue,
                        bufferInfo.SourceBufferOffset,
                        bufferInfo.Length
                    );
 
                    if (idx >= 0)
                    {
                        return (idx - ReadOffset) + (bufferInfo.SourceBufferIndex * InternalFragmentBufferSize);
                    }
                }
 
                return -1;
            }
        }
 
        public void FillAndSetSendBuffer(SocketAsyncEventArgs saea, int maxBytes, bool AddToExistingBufferData = false)
        {
            lock (SyncRoot)
            {
                maxBytes = Math.Min(maxBytes + (AddToExistingBufferData ? saea.Count : 0), Count);
 
                if ((saea.Buffer == null) || (saea.Buffer.Length != maxBytes) || (saea.Offset > 0))
                {
                    saea.SetBuffer(new byte[maxBytes], 0, maxBytes);
                }
                if (AddToExistingBufferData)
                {
                    Remove(saea.Buffer, saea.Count, maxBytes);
                }
                else
                {
                    Remove(saea.Buffer, 0, maxBytes);
                }
            }
        }
 
        public int MoveTo(DynamicBuffer Target)
        {
            int count;
 
            lock (SyncRoot)
            {
                // reference
                count = Count;
                // nothing to do?
                if (count == 0) return 0;
                // not a candidate for fast buffer swap?
                if (RealCount != count) return MoveTo(Target, Count);
 
                // lock other buffer
                lock (Target.SyncRoot)
                {
                    // not a candidate for fast buffer swap ?
                    if (Target.RealCount != 0) return MoveTo(Target, Count);
 
                    // remember own settings
                    var _Buffers = Buffers;
                    var _ReadOffset = ReadOffset;
                    var _WriteOffset = WriteOffset;
 
                    // swap buffers
                    Buffers = Target.Buffers;
 
                    // Reset self
                    _Position = 0;
                    ReadOffset = 0;
                    WriteOffset = 0;
                    CappedPosition = null;
                    StreamEndOffset = null;
 
                    // swap buffers and cursors
                    Target.Buffers = _Buffers;
                    Target.ReadOffset = _ReadOffset;
                    Target.WriteOffset = _WriteOffset;
                }
 
                // trigger event
                UpdateCappedPositionAndTriggerEmptyEventIfNeeded(count);
            }
 
            // trigger event
            Target.TriggerAdded(count);
 
            return count;
        }
 
        public int MoveTo(DynamicBuffer Target, int MaxBytes, DynamicBufferConversionCallback ConversionCallback = null)
        {
            lock (SyncRoot)
            {
                // determine how many bytes to move
                int BytesToMove = Math.Min(MaxBytes, Count);
 
                // remember how many bytes
                int BytesMoved = 0;
 
                // create temporary buffer
                byte[] TempBuffer = MemoryManager.TakeBuffer(Math.Min(InternalFragmentBufferSize, BytesToMove));
 
                try
                {
                    // loop
                    while (BytesToMove > 0)
                    {
                        // remove next block
                        int BytesRemoved = Remove(TempBuffer, 0, Math.Min(BytesToMove, TempBuffer.Length));
 
                        // converting?
                        if (ConversionCallback != null)
                        {
                            // call convertion callback
                            ConversionCallback(TempBuffer, BytesMoved, 0, BytesRemoved);
                        }
 
                        // add it to target
                        Target.Add(TempBuffer, 0, BytesRemoved);
 
                        // update counter
                        BytesToMove -= BytesRemoved;
                        BytesMoved += BytesRemoved;
                    }
                }
                finally
                {
                    // return temporary buffer
                    MemoryManager.ReturnBuffer(TempBuffer);
                }
 
                // return count
                return BytesMoved;
            }
        }
 
        public void Reset()
        {
            lock (SyncRoot)
            {
                var oldSize = Count;
 
                ReadOffset = 0;
                WriteOffset = 0;
                Position = 0;
                CappedPosition = null;
                StreamEndOffset = null;
 
                foreach (var buffer in Buffers)
                {
                    MemoryManager.ReturnBuffer(buffer);
                }
                Buffers.Clear();
 
                if (oldSize > 0)
                {
                    UpdateCappedPositionAndTriggerEmptyEventIfNeeded(oldSize);
                }
            }
        }
 
        public int Count
        {
            get
            {
                lock (SyncRoot)
                {
                    if (CappedPosition.HasValue)
                        return CappedPosition.Value;
 
                    return WriteOffset - ReadOffset;
                }
            }
        }
 
        public int? CappedPosition
        {
            get
            {
                lock (SyncRoot)
                {
                    return _CappedPosition;
                }
            }
 
            set
            {
                lock (SyncRoot)
                {
                    _CappedPosition = value;
                }
            }
        }
 
        public int RealCount
        {
            get
            {
                lock (SyncRoot)
                {
                    return WriteOffset - ReadOffset;
                }
            }
        }
 
        public byte this[int index]
        {
            get
            {
                lock (SyncRoot)
                {
                    index += ReadOffset;
                    return Buffers[index / InternalFragmentBufferSize][index % InternalFragmentBufferSize];
                }
            }
 
            set
            {
                lock (SyncRoot)
                {
                    index += ReadOffset;
                    Buffers[index / InternalFragmentBufferSize][index % InternalFragmentBufferSize] = value;
                }
            }
        }
 
        public int MoveTo(Encoding SourceEncoding, DynamicBuffer Target, Encoding TargetEncoding, int MaxBytes,
                          DynamicBufferConversionCallback ConversionCallback = null, int startPosition = 0)
        {
            lock (SyncRoot)
            {
                // check
                if (Count == 0)
                    return 0;
 
                // create counter;
                int TotalNrOfBytesToConvert = Convert.ToInt32(Math.Min(MaxBytes, Count - startPosition));
                int BytesLeft = TotalNrOfBytesToConvert;
 
                // init buffer variables
                byte[] TargetConversionBuffer = null;
                byte[] SourceUnicodeConversionBuffer = null;
                byte[] SourceEncodingByteBuffer = null;
 
                try
                {
                    // create buffer to hold the bytes that are moved and converted
                    int SourceEncodingByteBufferLength = Convert.ToInt32(Math.Ceiling(Math.Min(Convert.ToDouble(TotalNrOfBytesToConvert),
                            Convert.ToDouble(Math.Min(Convert.ToDouble(InternalFragmentBufferSize),
                            Convert.ToDouble(InternalFragmentBufferSize / (2d * Math.Max(1d, (TargetEncoding.GetMaxByteCount(1) / 2d)))))))));
 
                    SourceEncodingByteBuffer = MemoryManager.TakeBuffer(SourceEncodingByteBufferLength);
 
                    // check
                    if ((SourceEncodingByteBuffer == null) || (SourceEncodingByteBuffer.Length < SourceEncodingByteBufferLength))
                    {
                        throw new InvalidOperationException("Out of memory");
                    }
 
                    // get buffer for conversion from Source to unicode
                    int SourceUnicodeConversionBufferLength = SourceEncodingByteBufferLength * 2;
                    SourceUnicodeConversionBuffer = MemoryManager.TakeBuffer(SourceUnicodeConversionBufferLength);
 
                    // failed?
                    if ((SourceUnicodeConversionBuffer == null) || (SourceUnicodeConversionBuffer.Length < SourceUnicodeConversionBufferLength))
                    {
                        throw new InvalidOperationException("Out of memory");
                    }
 
                    // get large enough buffer for conversion from unicode to target encoding
                    int TargetConversionBufferLength = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(SourceUnicodeConversionBufferLength) / (2d / Convert.ToDouble(TargetEncoding.GetMaxByteCount(1)))));
                    TargetConversionBuffer = MemoryManager.TakeBuffer(TargetConversionBufferLength);
 
                    // failed?
                    if ((TargetConversionBuffer == null) || (TargetConversionBuffer.Length < TargetConversionBufferLength))
                    {
                        throw new InvalidOperationException("Out of memory");
                    }
 
                    // loop
                    while (BytesLeft > 0)
                    {
                        // get the payload of this batch
                        int NrOfBytesToConvert = RemoveAt(startPosition, SourceEncodingByteBuffer, 0, Math.Min(SourceEncodingByteBufferLength, BytesLeft));
 
                        // let caller convert bytes if requested on a fixed size byte to byte basis
                        if (ConversionCallback != null)
                        {
                            ConversionCallback(SourceEncodingByteBuffer, TotalNrOfBytesToConvert - BytesLeft, 0, NrOfBytesToConvert);
                        }
 
                        int bytesConverted;
                        unsafe
                        {
                            // get byte pointer to Target conversionbuffer
                            fixed (byte* TargetConversionBufferBytePointer = &TargetConversionBuffer[0])
                            {
                                // get byte pointer to sourcerencoding conversion buffer
                                fixed (byte* SourceEncodingByteBufferBytePointer = &SourceEncodingByteBuffer[0])
                                {
                                    // get byte pointer to Source conversion buffer
                                    fixed (byte* SourceUnicodeConversionBufferBytePointer = &SourceUnicodeConversionBuffer[0])
                                    {
                                        // get char pointer to Source conversion buffer
                                        char* SourceUnicodeConversionBufferCharPointer = (char*)SourceUnicodeConversionBufferBytePointer;
 
                                        // convert!
                                        bytesConverted = TargetEncoding.GetBytes(SourceUnicodeConversionBufferCharPointer,
                                            SourceEncoding.GetChars(SourceEncodingByteBufferBytePointer, NrOfBytesToConvert, SourceUnicodeConversionBufferCharPointer,
                                            NrOfBytesToConvert),
                                            TargetConversionBufferBytePointer, TargetConversionBufferLength);
                                    }
                                }
                            }
                        }
 
                        // copy result to target
                        Target.Add(TargetConversionBuffer, 0, bytesConverted);
 
                        // update cursors
                        BytesLeft -= NrOfBytesToConvert;
                    }
                }
                finally
                {
                    if (SourceEncodingByteBuffer != null)
                    {
                        MemoryManager.ReturnBuffer(SourceEncodingByteBuffer);
                    }
                    if (TargetConversionBuffer != null)
                    {
                        MemoryManager.ReturnBuffer(TargetConversionBuffer);
                    }
                    if (SourceUnicodeConversionBuffer != null)
                    {
                        MemoryManager.ReturnBuffer(SourceUnicodeConversionBuffer);
                    }
                }
 
                return TotalNrOfBytesToConvert;
            }
        }
 
        public int Add(Encoding SourceEncoding, byte[] Source, int SourceOffset, Encoding TargetEncoding, int Length, DynamicBufferConversionCallback ConversionCallback = null)
        {
            lock (SyncRoot)
            {
                // check
                if (Length <= 0)
                    return 0;
 
                // create counter;
                int TotalNrOfBytesToConvert = Convert.ToInt32(Math.Min(Length, Source.Length - SourceOffset));
                int BytesLeft = TotalNrOfBytesToConvert;
                int BytesAdded = 0;
 
                // init buffer variables
                byte[] TargetConversionBuffer = null;
                byte[] SourceUnicodeConversionBuffer = null;
 
                try
                {
                    // create buffer to hold the bytes that are moved and converted
                    int MaxBatchSize = Convert.ToInt32(Math.Ceiling(Math.Min(Convert.ToDouble(TotalNrOfBytesToConvert),
                            Convert.ToDouble(Math.Min(Convert.ToDouble(InternalFragmentBufferSize),
                            Convert.ToDouble(InternalFragmentBufferSize / (2d * Math.Max(1d, (TargetEncoding.GetMaxByteCount(1) / 2d)))))))));
 
                    // get buffer for conversion from Source to unicode
                    int SourceUnicodeConversionBufferLength = MaxBatchSize * 2;
                    SourceUnicodeConversionBuffer = MemoryManager.TakeBuffer(SourceUnicodeConversionBufferLength);
 
                    // failed?
                    if ((SourceUnicodeConversionBuffer == null) || (SourceUnicodeConversionBuffer.Length < SourceUnicodeConversionBufferLength))
                    {
                        throw new InvalidOperationException("Out of memory");
                    }
 
                    // get large enough buffer for conversion from unicode to target encoding
                    int TargetConversionBufferLength = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(SourceUnicodeConversionBufferLength) /
                                                         (2d / Convert.ToDouble(TargetEncoding.GetMaxByteCount(1)))));
                    TargetConversionBuffer = MemoryManager.TakeBuffer(TargetConversionBufferLength);
 
                    // failed?
                    if ((TargetConversionBuffer == null) || (TargetConversionBuffer.Length < TargetConversionBufferLength))
                    {
                        throw new InvalidOperationException("Out of memory");
                    }
 
                    // loop
                    while (BytesLeft > 0)
                    {
                        // get the payload of this batch
                        int NrOfBytesToConvert = Math.Min(MaxBatchSize, BytesLeft);
 
                        // let caller convert bytes if requested on a fixed size byte to byte basis
                        if (ConversionCallback != null)
                        {
                            ConversionCallback(Source, (TotalNrOfBytesToConvert - BytesLeft) - SourceOffset, (TotalNrOfBytesToConvert - BytesLeft) + SourceOffset, NrOfBytesToConvert);
                        }
 
                        int bytesConverted;
                        unsafe
                        {
                            // get byte pointer to Target conversionbuffer
                            fixed (byte* TargetConversionBufferBytePointer = &TargetConversionBuffer[0])
                            {
                                // get byte pointer to sourcerencoding conversion buffer
                                fixed (byte* SourceEncodingByteBufferBytePointer = &Source[(TotalNrOfBytesToConvert - BytesLeft) + SourceOffset])
                                {
                                    // get byte pointer to Source conversion buffer
                                    fixed (byte* SourceUnicodeConversionBufferBytePointer = &SourceUnicodeConversionBuffer[0])
                                    {
                                        // get char pointer to Source conversion buffer
                                        char* SourceUnicodeConversionBufferCharPointer = (char*)SourceUnicodeConversionBufferBytePointer;
 
                                        // convert!
                                        bytesConverted = TargetEncoding.GetBytes(SourceUnicodeConversionBufferCharPointer,
                                            SourceEncoding.GetChars(SourceEncodingByteBufferBytePointer, NrOfBytesToConvert, SourceUnicodeConversionBufferCharPointer,
                                            NrOfBytesToConvert),
                                            TargetConversionBufferBytePointer, TargetConversionBufferLength);
                                    }
                                }
                            }
                        }
 
                        // copy result to target
                        Add(TargetConversionBuffer, 0, bytesConverted);
 
                        // update counters
                        BytesAdded += NrOfBytesToConvert;
                        BytesLeft -= NrOfBytesToConvert;
                    }
                }
                finally
                {
                    if (TargetConversionBuffer != null)
                    {
                        MemoryManager.ReturnBuffer(TargetConversionBuffer);
                    }
                    if (SourceUnicodeConversionBuffer != null)
                    {
                        MemoryManager.ReturnBuffer(SourceUnicodeConversionBuffer);
                    }
                }
 
                return BytesAdded;
            }
        }
 
        #endregion
 
        #region Private
 
        void ReleaseUnneededBuffers()
        {
            lock (SyncRoot)
            {
                // release unneeded buffers
                for (var i = Buffers.Count - 1; i > (WriteOffset / InternalFragmentBufferSize); i--)
                {
                    MemoryManager.ReturnBuffer(Buffers[i]);
 
                    Buffers.RemoveAt(i);
                }
                while (ReadOffset >= InternalFragmentBufferSize)
                {
                    ReturnFirstBuffer();
                    ReadOffset -= InternalFragmentBufferSize;
                }
                if ((Buffers.Count == 1) && (ReadOffset >= WriteOffset))
                {
                    Reset();
                }
            }
        }
 
        void AddBuffer()
        {
            byte[] buffer = MemoryManager.TakeBuffer(InternalFragmentBufferSize);
 
            if ((buffer == null) || (buffer.Length < InternalFragmentBufferSize))
            {
                throw new InvalidOperationException("Out of memory when creating dynamic buffer");
            }
 
            Buffers.Add(buffer);
        }
 
        void InsertBuffer(int position)
        {
            byte[] buffer = MemoryManager.TakeBuffer(InternalFragmentBufferSize);
 
            if ((buffer == null) || (buffer.Length < InternalFragmentBufferSize))
            {
                throw new InvalidOperationException("Out of memory when creating dynamic buffer");
            }
 
            Buffers.Insert(position, buffer);
        }
 
        void ReturnFirstBuffer()
        {
            MemoryManager.ReturnBuffer(Buffers[0]);
            Buffers.RemoveAt(0);
        }
 
        #endregion
 
        #region Stream
 
        public int? StreamEndOffset;
 
        // Summary:
        // When overridden in a derived class, gets a value indicating whether the current
        // stream supports reading.
        //
        // Returns:
        // true if the stream supports reading; otherwise, false.
        public override bool CanRead
        {
            get
            {
                return true;
            }
        }
 
        //
        // Summary:
        // When overridden in a derived class, gets a value indicating whether the current
        // stream supports seeking.
        //
        // Returns:
        // true if the stream supports seeking; otherwise, false.
        public override bool CanSeek
        {
            get
            {
                return true;
            }
        }
        //
        // Summary:
        // Gets a value that determines whether the current stream can time out.
        //
        // Returns:
        // A value that determines whether the current stream can time out.
        public override bool CanTimeout
        {
            get
            {
                return false;
            }
        }
 
        //
        // Summary:
        // When overridden in a derived class, gets a value indicating whether the current
        // stream supports writing.
        //
        // Returns:
        // true if the stream supports writing; otherwise, false.
        public override bool CanWrite
        {
            get
            {
                return true;
            }
        }
 
        //
        // Summary:
        // When overridden in a derived class, gets the length in bytes of the stream.
        //
        // Returns:
        // A long value representing the length of the stream in bytes.
        //
        // Exceptions:
        // System.NotSupportedException:
        // A class derived from Stream does not support seeking.
        //
        // System.ObjectDisposedException:
        // Methods were called after the stream was closed.
        public override long Length
        {
            get
            {
                return Count;
            }
        }
        //
        // Summary:
        // When overridden in a derived class, gets or sets the position within the
        // current stream.
        //
        // Returns:
        // The current position within the stream.
        //
        // Exceptions:
        // System.IO.IOException:
        // An I/O error occurs.
        //
        // System.NotSupportedException:
        // The stream does not support seeking.
        //
        // System.ObjectDisposedException:
        // Methods were called after the stream was closed.
        public override long Position
        {
            get
            {
                lock (SyncRoot)
                {
                    return _Position;
                }
            }
 
            set
            {
                lock (SyncRoot)
                {
                    _Position = value;
                }
            }
        }
 
        // Summary:
        // Begins an asynchronous read operation.
        //
        // Parameters:
        // buffer:
        // The buffer to read the data into.
        //
        // offset:
        // The byte offset in buffer at which to begin writing data read from the stream.
        //
        // count:
        // The maximum number of bytes to read.
        //
        // callback:
        // An optional asynchronous callback, to be called when the read is complete.
        //
        // state:
        // A user-provided object that distinguishes this particular asynchronous read
        // request from other requests.
        //
        // Returns:
        // An System.IAsyncResult that represents the asynchronous read, which could
        // still be pending.
        //
        // Exceptions:
        // System.IO.IOException:
        // Attempted an asynchronous read past the end of the stream, or a disk error
        // occurs.
        //
        // System.ArgumentException:
        // One or more of the arguments is invalid.
        //
        // System.ObjectDisposedException:
        // Methods were called after the stream was closed.
        //
        // System.NotSupportedException:
        // The current Stream implementation does not support the read operation.
        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            throw new NotSupportedException();
        }
        //
        // Summary:
        // Begins an asynchronous write operation.
        //
        // Parameters:
        // buffer:
        // The buffer to write data from.
        //
        // offset:
        // The byte offset in buffer from which to begin writing.
        //
        // count:
        // The maximum number of bytes to write.
        //
        // callback:
        // An optional asynchronous callback, to be called when the write is complete.
        //
        // state:
        // A user-provided object that distinguishes this particular asynchronous write
        // request from other requests.
        //
        // Returns:
        // An IAsyncResult that represents the asynchronous write, which could still
        // be pending.
        //
        // Exceptions:
        // System.IO.IOException:
        // Attempted an asynchronous write past the end of the stream, or a disk error
        // occurs.
        //
        // System.ArgumentException:
        // One or more of the arguments is invalid.
        //
        // System.ObjectDisposedException:
        // Methods were called after the stream was closed.
        //
        // System.NotSupportedException:
        // The current Stream implementation does not support the write operation.
        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            throw new NotSupportedException();
        }
        //
        // Summary:
        // Closes the current stream and releases any resources (such as sockets and
        // file handles) associated with the current stream.
        public override void Close()
        {
            Reset();
        }
 
        //
        // Summary:
        // Releases the unmanaged resources used by the System.IO.Stream and optionally
        // releases the managed resources.
        //
        // Parameters:
        // disposing:
        // true to release both managed and unmanaged resources; false to release only
        // unmanaged resources.
        protected override void Dispose(bool disposing)
        {
            try
            {
                lock (SyncRoot)
                {
                    if (disposing)
                    {
                        OnAdded = null;
                        OnEmpty = null;
 
                        Reset();
                    }
                }
            }
            finally
            {
                base.Dispose(disposing);
            }
        }
 
        //
        // Summary:
        // When overridden in a derived class, clears all buffers for this stream and
        // causes any buffered data to be written to the underlying device.
        //
        // Exceptions:
        // System.IO.IOException:
        // An I/O error occurs.
        public override void Flush()
        {
        }
 
        //
        // Summary:
        // When overridden in a derived class, reads a sequence of bytes from the current
        // stream and advances the position within the stream by the number of bytes
        // read.
        //
        // Parameters:
        // buffer:
        // An array of bytes. When this method returns, the buffer contains the specified
        // byte array with the values between offset and (offset + count - 1) replaced
        // by the bytes read from the current source.
        //
        // offset:
        // The zero-based byte offset in buffer at which to begin storing the data read
        // from the current stream.
        //
        // count:
        // The maximum number of bytes to be read from the current stream.
        //
        // Returns:
        // The total number of bytes read into the buffer. This can be less than the
        // number of bytes requested if that many bytes are not currently available,
        // or zero (0) if the end of the stream has been reached.
        //
        // Exceptions:
        // System.ArgumentException:
        // The sum of offset and count is larger than the buffer length.
        //
        // System.ArgumentNullException:
        // buffer is null.
        //
        // System.ArgumentOutOfRangeException:
        // offset or count is negative.
        //
        // System.IO.IOException:
        // An I/O error occurs.
        //
        // System.NotSupportedException:
        // The stream does not support reading.
        //
        // System.ObjectDisposedException:
        // Methods were called after the stream was closed.
        public override int Read(byte[] buffer, int offset, int length)
        {
            lock (SyncRoot)
            {
                var bufferEnumerator = new DynamicBufferReadEnumerator(this, (int)Position, ref length);
                int written = 0;
 
                foreach (var bufferInfo in bufferEnumerator)
                {
                    Buffer.BlockCopy(
                        Buffers[bufferInfo.SourceBufferIndex],
                        bufferInfo.SourceBufferOffset,
 
                        buffer,
                        offset + written,
                        bufferInfo.Length
                    );
 
                    written += bufferInfo.Length;
                    Position += bufferInfo.Length;
                }
 
                return written;
            }
        }
 
        //
        // Summary:
        // Reads a byte from the stream and advances the position within the stream
        // by one byte, or returns -1 if at the end of the stream.
        //
        // Returns:
        // The unsigned byte cast to an Int32, or -1 if at the end of the stream.
        //
        // Exceptions:
        // System.NotSupportedException:
        // The stream does not support reading.
        //
        // System.ObjectDisposedException:
        // Methods were called after the stream was closed.
        public override int ReadByte()
        {
            lock (SyncRoot)
            {
                int result;
                if (Position < (StreamEndOffset.HasValue ? StreamEndOffset.Value : Count))
                {
                    result = this[Convert.ToInt32(Position)];
                }
                else
                {
                    return -1;
                }
                _Position++;
 
                return result;
            }
        }
 
        //
        // Summary:
        // When overridden in a derived class, sets the position within the current
        // stream.
        //
        // Parameters:
        // offset:
        // A byte offset relative to the origin parameter.
        //
        // origin:
        // A value of type System.IO.SeekOrigin indicating the reference point used
        // to obtain the new position.
        //
        // Returns:
        // The new position within the current stream.
        //
        // Exceptions:
        // System.IO.IOException:
        // An I/O error occurs.
        //
        // System.NotSupportedException:
        // The stream does not support seeking, such as if the stream is constructed
        // from a pipe or console output.
        //
        // System.ObjectDisposedException:
        // Methods were called after the stream was closed.
        public override long Seek(long offset, SeekOrigin origin)
        {
            lock (SyncRoot)
            {
                switch (origin)
                {
                    case SeekOrigin.Begin:
                        _Position = offset;
                        break;
                    case SeekOrigin.Current:
                        _Position += offset;
                        break;
                    case SeekOrigin.End:
                        _Position = (StreamEndOffset.HasValue ? StreamEndOffset.Value : Count) - offset;
                        break;
                }
                return Position;
            }
        }
        //
        // Summary:
        // When overridden in a derived class, sets the length of the current stream.
        //
        // Parameters:
        // value:
        // The desired length of the current stream in bytes.
        //
        // Exceptions:
        // System.IO.IOException:
        // An I/O error occurs.
        //
        // System.NotSupportedException:
        // The stream does not support both writing and seeking, such as if the stream
        // is constructed from a pipe or console output.
        //
        // System.ObjectDisposedException:
        // Methods were called after the stream was closed.
        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }
 
        //
        // Summary:
        // When overridden in a derived class, writes a sequence of bytes to the current
        // stream and advances the current position within this stream by the number
        // of bytes written.
        //
        // Parameters:
        // buffer:
        // An array of bytes. This method copies count bytes from buffer to the current
        // stream.
        //
        // offset:
        // The zero-based byte offset in buffer at which to begin copying bytes to the
        // current stream.
        //
        // count:
        // The number of bytes to be written to the current stream.
        //
        // Exceptions:
        // System.ArgumentException:
        // The sum of offset and count is greater than the buffer length.
        //
        // System.ObjectDisposedException:
        // Methods were called after the stream was closed.
        public override void Write(byte[] buffer, int offset, int count)
        {
            lock (SyncRoot)
            {
                Add(buffer, 0, count);
            }
        }
 
        //
        // Summary:
        // Writes a byte to the current position in the stream and advances the position
        // within the stream by one byte.
        //
        // Parameters:
        // value:
        // The byte to write to the stream.
        //
        // System.ObjectDisposedException:
        // Methods were called after the stream was closed.
        public override void WriteByte(byte value)
        {
            Add(value);
        }
 
        #endregion
 
        #region crc32
        public const uint DefaultSeed = 0xffffffff;
 
        readonly static uint[] crc32LookupTable = new uint[] {
            0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419,
            0x706AF48F, 0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4,
            0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07,
            0x90BF1D91, 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
            0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 0x136C9856,
            0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
            0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4,
            0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
            0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3,
            0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 0x26D930AC, 0x51DE003A,
            0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599,
            0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
            0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190,
            0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F,
            0x9FBFE4A5, 0xE8B8D433, 0x7807C9A2, 0x0F00F934, 0x9609A88E,
            0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
            0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED,
            0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
            0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3,
            0xFBD44C65, 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
            0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A,
            0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5,
            0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA, 0xBE0B1010,
            0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
            0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17,
            0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6,
            0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615,
            0x73DC1683, 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
            0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 0xF00F9344,
            0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
            0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A,
            0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
            0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1,
            0xA6BC5767, 0x3FB506DD, 0x48B2364B, 0xD80D2BDA, 0xAF0A1B4C,
            0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF,
            0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
            0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE,
            0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31,
            0x2CD99E8B, 0x5BDEAE1D, 0x9B64C2B0, 0xEC63F226, 0x756AA39C,
            0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
            0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B,
            0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
            0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1,
            0x18B74777, 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
            0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 0xA00AE278,
            0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7,
            0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66,
            0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
            0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605,
            0xCDD70693, 0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8,
            0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B,
            0x2D02EF8D
        };
 
        public UInt32 Calccrc32(int offset = 0, int length = -1)
        {
            lock (SyncRoot)
            {
                // init
                uint _crc = 0 ^ DefaultSeed;
 
                Updatecrc32(ref _crc, offset, length);
 
                _crc ^= DefaultSeed;
 
                return _crc;
            }
        }
 
        public static UInt32 Initializecrc32(ref uint _crc)
        {
            // init
            _crc = 0 ^ DefaultSeed;
 
            return _crc;
        }
 
        public static unsafe void Updatecrc32(ref uint _crc, byte[] buffer, int offset = -1, int count = -1)
        {
            // offset
            if (offset < 0)
            {
                offset = 0;
            }
 
            // safeguard and check
            if (count < 0)
            {
                count = buffer.Length - offset;
            }
            else
            {
                count = Math.Min(buffer.Length - offset, count);
            }
 
            fixed (uint* tabP = crc32LookupTable)
            fixed (byte* bufP = buffer)
            {
                var until = offset + count;
                var buf = bufP;
                buf += offset;
                while (offset++ < until)
                    unchecked
                    {
                        _crc = tabP[(_crc ^ *buf++) & 0xFF] ^ (_crc >> 8);
                    }
            }
        }
 
        public unsafe UInt32 Updatecrc32(ref uint _crc, int offset = 0, int length = -1)
        {
            lock (SyncRoot)
            {
                if (length < 0)
                    length = Count - offset;
 
                var bufferEnumerator = new DynamicBufferReadEnumerator(this, offset, ref length);
 
                fixed (uint* tabP = crc32LookupTable)
                    foreach (var bufferInfo in bufferEnumerator)
                    {
                        fixed (byte* bufP = Buffers[bufferInfo.SourceBufferIndex])
                        {
                            var buf = bufP;
                            buf += bufferInfo.SourceBufferOffset;
 
                            while (--bufferInfo.Length >= 0)
                                unchecked
                                {
                                    _crc = tabP[(_crc ^ *buf++) & 0xFF] ^ (_crc >> 8);
                                }
                        }
                    }
 
                return _crc;
            }
        }
 
        public static UInt32 Finishcrc32(ref uint _crc)
        {
            _crc ^= DefaultSeed;
 
            return _crc;
        }
 
        #endregion
    }
}
 
 
internal delegate void MoveCallback(DynamicBufferMoveEnumArgs e);
internal class DynamicBufferMoveEnumArgs
{
    public int SourceBufferIndex;
    public int SourceBufferOffset;
    public int TargetBufferIndex;
    public int TargetBufferOffset;
    public int Length;
 
}
 
internal class DynamicBufferMoveUpEnumerator : IEnumerator<DynamicBufferMoveEnumArgs>, IEnumerable<DynamicBufferMoveEnumArgs>
{
    DynamicBuffer Buffer;
    int FromUserIndex;
    int FromBufferIndex;
    int LengthToMove;
    int RemovedLength;
    int LengthAlreadyMoved;
    DynamicBufferMoveEnumArgs current;
 
    public DynamicBufferMoveUpEnumerator(DynamicBuffer buffer, int fromOffset, ref int length)
    {
        this.Buffer = buffer;
        this.FromUserIndex = fromOffset;
        this.RemovedLength = length;
        length = Math.Max(0, Math.Min(length, buffer.Count - fromOffset));
        this.LengthToMove = Math.Max(0, buffer.RealCount - (fromOffset + length));
        this.FromBufferIndex = (fromOffset + Buffer.ReadOffset) / buffer.InternalFragmentBufferSize;
    }
 
    public DynamicBufferMoveEnumArgs Current
    {
        get
        {
            return current;
        }
    }
 
    public void Dispose()
    {
        this.Buffer = null;
    }
 
    object System.Collections.IEnumerator.Current
    {
        get
        {
            DynamicBufferMoveEnumArgs r = this.Current;
            return r;
        }
    }
 
    public bool MoveNext()
    {
        int lengthStillToMove = this.LengthToMove - this.LengthAlreadyMoved;
 
        if (lengthStillToMove == 0)
        {
            current = null;
            return false;
        }
 
        int idxSource = FromUserIndex + Buffer.ReadOffset + RemovedLength + LengthAlreadyMoved;
        int idxTarget = FromUserIndex + Buffer.ReadOffset + LengthAlreadyMoved;
 
        int sourceBufferIndex = idxSource / Buffer.InternalFragmentBufferSize;
        int sourceBufferOffset = idxSource % Buffer.InternalFragmentBufferSize;
        int targetBufferIndex = idxTarget / Buffer.InternalFragmentBufferSize;
        int targetBufferOffset = idxTarget % Buffer.InternalFragmentBufferSize;
 
        int sourceLength = Math.Max(0, Math.Min(lengthStillToMove,
               sourceBufferIndex == Buffer.Buffers.Count - 1 ?
               Buffer.WriteOffset - sourceBufferOffset :
               Buffer.InternalFragmentBufferSize - sourceBufferOffset
            ));
 
        int targetLength = Math.Max(0, Math.Min(lengthStillToMove,
               targetBufferIndex == sourceBufferIndex ?
               sourceBufferOffset - targetBufferOffset :
               Buffer.InternalFragmentBufferSize - targetBufferOffset
            ));
 
        int length = Math.Min(sourceLength, targetLength);
 
        if (length == 0)
        {
            if (LengthAlreadyMoved < LengthToMove)
                throw new InvalidOperationException();
 
            current = null;
            return false;
        }
 
        LengthAlreadyMoved += length;
 
        current = new DynamicBufferMoveEnumArgs()
        {
            SourceBufferIndex = idxSource / Buffer.InternalFragmentBufferSize,
            SourceBufferOffset = idxSource % Buffer.InternalFragmentBufferSize,
            TargetBufferIndex = idxTarget / Buffer.InternalFragmentBufferSize,
            TargetBufferOffset = idxTarget % Buffer.InternalFragmentBufferSize,
            Length = length
        };
 
        return true;
    }
 
    public void Reset()
    {
        current = null;
        LengthAlreadyMoved = 0;
    }
 
    public IEnumerator<DynamicBufferMoveEnumArgs> GetEnumerator()
    {
        return this;
    }
 
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this;
    }
}
 
internal class DynamicBufferMoveDownEnumerator : IEnumerator<DynamicBufferMoveEnumArgs>, IEnumerable<DynamicBufferMoveEnumArgs>
{
    DynamicBuffer Buffer;
    int FromUserIndex;
    int FromBufferIndex;
    int LengthToInsert;
    int LengthToMove;
    int LengthAlreadyMoved;
    DynamicBufferMoveEnumArgs current;
 
    public DynamicBufferMoveDownEnumerator(DynamicBuffer buffer, int fromOffset, int length)
    {
        this.Buffer = buffer;
        this.FromUserIndex = fromOffset;
 
        this.LengthToInsert = length;
        this.LengthToMove = Math.Max(0, buffer.RealCount - fromOffset);
 
        if (Buffer.RealCount + length > (Buffer.Buffers.Count * Buffer.InternalFragmentBufferSize))
        {
            throw new InvalidOperationException();
        }
        this.FromBufferIndex = (fromOffset + Buffer.ReadOffset) / buffer.InternalFragmentBufferSize;
    }
 
    public DynamicBufferMoveEnumArgs Current
    {
        get
        {
            return current;
        }
    }
 
    public void Dispose()
    {
        this.Buffer = null;
    }
 
    object System.Collections.IEnumerator.Current
    {
        get
        {
            DynamicBufferMoveEnumArgs r = this.Current;
            return r;
        }
    }
 
    public bool MoveNext()
    {
        if (LengthAlreadyMoved == LengthToMove)
        {
            current = null;
            return false;
        }
 
        int length = 0;
        int stillNeeded = LengthToMove - LengthAlreadyMoved;
 
        // determine the position where writing can start
        int idxTarget = (((Buffer.WriteOffset + this.LengthToInsert) - LengthAlreadyMoved) - 1);
 
        // in what buffer is that?
        int targetBufferIndex = idxTarget / Buffer.InternalFragmentBufferSize;
        int targetBufferOffset = idxTarget % Buffer.InternalFragmentBufferSize;
 
        // determine the position where reading should start
        int idxSource = ((Buffer.WriteOffset) - LengthAlreadyMoved) - 1;
 
        // in what buffer is that?
        int sourceBufferIndex = idxSource / Buffer.InternalFragmentBufferSize;
        int sourceBufferOffset = idxSource % Buffer.InternalFragmentBufferSize;
 
        // different buffer?
        if (sourceBufferIndex != targetBufferIndex)
        {
            MoveNext_DifferentBuffers(stillNeeded,
                ref idxTarget, ref targetBufferIndex, ref targetBufferOffset,
                ref idxSource, ref sourceBufferIndex, ref sourceBufferOffset,
                out length
            );
        }
        else
        {
            MoveNext_SameBuffers(stillNeeded,
                ref idxTarget, ref targetBufferIndex, ref targetBufferOffset,
                ref idxSource, ref sourceBufferIndex, ref sourceBufferOffset,
                out length
            );
        }
 
        if (length <= 0)
        {
            if (LengthAlreadyMoved < LengthToMove)
                throw new InvalidOperationException();
 
            current = null;
            return false;
        }
 
        LengthAlreadyMoved += length;
 
        current = new DynamicBufferMoveEnumArgs()
        {
            SourceBufferIndex = idxSource / Buffer.InternalFragmentBufferSize,
            SourceBufferOffset = idxSource % Buffer.InternalFragmentBufferSize,
            TargetBufferIndex = idxTarget / Buffer.InternalFragmentBufferSize,
            TargetBufferOffset = idxTarget % Buffer.InternalFragmentBufferSize,
            Length = length
        };
 
        return true;
    }
 
    void MoveNext_DifferentBuffers(int stillNeeded, ref int idxTarget, ref int targetBufferIndex, ref int targetBufferOffset, ref int idxSource, ref int sourceBufferIndex, ref int sourceBufferOffset, out int length)
    {
        // extend target global index
        idxTarget = Math.Max(
            // the last absolute index we could possible write to
            FromUserIndex + Buffer.ReadOffset + LengthToInsert,
            // beginning of buffer if possible, or less if almost finished
            idxTarget - Math.Min(stillNeeded - 1, targetBufferOffset)
        );
 
        // determine new buffer offset
        int newTargetBufferOffset = idxTarget % Buffer.InternalFragmentBufferSize;
 
        // determine length
        int targetLength = Math.Max(0, Math.Min(stillNeeded, (targetBufferOffset - newTargetBufferOffset) + 1));
 
        // extend source global index
        idxSource = Math.Max(
            // the last absolute index we could possible read from
            FromUserIndex + Buffer.ReadOffset,
            // beginning of buffer if possible, or less if almost finished
            idxSource - Math.Min(stillNeeded - 1, sourceBufferOffset)
        );
 
        // determine new buffer offset
        int newSourceBufferOffset = idxSource % Buffer.InternalFragmentBufferSize;
 
        // determine length
        int sourceLength = Math.Max(0, Math.Min(stillNeeded, (sourceBufferOffset - newSourceBufferOffset) + 1));
 
        // now take the shortest
        length = Math.Min(sourceLength, targetLength);
 
        // now adjust indexes
 
        // determine the position where writing can start
        idxTarget -= length - 1;
 
        // in what buffer is that?
        targetBufferIndex = idxTarget / Buffer.InternalFragmentBufferSize;
        targetBufferOffset = idxTarget % Buffer.InternalFragmentBufferSize;
 
        // determine the position where reading should start
        idxSource -= length - 1;
 
        // in what buffer is that?
        sourceBufferIndex = idxSource / Buffer.InternalFragmentBufferSize;
        sourceBufferOffset = idxSource % Buffer.InternalFragmentBufferSize;
    }
 
    void MoveNext_SameBuffers(int stillNeeded, ref int idxTarget, ref int targetBufferIndex, ref int targetBufferOffset, ref int idxSource, ref int sourceBufferIndex, ref int sourceBufferOffset, out int length)
    {
        // determine limits
        int lowerBoundery =
            // is it the last buffer?
            targetBufferIndex == FromBufferIndex ?
 
            // then insertionpoint is lower limit
            (FromUserIndex + Buffer.ReadOffset) % Buffer.InternalFragmentBufferSize :
 
            // or else
            0;
 
        int upperBoundery = targetBufferOffset;
 
        int count = Math.Min(stillNeeded, (upperBoundery - lowerBoundery) + 1);
 
        // last bit?
        if (count == 1)
        {
            length = 1;
            return;
        }
 
        // determine how much we can copy
        length = count / 2;
 
        // now adjust indexes
 
        // determine the position where writing can start
        //idxTarget = (((Buffer.WriteOffset + this.LengthToInsert) - LengthAlreadyMoved) - length);
        idxTarget -= length - 1;
 
        // in what buffer is that?
        targetBufferIndex = idxTarget / Buffer.InternalFragmentBufferSize;
        targetBufferOffset = idxTarget % Buffer.InternalFragmentBufferSize;
 
        // determine the position where reading should start
        //idxSource = ((Buffer.WriteOffset) - LengthAlreadyMoved) - length;
        idxSource -= length - 1;
 
        // in what buffer is that?
        sourceBufferIndex = idxSource / Buffer.InternalFragmentBufferSize;
        sourceBufferOffset = idxSource % Buffer.InternalFragmentBufferSize;
    }
 
    public void Reset()
    {
        current = null;
        LengthAlreadyMoved = 0;
    }
 
 
    public IEnumerator<DynamicBufferMoveEnumArgs> GetEnumerator()
    {
        return this;
    }
 
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this;
    }
}
 
internal delegate void ReadCallback(DynamicBufferReadEnumArgs e);
internal class DynamicBufferReadEnumArgs
{
    public int SourceBufferIndex;
    public int SourceBufferOffset;
    public int Length;
}
 
internal class DynamicBufferDoubleReadEnumArgs
{
    public int Buffer1Index;
    public int Buffer1Offset;
    public int Buffer2Index;
    public int Buffer2Offset;
    public int Length;
}
 
internal class DynamicBufferReadEnumerator : IEnumerator<DynamicBufferReadEnumArgs>, IEnumerable<DynamicBufferReadEnumArgs>
{
    DynamicBuffer Buffer;
    int FromUserIndex;
    int FromBufferIndex;
    int Length;
    int done;
    DynamicBufferReadEnumArgs current;
 
    public DynamicBufferReadEnumerator(DynamicBuffer buffer, int fromOffset, ref int length)
    {
        this.Buffer = buffer;
        this.FromUserIndex = fromOffset;
        length = Math.Max(0, Math.Min(length, buffer.Count - fromOffset));
        this.Length = length;
        this.FromBufferIndex = (fromOffset + Buffer.ReadOffset) / buffer.InternalFragmentBufferSize;
    }
 
    public DynamicBufferReadEnumArgs Current
    {
        get
        {
            return current;
        }
    }
 
    public void Dispose()
    {
        this.Buffer = null;
    }
 
    object System.Collections.IEnumerator.Current
    {
        get
        {
            DynamicBufferReadEnumArgs r = this.Current;
            return r;
        }
    }
 
    public bool MoveNext()
    {
        if (done == Length)
        {
            current = null;
            return false;
        }
 
        int idx = FromUserIndex + Buffer.ReadOffset + done;
 
        int sourceBufferIndex = idx / Buffer.InternalFragmentBufferSize;
        int sourceBufferOffset = idx % Buffer.InternalFragmentBufferSize;
 
        int length = Math.Max(0, Math.Min(this.Length - this.done,
               sourceBufferIndex == Buffer.Buffers.Count - 1 ?
               Buffer.WriteOffset - sourceBufferOffset :
               Buffer.InternalFragmentBufferSize - sourceBufferOffset
            ));
 
        if (length == 0)
        {
            current = null;
            return false;
        }
 
        done += length;
 
        current = new DynamicBufferReadEnumArgs()
        {
            SourceBufferIndex = sourceBufferIndex,
            SourceBufferOffset = sourceBufferOffset,
            Length = length
        };
 
        return true;
    }
 
    public void Reset()
    {
        current = null;
        done = 0;
    }
 
    public IEnumerator<DynamicBufferReadEnumArgs> GetEnumerator()
    {
        return this;
    }
 
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this;
    }
}
 
internal class DynamicBufferDoubleReadEnumerator : IEnumerator<DynamicBufferDoubleReadEnumArgs>, IEnumerable<DynamicBufferDoubleReadEnumArgs>
{
    DynamicBuffer Buffer1;
    DynamicBuffer Buffer2;
    int FromUserIndex1;
    int FromUserIndex2;
    int FromBufferIndex1;
    int FromBufferIndex2;
    int Length;
    int done;
    DynamicBufferDoubleReadEnumArgs current;
 
    public DynamicBufferDoubleReadEnumerator(DynamicBuffer buffer1, int fromOffset1, DynamicBuffer buffer2, int fromOffset2, ref int length)
    {
        this.Buffer1 = buffer1;
        this.Buffer2 = buffer2;
 
        this.FromUserIndex1 = fromOffset1;
        this.FromUserIndex2 = fromOffset2;
 
        this.Length = Math.Max(0, Math.Min(length, Math.Min(buffer1.Count - fromOffset1, buffer2.Count - fromOffset2)));
        length = this.Length;
 
        this.FromBufferIndex1 = (fromOffset1 + Buffer1.ReadOffset) / buffer1.InternalFragmentBufferSize;
        this.FromBufferIndex2 = (fromOffset2 + Buffer2.ReadOffset) / buffer2.InternalFragmentBufferSize;
    }
 
    public DynamicBufferDoubleReadEnumArgs Current
    {
        get
        {
            return current;
        }
    }
 
    public void Dispose()
    {
        this.Buffer1 = null;
        this.Buffer2 = null;
    }
 
    object System.Collections.IEnumerator.Current
    {
        get
        {
            DynamicBufferDoubleReadEnumArgs r = this.Current;
            return r;
        }
    }
 
    public bool MoveNext()
    {
        if (done == Length)
        {
            current = null;
            return false;
        }
 
        int idx1 = FromUserIndex1 + Buffer1.ReadOffset + done;
 
        int sourceBufferIndex1 = idx1 / Buffer1.InternalFragmentBufferSize;
        int sourceBufferOffset1 = idx1 % Buffer1.InternalFragmentBufferSize;
 
        int length1 = Math.Max(0, Math.Min(this.Length - this.done,
               sourceBufferIndex1 == Buffer1.Buffers.Count - 1 ?
               Buffer1.WriteOffset - sourceBufferOffset1 :
               Buffer1.InternalFragmentBufferSize - sourceBufferOffset1
            ));
 
        int idx2 = FromUserIndex2 + Buffer2.ReadOffset + done;
 
        int sourceBufferIndex2 = idx2 / Buffer2.InternalFragmentBufferSize;
        int sourceBufferOffset2 = idx2 % Buffer2.InternalFragmentBufferSize;
 
        int length2 = Math.Max(0, Math.Min(this.Length - this.done,
               sourceBufferIndex2 == Buffer2.Buffers.Count - 1 ?
               Buffer2.WriteOffset - sourceBufferOffset2 :
               Buffer2.InternalFragmentBufferSize - sourceBufferOffset2
            ));
 
        int length = Math.Min(length1, length2);
 
        if (length == 0)
        {
            current = null;
            return false;
        }
 
        done += length;
 
        current = new DynamicBufferDoubleReadEnumArgs()
        {
            Buffer1Index = sourceBufferIndex1,
            Buffer1Offset = sourceBufferOffset1,
            Buffer2Index = sourceBufferIndex2,
            Buffer2Offset = sourceBufferOffset2,
            Length = length
        };
 
        return true;
    }
 
    public void Reset()
    {
        current = null;
        done = 0;
    }
 
    public IEnumerator<DynamicBufferDoubleReadEnumArgs> GetEnumerator()
    {
        return this;
    }
 
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return this;
    }
}