Expanded Unpooled functionality. Ported UnpooledTest(#36) (#251)

Motivation:
Make code to be as close to the original to benefit from co-development
UnpooledTests is needed

Modifications:
- added Copy* and CopiedBuffer functions with params support to Unpooled
- added new constuctors with params support to Unpooled
- added CombineBytes to ByteBufferUtil
- constants in AbstractByteBufferAllocator are now public

Result:
- can now verify that the current Unpooled class works as intended
This commit is contained in:
ScarletKuro 2017-05-17 00:07:07 +03:00 коммит произвёл Max Gortman
Родитель b345bb0b02
Коммит e31fced6c0
5 изменённых файлов: 1110 добавлений и 6 удалений

Просмотреть файл

@ -11,8 +11,9 @@ namespace DotNetty.Buffers
/// </summary>
public abstract class AbstractByteBufferAllocator : IByteBufferAllocator
{
const int DefaultInitialCapacity = 256;
const int DefaultMaxComponents = 16;
public const int DefaultInitialCapacity = 256;
public const int DefaultMaxComponents = 16;
public const int DefaultMaxCapacity = int.MaxValue;
protected static IByteBuffer ToLeakAwareBuffer(IByteBuffer buf)
{

Просмотреть файл

@ -41,7 +41,7 @@ namespace DotNetty.Buffers
bool freed;
public CompositeByteBuffer(IByteBufferAllocator allocator, int maxNumComponents)
: base(int.MaxValue)
: base(AbstractByteBufferAllocator.DefaultMaxCapacity)
{
Contract.Requires(allocator != null);
Contract.Requires(maxNumComponents >= 2);
@ -52,7 +52,7 @@ namespace DotNetty.Buffers
}
public CompositeByteBuffer(IByteBufferAllocator allocator, int maxNumComponents, params IByteBuffer[] buffers)
: base(int.MaxValue)
: base(AbstractByteBufferAllocator.DefaultMaxCapacity)
{
Contract.Requires(allocator != null);
Contract.Requires(maxNumComponents >= 2);
@ -66,9 +66,23 @@ namespace DotNetty.Buffers
this.leak = LeakDetector.Open(this);
}
public CompositeByteBuffer(IByteBufferAllocator allocator, int maxNumComponents, IByteBuffer[] buffers, int offset, int length)
: base(AbstractByteBufferAllocator.DefaultMaxCapacity)
{
Contract.Requires(allocator != null);
Contract.Requires(maxNumComponents >= 2);
this.allocator = allocator;
this.maxNumComponents = maxNumComponents;
this.AddComponents0(0, buffers, offset, length);
this.ConsolidateIfNeeded();
this.SetIndex(0, this.Capacity);
this.leak = LeakDetector.Open(this);
}
public CompositeByteBuffer(
IByteBufferAllocator allocator, int maxNumComponents, IEnumerable<IByteBuffer> buffers)
: base(int.MaxValue)
: base(AbstractByteBufferAllocator.DefaultMaxCapacity)
{
Contract.Requires(allocator != null);
Contract.Requires(maxNumComponents >= 2);
@ -134,7 +148,7 @@ namespace DotNetty.Buffers
return this;
}
void AddComponent0(int cIndex, IByteBuffer buffer)
int AddComponent0(int cIndex, IByteBuffer buffer)
{
Contract.Requires(buffer != null);
@ -166,6 +180,7 @@ namespace DotNetty.Buffers
this.UpdateComponentOffsets(cIndex);
}
}
return cIndex;
}
/// <summary>
@ -205,6 +220,59 @@ namespace DotNetty.Buffers
}
}
int AddComponents0(int cIndex, IByteBuffer[] buffers, int offset, int length)
{
Contract.Requires(buffers != null);
int i = offset;
try
{
this.CheckComponentIndex(cIndex);
// No need for consolidation
while (i < length)
{
// Increment i now to prepare for the next iteration and prevent a duplicate release (addComponent0
// will release if an exception occurs, and we also release in the finally block here).
IByteBuffer b = buffers[i++];
if (b == null)
{
break;
}
cIndex = this.AddComponent0(cIndex, b) + 1;
int size = this.components.Count;
if (cIndex > size)
{
cIndex = size;
}
}
return cIndex;
}
finally
{
for (; i < length; ++i)
{
IByteBuffer b = buffers[i];
if (b != null)
{
try
{
b.Release();
}
catch
{
// ignore
}
}
}
}
}
/// <summary>
/// Add the given {@link ByteBuf}s on the specific index
/// Be aware that this method does not increase the {@code writerIndex} of the {@link CompositeByteBuffer}.

Просмотреть файл

