瀏覽代碼

Removed IsEqualTo<T>(this IEnumerable ...) overloads.
Renamed TrimLeadingZero to TrimLeadingZeros.
Added optimizations for Take(this byte[], int, int).
Added Take(this byte[], int) overload.
Added optimized IsEqualTo(this byte[], byte[]).
Added Concat(this byte[], byte[]).

drieseng 9 年之前
父節點
當前提交
c7e3268569

+ 177 - 0
src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Concat.cs

@@ -0,0 +1,177 @@
+using System;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.Tests.Classes.Common
+{
+    [TestClass]
+    [SuppressMessage("ReSharper", "InvokeAsExtensionMethod")]
+    public class ExtensionsTest_Concat
+    {
+        private Random _random;
+
+        [TestInitialize]
+        public void Init()
+        {
+            _random = new Random();
+        }
+
+        [TestMethod]
+        public void ShouldReturnSecondWhenFirstIsEmpty()
+        {
+            var first = Array<byte>.Empty;
+            var second = CreateBuffer(16);
+
+            var actual = Extensions.Concat(first, second);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(second, actual);
+        }
+
+        [TestMethod]
+        public void ShouldReturnSecondWhenFirstIsNull()
+        {
+            const byte[] first = null;
+            var second = CreateBuffer(16);
+
+            var actual = Extensions.Concat(first, second);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(second, actual);
+        }
+
+        [TestMethod]
+        public void ShouldReturnFirstWhenSecondIsEmpty()
+        {
+            var first = CreateBuffer(16);
+            var second = Array<byte>.Empty;
+
+            var actual = Extensions.Concat(first, second);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(first, actual);
+        }
+
+        [TestMethod]
+        public void ShouldReturnFirstWhenSecondIsNull()
+        {
+            var first = CreateBuffer(16);
+            const byte[] second = null;
+
+            var actual = Extensions.Concat(first, second);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(first, actual);
+        }
+
+        [TestMethod]
+        public void ShouldReturnNullWhenFirstAndSecondAreNull()
+        {
+            const byte[] first = null;
+            const byte[] second = null;
+
+            var actual = Extensions.Concat(first, second);
+
+            Assert.IsNull(actual);
+        }
+
+        [TestMethod]
+        public void ShouldConcatSecondToFirstWhenBothAreNotEmpty()
+        {
+            var first = CreateBuffer(4);
+            var second = CreateBuffer(2);
+
+            var actual = Extensions.Concat(first, second);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(first.Length + second.Length, actual.Length);
+            Assert.AreEqual(first[0], actual[0]);
+            Assert.AreEqual(first[1], actual[1]);
+            Assert.AreEqual(first[2], actual[2]);
+            Assert.AreEqual(first[3], actual[3]);
+            Assert.AreEqual(second[0], actual[4]);
+            Assert.AreEqual(second[1], actual[5]);
+        }
+
+        [TestMethod]
+        public void Performance_LargeArray_FirstEmpty()
+        {
+            var first = Array<byte>.Empty;
+            var second = CreateBuffer(50000);
+            const int runs = 10000;
+
+            Performance(first, second, runs);
+        }
+
+
+        [TestMethod]
+        public void Performance_LargeArray_SecondEmpty()
+        {
+            var first = CreateBuffer(50000);
+            var second = Array<byte>.Empty;
+            const int runs = 10000;
+
+            Performance(first, second, runs);
+        }
+
+        [TestMethod]
+        public void Performance_LargeArray_BothNotEmpty()
+        {
+            var first = CreateBuffer(50000);
+            var second = CreateBuffer(20000);
+            const int runs = 10000;
+
+            Performance(first, second, runs);
+        }
+
+        private void Performance(byte[] first, byte[] second, int runs)
+        {
+            var stopWatch = new Stopwatch();
+
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+            GC.WaitForFullGCComplete();
+
+            stopWatch.Start();
+
+            for (var i = 0; i < runs; i++)
+            {
+                var result = Extensions.Concat(first, second);
+                var resultLength = result.Length;
+            }
+
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+            GC.WaitForFullGCComplete();
+
+            stopWatch.Stop();
+
+            Console.WriteLine(stopWatch.ElapsedMilliseconds);
+
+            stopWatch.Restart();
+
+            for (var i = 0; i < runs; i++)
+            {
+                var result = Enumerable.Concat(first, second);
+                var resultLength = result.ToArray().Length;
+            }
+
+            GC.Collect();
+            GC.WaitForFullGCComplete();
+
+            stopWatch.Stop();
+
+            Console.WriteLine(stopWatch.ElapsedMilliseconds);
+        }
+
+        private byte[] CreateBuffer(int length)
+        {
+            var buffer = new byte[length];
+            _random.NextBytes(buffer);
+            return buffer;
+        }
+    }
+}

