From 78633ed353499957680960df01f44055106a8bc2 Mon Sep 17 00:00:00 2001 From: Johnny Z Date: Wed, 7 Mar 2018 06:45:23 +1000 Subject: [PATCH] Buffer read/write/get/set strings. Motivation: Implement commonly used string-based operations (originally Netty CharSequence) for byte buffers. Modifications: Added read/write/get/set strings methods to byte buffers. Also reduced unsafe pin/unpins for byte operations. Result: String-based operations are supported in DotNetty --- src/DotNetty.Buffers/AbstractByteBuffer.cs | 110 +++++- .../AbstractByteBufferAllocator.cs | 4 +- src/DotNetty.Buffers/ByteBufferUtil.cs | 221 ++++++++++++ src/DotNetty.Buffers/EmptyByteBuffer.cs | 323 +++++------------- src/DotNetty.Buffers/IByteBuffer.cs | 47 +++ src/DotNetty.Buffers/UnsafeByteBufferUtil .cs | 62 +++- src/DotNetty.Buffers/WrappedByteBuffer.cs | 8 + .../WrappedCompositeByteBuffer.cs | 8 + .../Internal/PlatformDependent.cs | 4 +- src/DotNetty.Common/Utilities/CharUtil.cs | 20 ++ .../AbstractByteBufferTests.cs | 129 ++++++- .../EmptyByteBufferTests.cs | 2 +- .../Utilities/HashedWheelTimerTest.cs | 2 +- .../Flow/FlowControlHandlerTest.cs | 15 +- .../IdleStateHandlerTest.cs | 108 +++--- .../Buffers/ByteBufUtilBenchmark.cs | 137 ++++++++ .../DotNetty.Microbench.csproj | 2 +- test/DotNetty.Microbench/Program.cs | 1 + test/DotNetty.Tests.End2End/End2EndTests.cs | 4 +- .../ConnectionAttemptTests.cs | 4 +- .../WriteBeforeRegisteredTests.cs | 4 +- .../Channel/DefaulChannelIdTest.cs | 5 +- .../Channel/Embedded/EmbeddedChannelTest.cs | 6 +- .../Channel/Pool/SimpleChannelPoolTest.cs | 8 +- 24 files changed, 861 insertions(+), 373 deletions(-) create mode 100644 src/DotNetty.Common/Utilities/CharUtil.cs create mode 100644 test/DotNetty.Microbench/Buffers/ByteBufUtilBenchmark.cs diff --git a/src/DotNetty.Buffers/AbstractByteBuffer.cs b/src/DotNetty.Buffers/AbstractByteBuffer.cs index b905aa2c3..59a0add9d 100644 --- a/src/DotNetty.Buffers/AbstractByteBuffer.cs +++ b/src/DotNetty.Buffers/AbstractByteBuffer.cs @@ -72,7 +72,7 @@ public virtual IByteBuffer SetReaderIndex(int index) { if (index < 0 || index > this.writerIndex) { - throw new IndexOutOfRangeException($"ReaderIndex: {index} (expected: 0 <= readerIndex <= writerIndex({this.WriterIndex})"); + ThrowHelper.ThrowIndexOutOfRangeException($"ReaderIndex: {index} (expected: 0 <= readerIndex <= writerIndex({this.WriterIndex})"); } this.readerIndex = index; @@ -85,7 +85,7 @@ public virtual IByteBuffer SetWriterIndex(int index) { if (index < this.readerIndex || index > this.Capacity) { - throw new IndexOutOfRangeException($"WriterIndex: {index} (expected: 0 <= readerIndex({this.readerIndex}) <= writerIndex <= capacity ({this.Capacity})"); + ThrowHelper.ThrowIndexOutOfRangeException($"WriterIndex: {index} (expected: 0 <= readerIndex({this.readerIndex}) <= writerIndex <= capacity ({this.Capacity})"); } this.SetWriterIndex0(index); @@ -101,7 +101,7 @@ public virtual IByteBuffer SetIndex(int readerIdx, int writerIdx) { if (readerIdx < 0 || readerIdx > writerIdx || writerIdx > this.Capacity) { - throw new IndexOutOfRangeException($"ReaderIndex: {readerIdx}, WriterIndex: {writerIdx} (expected: 0 <= readerIndex <= writerIndex <= capacity ({this.Capacity})"); + ThrowHelper.ThrowIndexOutOfRangeException($"ReaderIndex: {readerIdx}, WriterIndex: {writerIdx} (expected: 0 <= readerIndex <= writerIndex <= capacity ({this.Capacity})"); } this.SetIndex0(readerIdx, writerIdx); @@ -235,14 +235,14 @@ public virtual IByteBuffer EnsureWritable(int minWritableBytes) { if (minWritableBytes < 0) { - throw new ArgumentOutOfRangeException(nameof(minWritableBytes), - "expected minWritableBytes to be greater than zero"); + ThrowHelper.ThrowArgumentOutOfRangeException(nameof(minWritableBytes), "expected minWritableBytes to be greater than zero"); } this.EnsureWritable0(minWritableBytes); return this; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected internal void EnsureWritable0(int minWritableBytes) { this.EnsureAccessible(); @@ -253,7 +253,7 @@ protected internal void EnsureWritable0(int minWritableBytes) if (minWritableBytes > this.MaxCapacity - this.writerIndex) { - throw new IndexOutOfRangeException($"writerIndex({this.writerIndex}) + minWritableBytes({minWritableBytes}) exceeds maxCapacity({this.MaxCapacity}): {this}"); + ThrowHelper.ThrowIndexOutOfRangeException($"writerIndex({this.writerIndex}) + minWritableBytes({minWritableBytes}) exceeds maxCapacity({this.MaxCapacity}): {this}"); } // Normalize the current capacity to the power of 2. @@ -455,6 +455,42 @@ public virtual IByteBuffer GetBytes(int index, IByteBuffer destination, int leng public abstract IByteBuffer GetBytes(int index, Stream destination, int length); + public virtual unsafe string GetString(int index, int length, Encoding encoding) + { + this.CheckIndex0(index, length); + if (length == 0) + { + return string.Empty; + } + + if (this.HasMemoryAddress) + { + IntPtr ptr = this.AddressOfPinnedMemory(); + if (ptr != IntPtr.Zero) + { + return UnsafeByteBufferUtil.GetString((byte*)(ptr + index), length, encoding); + } + else + { + fixed (byte* p = &this.GetPinnableMemoryAddress()) + return UnsafeByteBufferUtil.GetString(p + index, length, encoding); + } + } + if (this.HasArray) + { + return encoding.GetString(this.Array, this.ArrayOffset + index, length); + } + + return this.ToString(index, length, encoding); + } + + public virtual string ReadString(int length, Encoding encoding) + { + string value = this.GetString(this.readerIndex, length, encoding); + this.readerIndex += length; + return value; + } + public virtual IByteBuffer SetByte(int index, int value) { this.CheckIndex(index); @@ -612,7 +648,7 @@ public virtual IByteBuffer SetBytes(int index, IByteBuffer src, int length) this.CheckIndex(index, length); if (length > src.ReadableBytes) { - throw new IndexOutOfRangeException($"length({length}) exceeds src.readableBytes({src.ReadableBytes}) where src is: {src}"); + ThrowHelper.ThrowIndexOutOfRangeException($"length({length}) exceeds src.readableBytes({src.ReadableBytes}) where src is: {src}"); } this.SetBytes(index, src, src.ReaderIndex, length); src.SetReaderIndex(src.ReaderIndex + length); @@ -666,6 +702,48 @@ public virtual IByteBuffer SetZero(int index, int length) return this; } + public virtual int SetString(int index, string value, Encoding encoding) => this.SetString0(index, value, encoding, false); + + int SetString0(int index, string value, Encoding encoding, bool expand) + { + if (ReferenceEquals(encoding, Encoding.UTF8)) + { + int length = ByteBufferUtil.Utf8MaxBytes(value); + if (expand) + { + this.EnsureWritable0(length); + this.CheckIndex0(index, length); + } + else + { + this.CheckIndex(index, length); + } + return ByteBufferUtil.WriteUtf8(this, index, value, value.Length); + } + if (ReferenceEquals(encoding, Encoding.ASCII)) + { + int length = value.Length; + if (expand) + { + this.EnsureWritable0(length); + this.CheckIndex0(index, length); + } + else + { + this.CheckIndex(index, length); + } + return ByteBufferUtil.WriteAscii(this, index, value, length); + } + byte[] bytes = encoding.GetBytes(value); + if (expand) + { + this.EnsureWritable0(bytes.Length); + // setBytes(...) will take care of checking the indices. + } + this.SetBytes(index, bytes); + return bytes.Length; + } + public virtual byte ReadByte() { this.CheckReadableBytes0(1); @@ -859,7 +937,7 @@ public virtual IByteBuffer ReadBytes(IByteBuffer dst, int length) { if (length > dst.WritableBytes) { - throw new IndexOutOfRangeException($"length({length}) exceeds destination.WritableBytes({dst.WritableBytes}) where destination is: {dst}"); + ThrowHelper.ThrowIndexOutOfRangeException($"length({length}) exceeds destination.WritableBytes({dst.WritableBytes}) where destination is: {dst}"); } this.ReadBytes(dst, dst.WriterIndex, length); dst.SetWriterIndex(dst.WriterIndex + length); @@ -1028,7 +1106,7 @@ public virtual IByteBuffer WriteBytes(IByteBuffer src, int length) { if (length > src.ReadableBytes) { - throw new IndexOutOfRangeException($"length({length}) exceeds src.readableBytes({src.ReadableBytes}) where src is: {src}"); + ThrowHelper.ThrowIndexOutOfRangeException($"length({length}) exceeds src.readableBytes({src.ReadableBytes}) where src is: {src}"); } this.WriteBytes(src, src.ReaderIndex, length); src.SetReaderIndex(src.ReaderIndex + length); @@ -1106,6 +1184,13 @@ public virtual IByteBuffer WriteZero(int length) return this; } + public virtual int WriteString(string value, Encoding encoding) + { + int written = this.SetString0(this.writerIndex, value, encoding, true); + this.writerIndex += written; + return written; + } + public virtual IByteBuffer Copy() => this.Copy(this.readerIndex, this.ReadableBytes); public abstract IByteBuffer Copy(int index, int length); @@ -1273,7 +1358,7 @@ protected void CheckReadableBytes(int minimumReadableBytes) { if (minimumReadableBytes < 0) { - throw new ArgumentOutOfRangeException(nameof(minimumReadableBytes), $"minimumReadableBytes: {minimumReadableBytes} (expected: >= 0)"); + ThrowHelper.ThrowArgumentOutOfRangeException(nameof(minimumReadableBytes), $"minimumReadableBytes: {minimumReadableBytes} (expected: >= 0)"); } this.CheckReadableBytes0(minimumReadableBytes); @@ -1284,16 +1369,17 @@ protected void CheckNewCapacity(int newCapacity) this.EnsureAccessible(); if (newCapacity < 0 || newCapacity > this.MaxCapacity) { - throw new ArgumentOutOfRangeException(nameof(newCapacity), $"newCapacity: {newCapacity} (expected: 0-{this.MaxCapacity})"); + ThrowHelper.ThrowArgumentOutOfRangeException(nameof(newCapacity), $"newCapacity: {newCapacity} (expected: 0-{this.MaxCapacity})"); } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] void CheckReadableBytes0(int minimumReadableBytes) { this.EnsureAccessible(); if (this.readerIndex > this.writerIndex - minimumReadableBytes) { - throw new IndexOutOfRangeException($"readerIndex({this.readerIndex}) + length({minimumReadableBytes}) exceeds writerIndex({this.writerIndex}): {this}"); + ThrowHelper.ThrowIndexOutOfRangeException($"readerIndex({this.readerIndex}) + length({minimumReadableBytes}) exceeds writerIndex({this.writerIndex}): {this}"); } } diff --git a/src/DotNetty.Buffers/AbstractByteBufferAllocator.cs b/src/DotNetty.Buffers/AbstractByteBufferAllocator.cs index d44e38172..470071b81 100644 --- a/src/DotNetty.Buffers/AbstractByteBufferAllocator.cs +++ b/src/DotNetty.Buffers/AbstractByteBufferAllocator.cs @@ -167,11 +167,11 @@ public int CalculateNewCapacity(int minNewCapacity, int maxCapacity) { if (minNewCapacity < 0) { - throw new ArgumentOutOfRangeException($"minNewCapacity: {minNewCapacity} (expected: 0+)"); + ThrowHelper.ThrowArgumentOutOfRangeException(nameof(minNewCapacity), $"minNewCapacity: {minNewCapacity} (expected: 0+)"); } if (minNewCapacity > maxCapacity) { - throw new ArgumentOutOfRangeException($"minNewCapacity: {minNewCapacity} (expected: not greater than maxCapacity({maxCapacity})"); + ThrowHelper.ThrowArgumentOutOfRangeException(nameof(maxCapacity), $"minNewCapacity: {minNewCapacity} (expected: not greater than maxCapacity({maxCapacity})"); } const int Threshold = CalculateThreshold; // 4 MiB page diff --git a/src/DotNetty.Buffers/ByteBufferUtil.cs b/src/DotNetty.Buffers/ByteBufferUtil.cs index 988786ddd..3ee65015c 100644 --- a/src/DotNetty.Buffers/ByteBufferUtil.cs +++ b/src/DotNetty.Buffers/ByteBufferUtil.cs @@ -12,6 +12,9 @@ namespace DotNetty.Buffers public static class ByteBufferUtil { + const char WriteUtfUnknown = '?'; + static readonly int MaxBytesPerCharUtf8 = Encoding.UTF8.GetMaxByteCount(1); + static readonly IInternalLogger Logger = InternalLoggerFactory.GetInstance(typeof(ByteBufferUtil)); public static readonly IByteBufferAllocator DefaultAllocator; @@ -295,6 +298,224 @@ static int LastIndexOf(IByteBuffer buffer, int fromIndex, int toIndex, byte valu return buffer.ForEachByteDesc(toIndex, fromIndex - toIndex, new IndexOfProcessor(value)); } + public static IByteBuffer WriteUtf8(IByteBufferAllocator alloc, string value) + { + // UTF-8 uses max. 3 bytes per char, so calculate the worst case. + IByteBuffer buf = alloc.Buffer(Utf8MaxBytes(value)); + WriteUtf8(buf, value); + return buf; + } + + public static int WriteUtf8(IByteBuffer buf, string seq) => ReserveAndWriteUtf8(buf, seq, Utf8MaxBytes(seq)); + + /// + /// Encode a string in http://en.wikipedia.org/wiki/UTF-8 and write it into reserveBytes of + /// a byte buffer. The reserveBytes must be computed (ie eagerly using {@link #utf8MaxBytes(string)} + /// or exactly with #utf8Bytes(string)}) to ensure this method not to not: for performance reasons + /// the index checks will be performed using just reserveBytes. + /// + /// This method returns the actual number of bytes written. + public static int ReserveAndWriteUtf8(IByteBuffer buf, string value, int reserveBytes) + { + for (;;) + { + if (buf is AbstractByteBuffer byteBuf) + { + byteBuf.EnsureWritable0(reserveBytes); + int written = WriteUtf8(byteBuf, byteBuf.WriterIndex, value, value.Length); + byteBuf.SetWriterIndex(byteBuf.WriterIndex + written); + return written; + } + else if (buf is WrappedByteBuffer) + { + // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path. + buf = buf.Unwrap(); + } + else + { + byte[] bytes = Encoding.UTF8.GetBytes(value); + buf.WriteBytes(bytes); + return bytes.Length; + } + } + } + + // Fast-Path implementation + internal static int WriteUtf8(AbstractByteBuffer buffer, int writerIndex, string value, int len) + { + int oldWriterIndex = writerIndex; + + // We can use the _set methods as these not need to do any index checks and reference checks. + // This is possible as we called ensureWritable(...) before. + for (int i = 0; i < len; i++) + { + char c = value[i]; + if (c < 0x80) + { + buffer._SetByte(writerIndex++, (byte)c); + } + else if (c < 0x800) + { + buffer._SetByte(writerIndex++, (byte)(0xc0 | (c >> 6))); + buffer._SetByte(writerIndex++, (byte)(0x80 | (c & 0x3f))); + } + else if (char.IsSurrogate(c)) + { + if (!char.IsHighSurrogate(c)) + { + buffer._SetByte(writerIndex++, WriteUtfUnknown); + continue; + } + char c2; + try + { + // Surrogate Pair consumes 2 characters. Optimistically try to get the next character to avoid + // duplicate bounds checking with charAt. If an IndexOutOfBoundsException is thrown we will + // re-throw a more informative exception describing the problem. + c2 = value[++i]; + } + catch (IndexOutOfRangeException) + { + buffer._SetByte(writerIndex++, WriteUtfUnknown); + break; + } + if (!char.IsLowSurrogate(c2)) + { + buffer._SetByte(writerIndex++, WriteUtfUnknown); + buffer._SetByte(writerIndex++, char.IsHighSurrogate(c2) ? WriteUtfUnknown : c2); + continue; + } + int codePoint = CharUtil.ToCodePoint(c, c2); + // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630. + buffer._SetByte(writerIndex++, (byte)(0xf0 | (codePoint >> 18))); + buffer._SetByte(writerIndex++, (byte)(0x80 | ((codePoint >> 12) & 0x3f))); + buffer._SetByte(writerIndex++, (byte)(0x80 | ((codePoint >> 6) & 0x3f))); + buffer._SetByte(writerIndex++, (byte)(0x80 | (codePoint & 0x3f))); + } + else + { + buffer._SetByte(writerIndex++, (byte)(0xe0 | (c >> 12))); + buffer._SetByte(writerIndex++, (byte)(0x80 | ((c >> 6) & 0x3f))); + buffer._SetByte(writerIndex++, (byte)(0x80 | (c & 0x3f))); + } + } + + return writerIndex - oldWriterIndex; + } + + internal static int Utf8MaxBytes(string seq) => Utf8MaxBytes(seq.Length); + + internal static int Utf8MaxBytes(int seqLength) => seqLength * MaxBytesPerCharUtf8; + + internal static int Utf8Bytes(string seq) + { + int seqLength = seq.Length; + int i = 0; + // ASCII fast path + while (i < seqLength && seq[i] < 0x80) + { + ++i; + } + // !ASCII is packed in a separate method to let the ASCII case be smaller + return i < seqLength ? i + Utf8Bytes(seq, i, seqLength) : i; + } + + static int Utf8Bytes(string seq, int start, int length) + { + int encodedLength = 0; + for (int i = start; i < length; i++) + { + char c = seq[i]; + // making it 100% branchless isn't rewarding due to the many bit operations necessary! + if (c < 0x800) + { + // branchless version of: (c <= 127 ? 0:1) + 1 + encodedLength += ((0x7f - c).RightUShift(31)) + 1; + } + else if (char.IsSurrogate(c)) + { + if (!char.IsHighSurrogate(c)) + { + encodedLength++; + // WRITE_UTF_UNKNOWN + continue; + } + char c2; + try + { + // Surrogate Pair consumes 2 characters. Optimistically try to get the next character to avoid + // duplicate bounds checking with charAt. + c2 = seq[++i]; + } + catch (IndexOutOfRangeException) + { + encodedLength++; + // WRITE_UTF_UNKNOWN + break; + } + if (!char.IsLowSurrogate(c2)) + { + // WRITE_UTF_UNKNOWN + (Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2) + encodedLength += 2; + continue; + } + // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630. + encodedLength += 4; + } + else + { + encodedLength += 3; + } + } + return encodedLength; + } + + public static IByteBuffer WriteAscii(IByteBufferAllocator alloc, string value) + { + // ASCII uses 1 byte per char + IByteBuffer buf = alloc.Buffer(value.Length); + WriteAscii(buf, value); + return buf; + } + + public static int WriteAscii(IByteBuffer buf, string value) + { + // ASCII uses 1 byte per char + int len = value.Length; + for (;;) + { + if (buf is AbstractByteBuffer byteBuf) + { + byteBuf.EnsureWritable0(len); + int written = WriteAscii(byteBuf, byteBuf.WriterIndex, value, len); + byteBuf.SetWriterIndex(byteBuf.WriterIndex + written); + return written; + } + else if (buf is WrappedByteBuffer) + { + // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path. + buf = buf.Unwrap(); + } + else + { + byte[] bytes = Encoding.ASCII.GetBytes(value); + buf.WriteBytes(bytes); + return bytes.Length; + } + } + } + + internal static int WriteAscii(AbstractByteBuffer buffer, int writerIndex, string value, int len) + { + // We can use the _set methods as these not need to do any index checks and reference checks. + // This is possible as we called ensureWritable(...) before. + for (int i = 0; i < len; i++) + { + buffer._SetByte(writerIndex++, (byte)value[i]); + } + return len; + } + /// /// Encode the given using the given into a new /// which diff --git a/src/DotNetty.Buffers/EmptyByteBuffer.cs b/src/DotNetty.Buffers/EmptyByteBuffer.cs index 1797ea0fa..f65941cec 100644 --- a/src/DotNetty.Buffers/EmptyByteBuffer.cs +++ b/src/DotNetty.Buffers/EmptyByteBuffer.cs @@ -102,109 +102,47 @@ public int EnsureWritable(int minWritableBytes, bool force) return 1; } - public bool GetBoolean(int index) - { - throw new IndexOutOfRangeException(); - } + public bool GetBoolean(int index) => throw new IndexOutOfRangeException(); - public byte GetByte(int index) - { - throw new IndexOutOfRangeException(); - } + public byte GetByte(int index) => throw new IndexOutOfRangeException(); - public short GetShort(int index) - { - throw new IndexOutOfRangeException(); - } + public short GetShort(int index) => throw new IndexOutOfRangeException(); - public short GetShortLE(int index) - { - throw new IndexOutOfRangeException(); - } + public short GetShortLE(int index) => throw new IndexOutOfRangeException(); - public ushort GetUnsignedShort(int index) - { - throw new IndexOutOfRangeException(); - } + public ushort GetUnsignedShort(int index) => throw new IndexOutOfRangeException(); - public ushort GetUnsignedShortLE(int index) - { - throw new IndexOutOfRangeException(); - } + public ushort GetUnsignedShortLE(int index) => throw new IndexOutOfRangeException(); - public int GetMedium(int index) - { - throw new IndexOutOfRangeException(); - } - public int GetMediumLE(int index) - { - throw new IndexOutOfRangeException(); - } + public int GetMedium(int index) => throw new IndexOutOfRangeException(); - public int GetUnsignedMedium(int index) - { - throw new IndexOutOfRangeException(); - } + public int GetMediumLE(int index) => throw new IndexOutOfRangeException(); - public int GetUnsignedMediumLE(int index) - { - throw new IndexOutOfRangeException(); - } + public int GetUnsignedMedium(int index) => throw new IndexOutOfRangeException(); - public int GetInt(int index) - { - throw new IndexOutOfRangeException(); - } + public int GetUnsignedMediumLE(int index) => throw new IndexOutOfRangeException(); - public int GetIntLE(int index) - { - throw new IndexOutOfRangeException(); - } + public int GetInt(int index) => throw new IndexOutOfRangeException(); - public uint GetUnsignedInt(int index) - { - throw new IndexOutOfRangeException(); - } + public int GetIntLE(int index) => throw new IndexOutOfRangeException(); - public uint GetUnsignedIntLE(int index) - { - throw new IndexOutOfRangeException(); - } + public uint GetUnsignedInt(int index) => throw new IndexOutOfRangeException(); - public long GetLong(int index) - { - throw new IndexOutOfRangeException(); - } + public uint GetUnsignedIntLE(int index) => throw new IndexOutOfRangeException(); - public long GetLongLE(int index) - { - throw new IndexOutOfRangeException(); - } + public long GetLong(int index) => throw new IndexOutOfRangeException(); - public char GetChar(int index) - { - throw new IndexOutOfRangeException(); - } + public long GetLongLE(int index) => throw new IndexOutOfRangeException(); - public float GetFloat(int index) - { - throw new IndexOutOfRangeException(); - } + public char GetChar(int index) => throw new IndexOutOfRangeException(); - public float GetFloatLE(int index) - { - throw new IndexOutOfRangeException(); - } + public float GetFloat(int index) => throw new IndexOutOfRangeException(); - public double GetDouble(int index) - { - throw new IndexOutOfRangeException(); - } + public float GetFloatLE(int index) => throw new IndexOutOfRangeException(); - public double GetDoubleLE(int index) - { - throw new IndexOutOfRangeException(); - } + public double GetDouble(int index) => throw new IndexOutOfRangeException(); + + public double GetDoubleLE(int index) => throw new IndexOutOfRangeException(); public IByteBuffer GetBytes(int index, IByteBuffer destination) => this.CheckIndex(index, destination.WritableBytes); @@ -218,105 +156,51 @@ public double GetDoubleLE(int index) public IByteBuffer GetBytes(int index, Stream destination, int length) => this.CheckIndex(index, length); - public IByteBuffer SetBoolean(int index, bool value) + public string GetString(int index, int length, Encoding encoding) { - throw new IndexOutOfRangeException(); + this.CheckIndex(index, length); + return null; } - public IByteBuffer SetByte(int index, int value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetBoolean(int index, bool value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetShort(int index, int value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetByte(int index, int value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetShortLE(int index, int value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetShort(int index, int value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetUnsignedShort(int index, ushort value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetShortLE(int index, int value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetUnsignedShortLE(int index, ushort value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetUnsignedShort(int index, ushort value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetMedium(int index, int value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetUnsignedShortLE(int index, ushort value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetMediumLE(int index, int value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetMedium(int index, int value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetInt(int index, int value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetMediumLE(int index, int value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetIntLE(int index, int value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetInt(int index, int value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetUnsignedInt(int index, uint value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetIntLE(int index, int value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetUnsignedIntLE(int index, uint value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetUnsignedInt(int index, uint value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetLong(int index, long value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetUnsignedIntLE(int index, uint value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetLongLE(int index, long value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetLong(int index, long value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetChar(int index, char value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetLongLE(int index, long value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetFloat(int index, float value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetChar(int index, char value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetFloatLE(int index, float value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetFloat(int index, float value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetDouble(int index, double value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetFloatLE(int index, float value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetDoubleLE(int index, double value) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetDouble(int index, double value) => throw new IndexOutOfRangeException(); - public IByteBuffer SetBytes(int index, IByteBuffer src) - { - throw new IndexOutOfRangeException(); - } + public IByteBuffer SetDoubleLE(int index, double value) => throw new IndexOutOfRangeException(); + + public IByteBuffer SetBytes(int index, IByteBuffer src) => throw new IndexOutOfRangeException(); public IByteBuffer SetBytes(int index, IByteBuffer src, int length) => this.CheckIndex(index, length); @@ -334,110 +218,49 @@ public Task SetBytesAsync(int index, Stream src, int length, CancellationTo public IByteBuffer SetZero(int index, int length) => this.CheckIndex(index, length); - public bool ReadBoolean() - { - throw new IndexOutOfRangeException(); - } + public int SetString(int index, string value, Encoding encoding) => throw new IndexOutOfRangeException(); - public byte ReadByte() - { - throw new IndexOutOfRangeException(); - } + public bool ReadBoolean() => throw new IndexOutOfRangeException(); - public short ReadShort() - { - throw new IndexOutOfRangeException(); - } + public byte ReadByte() => throw new IndexOutOfRangeException(); - public short ReadShortLE() - { - throw new IndexOutOfRangeException(); - } + public short ReadShort() => throw new IndexOutOfRangeException(); - public ushort ReadUnsignedShort() - { - throw new IndexOutOfRangeException(); - } + public short ReadShortLE() => throw new IndexOutOfRangeException(); - public ushort ReadUnsignedShortLE() - { - throw new IndexOutOfRangeException(); - } + public ushort ReadUnsignedShort() => throw new IndexOutOfRangeException(); - public int ReadMedium() - { - throw new IndexOutOfRangeException(); - } + public ushort ReadUnsignedShortLE() => throw new IndexOutOfRangeException(); - public int ReadMediumLE() - { - throw new IndexOutOfRangeException(); - } + public int ReadMedium() => throw new IndexOutOfRangeException(); - public int ReadUnsignedMedium() - { - throw new IndexOutOfRangeException(); - } + public int ReadMediumLE() => throw new IndexOutOfRangeException(); - public int ReadUnsignedMediumLE() - { - throw new IndexOutOfRangeException(); - } + public int ReadUnsignedMedium() => throw new IndexOutOfRangeException(); - public int ReadInt() - { - throw new IndexOutOfRangeException(); - } + public int ReadUnsignedMediumLE() => throw new IndexOutOfRangeException(); - public int ReadIntLE() - { - throw new IndexOutOfRangeException(); - } + public int ReadInt() => throw new IndexOutOfRangeException(); - public uint ReadUnsignedInt() - { - throw new IndexOutOfRangeException(); - } + public int ReadIntLE() => throw new IndexOutOfRangeException(); - public uint ReadUnsignedIntLE() - { - throw new IndexOutOfRangeException(); - } + public uint ReadUnsignedInt() => throw new IndexOutOfRangeException(); - public long ReadLong() - { - throw new IndexOutOfRangeException(); - } + public uint ReadUnsignedIntLE() => throw new IndexOutOfRangeException(); - public long ReadLongLE() - { - throw new IndexOutOfRangeException(); - } + public long ReadLong() => throw new IndexOutOfRangeException(); - public char ReadChar() - { - throw new IndexOutOfRangeException(); - } + public long ReadLongLE() => throw new IndexOutOfRangeException(); - public float ReadFloat() - { - throw new IndexOutOfRangeException(); - } + public char ReadChar() => throw new IndexOutOfRangeException(); - public float ReadFloatLE() - { - throw new IndexOutOfRangeException(); - } + public float ReadFloat() => throw new IndexOutOfRangeException(); - public double ReadDouble() - { - throw new IndexOutOfRangeException(); - } + public float ReadFloatLE() => throw new IndexOutOfRangeException(); - public double ReadDoubleLE() - { - throw new IndexOutOfRangeException(); - } + public double ReadDouble() => throw new IndexOutOfRangeException(); + + public double ReadDoubleLE() => throw new IndexOutOfRangeException(); public IByteBuffer ReadBytes(int length) => this.CheckLength(length); @@ -453,6 +276,12 @@ public double ReadDoubleLE() public IByteBuffer ReadBytes(Stream destination, int length) => this.CheckLength(length); + public string ReadString(int length, Encoding encoding) + { + this.CheckLength(length); + return null; + } + public IByteBuffer SkipBytes(int length) => this.CheckLength(length); public IByteBuffer WriteBoolean(bool value) => throw new IndexOutOfRangeException(); @@ -509,6 +338,8 @@ public double ReadDoubleLE() public IByteBuffer WriteZero(int length) => this.CheckLength(length); + public int WriteString(string value, Encoding encoding) => throw new IndexOutOfRangeException(); + public int IndexOf(int fromIndex, int toIndex, byte value) { this.CheckIndex(fromIndex); diff --git a/src/DotNetty.Buffers/IByteBuffer.cs b/src/DotNetty.Buffers/IByteBuffer.cs index 75beb1ea6..de4b8d700 100644 --- a/src/DotNetty.Buffers/IByteBuffer.cs +++ b/src/DotNetty.Buffers/IByteBuffer.cs @@ -483,6 +483,18 @@ public interface IByteBuffer : IReferenceCounted, IComparable, IEqu /// IByteBuffer GetBytes(int index, Stream destination, int length); + /// + /// Gets a string with the given length at the given index. + /// + /// + /// length the length to read + /// charset that should be use + /// the string value. + /// + /// if length is greater than readable bytes. + /// + string GetString(int index, int length, Encoding encoding); + /// /// Sets the specified boolean at the specified absolute in this buffer. /// This method does not directly modify or of this buffer. @@ -756,8 +768,32 @@ public interface IByteBuffer : IReferenceCounted, IComparable, IEqu /// Task SetBytesAsync(int index, Stream src, int length, CancellationToken cancellationToken); + /// + /// Fills this buffer with NULL (0x00) starting at the specified + /// absolute index. This method does not modify reader index + /// or writer index of this buffer + /// + /// absolute index in this byte buffer to start writing to + /// length the number of NULs to write to the buffer + /// + /// if the specified index is less than 0 or if index + length + /// is greater than capacity. + /// IByteBuffer SetZero(int index, int length); + /// + /// Writes the specified string at the current writer index and increases + /// the writer index by the written bytes. + /// + /// Index on which the string should be written + /// The string value. + /// Encoding that should be used. + /// The written number of bytes. + /// + /// if writable bytes is not large enough to write the whole string. + /// + int SetString(int index, string value, Encoding encoding); + /// /// Gets a boolean at the current and increases the /// by 1 in this buffer. @@ -937,6 +973,15 @@ public interface IByteBuffer : IReferenceCounted, IComparable, IEqu IByteBuffer ReadBytes(Stream destination, int length); + /// + /// Gets a string with the given length at the current reader index + /// and increases the reader index by the given length. + /// + /// The length to read + /// Encoding that should be used + /// The string value + string ReadString(int length, Encoding encoding); + /// /// Increases the current by the specified in this buffer. /// @@ -1141,6 +1186,8 @@ public interface IByteBuffer : IReferenceCounted, IComparable, IEqu IByteBuffer WriteZero(int length); + int WriteString(string value, Encoding encoding); + int IndexOf(int fromIndex, int toIndex, byte value); int BytesBefore(byte value); diff --git a/src/DotNetty.Buffers/UnsafeByteBufferUtil .cs b/src/DotNetty.Buffers/UnsafeByteBufferUtil .cs index c876459d5..e2f9816c2 100644 --- a/src/DotNetty.Buffers/UnsafeByteBufferUtil .cs +++ b/src/DotNetty.Buffers/UnsafeByteBufferUtil .cs @@ -7,6 +7,7 @@ namespace DotNetty.Buffers using System.Diagnostics.Contracts; using System.IO; using System.Runtime.CompilerServices; + using System.Text; using DotNetty.Common.Internal; static unsafe class UnsafeByteBufferUtil @@ -184,14 +185,22 @@ internal static void SetZero(byte[] array, int index, int length) internal static IByteBuffer Copy(AbstractByteBuffer buf, byte* addr, int index, int length) { - IByteBuffer copy = buf.Allocator.Buffer(length, buf.MaxCapacity); + IByteBuffer copy = buf.Allocator.DirectBuffer(length, buf.MaxCapacity); if (length != 0) { if (copy.HasMemoryAddress) { - fixed (byte* dst = ©.GetPinnableMemoryAddress()) + IntPtr ptr = copy.AddressOfPinnedMemory(); + if (ptr != IntPtr.Zero) { - PlatformDependent.CopyMemory(addr, dst, length); + PlatformDependent.CopyMemory(addr, (byte*)ptr, length); + } + else + { + fixed (byte* dst = ©.GetPinnableMemoryAddress()) + { + PlatformDependent.CopyMemory(addr, dst, length); + } } copy.SetIndex(0, length); } @@ -230,14 +239,22 @@ internal static void GetBytes(AbstractByteBuffer buf, byte* addr, int index, IBy if (MathUtil.IsOutOfBounds(dstIndex, length, dst.Capacity)) { - throw new IndexOutOfRangeException($"dstIndex: {dstIndex}"); + ThrowHelper.ThrowIndexOutOfRangeException($"dstIndex: {dstIndex}"); } if (dst.HasMemoryAddress) { - fixed (byte* destination = &dst.GetPinnableMemoryAddress()) + IntPtr ptr = dst.AddressOfPinnedMemory(); + if (ptr != IntPtr.Zero) + { + PlatformDependent.CopyMemory(addr, (byte*)(ptr + dstIndex), length); + } + else { - PlatformDependent.CopyMemory(addr, destination + dstIndex, length); + fixed (byte* destination = &dst.GetPinnableMemoryAddress()) + { + PlatformDependent.CopyMemory(addr, destination + dstIndex, length); + } } } else if (dst.HasArray) @@ -256,7 +273,7 @@ internal static void GetBytes(AbstractByteBuffer buf, byte* addr, int index, byt if (MathUtil.IsOutOfBounds(dstIndex, length, dst.Length)) { - throw new IndexOutOfRangeException($"dstIndex: {dstIndex}"); + ThrowHelper.ThrowIndexOutOfRangeException($"dstIndex: {dstIndex}"); } if (length != 0) { @@ -270,16 +287,24 @@ internal static void SetBytes(AbstractByteBuffer buf, byte* addr, int index, IBy if (MathUtil.IsOutOfBounds(srcIndex, length, src.Capacity)) { - throw new IndexOutOfRangeException($"srcIndex: {srcIndex}"); + ThrowHelper.ThrowIndexOutOfRangeException($"srcIndex: {srcIndex}"); } if (length != 0) { if (src.HasMemoryAddress) { - fixed (byte* source = &src.GetPinnableMemoryAddress()) + IntPtr ptr = src.AddressOfPinnedMemory(); + if (ptr != IntPtr.Zero) + { + PlatformDependent.CopyMemory((byte*)(ptr + srcIndex), addr, length); + } + else { - PlatformDependent.CopyMemory(source + srcIndex, addr, length); + fixed (byte* source = &src.GetPinnableMemoryAddress()) + { + PlatformDependent.CopyMemory(source + srcIndex, addr, length); + } } } else if (src.HasArray) @@ -325,8 +350,19 @@ internal static void SetZero(byte* addr, int length) PlatformDependent.SetMemory(addr, length, Zero); } - internal static UnpooledUnsafeDirectByteBuffer NewUnsafeDirectByteBuffer( - IByteBufferAllocator alloc, int initialCapacity, int maxCapacity) => - new UnpooledUnsafeDirectByteBuffer(alloc, initialCapacity, maxCapacity); + internal static string GetString(byte* src, int length, Encoding encoding) + { +#if NETSTANDARD1_3 + return encoding.GetString(src, length); +#else + int charCount = encoding.GetCharCount(src, length); + char* chars = stackalloc char[charCount]; + encoding.GetChars(src, length, chars, charCount); + return new string(chars, 0, charCount); +#endif + } + + internal static UnpooledUnsafeDirectByteBuffer NewUnsafeDirectByteBuffer(IByteBufferAllocator alloc, int initialCapacity, int maxCapacity) => + new UnpooledUnsafeDirectByteBuffer(alloc, initialCapacity, maxCapacity); } } diff --git a/src/DotNetty.Buffers/WrappedByteBuffer.cs b/src/DotNetty.Buffers/WrappedByteBuffer.cs index 173c8cf15..4fd2007c7 100644 --- a/src/DotNetty.Buffers/WrappedByteBuffer.cs +++ b/src/DotNetty.Buffers/WrappedByteBuffer.cs @@ -210,6 +210,8 @@ public virtual IByteBuffer GetBytes(int index, Stream output, int length) return this; } + public string GetString(int index, int length, Encoding encoding) => this.Buf.GetString(index, length, encoding); + public virtual IByteBuffer SetBoolean(int index, bool value) { this.Buf.SetBoolean(index, value); @@ -340,6 +342,8 @@ public virtual IByteBuffer SetBytes(int index, byte[] src, int srcIndex, int len public virtual Task SetBytesAsync(int index, Stream src, int length, CancellationToken cancellationToken) => this.Buf.SetBytesAsync(index, src, length, cancellationToken); + public int SetString(int index, string value, Encoding encoding) => this.Buf.SetString(index, value, encoding); + public virtual IByteBuffer SetZero(int index, int length) { this.Buf.SetZero(index, length); @@ -432,6 +436,8 @@ public virtual IByteBuffer ReadBytes(Stream output, int length) return this; } + public string ReadString(int length, Encoding encoding) => this.Buf.ReadString(length, encoding); + public virtual IByteBuffer SkipBytes(int length) { this.Buf.SkipBytes(length); @@ -570,6 +576,8 @@ public virtual IByteBuffer WriteZero(int length) return this; } + public int WriteString(string value, Encoding encoding) => this.Buf.WriteString(value, encoding); + public virtual int IndexOf(int fromIndex, int toIndex, byte value) => this.Buf.IndexOf(fromIndex, toIndex, value); public virtual int BytesBefore(byte value) => this.Buf.BytesBefore(value); diff --git a/src/DotNetty.Buffers/WrappedCompositeByteBuffer.cs b/src/DotNetty.Buffers/WrappedCompositeByteBuffer.cs index 68de47d24..362ceb406 100644 --- a/src/DotNetty.Buffers/WrappedCompositeByteBuffer.cs +++ b/src/DotNetty.Buffers/WrappedCompositeByteBuffer.cs @@ -522,8 +522,16 @@ public override IByteBuffer ReadBytes(byte[] dst, int dstIndex, int length) return this; } + public override string GetString(int index, int length, Encoding encoding) => this.wrapped.GetString(index, length, encoding); + + public override string ReadString(int length, Encoding encoding) => this.wrapped.ReadString(length, encoding); + + public override int SetString(int index, string value, Encoding encoding) => this.wrapped.SetString(index, value, encoding); + public override IByteBuffer ReadBytes(Stream destination, int length) => this.wrapped.ReadBytes(destination, length); + public override int WriteString(string value, Encoding encoding) => this.wrapped.WriteString(value, encoding); + public override IByteBuffer SkipBytes(int length) { this.wrapped.SkipBytes(length); diff --git a/src/DotNetty.Common/Internal/PlatformDependent.cs b/src/DotNetty.Common/Internal/PlatformDependent.cs index 8b242971d..2faa6308d 100644 --- a/src/DotNetty.Common/Internal/PlatformDependent.cs +++ b/src/DotNetty.Common/Internal/PlatformDependent.cs @@ -45,8 +45,8 @@ static PlatformDependent() public static unsafe bool ByteArrayEquals(byte[] bytes1, int startPos1, byte[] bytes2, int startPos2, int length) { fixed (byte* array1 = bytes1) - fixed (byte* array2 = bytes2) - return PlatformDependent0.ByteArrayEquals(array1, startPos1, array2, startPos2, length); + fixed (byte* array2 = bytes2) + return PlatformDependent0.ByteArrayEquals(array1, startPos1, array2, startPos2, length); } public static unsafe void CopyMemory(byte[] src, int srcIndex, byte[] dst, int dstIndex, int length) diff --git a/src/DotNetty.Common/Utilities/CharUtil.cs b/src/DotNetty.Common/Utilities/CharUtil.cs new file mode 100644 index 000000000..2598c0258 --- /dev/null +++ b/src/DotNetty.Common/Utilities/CharUtil.cs @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace DotNetty.Common.Utilities +{ + using System.Runtime.CompilerServices; + + public static class CharUtil + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int ToCodePoint(char high, char low) + { + // See RFC 2781, Section 2.2 + // http://www.faqs.org/rfcs/rfc2781.html + int h = (high & 0x3FF) << 10; + int l = low & 0x3FF; + return (h | l) + 0x10000; + } + } +} diff --git a/test/DotNetty.Buffers.Tests/AbstractByteBufferTests.cs b/test/DotNetty.Buffers.Tests/AbstractByteBufferTests.cs index 139c94518..2c39623a2 100644 --- a/test/DotNetty.Buffers.Tests/AbstractByteBufferTests.cs +++ b/test/DotNetty.Buffers.Tests/AbstractByteBufferTests.cs @@ -397,7 +397,7 @@ void RandomUnsignedShortAccess0(bool testBigEndian) [Fact] public void RandomMediumLEAccess() => this.RandomMediumAccess0(false); - public void RandomMediumAccess0(bool testBigEndian) + void RandomMediumAccess0(bool testBigEndian) { for (int i = 0; i < this.buffer.Capacity - 2; i += 3) { @@ -433,7 +433,7 @@ public void RandomMediumAccess0(bool testBigEndian) [Fact] public void RandomUnsignedMediumLEAccess() => this.RandomUnsignedMediumAccess0(false); - public void RandomUnsignedMediumAccess0(bool testBigEndian) + void RandomUnsignedMediumAccess0(bool testBigEndian) { for (int i = 0; i < this.buffer.Capacity - 2; i += 3) { @@ -1958,23 +1958,23 @@ public void HashCode() set.Add(elemB); Assert.Equal(2, set.Count); - Assert.True(set.Contains(this.ReleaseLater(elemA.Copy()))); + Assert.Contains(this.ReleaseLater(elemA.Copy()), set); IByteBuffer elemBCopy = this.ReleaseLater(elemB.Copy()); - Assert.True(set.Contains(elemBCopy)); + Assert.Contains(elemBCopy, set); this.buffer.Clear(); this.buffer.WriteBytes(elemA.Duplicate()); Assert.True(set.Remove(this.buffer)); - Assert.False(set.Contains(elemA)); - Assert.Equal(1, set.Count); + Assert.DoesNotContain(elemA, set); + Assert.Single(set); this.buffer.Clear(); this.buffer.WriteBytes(elemB.Duplicate()); Assert.True(set.Remove(this.buffer)); - Assert.False(set.Contains(elemB)); - Assert.Equal(0, set.Count); + Assert.DoesNotContain(elemB, set); + Assert.Empty(set); } // Test case for https://github.com/netty/netty/issues/325 @@ -2262,6 +2262,17 @@ IByteBuffer ReleasedBuffer() [Fact] public void SetBytesAfterRelease3() => Assert.Throws(() => this.ReleasedBuffer().SetBytes(0, this.ReleaseLater(Unpooled.Buffer()), 0, 1)); + [Fact] + public void SetUsAsciiStringAfterRelease() => Assert.Throws(() => this.SetStringAfterRelease0(Encoding.ASCII)); + + [Fact] + public void SetUtf8StringAfterRelease() => Assert.Throws(() => this.SetStringAfterRelease0(Encoding.UTF8)); + + [Fact] + public void SetUtf16StringAfterRelease() => Assert.Throws(() => this.SetStringAfterRelease0(Encoding.Unicode)); + + void SetStringAfterRelease0(Encoding encoding) => this.ReleasedBuffer().SetString(0, "x", encoding); + [Fact] public void SetBytesAfterRelease4() => Assert.Throws(() => this.ReleasedBuffer().SetBytes(0, new byte[8])); @@ -2403,6 +2414,17 @@ IByteBuffer ReleasedBuffer() [Fact] public void WriteZeroAfterRelease() => Assert.Throws(() => this.ReleasedBuffer().WriteZero(1)); + [Fact] + public void WriteUsAsciiStringAfterRelease() => Assert.Throws(() => this.WriteStringAfterRelease0(Encoding.ASCII)); + + [Fact] + public void WriteUtf8StringAfterRelease() => Assert.Throws(() => this.WriteStringAfterRelease0(Encoding.UTF8)); + + [Fact] + public void WriteUtf16StringAfterRelease() => Assert.Throws(() => this.WriteStringAfterRelease0(Encoding.Unicode)); + + void WriteStringAfterRelease0(Encoding encoding) => this.ReleasedBuffer().WriteString("x", encoding); + [Fact] public void ForEachByteAfterRelease() => Assert.Throws(() => this.ReleasedBuffer().ForEachByte(new TestByteProcessor())); @@ -2441,7 +2463,7 @@ public void ArrayAfterRelease() { Assert.Throws(() => { - byte[] a = buf.Array; + byte[] _ = buf.Array; }); } } @@ -2492,6 +2514,66 @@ void ReadSliceOutOfBounds0(bool retainedSlice) } } + [Fact] + public void SetUsAsciiStringNoExpand() => Assert.Throws(() => this.SetStringNoExpand(Encoding.ASCII)); + + [Fact] + public void SetUtf8StringNoExpand() => Assert.Throws(() => this.SetStringNoExpand(Encoding.UTF8)); + + [Fact] + public void SetUtf16StringNoExpand() => Assert.Throws(() => this.SetStringNoExpand(Encoding.Unicode)); + + void SetStringNoExpand(Encoding encoding) + { + IByteBuffer buf = this.NewBuffer(1); + try + { + buf.SetString(0, "AB", encoding); + } + finally + { + buf.Release(); + } + } + + [Fact] + public void SetUsAsciiString() => this.SetGetString(Encoding.ASCII); + + [Fact] + public void SetUtf8String() => this.SetGetString(Encoding.UTF8); + + [Fact] + public void SetUtf16String() => this.SetGetString(Encoding.Unicode); + + void SetGetString(Encoding encoding) + { + IByteBuffer buf = this.NewBuffer(16); + const string Sequence = "AB"; + int bytes = buf.SetString(1, Sequence, encoding); + Assert.Equal(Sequence, buf.GetString(1, bytes, encoding)); + buf.Release(); + } + + [Fact] + public void WriteReadUsAsciiString() => this.WriteReadString(Encoding.ASCII); + + [Fact] + public void WriteReadUtf8String() => this.WriteReadString(Encoding.UTF8); + + [Fact] + public void WriteReadUtf16String() => this.WriteReadString(Encoding.Unicode); + + void WriteReadString(Encoding encoding) + { + IByteBuffer buf = this.NewBuffer(16); + const string Sequence = "AB"; + buf.SetWriterIndex(1); + int bytes = buf.WriteString(Sequence, encoding); + buf.SetReaderIndex(1); + Assert.Equal(Sequence, buf.ReadString(bytes, encoding)); + buf.Release(); + } + [Fact] public void RetainedSliceIndexOutOfBounds() => Assert.Throws(() => this.SliceOutOfBounds(true, true, true)); @@ -3192,7 +3274,6 @@ public bool Process(byte value) } } - void RefCnt0(bool parameter) { for (int i = 0; i < 10; i++) @@ -3239,8 +3320,8 @@ public void EmptyIoBuffers() buf.Clear(); Assert.False(buf.IsReadable()); ArraySegment[] nioBuffers = buf.GetIoBuffers(); - Assert.Equal(1, nioBuffers.Length); - Assert.Equal(0, nioBuffers[0].Count); + Assert.Single(nioBuffers); + Assert.Empty(nioBuffers[0]); buf.Release(); } @@ -3332,6 +3413,30 @@ public void CapacityIncrease() } } + [Fact] + public void ReaderIndexLargerThanWriterIndex() + { + const string Content1 = "hello"; + const string Content2 = "world"; + int length = Content1.Length + Content2.Length; + IByteBuffer buf = this.NewBuffer(length); + buf.SetIndex(0, 0); + buf.WriteString(Content1, Encoding.ASCII); + buf.MarkWriterIndex(); + buf.SkipBytes(Content1.Length); + buf.WriteString(Content2, Encoding.ASCII); + buf.SkipBytes(Content2.Length); + Assert.True(buf.ReaderIndex <= buf.WriterIndex); + try + { + Assert.Throws(() => buf.ResetWriterIndex()); + } + finally + { + buf.Release(); + } + } + protected IByteBuffer ReleaseLater(IByteBuffer buf) { this.buffers.Enqueue(buf); diff --git a/test/DotNetty.Buffers.Tests/EmptyByteBufferTests.cs b/test/DotNetty.Buffers.Tests/EmptyByteBufferTests.cs index 7ccff5831..95db68bb5 100644 --- a/test/DotNetty.Buffers.Tests/EmptyByteBufferTests.cs +++ b/test/DotNetty.Buffers.Tests/EmptyByteBufferTests.cs @@ -39,7 +39,7 @@ public void Array() { var empty = new EmptyByteBuffer(UnpooledByteBufferAllocator.Default); Assert.True(empty.HasArray); - Assert.Equal(0, empty.Array.Length); + Assert.Empty(empty.Array); Assert.Equal(0, empty.ArrayOffset); } diff --git a/test/DotNetty.Common.Tests/Utilities/HashedWheelTimerTest.cs b/test/DotNetty.Common.Tests/Utilities/HashedWheelTimerTest.cs index 440793dc0..22c52fefd 100644 --- a/test/DotNetty.Common.Tests/Utilities/HashedWheelTimerTest.cs +++ b/test/DotNetty.Common.Tests/Utilities/HashedWheelTimerTest.cs @@ -156,7 +156,7 @@ public void TestRejectedExecutionExceptionWhenTooManyTimeoutsAreAddedBackToBack( timer.NewTimeout(CreateNoOpTimerTask(), TimeSpan.FromMilliseconds(1)); Assert.True(false, "Timer allowed adding 3 timeouts when maxPendingTimeouts was 2"); } - catch (RejectedExecutionException e) + catch (RejectedExecutionException) { // Expected } diff --git a/test/DotNetty.Handlers.Tests/Flow/FlowControlHandlerTest.cs b/test/DotNetty.Handlers.Tests/Flow/FlowControlHandlerTest.cs index 237f39000..1156d7245 100644 --- a/test/DotNetty.Handlers.Tests/Flow/FlowControlHandlerTest.cs +++ b/test/DotNetty.Handlers.Tests/Flow/FlowControlHandlerTest.cs @@ -106,8 +106,7 @@ public async Task TestAutoReadingOn() } finally { - client.CloseAsync(); - server.CloseAsync(); + Task.WhenAll(client.CloseAsync(), server.CloseAsync()).Wait(TimeSpan.FromSeconds(5)); } } @@ -165,8 +164,7 @@ public async Task TestAutoReadingOff() } finally { - client.CloseAsync(); - server.CloseAsync(); + Task.WhenAll(client.CloseAsync(), server.CloseAsync()).Wait(TimeSpan.FromSeconds(5)); } } @@ -195,8 +193,7 @@ public async Task TestFlowAutoReadOn() } finally { - client.CloseAsync(); - server.CloseAsync(); + Task.WhenAll(client.CloseAsync(), server.CloseAsync()).Wait(TimeSpan.FromSeconds(5)); } } @@ -286,8 +283,7 @@ public async Task TestFlowToggleAutoRead() } finally { - client.CloseAsync(); - server.CloseAsync(); + Task.WhenAll(client.CloseAsync(), server.CloseAsync()).Wait(TimeSpan.FromSeconds(5)); } } @@ -349,8 +345,7 @@ public async Task TestFlowAutoReadOff() } finally { - client.CloseAsync(); - server.CloseAsync(); + Task.WhenAll(client.CloseAsync(), server.CloseAsync()).Wait(TimeSpan.FromSeconds(5)); } void Signal(CountdownEvent evt) diff --git a/test/DotNetty.Handlers.Tests/IdleStateHandlerTest.cs b/test/DotNetty.Handlers.Tests/IdleStateHandlerTest.cs index 787b9641f..bd7aeb26f 100644 --- a/test/DotNetty.Handlers.Tests/IdleStateHandlerTest.cs +++ b/test/DotNetty.Handlers.Tests/IdleStateHandlerTest.cs @@ -3,7 +3,6 @@ namespace DotNetty.Handlers.Tests { - using System.Threading.Tasks; using DotNetty.Tests.Common; using Xunit; using Xunit.Abstractions; @@ -18,8 +17,8 @@ namespace DotNetty.Handlers.Tests public class IdleStateHandlerTest : TestBase { - private readonly TimeSpan oneSecond = TimeSpan.FromSeconds(1); - private readonly TimeSpan zeroSecond = TimeSpan.Zero; + readonly TimeSpan oneSecond = TimeSpan.FromSeconds(1); + readonly TimeSpan zeroSecond = TimeSpan.Zero; public IdleStateHandlerTest(ITestOutputHelper output) : base(output) @@ -29,35 +28,35 @@ public IdleStateHandlerTest(ITestOutputHelper output) [Fact] public void TestReaderIdle() { - TestableIdleStateHandler idleStateHandler = new TestableIdleStateHandler( - false, oneSecond, zeroSecond, zeroSecond); + var idleStateHandler = new TestableIdleStateHandler( + false, this.oneSecond, this.zeroSecond, this.zeroSecond); // We start with one FIRST_READER_IDLE_STATE_EVENT, followed by an infinite number of READER_IDLE_STATE_EVENTs - AnyIdle(idleStateHandler, IdleStateEvent.FirstReaderIdleStateEvent, + this.AnyIdle(idleStateHandler, IdleStateEvent.FirstReaderIdleStateEvent, IdleStateEvent.ReaderIdleStateEvent, IdleStateEvent.ReaderIdleStateEvent); } [Fact] public void TestWriterIdle() { - TestableIdleStateHandler idleStateHandler = new TestableIdleStateHandler( - false, zeroSecond, oneSecond, zeroSecond); + var idleStateHandler = new TestableIdleStateHandler( + false, this.zeroSecond, this.oneSecond, this.zeroSecond); - AnyIdle(idleStateHandler, IdleStateEvent.FirstWriterIdleStateEvent, + this.AnyIdle(idleStateHandler, IdleStateEvent.FirstWriterIdleStateEvent, IdleStateEvent.WriterIdleStateEvent, IdleStateEvent.WriterIdleStateEvent); } [Fact] public void TestAllIdle() { - TestableIdleStateHandler idleStateHandler = new TestableIdleStateHandler( - false, zeroSecond, zeroSecond, oneSecond); + var idleStateHandler = new TestableIdleStateHandler( + false, this.zeroSecond, this.zeroSecond, this.oneSecond); - AnyIdle(idleStateHandler, IdleStateEvent.FirstAllIdleStateEvent, + this.AnyIdle(idleStateHandler, IdleStateEvent.FirstAllIdleStateEvent, IdleStateEvent.AllIdleStateEvent, IdleStateEvent.AllIdleStateEvent); } - private void AnyIdle(TestableIdleStateHandler idleStateHandler, params object[] expected) + void AnyIdle(TestableIdleStateHandler idleStateHandler, params object[] expected) { Assert.True(expected.Length >= 1, "The number of expected events must be >= 1"); @@ -80,7 +79,7 @@ private void AnyIdle(TestableIdleStateHandler idleStateHandler, params object[] // Compare the expected with the actual IdleStateEvents for (int i = 0; i < expected.Length; i++) { - Object evt = events[i]; + object evt = events[i]; Assert.Same(expected[i], evt);//"Element " + i + " is not matching" } } @@ -93,46 +92,46 @@ private void AnyIdle(TestableIdleStateHandler idleStateHandler, params object[] [Fact] public void TestReaderNotIdle() { - TestableIdleStateHandler idleStateHandler = new TestableIdleStateHandler( - false, oneSecond, zeroSecond, zeroSecond); + var idleStateHandler = new TestableIdleStateHandler( + false, this.oneSecond, this.zeroSecond, this.zeroSecond); Action action = (channel) => channel.WriteInbound("Hello, World!"); - AnyNotIdle(idleStateHandler, action, IdleStateEvent.FirstReaderIdleStateEvent); + this.AnyNotIdle(idleStateHandler, action, IdleStateEvent.FirstReaderIdleStateEvent); } [Fact] public void TestWriterNotIdle() { - TestableIdleStateHandler idleStateHandler = new TestableIdleStateHandler( - false, zeroSecond, oneSecond, zeroSecond); + var idleStateHandler = new TestableIdleStateHandler( + false, this.zeroSecond, this.oneSecond, this.zeroSecond); Action action = (channel) => channel.WriteAndFlushAsync("Hello, World!"); - AnyNotIdle(idleStateHandler, action, IdleStateEvent.FirstWriterIdleStateEvent); + this.AnyNotIdle(idleStateHandler, action, IdleStateEvent.FirstWriterIdleStateEvent); } [Fact] public void TestAllNotIdle() { // Reader... - TestableIdleStateHandler idleStateHandler = new TestableIdleStateHandler( - false, zeroSecond, zeroSecond, oneSecond); + var idleStateHandler = new TestableIdleStateHandler( + false, this.zeroSecond, this.zeroSecond, this.oneSecond); Action reader = (channel) => channel.WriteInbound("Hello, World!"); - AnyNotIdle(idleStateHandler, reader, IdleStateEvent.FirstAllIdleStateEvent); + this.AnyNotIdle(idleStateHandler, reader, IdleStateEvent.FirstAllIdleStateEvent); // Writer... idleStateHandler = new TestableIdleStateHandler( - false, zeroSecond, zeroSecond, oneSecond); + false, this.zeroSecond, this.zeroSecond, this.oneSecond); Action writer = (channel) => channel.WriteAndFlushAsync("Hello, World!"); - AnyNotIdle(idleStateHandler, writer, IdleStateEvent.FirstAllIdleStateEvent); + this.AnyNotIdle(idleStateHandler, writer, IdleStateEvent.FirstAllIdleStateEvent); } - private void AnyNotIdle(TestableIdleStateHandler idleStateHandler, + void AnyNotIdle(TestableIdleStateHandler idleStateHandler, Action action, object expected) { var events = new List(); @@ -150,15 +149,15 @@ private void AnyNotIdle(TestableIdleStateHandler idleStateHandler, // we've just performed an action on the channel that is meant // to reset the idle task. TimeSpan delayInNanos = idleStateHandler.Delay; - Assert.NotEqual(zeroSecond, delayInNanos); + Assert.NotEqual(this.zeroSecond, delayInNanos); idleStateHandler.TickRun(TimeSpan.FromTicks(delayInNanos.Ticks / 2)); - Assert.Equal(0, events.Count); + Assert.Empty(events); // Advance the ticker by the full amount and it should yield // in an IdleStateEvent. idleStateHandler.TickRun(); - Assert.Equal(1, events.Count); + Assert.Single(events); Assert.Same(expected, events[0]); } finally @@ -168,15 +167,15 @@ private void AnyNotIdle(TestableIdleStateHandler idleStateHandler, } [Fact] - public void TestObserveWriterIdle() => ObserveOutputIdle(true); + public void TestObserveWriterIdle() => this.ObserveOutputIdle(true); [Fact] - public void TestObserveAllIdle() => ObserveOutputIdle(false); + public void TestObserveAllIdle() => this.ObserveOutputIdle(false); - private void ObserveOutputIdle(bool writer) + void ObserveOutputIdle(bool writer) { - TimeSpan writerIdleTime = zeroSecond; - TimeSpan allIdleTime = zeroSecond; + TimeSpan writerIdleTime = this.zeroSecond; + TimeSpan allIdleTime = this.zeroSecond; IdleStateEvent expected; if (writer) @@ -190,13 +189,13 @@ private void ObserveOutputIdle(bool writer) expected = IdleStateEvent.FirstAllIdleStateEvent; } - TestableIdleStateHandler idleStateHandler = new TestableIdleStateHandler( - true, zeroSecond, writerIdleTime, allIdleTime); + var idleStateHandler = new TestableIdleStateHandler( + true, this.zeroSecond, writerIdleTime, allIdleTime); var events = new List(); var handler = new TestEventChannelInboundHandlerAdapter(events); - ObservableChannel channel = new ObservableChannel(idleStateHandler, handler); + var channel = new ObservableChannel(idleStateHandler, handler); try { // We're writing 3 messages that will be consumed at different rates! @@ -206,7 +205,7 @@ private void ObserveOutputIdle(bool writer) // Establish a baseline. We're not consuming anything and let it idle once. idleStateHandler.TickRun(); - Assert.Equal(1, events.Count); + Assert.Single(events); Assert.Same(expected, events[0]); events.Clear(); @@ -220,7 +219,7 @@ private void ObserveOutputIdle(bool writer) AssertNotNullAndRelease(channel.Consume()); idleStateHandler.TickRun(TimeSpan.FromSeconds(2)); - Assert.Equal(0, events.Count); + Assert.Empty(events); Assert.Equal(TimeSpan.FromSeconds(11), idleStateHandler.Tick); // 5s + 4s + 2s // Consume one message in 3 seconds, then be idle for 4 seconds, @@ -230,13 +229,13 @@ private void ObserveOutputIdle(bool writer) AssertNotNullAndRelease(channel.Consume()); idleStateHandler.TickRun(TimeSpan.FromSeconds(4)); - Assert.Equal(0, events.Count); + Assert.Empty(events); Assert.Equal(TimeSpan.FromSeconds(18), idleStateHandler.Tick); // 11s + 3s + 4s // Don't consume a message and be idle for 5 seconds. // We should get an IdleStateEvent! idleStateHandler.TickRun(TimeSpan.FromSeconds(5)); - Assert.Equal(1, events.Count); + Assert.Single(events); Assert.Equal(TimeSpan.FromSeconds(23), idleStateHandler.Tick); // 18s + 5s events.Clear(); @@ -247,7 +246,7 @@ private void ObserveOutputIdle(bool writer) AssertNotNullAndRelease(channel.Consume()); idleStateHandler.TickRun(TimeSpan.FromSeconds(1)); - Assert.Equal(0, events.Count); + Assert.Empty(events); Assert.Equal(TimeSpan.FromSeconds(26), idleStateHandler.Tick); // 23s + 2s + 1s // There are no messages left! Advance the ticker by 3 seconds, @@ -258,7 +257,7 @@ private void ObserveOutputIdle(bool writer) Assert.Null(channel.Consume()); idleStateHandler.TickRun(TimeSpan.FromSeconds(2)); - Assert.Equal(1, events.Count); + Assert.Single(events); Assert.Equal(TimeSpan.FromSeconds(31), idleStateHandler.Tick); // 26s + 3s + 2s // q.e.d. @@ -269,7 +268,7 @@ private void ObserveOutputIdle(bool writer) } } - private static void AssertNotNullAndRelease(Object msg) + static void AssertNotNullAndRelease(object msg) { Assert.NotNull(msg); ReferenceCountUtil.Release(msg); @@ -292,9 +291,9 @@ public TestableIdleStateHandler(bool observeOutput, { } - public void Run() => task.Invoke(); + void Run() => this.task.Invoke(); - public void TickRun() => this.TickRun(Delay); + public void TickRun() => this.TickRun(this.Delay); public void TickRun(TimeSpan delay) { @@ -328,7 +327,7 @@ public TestEventChannelInboundHandlerAdapter(List events) public override void UserEventTriggered(IChannelHandlerContext context, object evt) { - events.Add(evt); + this.events.Add(evt); } } @@ -347,16 +346,13 @@ protected override void DoWrite(ChannelOutboundBuffer input) public object Consume() { - ChannelOutboundBuffer buf = Unsafe.OutboundBuffer; - if (buf != null) + ChannelOutboundBuffer buf = this.Unsafe.OutboundBuffer; + object msg = buf?.Current; + if (msg != null) { - Object msg = buf.Current; - if (msg != null) - { - ReferenceCountUtil.Retain(msg); - buf.Remove(); - return msg; - } + ReferenceCountUtil.Retain(msg); + buf.Remove(); + return msg; } return null; } diff --git a/test/DotNetty.Microbench/Buffers/ByteBufUtilBenchmark.cs b/test/DotNetty.Microbench/Buffers/ByteBufUtilBenchmark.cs new file mode 100644 index 000000000..ae612e74f --- /dev/null +++ b/test/DotNetty.Microbench/Buffers/ByteBufUtilBenchmark.cs @@ -0,0 +1,137 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace DotNetty.Microbench.Buffers +{ + using System.Text; + using BenchmarkDotNet.Attributes; + using BenchmarkDotNet.Attributes.Jobs; + using DotNetty.Buffers; + using DotNetty.Common; + + [CoreJob] + [BenchmarkCategory("ByteBuffer")] + public class ByteBufUtilBenchmark + { + IByteBuffer buffer; + IByteBuffer wrapped; + IByteBuffer asciiBuffer; + IByteBuffer utf8Buffer; + + string ascii; + int asciiLength; + string utf8; + int utf8Length; + + static ByteBufUtilBenchmark() + { + ResourceLeakDetector.Level = ResourceLeakDetector.DetectionLevel.Disabled; + } + + [GlobalSetup] + public void GlobalSetup() + { + PooledByteBufferAllocator allocator = PooledByteBufferAllocator.Default; + + // Use buffer sizes that will also allow to write UTF-8 without grow the buffer + this.buffer = allocator.DirectBuffer(512); + this.wrapped = Unpooled.UnreleasableBuffer(allocator.DirectBuffer(512)); + var asciiSequence = new StringBuilder(128); + for (int i = 0; i < 128; i++) + { + asciiSequence.Append('a'); + } + this.ascii = asciiSequence.ToString(); + + // Generate some mixed UTF-8 String for benchmark + var utf8Sequence = new StringBuilder(128); + char[] chars = "Some UTF-8 like äÄ∏ŒŒ".ToCharArray(); + for (int i = 0; i < 128; i++) + { + utf8Sequence.Append(chars[i % chars.Length]); + } + this.utf8 = utf8Sequence.ToString(); + + byte[] bytes = Encoding.ASCII.GetBytes(this.ascii); + this.asciiLength = bytes.Length; + this.asciiBuffer = allocator.DirectBuffer(this.asciiLength); + this.asciiBuffer.WriteBytes(bytes); + + bytes = Encoding.UTF8.GetBytes(this.utf8); + this.utf8Length = bytes.Length; + this.utf8Buffer = allocator.DirectBuffer(bytes.Length); + this.utf8Buffer.WriteBytes(bytes); + } + + [GlobalCleanup] + public void GlobalCleanup() + { + this.buffer.Release(); + this.wrapped.Release(); + this.asciiBuffer.Release(); + this.utf8Buffer.Release(); + } + + [Benchmark] + public string GetAsciiString() => this.asciiBuffer.GetString(0, this.asciiLength, Encoding.ASCII); + + [Benchmark] + public string GetUtf8String() => this.utf8Buffer.GetString(0, this.utf8Length, Encoding.UTF8); + + [Benchmark] + public void WriteAsciiStringViaArray() + { + this.buffer.ResetWriterIndex(); + this.buffer.WriteBytes(Encoding.ASCII.GetBytes(this.ascii)); + } + + [Benchmark] + public void WriteAsciiStringViaArrayWrapped() + { + this.wrapped.ResetWriterIndex(); + this.wrapped.WriteBytes(Encoding.ASCII.GetBytes(this.ascii)); + } + + [Benchmark] + public void WriteAsciiString() + { + this.buffer.ResetWriterIndex(); + this.buffer.WriteString(this.ascii, Encoding.ASCII); + } + + [Benchmark] + public void WriteAsciiStringWrapped() + { + this.wrapped.ResetWriterIndex(); + this.wrapped.WriteString(this.ascii, Encoding.ASCII); + } + + [Benchmark] + public void WriteUtf8StringViaArray() + { + this.buffer.ResetWriterIndex(); + this.buffer.WriteBytes(Encoding.UTF8.GetBytes(this.utf8)); + } + + [Benchmark] + public void WriteUtf8StringViaArrayWrapped() + { + this.wrapped.ResetWriterIndex(); + this.wrapped.WriteBytes(Encoding.UTF8.GetBytes(this.utf8)); + } + + [Benchmark] + public void WriteUtf8String() + { + this.buffer.ResetWriterIndex(); + this.buffer.WriteString(this.utf8, Encoding.UTF8); + } + + [Benchmark] + public void WriteUtf8StringWrapped() + { + this.wrapped.ResetWriterIndex(); + this.wrapped.WriteString(this.utf8, Encoding.UTF8); + } + } +} diff --git a/test/DotNetty.Microbench/DotNetty.Microbench.csproj b/test/DotNetty.Microbench/DotNetty.Microbench.csproj index cf2443987..344cb7b79 100644 --- a/test/DotNetty.Microbench/DotNetty.Microbench.csproj +++ b/test/DotNetty.Microbench/DotNetty.Microbench.csproj @@ -8,7 +8,7 @@ true - + diff --git a/test/DotNetty.Microbench/Program.cs b/test/DotNetty.Microbench/Program.cs index 605a5c485..2c813557a 100644 --- a/test/DotNetty.Microbench/Program.cs +++ b/test/DotNetty.Microbench/Program.cs @@ -18,6 +18,7 @@ class Program typeof(ByteBufferBenchmark), typeof(UnpooledByteBufferBenchmark), typeof(PooledByteBufferBenchmark), + typeof(ByteBufUtilBenchmark), typeof(FastThreadLocalBenchmark), typeof(SingleThreadEventExecutorBenchmark) }; diff --git a/test/DotNetty.Tests.End2End/End2EndTests.cs b/test/DotNetty.Tests.End2End/End2EndTests.cs index 3cabff385..39473d46b 100644 --- a/test/DotNetty.Tests.End2End/End2EndTests.cs +++ b/test/DotNetty.Tests.End2End/End2EndTests.cs @@ -103,7 +103,7 @@ public async Task EchoServerAndClient() { Task serverCloseTask = closeServerFunc(); clientChannel?.CloseAsync().Wait(TimeSpan.FromSeconds(5)); - group.ShutdownGracefullyAsync(); + group.ShutdownGracefullyAsync().Wait(TimeSpan.FromSeconds(5)); if (!serverCloseTask.Wait(ShutdownTimeout)) { this.Output.WriteLine("Didn't stop in time."); @@ -170,7 +170,7 @@ await Task.WhenAll(this.RunMqttClientScenarioAsync(clientChannel, clientReadList { Task serverCloseTask = closeServerFunc(); clientChannel?.CloseAsync().Wait(TimeSpan.FromSeconds(5)); - group.ShutdownGracefullyAsync(); + group.ShutdownGracefullyAsync().Wait(TimeSpan.FromSeconds(5)); if (!serverCloseTask.Wait(ShutdownTimeout)) { this.Output.WriteLine("Didn't stop in time."); diff --git a/test/DotNetty.Transport.Libuv.Tests/ConnectionAttemptTests.cs b/test/DotNetty.Transport.Libuv.Tests/ConnectionAttemptTests.cs index 268448f1c..fb1f1517b 100644 --- a/test/DotNetty.Transport.Libuv.Tests/ConnectionAttemptTests.cs +++ b/test/DotNetty.Transport.Libuv.Tests/ConnectionAttemptTests.cs @@ -46,7 +46,7 @@ static void ConnectTimeout0(Bootstrap cb) Task task = cb.ConnectAsync(badAddress); var error = Assert.Throws(() => task.Wait(DefaultTimeout)); - Assert.Equal(1, error.InnerExceptions.Count); + Assert.Single(error.InnerExceptions); Assert.IsType(error.InnerException); Assert.Equal(0, handler.Active); Assert.Null(handler.Error); @@ -69,7 +69,7 @@ static void ConnectRefused0(Bootstrap cb) Task task = cb.ConnectAsync(badAddress); var error = Assert.Throws(() => task.Wait(DefaultTimeout)); - Assert.Equal(1, error.InnerExceptions.Count); + Assert.Single(error.InnerExceptions); Assert.IsType(error.InnerException); var exception = (ChannelException)error.InnerException; Assert.IsType(exception.InnerException); diff --git a/test/DotNetty.Transport.Libuv.Tests/WriteBeforeRegisteredTests.cs b/test/DotNetty.Transport.Libuv.Tests/WriteBeforeRegisteredTests.cs index 3a2f8a406..8c91a0913 100644 --- a/test/DotNetty.Transport.Libuv.Tests/WriteBeforeRegisteredTests.cs +++ b/test/DotNetty.Transport.Libuv.Tests/WriteBeforeRegisteredTests.cs @@ -45,13 +45,13 @@ void WriteBeforeConnect0(Bootstrap cb) Task writeTask = this.clientChannel.WriteAndFlushAsync(Unpooled.WrappedBuffer(new byte[] { 1 })); var error = Assert.Throws(() => writeTask.Wait(DefaultTimeout)); - Assert.Equal(1, error.InnerExceptions.Count); + Assert.Single(error.InnerExceptions); Assert.IsType(error.InnerException); Assert.Null(h.Error); // Connect task should fail error = Assert.Throws(() => connectTask.Wait(DefaultTimeout)); - Assert.Equal(1, error.InnerExceptions.Count); + Assert.Single(error.InnerExceptions); Assert.IsType(error.InnerException); var exception = (OperationException)error.InnerException; Assert.Equal("EADDRNOTAVAIL", exception.Name); // address not available (port : 0) diff --git a/test/DotNetty.Transport.Tests/Channel/DefaulChannelIdTest.cs b/test/DotNetty.Transport.Tests/Channel/DefaulChannelIdTest.cs index f66faed8a..de46bf640 100644 --- a/test/DotNetty.Transport.Tests/Channel/DefaulChannelIdTest.cs +++ b/test/DotNetty.Transport.Tests/Channel/DefaulChannelIdTest.cs @@ -3,7 +3,6 @@ namespace DotNetty.Transport.Tests.Channel { - using System.Text.RegularExpressions; using DotNetty.Transport.Channels; using Xunit; @@ -13,14 +12,14 @@ public class DefaulChannelIdTest public void TestShortText() { string text = DefaultChannelId.NewInstance().AsShortText(); - Assert.True(Regex.IsMatch(text, @"^[0-9a-f]{8}$")); + Assert.Matches(@"^[0-9a-f]{8}$", text); } [Fact] public void TestLongText() { string text = DefaultChannelId.NewInstance().AsLongText(); - Assert.True(Regex.IsMatch(text, @"^[0-9a-f]{16}-[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{16}-[0-9a-f]{8}$")); + Assert.Matches(@"^[0-9a-f]{16}-[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{16}-[0-9a-f]{8}$", text); } [Fact] diff --git a/test/DotNetty.Transport.Tests/Channel/Embedded/EmbeddedChannelTest.cs b/test/DotNetty.Transport.Tests/Channel/Embedded/EmbeddedChannelTest.cs index 3ba3ac11c..23da26e3a 100644 --- a/test/DotNetty.Transport.Tests/Channel/Embedded/EmbeddedChannelTest.cs +++ b/test/DotNetty.Transport.Tests/Channel/Embedded/EmbeddedChannelTest.cs @@ -166,12 +166,10 @@ public void TestConstructWithoutHandler() [Theory] [InlineData(1000)] - public void TestFireChannelInactiveAndUnregisteredOnDisconnect(int timeout) - { + public void TestFireChannelInactiveAndUnregisteredOnDisconnect(int timeout) => this.TestFireChannelInactiveAndUnregisteredOnClose(channel => channel.DisconnectAsync(), timeout); - } - public void TestFireChannelInactiveAndUnregisteredOnClose(Func action, int timeout) + void TestFireChannelInactiveAndUnregisteredOnClose(Func action, int timeout) { var latch = new CountdownEvent(3); var channel = new EmbeddedChannel(new ChannelHandlerWithInactiveAndRegister(latch)); diff --git a/test/DotNetty.Transport.Tests/Channel/Pool/SimpleChannelPoolTest.cs b/test/DotNetty.Transport.Tests/Channel/Pool/SimpleChannelPoolTest.cs index 7fb29df86..b104712c7 100644 --- a/test/DotNetty.Transport.Tests/Channel/Pool/SimpleChannelPoolTest.cs +++ b/test/DotNetty.Transport.Tests/Channel/Pool/SimpleChannelPoolTest.cs @@ -64,7 +64,7 @@ public async Task TestAcquire() Assert.Equal(2, handler.ReleasedCount); await sc.CloseAsync(); - group.ShutdownGracefullyAsync(); + await group.ShutdownGracefullyAsync(); } [Fact] @@ -102,7 +102,7 @@ public async Task TestBoundedChannelPoolSegment() await sc.CloseAsync(); await channel.CloseAsync(); await channel2.CloseAsync(); - group.ShutdownGracefullyAsync(); + await group.ShutdownGracefullyAsync(); } /** @@ -143,7 +143,7 @@ public async Task TestUnhealthyChannelIsNotOffered() Assert.NotSame(channel1, channel3); await sc.CloseAsync(); await channel3.CloseAsync(); - group.ShutdownGracefullyAsync(); + await group.ShutdownGracefullyAsync(); } /** @@ -180,7 +180,7 @@ public async Task TestUnhealthyChannelIsOfferedWhenNoHealthCheckRequested() Assert.NotSame(channel1, channel2); await sc.CloseAsync(); await channel2.CloseAsync(); - group.ShutdownGracefullyAsync(); + await group.ShutdownGracefullyAsync(); } [Fact]