@ -4,7 +4,9 @@
namespace DotNetty.Buffers
{
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using DotNetty.Common.Utilities;
/// <summary>
/// Utility class for managing and creating unpooled buffers
@ -32,6 +34,10 @@ namespace DotNetty.Buffers
return Allocator.Buffer(initialCapacity, maxCapacity);
}
/// <summary>
/// Creates a new big-endian buffer which wraps the specified <see cref="array"/>.
/// A modification on the specified array's content will be visible to the returned buffer.
/// </summary>
public static IByteBuffer WrappedBuffer(byte[] array)
{
Contract.Requires(array != null);
@ -39,6 +45,47 @@ namespace DotNetty.Buffers
return array.Length == 0 ? Empty : new UnpooledHeapByteBuffer(Allocator, array, array.Length);
}
/// <summary>
/// Creates a new buffer which wraps the specified buffer's readable bytes.
/// A modification on the specified buffer's content will be visible to the returned buffer.
/// </summary>
/// <param name="buffer">The buffer to wrap. Reference count ownership of this variable is transfered to this method.</param>
/// <returns>The readable portion of the <see cref="buffer"/>, or an empty buffer if there is no readable portion.</returns>
public static IByteBuffer WrappedBuffer(IByteBuffer buffer)
{
Contract.Requires(buffer != null);
if (buffer.IsReadable())
{
return buffer.Slice();
}
else
{
buffer.Release();
return Empty;
}
}
/// <summary>
/// Creates a new big-endian composite buffer which wraps the specified arrays without copying them.
/// A modification on the specified arrays' content will be visible to the returned buffer.
/// </summary>
public static IByteBuffer WrappedBuffer(params byte[][] arrays)
{
return WrappedBuffer(AbstractByteBufferAllocator.DefaultMaxComponents, arrays);
}
/// <summary>
/// Creates a new big-endian composite buffer which wraps the readable bytes of the specified buffers without copying them.
/// A modification on the content of the specified buffers will be visible to the returned buffer.
/// </summary>
/// <param name="buffers">The buffers to wrap. Reference count ownership of all variables is transfered to this method.</param>
/// <returns>The readable portion of the <see cref="buffers"/>. The caller is responsible for releasing this buffer.</returns>
public static IByteBuffer WrappedBuffer(params IByteBuffer[] buffers)
{
return WrappedBuffer(AbstractByteBufferAllocator.DefaultMaxComponents, buffers);
}
public static IByteBuffer WrappedBuffer(byte[] array, int offset, int length)
{
Contract.Requires(array != null);
@ -58,6 +105,74 @@ namespace DotNetty.Buffers
return WrappedBuffer(array).Slice(offset, length);
}
/// <summary>
/// Creates a new big-endian composite buffer which wraps the readable bytes of the specified buffers without copying them.
/// A modification on the content of the specified buffers will be visible to the returned buffer.
/// </summary>
/// <param name="maxNumComponents">Advisement as to how many independent buffers are allowed to exist before consolidation occurs.</param>
/// <param name="buffers">The buffers to wrap. Reference count ownership of all variables is transfered to this method.</param>
/// <returns>The readable portion of the <see cref="buffers"/>. The caller is responsible for releasing this buffer.</returns>
public static IByteBuffer WrappedBuffer(int maxNumComponents, params IByteBuffer[] buffers)
{
switch (buffers.Length)
{
case 0:
break;
case 1:
IByteBuffer buffer = buffers[0];
if (buffer.IsReadable())
return WrappedBuffer(buffer.WithOrder(ByteOrder.BigEndian));
else
buffer.Release();
break;
default:
for (int i = 0; i < buffers.Length; i++)
{
IByteBuffer buf = buffers[i];
if (buf.IsReadable())
return new CompositeByteBuffer(Allocator, maxNumComponents, buffers, i, buffers.Length);
else
buf.Release();
}
break;
}
return Empty;
}
/// <summary>
/// Creates a new big-endian composite buffer which wraps the specified arrays without copying them.
/// A modification on the specified arrays' content will be visible to the returned buffer.
/// </summary>
public static IByteBuffer WrappedBuffer(int maxNumComponents, params byte[][] arrays)
{
if (arrays.Length == 0)
{
return Empty;
}
if (arrays.Length == 1 && arrays[0].Length != 0)
{
return WrappedBuffer(arrays[0]);
}
// Get the list of the component, while guessing the byte order.
IList<IByteBuffer> components = new List<IByteBuffer>(arrays.Length);
foreach (byte[] a in arrays)
{
if (a == null)
{
break;
}
if (a.Length > 0)
{
components.Add(WrappedBuffer(a));
}
}
return components.Count > 0 ? new CompositeByteBuffer(Allocator, maxNumComponents, components) : Empty;
}
public static IByteBuffer UnreleasableBuffer(IByteBuffer buffer)
{
Contract.Requires(buffer != null);
@ -91,6 +206,29 @@ namespace DotNetty.Buffers
return WrappedBuffer(newArray);
}
/// <summary>
/// Creates a new big-endian buffer whose content is a merged copy of of the specified <see cref="arrays" />.
/// The new buffer's <see cref="IByteBuffer.ReaderIndex" /> and <see cref="IByteBuffer.WriterIndex" />
/// are <c>0</c> and <see cref="Array.Length" /> respectively.
/// </summary>
/// <param name="arrays"></param>
/// <returns></returns>
public static IByteBuffer CopiedBuffer(params byte[][] arrays)
{
if (arrays.Length == 0)
{
return Empty;
}
if (arrays.Length == 1)
{
return arrays[0].Length == 0 ? Empty : CopiedBuffer(arrays[0]);
}
byte[] mergedArray = arrays.CombineBytes();
return WrappedBuffer(mergedArray);
}
/// <summary>
/// Creates a new big-endian buffer whose content is a copy of the specified <see cref="array" />.
/// The new buffer's <see cref="IByteBuffer.ReaderIndex" /> and <see cref="IByteBuffer.WriterIndex" />
@ -106,10 +244,12 @@ namespace DotNetty.Buffers
public static IByteBuffer CopiedBuffer(byte[] array, int offset, int length)
{
Contract.Requires(array != null);
if (array.Length == 0)
{
return Empty;
}
var copy = new byte[length];
Array.Copy(array, offset, copy, 0, length);
return WrappedBuffer(copy);
@ -137,5 +277,252 @@ namespace DotNetty.Buffers
duplicate.GetBytes(0, copy);
return WrappedBuffer(copy).WithOrder(duplicate.Order);
}
/// <summary>
/// Creates a new big-endian buffer whose content is a merged copy of the specified <see cref="Array" />.
/// The new buffer's <see cref="IByteBuffer.ReaderIndex" /> and <see cref="IByteBuffer.WriterIndex" />
/// are <c>0</c> and <see cref="IByteBuffer.Capacity" /> respectively.
/// </summary>
/// <param name="buffers">Buffers we're going to copy.</param>
/// <returns>The new buffer that copies the contents of <see cref="buffers" />.</returns>
public static IByteBuffer CopiedBuffer(params IByteBuffer[] buffers)
{
if (buffers.Length == 0)
{
return Empty;
}
if (buffers.Length == 1)
{
return CopiedBuffer(buffers[0]);
}
long newlength = 0;
ByteOrder order = buffers[0].Order;
foreach (IByteBuffer buffer in buffers)
{
newlength += buffer.ReadableBytes;
}
var mergedArray = new byte[newlength];
for (int i = 0, j = 0; i < buffers.Length; i++)
{
IByteBuffer b = buffers[i];
if (!order.Equals(b.Order))
{
throw new ArgumentException($"The byte orders in {nameof(buffers)} are inconsistent ");
}
int bLen = b.ReadableBytes;
b.GetBytes(b.ReaderIndex, mergedArray, j, bLen);
j += bLen;
}
return WrappedBuffer(mergedArray).WithOrder(order);
}
/// <summary>
/// Creates a new 4-byte big-endian buffer that holds the specified 32-bit integer.
/// </summary>
public static IByteBuffer CopyInt(int value)
{
IByteBuffer buf = Buffer(4);
buf.WriteInt(value);
return buf;
}
/// <summary>
/// Create a big-endian buffer that holds a sequence of the specified 32-bit integers.
/// </summary>
public static IByteBuffer CopyInt(params int[] values)
{
if (values == null || values.Length == 0)
{
return Empty;
}
IByteBuffer buffer = Buffer(values.Length * 4);
foreach (int v in values)
{
buffer.WriteInt(v);
}
return buffer;
}
/// <summary>
/// Creates a new 2-byte big-endian buffer that holds the specified 16-bit integer.
/// </summary>
public static IByteBuffer CopyShort(int value)
{
IByteBuffer buf = Buffer(2);
buf.WriteShort(value);
return buf;
}
/// <summary>
/// Create a new big-endian buffer that holds a sequence of the specified 16-bit integers.
/// </summary>
public static IByteBuffer CopyShort(params short[] values)
{
if (values == null || values.Length == 0)
{
return Empty;
}
IByteBuffer buffer = Buffer(values.Length * 2);
foreach (short v in values)
{
buffer.WriteShort(v);
}
return buffer;
}
/// <summary>
/// Creates a new 3-byte big-endian buffer that holds the specified 24-bit integer.
/// </summary>
public static IByteBuffer CopyMedium(int value)
{
IByteBuffer buf = Buffer(3);
buf.WriteMedium(value);
return buf;
}
/// <summary>
/// Create a new big-endian buffer that holds a sequence of the specified 24-bit integers.
/// </summary>
public static IByteBuffer CopyMedium(params int[] values)
{
if (values == null || values.Length == 0)
{
return Empty;
}
IByteBuffer buffer = Buffer(values.Length * 3);
foreach (int v in values)
{
buffer.WriteMedium(v);
}
return buffer;
}
/// <summary>
/// Creates a new 8-byte big-endian buffer that holds the specified 64-bit integer.
/// </summary>
public static IByteBuffer CopyLong(long value)
{
IByteBuffer buf = Buffer(8);
buf.WriteLong(value);
return buf;
}
/// <summary>
/// Create a new big-endian buffer that holds a sequence of the specified 64-bit integers.
/// </summary>
public static IByteBuffer CopyLong(params long[] values)
{
if (values == null || values.Length == 0)
{
return Empty;
}
IByteBuffer buffer = Buffer(values.Length * 8);
foreach (long v in values)
{
buffer.WriteLong(v);
}
return buffer;
}
/// <summary>
/// Creates a new single-byte big-endian buffer that holds the specified boolean value.
/// </summary>
public static IByteBuffer CopyBoolean(bool value)
{
IByteBuffer buf = Buffer(1);
buf.WriteBoolean(value);
return buf;
}
/// <summary>
/// Create a new big-endian buffer that holds a sequence of the specified boolean values.
/// </summary>
public static IByteBuffer CopyBoolean(params bool[] values)
{
if (values == null || values.Length == 0)
{
return Empty;
}
IByteBuffer buffer = Buffer(values.Length);
foreach (bool v in values)
{
buffer.WriteBoolean(v);
}
return buffer;
}
/// <summary>
/// Creates a new 4-byte big-endian buffer that holds the specified 32-bit floating point number.
/// </summary>
public static IByteBuffer CopyFloat(float value)
{
IByteBuffer buf = Buffer(4);
buf.WriteFloat(value);
return buf;
}
/// <summary>
/// Create a new big-endian buffer that holds a sequence of the specified 32-bit floating point numbers.
/// </summary>
public static IByteBuffer CopyFloat(params float[] values)
{
if (values == null || values.Length == 0)
{
return Empty;
}
IByteBuffer buffer = Buffer(values.Length * 4);
foreach (float v in values)
{
buffer.WriteFloat(v);
}
return buffer;
}
/// <summary>
/// Creates a new 8-byte big-endian buffer that holds the specified 64-bit floating point number.
/// </summary>
public static IByteBuffer CopyDouble(double value)
{
IByteBuffer buf = Buffer(8);
buf.WriteDouble(value);
return buf;
}
/// <summary>
/// Create a new big-endian buffer that holds a sequence of the specified 64-bit floating point numbers.
/// </summary>
public static IByteBuffer CopyDouble(params double[] values)
{
if (values == null || values.Length == 0)
{
return Empty;
}
IByteBuffer buffer = Buffer(values.Length * 8);
foreach (double v in values)
{
buffer.WriteDouble(v);
}
return buffer;
}
}
}

