src/Proton/Buffer/ProtonByteBuffer.cs (749 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Text; using Apache.Qpid.Proton.Utilities; namespace Apache.Qpid.Proton.Buffer { /// <summary> /// A proton buffer implementation that wraps a single heap allocated /// byte array and provides read and write operations on that array /// along with self resizing based on capacity limits. /// </summary> public sealed class ProtonByteBuffer : IProtonBuffer, IReadableComponent, IWritableComponent { /// <summary> /// Default initial capacity when created without initial value. /// </summary> public static readonly int DefaultCapacity = 64; /// <summary> /// Default max capacity based on maximum array size limit as this /// buffer is backed by a byte array. /// </summary> public static readonly int DefaultMaximumCapacity = int.MaxValue; private byte[] array; /// <summary> /// How far into the array does position zero begin /// </summary> private int arrayOffset; /// <summary> /// Limits the usable region of the array if not set to the array length. /// This allows the window of operations to exists in a subregion of the /// array. /// </summary> private int arrayLimit; private long readOffset; private long writeOffset; private readonly long maxCapacity; /// <summary> /// Create a new proton byte buffer instance with default initial capacity /// and limited only by the size of a byte array in max capacity. /// </summary> public ProtonByteBuffer() : this(DefaultCapacity, DefaultMaximumCapacity) { } /// <summary> /// Create a new proton byte buffer instance with given initial capacity /// and limited only by the size of a byte array in max capacity. /// </summary> /// <param name="initialCapacity">The initial capacity of this buffer</param> public ProtonByteBuffer(long initialCapacity) : this(initialCapacity, DefaultMaximumCapacity) { } /// <summary> /// Create a new proton byte buffer instance with given initial capacity /// and limited to a max capacity of the given amount. /// </summary> /// <param name="initialCapacity">The initial capacity of this buffer</param> public ProtonByteBuffer(long initialCapacity, long maxCapacity) : base() { if (initialCapacity < 0) { throw new ArgumentOutOfRangeException(nameof(initialCapacity), "Initial capacity cannot be negative"); } if (maxCapacity < initialCapacity) { throw new ArgumentOutOfRangeException(nameof(maxCapacity), "The maximum capacity cannot be less than the initial value"); } this.array = new byte[initialCapacity]; this.arrayOffset = 0; this.arrayLimit = array.Length; this.maxCapacity = maxCapacity; } /// <summary> /// Create a new proton byte buffer instance with given backing array whose /// size determines that largest the buffer can ever be. /// </summary> /// <param name="backingArray">The actual byte array that backs this buffer</param> public ProtonByteBuffer(byte[] backingArray) : this(backingArray, 0, int.MaxValue) { } /// <summary> /// Create a new proton byte buffer instance with given backing array as the /// starting backing store and uses the provided max capacity value to control /// how large the buffer could ever grow. /// </summary> /// <param name="backingArray">The actual byte array that backs this buffer</param> /// <param name="maxCapacity">The maximum capacity this buffer can grow to</param> public ProtonByteBuffer(byte[] backingArray, long maxCapacity) : this(backingArray, 0, maxCapacity) { } /// <summary> /// Create a new proton byte buffer instance with given backing array as the /// starting backing store and uses the provided max capacity value to control /// how large the buffer could ever grow. /// </summary> /// <param name="backingArray">The actual byte array that backs this buffer</param> /// <param name="arrayOffset">The offset into the backing array where the buffer starts</param> /// <param name="maxCapacity">The maximum capacity this buffer can grow to</param> public ProtonByteBuffer(byte[] backingArray, int arrayOffset, long maxCapacity) : this(backingArray, arrayOffset, backingArray.Length - arrayOffset, maxCapacity) { } /// <summary> /// Create a new proton byte buffer instance with given backing array as the /// starting backing store and uses the provided max capacity value to control /// how large the buffer could ever grow. /// </summary> /// <param name="backingArray">The actual byte array that backs this buffer</param> /// <param name="arrayOffset">The offset index into the backing array where the buffer starts</param> /// <param name="capacity">The capacity limit for this view of the assigned array</param> /// <param name="maxCapacity">The maximum capacity this buffer can grow to</param> public ProtonByteBuffer(byte[] backingArray, int arrayOffset, int capacity, long maxCapacity) : base() { if (arrayOffset > backingArray.Length) { throw new ArgumentOutOfRangeException(nameof(arrayOffset), "Array offset cannot exceed the array length"); } if (capacity > backingArray.Length - arrayOffset) { throw new ArgumentOutOfRangeException(nameof(capacity), "Array segment capacity cannot exceed the configured array length minus the offset"); } this.array = backingArray; this.arrayOffset = arrayOffset; this.arrayLimit = arrayOffset + capacity; this.maxCapacity = maxCapacity; } #region Buffer State and Management APIs public long Capacity => arrayLimit - arrayOffset; public bool IsReadable => ReadOffset < WriteOffset; public long ReadableBytes => WriteOffset - ReadOffset; public bool IsWritable => WriteOffset < Capacity; public long WritableBytes => Capacity - WriteOffset; public long ReadOffset { get => readOffset; set { CheckRead(value, 0); readOffset = value; } } public long WriteOffset { get => writeOffset; set { CheckWrite(value, 0); writeOffset = value; } } public uint ComponentCount => 1; public uint ReadableComponentCount => IsReadable ? 1u : 0u; public uint WritableComponentCount => IsWritable ? 1u : 0u; public IProtonBuffer EnsureWritable(long amount) { return InternalEnsureWritable(true, 1, amount); } public IProtonBuffer Compact() { if (readOffset != 0) { // Compress the current readable section into the front of the // array and then update the offsets to match the new reality. Array.Copy(array, Offset(readOffset), array, arrayOffset, writeOffset - readOffset); writeOffset -= readOffset; readOffset = 0; } return this; } public IProtonBuffer Reclaim() { // For now we do nothing, but in the future some logic might be added // to compact and or reallocate to a smaller buffer in order to free // some memory space. return this; } public IProtonBuffer WriteSplit(long offset) { return Split(WriteOffset + offset); } public IProtonBuffer ReadSplit(long offset) { return Split(ReadOffset + offset); } public IProtonBuffer Split() { return Split(WriteOffset); } public IProtonBuffer Split(long offset) { if (offset > int.MaxValue) { throw new ArgumentOutOfRangeException(nameof(offset), "Proton byte buffer cannot exceed Int32.MaxValue bytes in capacity"); } if (offset < 0 || offset > Capacity) { throw new ArgumentOutOfRangeException(nameof(offset), "The buffer split offset must be within the current buffer capacity"); } int iOffset = (int)offset; // Conceptual definition of the split buffer outcome //----------------------------------------------------------------- // This buffer: // +--------------------------------+ // 0| |splitOffset |cap // +---------------+----------------+ // / / \ \ // / / \ \ // / / \ \ // / / \ \ // / / \ \ // +---------------+ +---------------+ // |0 |cap |0 |cap // +---------------+ +---------------+ // Returned buffer. This buffer. // The new buffer can only view the portion of the array before the split. ProtonByteBuffer front = new(array, arrayOffset, iOffset, maxCapacity); // This buffer is adjusted to view only the portion of the array after the split arrayOffset += iOffset; // TODO compute the index values front.readOffset = Math.Min(readOffset, front.Capacity); front.writeOffset = Math.Min(writeOffset, front.Capacity); this.readOffset = Math.Max(0, readOffset - iOffset); this.writeOffset = Math.Max(0, writeOffset - iOffset); return front; } public IProtonBuffer Reset() { ReadOffset = 0; WriteOffset = 0; return this; } public IProtonBuffer SkipBytes(long amount) { CheckRead(readOffset, amount); readOffset += amount; return this; } public IProtonBuffer Fill(byte value) { if (Capacity > 0) { Array.Fill(array, value, arrayOffset, (int)Capacity); } return this; } #endregion #region Buffer Copy API implementation public IProtonBuffer Copy() { return Copy(readOffset, writeOffset - readOffset); } public IProtonBuffer Copy(long index, long length) { CheckGet(index, length); byte[] copyBytes = new byte[length]; Array.Copy(array, Offset(index), copyBytes, 0, length); IProtonBuffer copy = new ProtonByteBuffer(copyBytes, maxCapacity) { WriteOffset = length }; return copy; } public IProtonBuffer CopyInto(long srcPos, byte[] dest, long destPos, long length) { CheckCopyIntoArgs(srcPos, length, destPos, dest.LongLength); Array.Copy(array, Offset(srcPos), dest, destPos, length); return this; } public IProtonBuffer CopyInto(long srcPos, IProtonBuffer dest, long destPos, long length) { CheckCopyIntoArgs(srcPos, length, destPos, dest.Capacity); if (dest is ProtonByteBuffer destByteBuffer) { Array.Copy(array, Offset(srcPos), destByteBuffer.array, destPos, length); } else { long writePos = dest.WriteOffset; for (long i = 0; i < length; i++) { dest.SetUnsignedByte(writePos + i, array[srcPos + i]); } } return this; } #endregion #region Buffer iteration API implementations public bool HasReadableArray => true; public byte[] ReadableArray => array; public int ReadableArrayOffset => (int)Offset(readOffset); // Array backing means int casting is ok. public int ReadableArrayLength => (int)ReadableBytes; // Array backing means int casting is ok. public bool HasWritableArray => true; public byte[] WritableArray => array; public int WritableArrayOffset => (int)Offset(writeOffset); // Array backing means int casting is ok. public int WritableArrayLength => (int)WritableBytes; // Array backing means int casting is ok. public int ForEachReadableComponent(in int index, in Func<int, IReadableComponent, bool> processor) { CheckGet(readOffset, Math.Max(1, ReadableBytes)); return processor.Invoke(index, this) ? 1 : -1; } public int ForEachWritableComponent(in int index, in Func<int, IWritableComponent, bool> processor) { CheckSet(writeOffset, Math.Max(1, WritableBytes)); return processor.Invoke(index, this) ? 1 : -1; } #endregion #region Implementation of the proton buffer accessors API public bool GetBoolean(long index) { CheckGet(index, sizeof(byte)); return ProtonByteUtils.ReadBoolean(array, (int)Offset(index)); } public sbyte GetByte(long index) { CheckGet(index, sizeof(sbyte)); return ProtonByteUtils.ReadByte(array, (int)Offset(index)); } public char GetChar(long index) { CheckGet(index, sizeof(char)); return ProtonByteUtils.ReadChar(array, (int)Offset(index)); } public double GetDouble(long index) { CheckGet(index, sizeof(double)); return ProtonByteUtils.ReadDouble(array, (int)Offset(index)); } public float GetFloat(long index) { CheckGet(index, sizeof(float)); return ProtonByteUtils.ReadFloat(array, (int)Offset(index)); } public int GetInt(long index) { CheckGet(index, sizeof(int)); return ProtonByteUtils.ReadInt(array, (int)Offset(index)); } public long GetLong(long index) { CheckGet(index, sizeof(long)); return ProtonByteUtils.ReadLong(array, (int)Offset(index)); } public short GetShort(long index) { CheckGet(index, sizeof(short)); return ProtonByteUtils.ReadShort(array, (int)Offset(index)); } public byte GetUnsignedByte(long index) { CheckGet(index, sizeof(byte)); return ProtonByteUtils.ReadUnsignedByte(array, (int)Offset(index)); } public uint GetUnsignedInt(long index) { CheckGet(index, sizeof(uint)); return ProtonByteUtils.ReadUnsignedInt(array, (int)Offset(index)); } public ulong GetUnsignedLong(long index) { CheckGet(index, sizeof(ulong)); return ProtonByteUtils.ReadUnsignedLong(array, (int)Offset(index)); } public ushort GetUnsignedShort(long index) { CheckGet(index, sizeof(ushort)); return ProtonByteUtils.ReadUnsignedShort(array, (int)Offset(index)); } public bool ReadBoolean() { CheckRead(readOffset, sizeof(byte)); bool result = ProtonByteUtils.ReadBoolean(array, (int)Offset(readOffset)); readOffset += sizeof(byte); return result; } public sbyte ReadByte() { CheckRead(readOffset, sizeof(sbyte)); sbyte result = ProtonByteUtils.ReadByte(array, (int)Offset(readOffset)); readOffset += sizeof(sbyte); return result; } public char ReadChar() { CheckRead(readOffset, sizeof(char)); char result = ProtonByteUtils.ReadChar(array, (int)Offset(readOffset)); readOffset += sizeof(char); return result; } public double ReadDouble() { CheckRead(readOffset, sizeof(double)); double result = ProtonByteUtils.ReadDouble(array, (int)Offset(readOffset)); readOffset += sizeof(double); return result; } public float ReadFloat() { CheckRead(readOffset, sizeof(float)); float result = ProtonByteUtils.ReadFloat(array, (int)Offset(readOffset)); readOffset += sizeof(float); return result; } public int ReadInt() { CheckRead(readOffset, sizeof(int)); int result = ProtonByteUtils.ReadInt(array, (int)Offset(readOffset)); readOffset += sizeof(int); return result; } public long ReadLong() { CheckRead(readOffset, sizeof(long)); long result = ProtonByteUtils.ReadLong(array, (int)Offset(readOffset)); readOffset += sizeof(long); return result; } public short ReadShort() { CheckRead(readOffset, sizeof(short)); short result = ProtonByteUtils.ReadShort(array, (int)Offset(readOffset)); readOffset += sizeof(short); return result; } public byte ReadUnsignedByte() { CheckRead(readOffset, sizeof(byte)); byte result = ProtonByteUtils.ReadUnsignedByte(array, (int)Offset(readOffset)); readOffset += sizeof(byte); return result; } public uint ReadUnsignedInt() { CheckRead(readOffset, sizeof(uint)); uint result = ProtonByteUtils.ReadUnsignedInt(array, (int)Offset(readOffset)); readOffset += sizeof(uint); return result; } public ulong ReadUnsignedLong() { CheckRead(readOffset, sizeof(ulong)); ulong result = ProtonByteUtils.ReadUnsignedLong(array, (int)Offset(readOffset)); readOffset += sizeof(ulong); return result; } public ushort ReadUnsignedShort() { CheckRead(readOffset, sizeof(ushort)); ushort result = ProtonByteUtils.ReadUnsignedShort(array, (int)Offset(readOffset)); readOffset += sizeof(ushort); return result; } public IProtonBuffer SetBoolean(long index, bool value) { CheckSet(index, sizeof(byte)); ProtonByteUtils.WriteBoolean(value, array, (int)Offset(index)); return this; } public IProtonBuffer SetByte(long index, sbyte value) { CheckSet(index, sizeof(sbyte)); ProtonByteUtils.WriteByte(value, array, (int)Offset(index)); return this; } public IProtonBuffer SetChar(long index, char value) { CheckSet(index, sizeof(short)); ProtonByteUtils.WriteShort((short)value, array, (int)Offset(index)); return this; } public IProtonBuffer SetDouble(long index, double value) { CheckSet(index, sizeof(double)); ProtonByteUtils.WriteDouble(value, array, (int)Offset(index)); return this; } public IProtonBuffer SetFloat(long index, float value) { CheckSet(index, sizeof(float)); ProtonByteUtils.WriteFloat(value, array, (int)Offset(index)); return this; } public IProtonBuffer SetInt(long index, int value) { CheckSet(index, sizeof(int)); ProtonByteUtils.WriteInt(value, array, (int)Offset(index)); return this; } public IProtonBuffer SetLong(long index, long value) { CheckSet(index, sizeof(long)); ProtonByteUtils.WriteLong(value, array, (int)Offset(index)); return this; } public IProtonBuffer SetShort(long index, short value) { CheckSet(index, sizeof(short)); ProtonByteUtils.WriteShort(value, array, (int)Offset(index)); return this; } public IProtonBuffer SetUnsignedByte(long index, byte value) { CheckSet(index, sizeof(byte)); ProtonByteUtils.WriteUnsignedByte(value, array, (int)Offset(index)); return this; } public IProtonBuffer SetUnsignedInt(long index, uint value) { CheckSet(index, sizeof(uint)); ProtonByteUtils.WriteUnsignedInt(value, array, (int)Offset(index)); return this; } public IProtonBuffer SetUnsignedLong(long index, ulong value) { CheckSet(index, sizeof(ulong)); ProtonByteUtils.WriteUnsignedLong(value, array, (int)Offset(index)); return this; } public IProtonBuffer SetUnsignedShort(long index, ushort value) { CheckSet(index, sizeof(ushort)); ProtonByteUtils.WriteUnsignedShort(value, array, (int)Offset(index)); return this; } public IProtonBuffer WriteBoolean(bool value) { CheckWrite(writeOffset, sizeof(byte)); ProtonByteUtils.WriteBoolean(value, array, (int)Offset(writeOffset)); writeOffset += sizeof(byte); return this; } public IProtonBuffer WriteByte(sbyte value) { CheckWrite(writeOffset, sizeof(byte)); ProtonByteUtils.WriteByte(value, array, (int)Offset(writeOffset)); writeOffset += sizeof(byte); return this; } public IProtonBuffer WriteDouble(double value) { CheckWrite(writeOffset, sizeof(double)); ProtonByteUtils.WriteDouble(value, array, (int)Offset(writeOffset)); writeOffset += sizeof(double); return this; } public IProtonBuffer WriteFloat(float value) { CheckWrite(writeOffset, sizeof(float)); ProtonByteUtils.WriteFloat(value, array, (int)Offset(writeOffset)); writeOffset += sizeof(float); return this; } public IProtonBuffer WriteInt(int value) { CheckWrite(writeOffset, sizeof(int)); ProtonByteUtils.WriteInt(value, array, (int)Offset(writeOffset)); writeOffset += sizeof(int); return this; } public IProtonBuffer WriteLong(long value) { CheckWrite(writeOffset, sizeof(long)); ProtonByteUtils.WriteLong(value, array, (int)Offset(writeOffset)); writeOffset += sizeof(long); return this; } public IProtonBuffer WriteShort(short value) { CheckWrite(writeOffset, sizeof(short)); ProtonByteUtils.WriteShort(value, array, (int)Offset(writeOffset)); writeOffset += sizeof(short); return this; } public IProtonBuffer WriteUnsignedByte(byte value) { CheckWrite(writeOffset, sizeof(byte)); ProtonByteUtils.WriteUnsignedByte(value, array, (int)Offset(writeOffset)); writeOffset += sizeof(byte); return this; } public IProtonBuffer WriteUnsignedInt(uint value) { CheckWrite(writeOffset, sizeof(uint)); ProtonByteUtils.WriteUnsignedInt(value, array, (int)Offset(writeOffset)); writeOffset += sizeof(uint); return this; } public IProtonBuffer WriteUnsignedLong(ulong value) { CheckWrite(writeOffset, sizeof(ulong)); ProtonByteUtils.WriteUnsignedLong(value, array, (int)Offset(writeOffset)); writeOffset += sizeof(ulong); return this; } public IProtonBuffer WriteUnsignedShort(ushort value) { CheckWrite(writeOffset, sizeof(ushort)); ProtonByteUtils.WriteUnsignedShort(value, array, (int)Offset(writeOffset)); writeOffset += sizeof(ushort); return this; } public IProtonBuffer WriteBytes(byte[] source) { if (source == null) { throw new ArgumentNullException(nameof(source), "Input source array cannot be null"); } return WriteBytes(source, 0, source.Length); } public IProtonBuffer WriteBytes(byte[] source, long offset, long length) { if (source == null) { throw new ArgumentNullException(nameof(source), "Input source array cannot be null"); } if (length > source.LongLength - offset) { throw new ArgumentOutOfRangeException("Number of bytes to copy from source array: " + length + " is greater than the readable span of the array: " + (source.LongLength - offset)); } CheckWriteIntoArgs(source.LongLength, offset, length, writeOffset, Capacity); Array.Copy(source, offset, array, Offset(writeOffset), length); writeOffset += length; return this; } public IProtonBuffer WriteBytes(IProtonBuffer source) { long length = source.ReadableBytes; CheckWrite(writeOffset, length); source.CopyInto(source.ReadOffset, array, Offset(writeOffset), length); source.ReadOffset += length; writeOffset += length; return this; } #endregion #region Standard language API implementations public int CompareTo(object obj) { Statics.RequireNonNull(obj, "Cannot compare a buffer with null"); if (obj is IProtonBuffer) { return CompareTo(obj as IProtonBuffer); } throw new InvalidCastException("Cannot compare input type to an proton buffer type"); } public int CompareTo(IProtonBuffer other) { Statics.RequireNonNull(other, "Cannot compare a buffer with null"); long stopIndex = ReadOffset + Math.Min(ReadableBytes, other.ReadableBytes); for (long i = ReadOffset, j = other.ReadOffset; i < stopIndex; i++, j++) { int cmp = Compare(GetUnsignedByte(i), other.GetUnsignedByte(j)); if (cmp != 0) { return cmp; } } return (int)(ReadableBytes - other.ReadableBytes); } public override int GetHashCode() { return ProtonBufferSupport.GetHashCode(this); } public override bool Equals(object other) { if (other is IProtonBuffer) { return this.Equals(other as IProtonBuffer); } else { return false; } } public bool Equals(IProtonBuffer other) { return ProtonBufferSupport.Equals(this, other); } public string ToString(Encoding encoding) { if (!IsReadable) { return ""; } Decoder decoder = encoding.GetDecoder(); int charCount = decoder.GetCharCount(array, (int)Offset(readOffset), (int)(writeOffset - readOffset)); char[] output = new char[charCount]; int outputChars = decoder.GetChars(array, (int)Offset(readOffset), (int)(writeOffset - readOffset), output, 0); return new string(output, 0, outputChars); } public override string ToString() { return GetType().Name + "{ read:" + ReadOffset + ", write: " + WriteOffset + ", capacity: " + Capacity + "}"; } #endregion #region Internal byte buffer utility methods private long Offset(long index) { return index + arrayOffset; } private IProtonBuffer InternalEnsureWritable(bool allowCompaction, long minimumGrowth, long requiredWritable) { if (requiredWritable < 0) { throw new ArgumentOutOfRangeException(nameof(requiredWritable), "Requested writable bytes cannot be negative"); } if (minimumGrowth < 0) { throw new ArgumentOutOfRangeException(nameof(minimumGrowth), "Requested minimum growth bytes cannot be negative"); } // Called when we know that we don't need to validate if the minimum writable // value is negative. if (requiredWritable <= WritableBytes) { return this; } if (requiredWritable > maxCapacity - writeOffset) { throw new ArgumentOutOfRangeException(string.Format( "writeOffset({0}) + requiredWritable({1}) exceeds maximum buffer capacity({2}): {3}", writeOffset, requiredWritable, maxCapacity, this)); } // Can we solve this with compaction instead of creating more buffers if (allowCompaction && WritableBytes + ReadOffset >= requiredWritable) { Compact(); } else { long newCapacity = Capacity + Math.Max(requiredWritable - WritableBytes, minimumGrowth); if (newCapacity < 1) { throw new ArgumentOutOfRangeException("Buffer size must be positive, but was " + newCapacity + '.'); } int oldCapacity = arrayLimit - arrayOffset; if (newCapacity > oldCapacity) { byte[] newArray = new byte[newCapacity]; Array.ConstrainedCopy(array, arrayOffset, newArray, 0, arrayLimit - arrayOffset); array = newArray; arrayOffset = 0; arrayLimit = newArray.Length; } } return this; } private void CheckWrite(long index, long size) { if (index < readOffset || (arrayLimit - arrayOffset) < index + size) { throw OutOfBounds(index); } } private void CheckRead(long index, long size) { if (index < 0 || writeOffset < index + size) { throw OutOfBounds(index); } } private void CheckGet(long index, long size) { if (index < 0 || Capacity < index + size) { throw OutOfBounds(index); } } private void CheckSet(long index, long size) { if (index < 0 || Capacity < index + size) { throw OutOfBounds(index); } } private void CheckCopyIntoArgs(long srcPos, long length, long destPos, long destLength) { if (srcPos < 0) { throw new ArgumentOutOfRangeException("The srcPos cannot be negative: " + srcPos + '.'); } if (length < 0) { throw new ArgumentOutOfRangeException("The length cannot be negative: " + length + '.'); } if (Capacity < srcPos + length) { throw new ArgumentOutOfRangeException("The srcPos + length is beyond the end of the buffer: " + "srcPos = " + srcPos + ", length = " + length + '.'); } if (destPos < 0) { throw new ArgumentOutOfRangeException("The destPos cannot be negative: " + destPos + '.'); } if (destLength < destPos + length) { throw new ArgumentOutOfRangeException("The destPos + length is beyond the end of the destination: " + "destPos = " + destPos + ", length = " + length + '.'); } } private static void CheckWriteIntoArgs(long srcCapacity, long srcPos, long length, long destPos, long destLength) { if (srcPos < 0) { throw new ArgumentOutOfRangeException("The srcPos cannot be negative: " + srcPos + '.'); } if (length < 0) { throw new ArgumentOutOfRangeException("The length cannot be negative: " + length + '.'); } if (srcCapacity < srcPos + length) { throw new ArgumentOutOfRangeException("The srcPos + length is beyond the end of the source buffer: " + "srcPos = " + srcPos + ", length = " + length + '.'); } if (destPos < 0) { throw new ArgumentOutOfRangeException("The destPos cannot be negative: " + destPos + '.'); } if (destLength < destPos + length) { throw new ArgumentOutOfRangeException("The destPos + length is beyond the end of the destination: " + "destPos = " + destPos + ", length = " + length + '.'); } } private Exception OutOfBounds(long index) { return new IndexOutOfRangeException( "Index " + index + " is out of bounds: [read 0 to " + writeOffset + ", write 0 to " + Capacity + "]."); } private static int Compare(byte x, byte y) { return (x < y) ? -1 : ((x == y) ? 0 : 1); } #endregion } }