+ 6 - 0
src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_IsEqualTo_ByteArray.cs

@@ -0,0 +1,6 @@
+namespace Renci.SshNet.Tests.Classes.Common
+{
+    class ExtensionsTest_IsEqualTo_ByteArray
+    {
+    }
+}

+ 185 - 0
src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Take_Count.cs

@@ -0,0 +1,185 @@
+using System;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.Tests.Classes.Common
+{
+    [TestClass]
+    [SuppressMessage("ReSharper", "InvokeAsExtensionMethod")]
+    public class ExtensionsTest_Take_Count
+    {
+        private Random _random;
+
+        [TestInitialize]
+        public void Init()
+        {
+            _random = new Random();
+        }
+
+        [TestMethod]
+        public void ShouldThrowArgumentNullExceptionWhenValueIsNull()
+        {
+            const byte[] value = null;
+            const int count = 0;
+
+            try
+            {
+                Extensions.Take(value, count);
+                Assert.Fail();
+            }
+            catch (ArgumentNullException ex)
+            {
+                Assert.IsNull(ex.InnerException);
+                Assert.AreEqual("value", ex.ParamName);
+            }
+        }
+
+        [TestMethod]
+        public void ShouldReturnEmptyByteArrayWhenCountIsZero()
+        {
+            var value = CreateBuffer(16);
+            const int count = 0;
+
+            var actual = Extensions.Take(value, count);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(0, actual.Length);
+        }
+
+        [TestMethod]
+        public void ShouldReturnValueWhenCountIsEqualToLengthOfValue()
+        {
+            var value = CreateBuffer(16);
+            var count = value.Length;
+
+            var actual = Extensions.Take(value, count);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(value.Length, actual.Length);
+            Assert.AreEqual(value, actual);
+        }
+
+        [TestMethod]
+        public void ShouldReturnLeadingBytesWhenCountIsLessThanLengthOfValue()
+        {
+            var value = CreateBuffer(16);
+            const int count = 5;
+
+            var actual = Extensions.Take(value, count);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(count, actual.Length);
+            Assert.AreEqual(value[0], actual[0]);
+            Assert.AreEqual(value[1], actual[1]);
+            Assert.AreEqual(value[2], actual[2]);
+            Assert.AreEqual(value[3], actual[3]);
+            Assert.AreEqual(value[4], actual[4]);
+        }
+
+        [TestMethod]
+        public void ShouldThrowArgumentExceptionWhenCountIsGreaterThanLengthOfValue()
+        {
+            var value = CreateBuffer(16);
+            var count = value.Length + 1;
+
+            try
+            {
+                Extensions.Take(value, count);
+                Assert.Fail();
+            }
+            catch (ArgumentException)
+            {
+            }
+        }
+
+        [TestMethod]
+        public void Performance_LargeArray_All()
+        {
+            var value = CreateBuffer(50000);
+            var count = value.Length;
+            const int runs = 10000;
+
+            Performance(value, count, runs);
+        }
+
+        [TestMethod]
+        public void Performance_LargeArray_LargeCount()
+        {
+            var value = CreateBuffer(50000);
+            const int count = 40000;
+            const int runs = 1000000;
+
+            Performance(value, count, runs);
+        }
+
+        [TestMethod]
+        public void Performance_LargeArray_SmallCount()
+        {
+            var value = CreateBuffer(50000);
+            const int count = 50;
+            const int runs = 1000000;
+
+            Performance(value, count, runs);
+        }
+
+        [TestMethod]
+        public void Performance_LargeArray_ZeroCount()
+        {
+            var value = CreateBuffer(50000);
+            const int count = 0;
+            const int runs = 1000000;
+
+            Performance(value, count, runs);
+        }
+
+        private void Performance(byte[] value, int count, int runs)
+        {
+            var stopWatch = new Stopwatch();
+
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+            GC.WaitForFullGCComplete();
+
+            stopWatch.Start();
+
+            for (var i = 0; i < runs; i++)
+            {
+                var result = Extensions.Take(value, count);
+                var resultLength = result.Length;
+            }
+
+            GC.Collect();
+            GC.WaitForPendingFinalizers();
+            GC.WaitForFullGCComplete();
+
+            stopWatch.Stop();
+
+            Console.WriteLine(stopWatch.ElapsedMilliseconds);
+
+            stopWatch.Restart();
+
+            for (var i = 0; i < runs; i++)
+            {
+                var result = Enumerable.Take(value, count);
+                var resultLength = result.ToArray().Length;
+            }
+
+            GC.Collect();
+            GC.WaitForFullGCComplete();
+
+            stopWatch.Stop();
+
+            Console.WriteLine(stopWatch.ElapsedMilliseconds);
+        }
+
+        private byte[] CreateBuffer(int length)
+        {
+            var buffer = new byte[length];
+            _random.NextBytes(buffer);
+            return buffer;
+        }
+    }
+}