Просмотреть файл

@ -4,6 +4,7 @@
namespace DotNetty.Common.Utilities
{
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
/// <summary>
@ -72,5 +73,27 @@ namespace DotNetty.Common.Utilities
array[i] = value;
}
}
/// <summary>
/// Merge the byte arrays into one byte array.
/// </summary>
public static byte[] CombineBytes(this byte[][] arrays)
{
long newlength = 0;
foreach (byte[] array in arrays)
{
newlength += array.Length;
}
var mergedArray = new byte[newlength];
int offset = 0;
foreach (byte[] array in arrays)
{
Buffer.BlockCopy(array, 0, mergedArray, offset, array.Length);
offset += array.Length;
}
return mergedArray;
}
}
}

Просмотреть файл

@ -0,0 +1,625 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace DotNetty.Buffers.Tests
{
using System;
using System.Collections.Generic;
using Xunit;
using static Unpooled;
/// <summary>
/// Tests channel buffers
/// </summary>
public class UnpooledTest
{
static readonly IByteBuffer[] EmptyByteBuffer = new IByteBuffer[0];
static readonly byte[][] EmptyBytes2D = new byte[0][];
static readonly byte[] EmptyBytes = { };
[Fact]
public void TestCompositeWrappedBuffer()
{
IByteBuffer header = Buffer(12);
IByteBuffer payload = Buffer(512);
header.WriteBytes(new byte[12]);
payload.WriteBytes(new byte[512]);
IByteBuffer buffer = WrappedBuffer(header, payload);
Assert.Equal(12, header.ReadableBytes);
Assert.Equal(512, payload.ReadableBytes);
Assert.Equal(12 + 512, buffer.ReadableBytes);
Assert.Equal(2, buffer.IoBufferCount);
buffer.Release();
}
[Fact]
public void TestHashCode()
{
var map = new Dictionary<byte[], int>();
map.Add(EmptyBytes, 1);
map.Add(new byte[] { 1 }, 32);
map.Add(new byte[] { 2 }, 33);
map.Add(new byte[] { 0, 1 }, 962);
map.Add(new byte[] { 1, 2 }, 994);
map.Add(new byte[] { 0, 1, 2, 3, 4, 5 }, 63504931);
map.Add(new byte[] { 6, 7, 8, 9, 0, 1 }, unchecked((int)97180294697L));
foreach (KeyValuePair<byte[], int> e in map)
{
IByteBuffer buffer = WrappedBuffer(e.Key);
Assert.Equal(e.Value, ByteBufferUtil.HashCode(buffer));
buffer.Release();
}
}
[Fact]
public void TestEquals()
{
IByteBuffer a, b;
// Different length.
a = WrappedBuffer(new byte[] { 1 });
b = WrappedBuffer(new byte[] { 1, 2 });
Assert.False(ByteBufferUtil.Equals(a, b));
a.Release();
b.Release();
// Same content, same firstIndex, short length.
a = WrappedBuffer(new byte[] { 1, 2, 3 });
b = WrappedBuffer(new byte[] { 1, 2, 3 });
Assert.True(ByteBufferUtil.Equals(a, b));
a.Release();
b.Release();
// Same content, different firstIndex, short length.
a = WrappedBuffer(new byte[] { 1, 2, 3 });
b = WrappedBuffer(new byte[] { 0, 1, 2, 3, 4 }, 1, 3);
Assert.True(ByteBufferUtil.Equals(a, b));
a.Release();
b.Release();
// Different content, same firstIndex, short length.
a = WrappedBuffer(new byte[] { 1, 2, 3 });
b = WrappedBuffer(new byte[] { 1, 2, 4 });
Assert.False(ByteBufferUtil.Equals(a, b));
a.Release();
b.Release();
// Different content, different firstIndex, short length.
a = WrappedBuffer(new byte[] { 1, 2, 3 });
b = WrappedBuffer(new byte[] { 0, 1, 2, 4, 5 }, 1, 3);
Assert.False(ByteBufferUtil.Equals(a, b));
a.Release();
b.Release();
// Same content, same firstIndex, long length.
a = WrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
b = WrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
Assert.True(ByteBufferUtil.Equals(a, b));
a.Release();
b.Release();
// Same content, different firstIndex, long length.
a = WrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
b = WrappedBuffer(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }, 1, 10);
Assert.True(ByteBufferUtil.Equals(a, b));
a.Release();
b.Release();
// Different content, same firstIndex, long length.
a = WrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
b = WrappedBuffer(new byte[] { 1, 2, 3, 4, 6, 7, 8, 5, 9, 10 });
Assert.False(ByteBufferUtil.Equals(a, b));
a.Release();
b.Release();
// Different content, different firstIndex, long length.
a = WrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
b = WrappedBuffer(new byte[] { 0, 1, 2, 3, 4, 6, 7, 8, 5, 9, 10, 11 }, 1, 10);
Assert.False(ByteBufferUtil.Equals(a, b));
a.Release();
b.Release();
}
[Fact]
public void TestCompare()
{
IList<IByteBuffer> expected = new List<IByteBuffer>();
expected.Add(WrappedBuffer(new byte[] { 1 }));
expected.Add(WrappedBuffer(new byte[] { 1, 2 }));
expected.Add(WrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }));
expected.Add(WrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }));
expected.Add(WrappedBuffer(new byte[] { 2 }));
expected.Add(WrappedBuffer(new byte[] { 2, 3 }));
expected.Add(WrappedBuffer(new byte[] { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 }));
expected.Add(WrappedBuffer(new byte[] { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 }));
expected.Add(WrappedBuffer(new byte[] { 2, 3, 4 }, 1, 1));
expected.Add(WrappedBuffer(new byte[] { 1, 2, 3, 4 }, 2, 2));
expected.Add(WrappedBuffer(new byte[] { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, 1, 10));
expected.Add(WrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, 2, 12));
expected.Add(WrappedBuffer(new byte[] { 2, 3, 4, 5 }, 2, 1));
expected.Add(WrappedBuffer(new byte[] { 1, 2, 3, 4, 5 }, 3, 2));
expected.Add(WrappedBuffer(new byte[] { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }, 2, 10));
expected.Add(WrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, 3, 12));
for (int i = 0; i < expected.Count; i++)
{
for (int j = 0; j < expected.Count; j++)
{
if (i == j)
{
Assert.Equal(0, ByteBufferUtil.Compare(expected[i], expected[j]));
}
else if (i < j)
{
Assert.True(ByteBufferUtil.Compare(expected[i], expected[j]) < 0);
}
else
{
Assert.True(ByteBufferUtil.Compare(expected[i], expected[j]) > 0);
}
}
}
foreach (IByteBuffer buffer in expected)
{
buffer.Release();
}
}
[Fact]
public void ShouldReturnEmptyBufferWhenLengthIsZero()
{
AssertSameAndRelease(Empty, WrappedBuffer(EmptyBytes));
AssertSameAndRelease(Empty, WrappedBuffer(new byte[8], 0, 0));
AssertSameAndRelease(Empty, WrappedBuffer(new byte[8], 8, 0));
AssertSameAndRelease(Empty, WrappedBuffer(Empty));
AssertSameAndRelease(Empty, WrappedBuffer(EmptyBytes2D));
AssertSameAndRelease(Empty, WrappedBuffer(new[] { EmptyBytes }));
AssertSameAndRelease(Empty, WrappedBuffer(EmptyByteBuffer));
AssertSameAndRelease(Empty, WrappedBuffer(new IByteBuffer[] { Buffer(0) }));
AssertSameAndRelease(Empty, WrappedBuffer(Buffer(0), Buffer(0)));
AssertSameAndRelease(Empty, CopiedBuffer(EmptyBytes));
AssertSameAndRelease(Empty, CopiedBuffer(new byte[8], 0, 0));
AssertSameAndRelease(Empty, CopiedBuffer(new byte[8], 8, 0));
AssertSameAndRelease(Empty, CopiedBuffer(Empty));
Assert.Same(Empty, CopiedBuffer(EmptyBytes2D));
AssertSameAndRelease(Empty, CopiedBuffer(new[] { EmptyBytes }));
AssertSameAndRelease(Empty, CopiedBuffer(EmptyByteBuffer));
AssertSameAndRelease(Empty, CopiedBuffer(new IByteBuffer[] { Buffer(0) }));
AssertSameAndRelease(Empty, CopiedBuffer(Buffer(0), Buffer(0)));
}
static void AssertSameAndRelease(IByteBuffer expected, IByteBuffer actual)
{
Assert.Same(expected, actual);
expected.Release();
actual.Release();
}
static void AssertEqualAndRelease(IByteBuffer expected, IByteBuffer actual)
{
Assert.Equal(expected, actual);
expected.Release();
actual.Release();
}
[Fact]
public void TestCompare2()
{
IByteBuffer expected = WrappedBuffer(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF });
IByteBuffer actual = WrappedBuffer(new byte[] { 0x00, 0x00, 0x00, 0x00 });
Assert.True(ByteBufferUtil.Compare(expected, actual) > 0);
expected.Release();
actual.Release();
expected = WrappedBuffer(new byte[] { 0xFF });
actual = WrappedBuffer(new byte[] { 0x00 });
Assert.True(ByteBufferUtil.Compare(expected, actual) > 0);
expected.Release();
actual.Release();
}
[Fact]
public void ShouldAllowEmptyBufferToCreateCompositeBuffer()
{
IByteBuffer buf = WrappedBuffer(Empty, WrappedBuffer(new byte[16]).WithOrder(ByteOrder.LittleEndian), Empty);
try
{
Assert.Equal(16, buf.Capacity);
}
finally
{
buf.Release();
}
}
[Fact]
public void TestWrappedBuffer()
{
AssertEqualAndRelease(
WrappedBuffer(new byte[] { 1, 2, 3 }),
WrappedBuffer(new byte[][] { new byte[] { 1, 2, 3 } }));
AssertEqualAndRelease(
WrappedBuffer(new byte[] { 1, 2, 3 }),
WrappedBuffer(new byte[] { 1 }, new byte[] { 2 }, new byte[] { 3 }));
AssertEqualAndRelease(WrappedBuffer(new byte[] { 1, 2, 3 }),
WrappedBuffer(new IByteBuffer[] { WrappedBuffer(new byte[] { 1, 2, 3 }) }));
AssertEqualAndRelease(
WrappedBuffer(new byte[] { 1, 2, 3 }),
WrappedBuffer(WrappedBuffer(new byte[] { 1 }),
WrappedBuffer(new byte[] { 2 }), WrappedBuffer(new byte[] { 3 })));
}
[Fact]
public void TestSingleWrappedByteBufReleased()
{
IByteBuffer buf = Buffer(12).WriteByte(0);
IByteBuffer wrapped = WrappedBuffer(buf);
Assert.True(wrapped.Release());
Assert.Equal(0, buf.ReferenceCount);
}
[Fact]
public void TestSingleUnReadableWrappedByteBufReleased()
{
IByteBuffer buf = Buffer(12);
IByteBuffer wrapped = WrappedBuffer(buf);
Assert.False(wrapped.Release()); // Empty Buffer cannot be released
Assert.Equal(0, buf.ReferenceCount);
}
[Fact]
public void TestMultiByteBufReleased()
{
IByteBuffer buf1 = Buffer(12).WriteByte(0);
IByteBuffer buf2 = Buffer(12).WriteByte(0);
IByteBuffer wrapped = WrappedBuffer(16, buf1, buf2);
Assert.True(wrapped.Release());
Assert.Equal(0, buf1.ReferenceCount);
Assert.Equal(0, buf2.ReferenceCount);
}
[Fact]
public void TestMultiUnReadableByteBufReleased()
{
IByteBuffer buf1 = Buffer(12);
IByteBuffer buf2 = Buffer(12);
IByteBuffer wrapped = WrappedBuffer(16, buf1, buf2);
Assert.False(wrapped.Release()); // Empty Buffer cannot be released
Assert.Equal(0, buf1.ReferenceCount);
Assert.Equal(0, buf2.ReferenceCount);
}
[Fact]
public void TestCopiedBuffer()
{
AssertEqualAndRelease(WrappedBuffer(new byte[] { 1, 2, 3 }),
CopiedBuffer(new byte[][] { new byte[] { 1, 2, 3 } }));
AssertEqualAndRelease(WrappedBuffer(new byte[] { 1, 2, 3 }),
CopiedBuffer(new byte[] { 1 }, new byte[] { 2 }, new byte[] { 3 }));
AssertEqualAndRelease(WrappedBuffer(new byte[] { 1, 2, 3 }),
CopiedBuffer(new IByteBuffer[] { WrappedBuffer(new byte[] { 1, 2, 3 }) }));
AssertEqualAndRelease(WrappedBuffer(new byte[] { 1, 2, 3 }),
CopiedBuffer(WrappedBuffer(new byte[] { 1 }),
WrappedBuffer(new byte[] { 2 }), WrappedBuffer(new byte[] { 3 })));
}
[Fact]
public void TestHexDump()
{
Assert.Equal("", ByteBufferUtil.HexDump(Empty));
IByteBuffer buffer = WrappedBuffer(new byte[] { 0x12, 0x34, 0x56 });
Assert.Equal("123456", ByteBufferUtil.HexDump(buffer));
buffer.Release();
buffer = WrappedBuffer(new byte[]{
0x12, 0x34, 0x56, 0x78,
0x90, 0xAB, 0xCD, 0xEF
});
Assert.Equal("1234567890abcdef", ByteBufferUtil.HexDump(buffer));
buffer.Release();
}
[Fact]
public void TestSwapMedium()
{
Assert.Equal(0x563412, ByteBufferUtil.SwapMedium(0x123456));
Assert.Equal(0x80, ByteBufferUtil.SwapMedium(0x800000));
}
[Fact]
public void TestWrapSingleInt()
{
IByteBuffer buffer = CopyInt(42);
Assert.Equal(4, buffer.Capacity);
Assert.Equal(42, buffer.ReadInt());
Assert.False(buffer.IsReadable());
buffer.Release();
}
[Fact]
public void TestWrapInt()
{
IByteBuffer buffer = CopyInt(1, 4);
Assert.Equal(8, buffer.Capacity);
Assert.Equal(1, buffer.ReadInt());
Assert.Equal(4, buffer.ReadInt());
Assert.False(buffer.IsReadable());
buffer.Release();
buffer = CopyInt(null);
Assert.Equal(0, buffer.Capacity);
buffer.Release();
buffer = CopyInt(new int[] { });
Assert.Equal(0, buffer.Capacity);
buffer.Release();
}
[Fact]
public void TestWrapSingleShort()
{
IByteBuffer buffer = CopyShort(42);
Assert.Equal(2, buffer.Capacity);
Assert.Equal(42, buffer.ReadShort());
Assert.False(buffer.IsReadable());
buffer.Release();
}
[Fact]
public void TestWrapShortFromShortArray()
{
IByteBuffer buffer = CopyShort(1, 4);
Assert.Equal(4, buffer.Capacity);
Assert.Equal(1, buffer.ReadShort());
Assert.Equal(4, buffer.ReadShort());
Assert.False(buffer.IsReadable());
buffer.Release();
buffer = CopyShort(null);
Assert.Equal(0, buffer.Capacity);
buffer.Release();
buffer = CopyShort(new short[] { });
Assert.Equal(0, buffer.Capacity);
buffer.Release();
}
[Fact]
public void TestWrapSingleMedium()
{
IByteBuffer buffer = CopyMedium(42);
Assert.Equal(3, buffer.Capacity);
Assert.Equal(42, buffer.ReadMedium());
Assert.False(buffer.IsReadable());
buffer.Release();
}
[Fact]
public void TestWrapMedium()
{
IByteBuffer buffer = CopyMedium(1, 4);
Assert.Equal(6, buffer.Capacity);
Assert.Equal(1, buffer.ReadMedium());
Assert.Equal(4, buffer.ReadMedium());
Assert.False(buffer.IsReadable());
buffer.Release();
buffer = CopyMedium(null);
Assert.Equal(0, buffer.Capacity);
buffer.Release();
buffer = CopyMedium(new int[] { });
Assert.Equal(0, buffer.Capacity);
buffer.Release();
}
[Fact]
public void TestWrapSingleLong()
{
IByteBuffer buffer = CopyLong(42);
Assert.Equal(8, buffer.Capacity);
Assert.Equal(42, buffer.ReadLong());
Assert.False(buffer.IsReadable());
buffer.Release();
}
[Fact]
public void TestWrapLong()
{
IByteBuffer buffer = CopyLong(1, 4);
Assert.Equal(16, buffer.Capacity);
Assert.Equal(1, buffer.ReadLong());
Assert.Equal(4, buffer.ReadLong());
Assert.False(buffer.IsReadable());
buffer.Release();
buffer = CopyLong(null);
Assert.Equal(0, buffer.Capacity);
buffer.Release();
buffer = CopyLong(new long[] { });
Assert.Equal(0, buffer.Capacity);
buffer.Release();
}
[Fact]
public void TestWrapSingleFloat()
{
IEqualityComparer<float> comparer = new ApproximateComparer(0.01);
IByteBuffer buffer = CopyFloat(42);
Assert.Equal(4, buffer.Capacity);
Assert.Equal(42, buffer.ReadFloat(), comparer);
Assert.False(buffer.IsReadable());
buffer.Release();
}
[Fact]
public void TestWrapFloat()
{
IEqualityComparer<float> comparer = new ApproximateComparer(0.01);
IByteBuffer buffer = CopyFloat(1, 4);
Assert.Equal(8, buffer.Capacity);
Assert.Equal(1, buffer.ReadFloat(), comparer);
Assert.Equal(4, buffer.ReadFloat(), comparer);
Assert.False(buffer.IsReadable());
buffer.Release();
buffer = CopyFloat(null);
Assert.Equal(0, buffer.Capacity);
buffer.Release();
buffer = CopyFloat(new float[] { });
Assert.Equal(0, buffer.Capacity);
buffer.Release();
}
[Fact]
public void TestWrapSingleDouble()
{
IEqualityComparer<double> comparer = new ApproximateComparer(0.01);
IByteBuffer buffer = CopyDouble(42);
Assert.Equal(8, buffer.Capacity);
Assert.Equal(42, buffer.ReadDouble(), comparer);
Assert.False(buffer.IsReadable());
buffer.Release();
}
[Fact]
public void TestWrapDouble()
{
IEqualityComparer<double> comparer = new ApproximateComparer(0.01);
IByteBuffer buffer = CopyDouble(1, 4);
Assert.Equal(16, buffer.Capacity);
Assert.Equal(1, buffer.ReadDouble(), comparer);
Assert.Equal(4, buffer.ReadDouble(), comparer);
Assert.False(buffer.IsReadable());
buffer = CopyDouble(null);
Assert.Equal(0, buffer.Capacity);
buffer.Release();
buffer = CopyDouble(new double[] { });
Assert.Equal(0, buffer.Capacity);
buffer.Release();
}
[Fact]
public void TestWrapBoolean()
{
IByteBuffer buffer = CopyBoolean(true, false);
Assert.Equal(2, buffer.Capacity);
Assert.True(buffer.ReadBoolean());
Assert.False(buffer.ReadBoolean());
Assert.False(buffer.IsReadable());
buffer.Release();
buffer = CopyBoolean(null);
Assert.Equal(0, buffer.Capacity);
buffer.Release();
buffer = CopyBoolean(new bool[] { });
Assert.Equal(0, buffer.Capacity);
buffer.Release();
}
[Fact]
public void SkipBytesNegativeLength()
{
Assert.Throws<ArgumentOutOfRangeException>(() =>
{
IByteBuffer buf = Buffer(8);
try
{
buf.SkipBytes(-1);
}
finally
{
buf.Release();
}
});
}
[Fact]
public void TestInconsistentOrder()
{
Assert.Throws<ArgumentException>(() =>
{
IByteBuffer buf = WrappedBuffer(new byte[] { 1 }).WithOrder(ByteOrder.BigEndian);
IByteBuffer buf1 = WrappedBuffer(new byte[] { 2, 3 }).WithOrder(ByteOrder.LittleEndian);
try
{
CopiedBuffer(buf, buf1);
}
finally
{
buf.Release();
buf1.Release();
}
});
}
[Fact]
public void TestWrapByteBufArrayStartsWithNonReadable()
{
IByteBuffer buffer1 = Buffer(8);
IByteBuffer buffer2 = Buffer(8).WriteZero(8); // Ensure the IByteBuffer is readable.
IByteBuffer buffer3 = Buffer(8);
IByteBuffer buffer4 = Buffer(8).WriteZero(8); // Ensure the IByteBuffer is readable.
IByteBuffer wrapped = WrappedBuffer(buffer1, buffer2, buffer3, buffer4);
Assert.Equal(16, wrapped.ReadableBytes);
Assert.True(wrapped.Release());
Assert.Equal(0, buffer1.ReferenceCount);
Assert.Equal(0, buffer2.ReferenceCount);
Assert.Equal(0, buffer3.ReferenceCount);
Assert.Equal(0, buffer4.ReferenceCount);
Assert.Equal(0, wrapped.ReferenceCount);
}
}
public class ApproximateComparer : IEqualityComparer<double>, IEqualityComparer<float>
{
public double MarginOfError { get; }
public ApproximateComparer(double marginOfError)
{
if (marginOfError <= 0 || marginOfError >= 1.0)
throw new ArgumentOutOfRangeException($"{nameof(marginOfError)} must be not less than 0 and not greater than 1");
this.MarginOfError = marginOfError;
}
public bool Equals(double x, double y)
{
return Math.Abs(x - y) <= this.MarginOfError;
}
public bool Equals(float x, float y)
{
return Math.Abs(x - y) <= (float)this.MarginOfError;
}
public int GetHashCode(double obj)
{
return obj.GetHashCode();
}
public int GetHashCode(float obj)
{
return obj.GetHashCode();
}
}
}