Skip to content

Commit

Permalink
Buffer read/write/get/set strings.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
StormHub committed Mar 15, 2018
1 parent 30f0280 commit 78633ed
Show file tree
Hide file tree
Showing 24 changed files with 861 additions and 373 deletions.
110 changes: 98 additions & 12 deletions src/DotNetty.Buffers/AbstractByteBuffer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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();
Expand All @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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}");
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/DotNetty.Buffers/AbstractByteBufferAllocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit 78633ed

Please sign in to comment.