+ 143 - 0
src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_Take_OffsetAndCount.cs

@@ -0,0 +1,143 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.Tests.Classes.Common
+{
+    [TestClass]
+    [SuppressMessage("ReSharper", "InvokeAsExtensionMethod")]
+    public class ExtensionsTest_Take_OffsetAndCount
+    {
+        private Random _random;
+
+        [TestInitialize]
+        public void Init()
+        {
+            _random = new Random();
+        }
+
+        [TestMethod]
+        public void ShouldThrowArgumentNullExceptionWhenValueIsNull()
+        {
+            const byte[] value = null;
+            const int offset = 0;
+            const int count = 0;
+
+            try
+            {
+                Extensions.Take(value, offset, count);
+                Assert.Fail();
+            }
+            catch (ArgumentNullException ex)
+            {
+                Assert.IsNull(ex.InnerException);
+                Assert.AreEqual("value", ex.ParamName);
+            }
+        }
+
+        [TestMethod]
+        public void ShouldReturnEmptyByteArrayWhenCountIsZero()
+        {
+            var value = CreateBuffer(16);
+            const int offset = 25;
+            const int count = 0;
+
+
+            var actual = Extensions.Take(value, offset, count);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(0, actual.Length);
+        }
+
+        [TestMethod]
+        public void ShouldReturnValueWhenCountIsEqualToLengthOfValueAndOffsetIsZero()
+        {
+            var value = CreateBuffer(16);
+            const int offset = 0;
+            var count = value.Length;
+
+            var actual = Extensions.Take(value, offset, count);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(value.Length, actual.Length);
+            Assert.AreEqual(value, actual);
+        }
+
+        [TestMethod]
+        public void ShouldReturnLeadingBytesWhenOffsetIsZeroAndCountIsLessThanLengthOfValue()
+        {
+            var value = CreateBuffer(16);
+            const int offset = 0;
+            const int count = 5;
+
+            var actual = Extensions.Take(value, offset, count);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(count, actual.Length);
+            Assert.AreEqual(value[0], actual[0]);
+            Assert.AreEqual(value[1], actual[1]);
+            Assert.AreEqual(value[2], actual[2]);
+            Assert.AreEqual(value[3], actual[3]);
+            Assert.AreEqual(value[4], actual[4]);
+        }
+
+        [TestMethod]
+        public void ShouldReturnCorrectPartOfValueWhenOffsetIsGreaterThanZeroAndOffsetPlusCountIsLessThanLengthOfValue()
+        {
+            var value = CreateBuffer(16);
+            const int offset = 3;
+            const int count = 4;
+
+            var actual = Extensions.Take(value, offset, count);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(count, actual.Length);
+            Assert.AreEqual(value[3], actual[0]);
+            Assert.AreEqual(value[4], actual[1]);
+            Assert.AreEqual(value[5], actual[2]);
+            Assert.AreEqual(value[6], actual[3]);
+        }
+
+        [TestMethod]
+        public void ShouldThrowArgumentExceptionWhenCountIsGreaterThanLengthOfValue()
+        {
+            var value = CreateBuffer(16);
+            const int offset = 0;
+            var count = value.Length + 1;
+
+            try
+            {
+                Extensions.Take(value, offset, count);
+                Assert.Fail();
+            }
+            catch (ArgumentException)
+            {
+            }
+        }
+
+        [TestMethod]
+        public void ShouldThrowArgumentExceptionWhenOffsetPlusCountIsGreaterThanLengthOfValue()
+        {
+            var value = CreateBuffer(16);
+            const int offset = 1;
+            var count = value.Length;
+
+            try
+            {
+                Extensions.Take(value, offset, count);
+                Assert.Fail();
+            }
+            catch (ArgumentException)
+            {
+            }
+        }
+
+        private byte[] CreateBuffer(int length)
+        {
+            var buffer = new byte[length];
+            _random.NextBytes(buffer);
+            return buffer;
+        }
+    }
+}

