//****************************************************************************************************** // BufferExtensions.cs - Gbtc // // Copyright © 2010, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the Eclipse Public License -v 1.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.opensource.org/licenses/eclipse-1.0.php // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 09/19/2008 - J. Ritchie Carroll // Generated original version of source code. // 12/03/2008 - J. Ritchie Carroll // Added "Combine" and "IndexOfSequence" overloaded extensions. // 02/13/2009 - Josh L. Patterson // Edited Code Comments. // 09/14/2009 - Stephen C. Wills // Added new header and license agreement. // 12/31/2009 - Andrew K. Hill // Modified the following methods per unit testing: // BlockCopy(byte[], int, int) // Combine(byte[], byte[]) // Combine(byte[], int, int, byte[], int, int) // Combine(byte[][]) // IndexOfSequence(byte[], byte[]) // IndexOfSequence(byte[], byte[], int) // IndexOfSequence(byte[], byte[], int, int) // 11/22/2011 - J. Ritchie Carroll // Added common case buffer parameter validation extensions. // 10/8/2012 - Danyelle Gilliam // Modified Header // //****************************************************************************************************** #region [ Contributor License Agreements ] //****************************************************************************************************** // // Copyright © 2011, Grid Protection Alliance. All Rights Reserved. // // The GPA licenses this file to you under the Eclipse Public License -v 1.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.opensource.org/licenses/eclipse-1.0.php // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // //****************************************************************************************************** #endregion using System; using System.IO; namespace GSF { /// Defines extension functions related to buffer manipulation. public static class BufferExtensions { /// /// Validates that the specified and are valid within the given . /// /// Buffer to validate. /// 0-based start index into the . /// Valid number of bytes within from . /// is null. /// /// or is less than 0 -or- /// and will exceed length. /// public static void ValidateParameters(this byte[] buffer, int startIndex, int length) { if ((object)buffer == null) throw new ArgumentNullException("buffer"); if (startIndex < 0) throw new ArgumentOutOfRangeException("startIndex", "Buffer parameter cannot be negative"); if (length < 0) throw new ArgumentOutOfRangeException("length", "Buffer parameter cannot be negative"); if (startIndex + length > buffer.Length) throw new ArgumentOutOfRangeException("length", string.Format("Buffer operation with startIndex of {0} and length of {1} will exceed {2} bytes of available buffer space", startIndex, length, buffer.Length)); } /// Returns a copy of the specified portion of the buffer. /// Source buffer. /// Offset into buffer. /// Length of buffer to copy at offset. /// A buffer of data copied from the specified portion of the source buffer. /// /// /// Returned buffer will be extended as needed to make it the specified , but /// it will never be less than the source buffer length - . /// /// /// This is a convenience function. If an existing buffer is already available, using the /// directly instead of this extension method will be optimal since this method always allocates a new return buffer. /// /// /// /// is outside the range of valid indexes for the source buffer -or- /// is less than 0. /// public static byte[] BlockCopy(this byte[] buffer, int startIndex, int length) { if ((object)buffer == null) throw new ArgumentNullException("buffer"); if (startIndex < 0) throw new ArgumentOutOfRangeException("startIndex", "cannot be negative"); if (length < 0) throw new ArgumentOutOfRangeException("length", "cannot be negative"); if (startIndex >= buffer.Length) throw new ArgumentOutOfRangeException("startIndex", "not a valid index into the buffer"); byte[] copiedBytes = new byte[buffer.Length - startIndex < length ? buffer.Length - startIndex : length]; Buffer.BlockCopy(buffer, startIndex, copiedBytes, 0, copiedBytes.Length); return copiedBytes; } /// /// Combines buffers together as a single image. /// /// Source buffer. /// Other buffer to combine to buffer. /// Combined buffers. public static byte[] Combine(this byte[] source, byte[] other) { if ((object)source == null) throw new ArgumentNullException("source"); if ((object)other == null) throw new ArgumentNullException("other"); return source.Combine(0, source.Length, other, 0, other.Length); } /// /// Combines specified portions of buffers together as a single image. /// /// Source buffer. /// Offset into buffer to begin copy. /// Number of bytes to copy from buffer. /// Other buffer to combine to buffer. /// Offset into buffer to begin copy. /// Number of bytes to copy from buffer. /// Combined specified portions of both buffers. /// /// or is outside the range of valid indexes for the associated buffer -or- /// or is less than 0 -or- /// or , /// and or do not specify a valid section in the the associated buffer. /// public static byte[] Combine(this byte[] source, int sourceOffset, int sourceCount, byte[] other, int otherOffset, int otherCount) { if ((object)source == null) throw new ArgumentNullException("source"); if ((object)other == null) throw new ArgumentNullException("other"); if (sourceOffset < 0) throw new ArgumentOutOfRangeException("sourceOffset", "cannot be negative"); if (otherOffset < 0) throw new ArgumentOutOfRangeException("otherOffset", "cannot be negative"); if (sourceCount < 0) throw new ArgumentOutOfRangeException("sourceCount", "cannot be negative"); if (otherCount < 0) throw new ArgumentOutOfRangeException("otherCount", "cannot be negative"); if (sourceOffset >= source.Length) throw new ArgumentOutOfRangeException("sourceOffset", "not a valid index into source buffer"); if (otherOffset >= other.Length) throw new ArgumentOutOfRangeException("otherOffset", "not a valid index into other buffer"); if (sourceOffset + sourceCount > source.Length) throw new ArgumentOutOfRangeException("sourceCount", "exceeds source buffer size"); if (otherOffset + otherCount > other.Length) throw new ArgumentOutOfRangeException("otherCount", "exceeds other buffer size"); // Overflow is possible, but unlikely. Therefore, this is omitted for performance // if ((int.MaxValue - sourceCount - otherCount) < 0) // throw new ArgumentOutOfRangeException("sourceCount + otherCount", "exceeds maximum buffer size"); // Combine buffers together as a single image byte[] combinedBuffer = new byte[sourceCount + otherCount]; Buffer.BlockCopy(source, sourceOffset, combinedBuffer, 0, sourceCount); Buffer.BlockCopy(other, otherOffset, combinedBuffer, sourceCount, otherCount); return combinedBuffer; } /// /// Combines buffers together as a single image. /// /// Source buffer. /// First buffer to combine to buffer. /// Second buffer to combine to buffer. /// Combined buffers. public static byte[] Combine(this byte[] source, byte[] other1, byte[] other2) { return (new byte[][] { source, other1, other2 }).Combine(); } /// /// Combines buffers together as a single image. /// /// Source buffer. /// First buffer to combine to buffer. /// Second buffer to combine to buffer. /// Third buffer to combine to buffer. /// Combined buffers. public static byte[] Combine(this byte[] source, byte[] other1, byte[] other2, byte[] other3) { return (new byte[][] { source, other1, other2, other3 }).Combine(); } /// /// Combines buffers together as a single image. /// /// Source buffer. /// First buffer to combine to buffer. /// Second buffer to combine to buffer. /// Third buffer to combine to buffer. /// Fourth buffer to combine to buffer. /// Combined buffers. public static byte[] Combine(this byte[] source, byte[] other1, byte[] other2, byte[] other3, byte[] other4) { return (new byte[][] { source, other1, other2, other3, other4 }).Combine(); } /// /// Combines array of buffers together as a single image. /// /// Array of byte buffers. /// Combined buffers. public static byte[] Combine(this byte[][] buffers) { if ((object)buffers == null) throw new ArgumentNullException("buffers"); MemoryStream combinedBuffer = new MemoryStream(); // Combine all currently queued buffers for (int x = 0; x < buffers.Length; x++) { if ((object)buffers[x] == null) throw new ArgumentNullException("buffers[" + x + "]"); combinedBuffer.Write(buffers[x], 0, buffers[x].Length); } // return combined data buffers return combinedBuffer.ToArray(); } /// /// Searches for the specified sequence of and returns the index of the first occurrence within the . /// /// Buffer to search. /// Byte sequence to search for. /// The zero-based index of the first occurance of the sequence of in the , if found; otherwise, -1. public static int IndexOfSequence(this byte[] buffer, byte[] bytesToFind) { if ((object)buffer == null) throw new ArgumentNullException("buffer"); if ((object)bytesToFind == null) throw new ArgumentNullException("bytesToFind"); return buffer.IndexOfSequence(bytesToFind, 0, buffer.Length); } /// /// Searches for the specified sequence of and returns the index of the first occurrence within the range of elements in the that starts at the specified index. /// /// Buffer to search. /// Byte sequence to search for. /// Start index in the to start searching. /// The zero-based index of the first occurance of the sequence of in the , if found; otherwise, -1. public static int IndexOfSequence(this byte[] buffer, byte[] bytesToFind, int startIndex) { if ((object)buffer == null) throw new ArgumentNullException("buffer"); if ((object)bytesToFind == null) throw new ArgumentNullException("bytesToFind"); return buffer.IndexOfSequence(bytesToFind, startIndex, buffer.Length - startIndex); } /// /// Searches for the specified sequence of and returns the index of the first occurrence within the range of elements in the that starts at the specified index and contains the specified number of elements. /// /// Buffer to search. /// Byte sequence to search for. /// Start index in the to start searching. /// Number of bytes in the to search through. /// The zero-based index of the first occurance of the sequence of in the , if found; otherwise, -1. /// /// is null or has zero length. /// /// /// is outside the range of valid indexes for the source buffer -or- /// is less than 0. /// public static int IndexOfSequence(this byte[] buffer, byte[] bytesToFind, int startIndex, int length) { if ((object)buffer == null) throw new ArgumentNullException("buffer"); if ((object)bytesToFind == null || bytesToFind.Length == 0) throw new ArgumentNullException("bytesToFind"); if (startIndex < 0) throw new ArgumentOutOfRangeException("startIndex", "cannot be negative"); if (length < 0) throw new ArgumentOutOfRangeException("length", "cannot be negative"); if (startIndex >= buffer.Length) throw new ArgumentOutOfRangeException("startIndex", "not a valid index into source buffer"); if (startIndex + length > buffer.Length) throw new ArgumentOutOfRangeException("length", "exceeds buffer size"); // Overflow is possible, but unlikely. Therefore, this is omitted for performance // if ((int.MaxValue - startIndex - length) < 0) // throw new ArgumentOutOfRangeException("startIndex + length", "exceeds maximum buffer size"); // Search for first byte in the sequence, if this doesn't exist then sequence doesn't exist int index = Array.IndexOf(buffer, bytesToFind[0], startIndex, length); if (bytesToFind.Length > 1) { bool foundSequence = false; while (index > -1 && !foundSequence) { // See if next bytes in sequence match for (int x = 1; x < bytesToFind.Length; x++) { // Make sure there's enough buffer remaining to accomodate this byte if (index + x < startIndex + length) { // If sequence doesn't match, search for next first-byte if (buffer[index + x] != bytesToFind[x]) { index = Array.IndexOf(buffer, bytesToFind[0], index + 1, length - (index + 1)); break; } // If each byte to find matched, we found the sequence foundSequence = (x == bytesToFind.Length - 1); } else { // Ran out of buffer, return -1 index = -1; } } } } return index; } /// Returns comparision results of two binary buffers. /// Source buffer. /// Other buffer to compare to buffer. /// /// /// A signed integer that indicates the relative comparison of buffer and buffer. /// /// /// /// /// Return Value /// Description /// /// /// Less than zero /// Source buffer is less than other buffer. /// /// /// Zero /// Source buffer is equal to other buffer. /// /// /// Greater than zero /// Source buffer is greater than other buffer. /// /// /// /// public static int CompareTo(this byte[] source, byte[] other) { if ((object)source == null && (object)other == null) { // Both buffers are assumed equal if both are nothing. return 0; } else if ((object)source == null) { // Buffer 2 has data, and buffer 1 is nothing. Buffer 2 is assumed larger. return 1; } else if ((object)other == null) { // Buffer 1 has data, and buffer 2 is nothing. Buffer 1 is assumed larger. return -1; } else { int length1 = source.Length; int length2 = other.Length; if (length1 == length2) { int comparision = 0; // Compares elements of buffers that are of equal size. for (int x = 0; x < length1; x++) { comparision = source[x].CompareTo(other[x]); if (comparision != 0) break; } return comparision; } else { // Buffer lengths are unequal. Buffer with largest number of elements is assumed to be largest. return length1.CompareTo(length2); } } } /// /// Returns comparision results of two binary buffers. /// /// Source buffer. /// Offset into buffer to begin compare. /// Other buffer to compare to buffer. /// Offset into buffer to begin compare. /// Number of bytes to compare in both buffers. /// /// /// A signed integer that indicates the relative comparison of buffer and buffer. /// /// /// /// /// Return Value /// Description /// /// /// Less than zero /// Source buffer is less than other buffer. /// /// /// Zero /// Source buffer is equal to other buffer. /// /// /// Greater than zero /// Source buffer is greater than other buffer. /// /// /// /// /// /// or is outside the range of valid indexes for the associated buffer -or- /// is less than 0 -or- /// or and do not specify a valid section in the the associated buffer. /// public static int CompareTo(this byte[] source, int sourceOffset, byte[] other, int otherOffset, int count) { if ((object)source == null && (object)other == null) { // Both buffers are assumed equal if both are nothing. return 0; } else if ((object)source == null) { // Buffer 2 has data, and buffer 1 is nothing. Buffer 2 is assumed larger. return 1; } else if ((object)other == null) { // Buffer 1 has data, and buffer 2 is nothing. Buffer 1 is assumed larger. return -1; } else { if (sourceOffset < 0) throw new ArgumentOutOfRangeException("sourceOffset", "cannot be negative"); if (otherOffset < 0) throw new ArgumentOutOfRangeException("otherOffset", "cannot be negative"); if (count < 0) throw new ArgumentOutOfRangeException("count", "cannot be negative"); if (sourceOffset >= source.Length) throw new ArgumentOutOfRangeException("sourceOffset", "not a valid index into source buffer"); if (otherOffset >= other.Length) throw new ArgumentOutOfRangeException("otherOffset", "not a valid index into other buffer"); if (sourceOffset + count > source.Length) throw new ArgumentOutOfRangeException("count", "exceeds source buffer size"); if (otherOffset + count > other.Length) throw new ArgumentOutOfRangeException("count", "exceeds other buffer size"); // Overflow is possible, but unlikely. Therefore, this is omitted for performance // if ((int.MaxValue - sourceOffset - count) < 0) // throw new ArgumentOutOfRangeException("sourceOffset + count", "exceeds maximum buffer size"); // Overflow is possible, but unlikely. Therefore, this is omitted for performance // if ((int.MaxValue - otherOffset - count) < 0) // throw new ArgumentOutOfRangeException("sourceOffset + count", "exceeds maximum buffer size"); int comparision = 0; // Compares elements of buffers that are of equal size. for (int x = 0; x < count; x++) { comparision = source[sourceOffset + x].CompareTo(other[otherOffset + x]); if (comparision != 0) break; } return comparision; } } } }