Skip to content

Commit

Permalink
add pooledbufferwriter
Browse files Browse the repository at this point in the history
  • Loading branch information
itn3000 committed Feb 9, 2022
1 parent a1ec57f commit 0cdf67e
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 0 deletions.
47 changes: 47 additions & 0 deletions PooledStream.Test/TestPooledBufferWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Buffers;
using Xunit;

namespace PooledStream.Test
{
public class TestPooledBufferWriter
{
[Fact]
public void Write()
{
using var bw = new PooledBufferWriter<byte>(ArrayPool<byte>.Shared, 1024);
var data = new byte[128];
for (int i = 0; i < 16; i++)
{
data.AsSpan().Fill((byte)(i + 1));
var sp = bw.GetSpan(data.Length);
data.AsSpan().CopyTo(sp);
bw.Advance(data.Length);
}
var resultsp = bw.ToSpanUnsafe();
for (int i = 0; i < 16; i++)
{
data.AsSpan().Fill((byte)(i + 1));
Assert.True(resultsp.Slice(i * 128, data.Length).SequenceEqual(data));
}
}
[Fact]
public void Reset()
{
using var bw = new PooledBufferWriter<byte>();
var sp = bw.GetSpan(128);
sp.Fill(1);
bw.Advance(128);
var rsp = bw.ToSpanUnsafe();
Assert.Equal(128, rsp.Length);
bw.Reset();
rsp = bw.ToSpanUnsafe();
Assert.Equal(0, rsp.Length);
sp = bw.GetSpan(128);
sp.Fill(2);
bw.Advance(128);
rsp = bw.ToSpanUnsafe();
Assert.Equal(128, rsp.Length);
}
}
}
114 changes: 114 additions & 0 deletions PooledStream/PooledBufferWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using System;
using System.Buffers;
using System.Runtime.CompilerServices;

namespace PooledStream
{
public class PooledBufferWriter<T> : IDisposable, IBufferWriter<T> where T : struct
{
ArrayPool<T> _Pool;
T[] _currentBuffer;
int _Position;
int _Length;
const int DefaultSize = 1024;
void Reallocate(int sizeHint)
{
var nar = _Pool.Rent(sizeHint);
if(_currentBuffer != null)
{
Buffer.BlockCopy(_currentBuffer, 0, nar, 0, _currentBuffer.Length < nar.Length ? _currentBuffer.Length : nar.Length);
_Pool.Return(_currentBuffer);
}
_currentBuffer = nar;
}
public PooledBufferWriter(): this(ArrayPool<T>.Shared)
{

}
public PooledBufferWriter(ArrayPool<T> pool): this(pool, DefaultSize)
{
}
public PooledBufferWriter(ArrayPool<T> pool, int preallocateSize)
{
_Pool = pool;
_currentBuffer = null;
_Position = 0;
_Length = 0;
Reallocate(preallocateSize);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Advance(int count)
{
if(_Position + count > _currentBuffer.Length)
{
throw new IndexOutOfRangeException("advance too many(" + count.ToString() + ")");
}
_Position += count;
if(_Length < _Position)
{
_Length = _Position;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if(_currentBuffer != null)
{
_Pool.Return(_currentBuffer);
_currentBuffer = null;
_Position = 0;
_Length = 0;
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Memory<T> GetMemory(int sizeHint = 0)
{
if(sizeHint == 0)
{
sizeHint = DefaultSize;
}
if(_Position + sizeHint > _currentBuffer.Length)
{
Reallocate(_Position + sizeHint);
}
return _currentBuffer.AsMemory(_Position, sizeHint);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Span<T> GetSpan(int sizeHint = 0)
{
if(sizeHint == 0)
{
sizeHint = DefaultSize;
}
if(_Position + sizeHint > _currentBuffer.Length)
{
Reallocate(_Position + sizeHint);
}
return _currentBuffer.AsSpan(_Position, sizeHint);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<T> ToSpanUnsafe()
{
return _currentBuffer.AsSpan(0, _Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlyMemory<T> ToMemoryUnsafe()
{
return _currentBuffer.AsMemory(0, _Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Reset(int preallocateSize = DefaultSize)
{
if(preallocateSize > _currentBuffer.Length)
{
_Pool.Return(_currentBuffer);
_currentBuffer = _Pool.Rent(preallocateSize);
}
_Length = 0;
_Position = 0;
}
}
}

0 comments on commit 0cdf67e

Please sign in to comment.