+ 82 - 0
src/Renci.SshNet.Tests/Classes/Common/ExtensionsTest_TrimLeadingZeros.cs

@@ -0,0 +1,82 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Renci.SshNet.Common;
+
+namespace Renci.SshNet.Tests.Classes.Common
+{
+    [TestClass]
+    [SuppressMessage("ReSharper", "InvokeAsExtensionMethod")]
+    public class ExtensionsTest_TrimLeadingZeros
+    {
+        [TestMethod]
+        public void ShouldThrowArgumentNullExceptionWhenValueIsNull()
+        {
+            const byte[] value = null;
+
+            try
+            {
+                Extensions.TrimLeadingZeros(value);
+                Assert.Fail();
+            }
+            catch (ArgumentNullException ex)
+            {
+                Assert.IsNull(ex.InnerException);
+                Assert.AreEqual("value", ex.ParamName);
+            }
+        }
+
+        [TestMethod]
+        public void ShouldRemoveAllLeadingZeros()
+        {
+            byte[] value = {0x00, 0x00, 0x0a, 0x0d};
+
+            var actual = Extensions.TrimLeadingZeros(value);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(2, actual.Length);
+            Assert.AreEqual(0x0a, actual[0]);
+            Assert.AreEqual(0x0d, actual[1]);
+        }
+
+        [TestMethod]
+        public void ShouldOnlyRemoveLeadingZeros()
+        {
+            byte[] value = { 0x00, 0x0a, 0x00, 0x0d, 0x00 };
+
+            var actual = Extensions.TrimLeadingZeros(value);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(4, actual.Length);
+            Assert.AreEqual(0x0a, actual[0]);
+            Assert.AreEqual(0x00, actual[1]);
+            Assert.AreEqual(0x0d, actual[2]);
+            Assert.AreEqual(0x00, actual[3]);
+        }
+
+        [TestMethod]
+        public void ShouldReturnOriginalEmptyByteArrayWhenValueHasNoLeadingZeros()
+        {
+            byte[] value = { 0x0a, 0x00, 0x0d };
+
+            var actual = Extensions.TrimLeadingZeros(value);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(3, actual.Length);
+            Assert.AreEqual(0x0a, actual[0]);
+            Assert.AreEqual(0x00, actual[1]);
+            Assert.AreEqual(0x0d, actual[2]);
+        }
+
+        [TestMethod]
+        public void ShouldReturnEmptyByteArrayWhenValueIsEmpty()
+        {
+            byte[] value = {};
+
+            var actual = Extensions.TrimLeadingZeros(value);
+
+            Assert.IsNotNull(actual);
+            Assert.AreEqual(0, actual.Length);
+        }
+    }
+}

+ 1 - 1
src/Renci.SshNet.Tests/Classes/PasswordConnectionInfoTest.cs

@@ -47,7 +47,7 @@ namespace Renci.SshNet.Tests.Classes
 
             #region Example PasswordConnectionInfo PasswordExpired
             var connectionInfo = new PasswordConnectionInfo("host", "username", "password");
-            var encoding = new Renci.SshNet.Common.ASCIIEncoding();
+            var encoding = SshData.Ascii;
             connectionInfo.PasswordExpired += delegate(object sender, AuthenticationPasswordChangeEventArgs e)
             {
                 e.NewPassword = encoding.GetBytes("123456");

+ 7 - 1
src/Renci.SshNet.Tests/Renci.SshNet.Tests.csproj

@@ -18,7 +18,7 @@
     <DebugType>full</DebugType>
     <Optimize>false</Optimize>
     <OutputPath>bin\Debug\</OutputPath>
-    <DefineConstants>TRACE;DEBUG</DefineConstants>
+    <DefineConstants>TRACE;DEBUG;FEATURE_ENCODING_ASCII</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
@@ -51,6 +51,7 @@
       <RequiredTargetFramework>3.5</RequiredTargetFramework>
     </Reference>
     <Reference Include="System.Data" />
+    <Reference Include="System.Numerics" />
     <Reference Include="System.Xml" />
     <Reference Include="System.Xml.Linq" />
   </ItemGroup>
@@ -124,6 +125,11 @@
     <Compile Include="Classes\ClientAuthenticationTest_Success_MultiList_SameAllowedAuthenticationsAfterPartialSuccess.cs" />
     <Compile Include="Classes\ClientAuthenticationTest_Success_MultiList_SkipFailedAuthenticationMethod.cs" />
     <Compile Include="Classes\ClientAuthenticationTest_Success_SingleList_SameAllowedAuthenticationAfterPartialSuccess.cs" />
+    <Compile Include="Classes\Common\ExtensionsTest_Concat.cs" />
+    <Compile Include="Classes\Common\ExtensionsTest_IsEqualTo_ByteArray.cs" />
+    <Compile Include="Classes\Common\ExtensionsTest_Take_Count.cs" />
+    <Compile Include="Classes\Common\ExtensionsTest_Take_OffsetAndCount.cs" />
+    <Compile Include="Classes\Common\ExtensionsTest_TrimLeadingZeros.cs" />
     <Compile Include="Classes\ConnectionInfoTest_Authenticate_Failure.cs" />
     <Compile Include="Classes\ConnectionInfoTest_Authenticate_Success.cs" />
     <Compile Include="Classes\ForwardedPortDynamicTest_Dispose_PortStarted_ChannelBound.cs" />

+ 97 - 86
src/Renci.SshNet/Common/Extensions.cs

@@ -24,7 +24,7 @@ namespace Renci.SshNet.Common
         /// <returns>
         ///   <c>true</c> if [is null or white space] [the specified value]; otherwise, <c>false</c>.
         /// </returns>
-        internal static bool IsNullOrWhiteSpace(this string value)
+        public static bool IsNullOrWhiteSpace(this string value)
         {
             if (string.IsNullOrEmpty(value)) return true;
 
@@ -105,65 +105,6 @@ namespace Renci.SshNet.Common
             return array;
         }
 
-
-        /// <summary>
-        /// Checks whether a collection is the same as another collection
-        /// </summary>
-        /// <param name="value">The current instance object</param>
-        /// <param name="compareList">The collection to compare with</param>
-        /// <param name="comparer">The comparer object to use to compare each item in the collection.  If null uses EqualityComparer(T).Default</param>
-        /// <returns>True if the two collections contain all the same items in the same order</returns>
-        internal static bool IsEqualTo<TSource>(this IEnumerable<TSource> value, IEnumerable<TSource> compareList, IEqualityComparer<TSource> comparer)
-        {
-            if (value == compareList)
-                return true;
-            if (value == null || compareList == null)
-                return false;
-
-            if (comparer == null)
-            {
-                comparer = EqualityComparer<TSource>.Default;
-            }
-
-            var enumerator1 = value.GetEnumerator();
-            var enumerator2 = compareList.GetEnumerator();
-
-            var enum1HasValue = enumerator1.MoveNext();
-            var enum2HasValue = enumerator2.MoveNext();
-
-            try
-            {
-                while (enum1HasValue && enum2HasValue)
-                {
-                    if (!comparer.Equals(enumerator1.Current, enumerator2.Current))
-                    {
-                        return false;
-                    }
-
-                    enum1HasValue = enumerator1.MoveNext();
-                    enum2HasValue = enumerator2.MoveNext();
-                }
-
-                return !(enum1HasValue || enum2HasValue);
-            }
-            finally
-            {
-                enumerator1.Dispose();
-                enumerator2.Dispose();
-            }
-        }
-
-        /// <summary>
-        /// Checks whether a collection is the same as another collection
-        /// </summary>
-        /// <param name="value">The current instance object</param>
-        /// <param name="compareList">The collection to compare with</param>
-        /// <returns>True if the two collections contain all the same items in the same order</returns>
-        internal static bool IsEqualTo<TSource>(this IEnumerable<TSource> value, IEnumerable<TSource> compareList)
-        {
-            return IsEqualTo(value, compareList, null);
-        }
-
         /// <summary>
         /// Prints out 
         /// </summary>
@@ -179,26 +120,6 @@ namespace Renci.SshNet.Common
             Debug.WriteLine(sb.ToString());
         }
 
-        /// <summary>
-        /// Trims the leading zero from bytes array.
-        /// </summary>
-        /// <param name="data">The data.</param>
-        /// <returns>Data without leading zeros.</returns>
-        internal static IEnumerable<byte> TrimLeadingZero(this IEnumerable<byte> data)
-        {
-            var leadingZero = true;
-            foreach (var item in data)
-            {
-                if (item == 0 & leadingZero)
-                {
-                    continue;
-                }
-                leadingZero = false;
-
-                yield return item;
-            }
-        }
-
         /// <summary>
         /// Creates an instance of the specified type using that type's default constructor.
         /// </summary>
@@ -300,20 +221,110 @@ namespace Renci.SshNet.Common
         /// <summary>
         /// Returns a specified number of contiguous bytes from a given offset.
         /// </summary>
-        /// <param name="data">The array to return a number of bytes from.</param>
-        /// <param name="offset">The zero-based offset in <paramref name="data"/> at which to begin taking bytes.</param>
-        /// <param name="length">The number of bytes to take from <paramref name="data"/>.</param>
+        /// <param name="value">The array to return a number of bytes from.</param>
+        /// <param name="offset">The zero-based offset in <paramref name="value"/> at which to begin taking bytes.</param>
+        /// <param name="count">The number of bytes to take from <paramref name="value"/>.</param>
         /// <returns>
         /// A <see cref="byte"/> array that contains the specified number of bytes at the specified offset
         /// of the input array.
         /// </returns>
-        internal static byte[] Take(this byte[] data, int offset, int length)
+        internal static byte[] Take(this byte[] value, int offset, int count)
         {
-            var taken = new byte[length];
-            Buffer.BlockCopy(data, offset, taken, 0, length);
+            if (value == null)
+                throw new ArgumentNullException("value");
+
+            if (count == 0)
+                return Array<byte>.Empty;
+
+            if (offset == 0 && value.Length == count)
+                return value;
+
+            var taken = new byte[count];
+            Buffer.BlockCopy(value, offset, taken, 0, count);
             return taken;
         }
 
+        internal static byte[] Take(this byte[] value, int count)
+        {
+            if (value == null)
+                throw new ArgumentNullException("value");
+
+            if (count == 0)
+                return Array<byte>.Empty;
+
+            if (value.Length == count)
+                return value;
+
+            var taken = new byte[count];
+            Buffer.BlockCopy(value, 0, taken, 0, count);
+            return taken;
+        }
+
+        public static bool IsEqualTo(this byte[] first, byte[] second)
+        {
+            if (first == second)
+                return true;
+
+            if (first == null || second == null)
+                return false;
+
+            if (first.Length != second.Length)
+                return false;
+
+            for (var i = 0; i < first.Length; i++)
+            {
+                if (first[i] != second[i])
+                    return false;
+            }
+
+            return true;
+        }
+
+        /// <summary>
+        /// Trims the leading zero from a byte array.
+        /// </summary>
+        /// <param name="value">The value.</param>
+        /// <returns>
+        /// <paramref name="value"/> without leading zeros.
+        /// </returns>
+        public static byte[] TrimLeadingZeros(this byte[] value)
+        {
+            if (value == null)
+                throw new ArgumentNullException("value");
+
+            for (var i = 0; i < value.Length; i++)
+            {
+                if (value[i] == 0)
+                    continue;
+
+                // if the first byte is non-zero, then we return the byte array as is
+                if (i == 0)
+                    return value;
+
+                var remainingBytes = value.Length - i;
+
+                var cleaned = new byte[remainingBytes];
+                Buffer.BlockCopy(value, i, cleaned, 0, remainingBytes);
+                return cleaned;
+            }
+
+            return value;
+        }
+
+        public static byte[] Concat(this byte[] first, byte[] second)
+        {
+            if (first == null || first.Length == 0)
+                return second;
+
+            if (second == null || second.Length == 0)
+                return first;
+
+            var concat = new byte[first.Length + second.Length];
+            Buffer.BlockCopy(first, 0, concat, 0, first.Length);
+            Buffer.BlockCopy(second, 0, concat, first.Length, second.Length);
+            return concat;
+        }
+
         internal static bool CanRead(this Socket socket)
         {
             return SocketAbstraction.CanRead(socket);