Skip to content

Commit

Permalink
Configurable buffers
Browse files Browse the repository at this point in the history
  • Loading branch information
clipperhouse committed Aug 2, 2024
1 parent 695e1f6 commit b99d992
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 36 deletions.
22 changes: 22 additions & 0 deletions Benchmarks/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,26 @@ public void SplitSearchValues()
// buffer.Append(token);
}
}

[Benchmark]
public void SplitStream()
{
sampleStream.Seek(0, SeekOrigin.Begin);
var splits = sampleStream.SplitOn((byte)' ');
foreach (var split in splits) { }
}

static readonly ArrayPool<byte> pool = ArrayPool<byte>.Shared;

[Benchmark]
public void SplitStreamArrayPool()
{
var storage = pool.Rent(4096);

sampleStream.Seek(0, SeekOrigin.Begin);
var splits = sampleStream.SplitOn((byte)' ', 2048, storage);
foreach (var split in splits) { }

pool.Return(storage);
}
}
10 changes: 6 additions & 4 deletions Split/Extensions/SplitOn.Byte.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ public static partial class SplitExtensions
public static Enumerator<byte> SplitOn(this ReadOnlySpan<byte> source, byte separator) => Split.Bytes(source, separator);


/// <inheritdoc cref="Split.Bytes(Stream, byte)"/>
public static StreamEnumerator<byte> SplitOn(this Stream stream, byte separator) => Split.Bytes(stream, separator);
/// <inheritdoc cref="Split.Bytes(Stream, byte, int, byte[]?)"/>
public static StreamEnumerator<byte> SplitOn(this Stream stream, byte separator, int minBufferBytes = 1024, byte[]? bufferStorage = null)
=> Split.Bytes(stream, separator, minBufferBytes, bufferStorage);

/// <inheritdoc cref="Split.Bytes(Stream, ReadOnlySpan{byte})"/>
public static StreamEnumerator<byte> SplitOn(this Stream stream, ReadOnlySpan<byte> separator) => Split.Bytes(stream, separator);
/// <inheritdoc cref="Split.Bytes(Stream, ReadOnlySpan{byte}, int, byte[]?)"/>
public static StreamEnumerator<byte> SplitOn(this Stream stream, ReadOnlySpan<byte> separator, int minBufferBytes = 1024, byte[]? bufferStorage = null)
=> Split.Bytes(stream, separator, minBufferBytes, bufferStorage);
}
10 changes: 6 additions & 4 deletions Split/Extensions/SplitOn.Char.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,11 @@ public static partial class SplitExtensions
public static Enumerator<char> SplitOn(this ReadOnlySpan<char> source, ReadOnlySpan<char> separator) => Split.Chars(source, separator);


/// <inheritdoc cref="Split.Chars(TextReader, char)"/>
public static StreamEnumerator<char> SplitOn(this TextReader reader, char separator) => Split.Chars(reader, separator);
/// <inheritdoc cref="Split.Chars(TextReader, char, int, char[]?)"/>
public static StreamEnumerator<char> SplitOn(this TextReader reader, char separator, int minBufferChars = 1024, char[]? bufferStorage = null)
=> Split.Chars(reader, separator, minBufferChars, bufferStorage);

/// <inheritdoc cref="Split.Chars(TextReader, ReadOnlySpan{char})"/>
public static StreamEnumerator<char> SplitOn(this TextReader reader, ReadOnlySpan<char> separator) => Split.Chars(reader, separator);
/// <inheritdoc cref="Split.Chars(TextReader, ReadOnlySpan{char}, int, char[]?)"/>
public static StreamEnumerator<char> SplitOn(this TextReader reader, ReadOnlySpan<char> separator, int minBufferChars = 1024, char[]? bufferStorage = null)
=> Split.Chars(reader, separator, minBufferChars, bufferStorage);
}
10 changes: 6 additions & 4 deletions Split/Extensions/SplitOnAny.Byte.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ public static partial class SplitExtensions
public static Enumerator<byte> SplitOnAny(this ReadOnlySpan<byte> source, SearchValues<byte> separators) => Split.BytesAny(source, separators);


/// <inheritdoc cref="Split.BytesAny(Stream, ReadOnlySpan{byte})"/>
public static StreamEnumerator<byte> SplitOnAny(this Stream stream, ReadOnlySpan<byte> separators) => Split.BytesAny(stream, separators);
/// <inheritdoc cref="Split.BytesAny(Stream, ReadOnlySpan{byte}, int, byte[]?)"/>
public static StreamEnumerator<byte> SplitOnAny(this Stream stream, ReadOnlySpan<byte> separators, int minBufferBytes = 1024, byte[]? bufferStorage = null)
=> Split.BytesAny(stream, separators, minBufferBytes, bufferStorage);

/// <inheritdoc cref="Split.BytesAny(Stream, SearchValues{byte})"/>
public static StreamEnumerator<byte> SplitOnAny(this Stream stream, SearchValues<byte> separators) => Split.BytesAny(stream, separators);
/// <inheritdoc cref="Split.BytesAny(Stream, SearchValues{byte}, int, byte[]?)"/>
public static StreamEnumerator<byte> SplitOnAny(this Stream stream, SearchValues<byte> separators, int minBufferBytes = 1024, byte[]? bufferStorage = null)
=> Split.BytesAny(stream, separators, minBufferBytes, bufferStorage);
}
10 changes: 6 additions & 4 deletions Split/Extensions/SplitOnAny.Char.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ public static partial class SplitExtensions
public static Enumerator<char> SplitOnAny(this ReadOnlySpan<char> source, SearchValues<char> separators) => Split.CharsAny(source, separators);


/// <inheritdoc cref="Split.CharsAny(TextReader, ReadOnlySpan{char})"/>
public static StreamEnumerator<char> SplitOnAny(this TextReader reader, ReadOnlySpan<char> separators) => Split.CharsAny(reader, separators);
/// <inheritdoc cref="Split.CharsAny(TextReader, SearchValues{char}, int, char[]?)"/>
public static StreamEnumerator<char> SplitOnAny(this TextReader reader, ReadOnlySpan<char> separators, int minBufferChars = 1024, char[]? bufferStorage = null)
=> Split.CharsAny(reader, separators, minBufferChars, bufferStorage);

/// <inheritdoc cref="Split.CharsAny(TextReader, SearchValues{char})"/>
public static StreamEnumerator<char> SplitOnAny(this TextReader reader, SearchValues<char> separators) => Split.CharsAny(reader, separators);
/// <inheritdoc cref="Split.CharsAny(TextReader, SearchValues{char}, int, char[]?)"/>
public static StreamEnumerator<char> SplitOnAny(this TextReader reader, SearchValues<char> separators, int minBufferChars = 1024, char[]? bufferStorage = null)
=> Split.CharsAny(reader, separators, minBufferChars, bufferStorage);
}
40 changes: 32 additions & 8 deletions Split/Split.Bytes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,16 @@ public static partial class Split
/// </summary>
/// <param name="stream">The source stream of bytes.</param>
/// <param name="separator">The byte on which to split the string.</param>
/// <param name="minBufferBytes">Optional. The minimum number of bytes to maintain in the buffer.</param>
/// <param name="bufferStorage">
/// Optional. The backing array for the required buffer. If not passed, a buffer of 2 * minBufferChars will be allocated automatically.
/// <para>You might pass your own buffer in order to manage your own allocations, such as with <see cref="ArrayPool{T}"/>.</para>
/// </param>
/// <returns>An enumerator of subslices. Use foreach.</returns>
public static StreamEnumerator<byte> Bytes(Stream stream, byte separator)
public static StreamEnumerator<byte> Bytes(Stream stream, byte separator, int minBufferBytes = 1024, byte[]? bufferStorage = null)
{
var buffer = new Buffer<byte>(stream.Read, 1024);
bufferStorage ??= new byte[minBufferBytes * 2];
var buffer = new Buffer<byte>(stream.Read, minBufferBytes, bufferStorage);
return new StreamEnumerator<byte>(buffer, separator);
}

Expand All @@ -53,10 +59,16 @@ public static StreamEnumerator<byte> Bytes(Stream stream, byte separator)
/// </summary>
/// <param name="stream">The source stream of bytes.</param>
/// <param name="separator">The byte string on which to split the source string.</param>
/// <param name="minBufferBytes">Optional. The minimum number of bytes to maintain in the buffer.</param>
/// <param name="bufferStorage">
/// Optional. The backing array for the required buffer. If not passed, a buffer of 2 * minBufferChars will be allocated automatically.
/// <para>You might pass your own buffer in order to manage your own allocations, such as with <see cref="ArrayPool{T}"/>.</para>
/// </param>
/// <returns>An enumerator of subslices. Use foreach.</returns>
public static StreamEnumerator<byte> Bytes(Stream stream, ReadOnlySpan<byte> separator)
public static StreamEnumerator<byte> Bytes(Stream stream, ReadOnlySpan<byte> separator, int minBufferBytes = 1024, byte[]? bufferStorage = null)
{
var buffer = new Buffer<byte>(stream.Read, 1024);
bufferStorage ??= new byte[minBufferBytes * 2];
var buffer = new Buffer<byte>(stream.Read, minBufferBytes, bufferStorage);
return new StreamEnumerator<byte>(buffer, separator, true);

}
Expand All @@ -66,10 +78,16 @@ public static StreamEnumerator<byte> Bytes(Stream stream, ReadOnlySpan<byte> sep
/// </summary>
/// <param name="stream">The source stream of bytes.</param>
/// <param name="separators">The set of bytes on which to split the source string. Any byte in the collection will cause a split.</param>
/// <param name="minBufferBytes">Optional. The minimum number of bytes to maintain in the buffer.</param>
/// <param name="bufferStorage">
/// Optional. The backing array for the required buffer. If not passed, a buffer of 2 * minBufferChars will be allocated automatically.
/// <para>You might pass your own buffer in order to manage your own allocations, such as with <see cref="ArrayPool{T}"/>.</para>
/// </param>
/// <returns>An enumerator of subslices. Use foreach.</returns>
public static StreamEnumerator<byte> BytesAny(Stream stream, ReadOnlySpan<byte> separators)
public static StreamEnumerator<byte> BytesAny(Stream stream, ReadOnlySpan<byte> separators, int minBufferBytes = 1024, byte[]? bufferStorage = null)
{
var buffer = new Buffer<byte>(stream.Read, 1024);
bufferStorage ??= new byte[minBufferBytes * 2];
var buffer = new Buffer<byte>(stream.Read, minBufferBytes, bufferStorage);
return new StreamEnumerator<byte>(buffer, separators);
}

Expand All @@ -78,10 +96,16 @@ public static StreamEnumerator<byte> BytesAny(Stream stream, ReadOnlySpan<byte>
/// </summary>
/// <param name="stream">The source stream of bytes.</param>
/// <param name="separators"><see cref="SearchValues{Byte}"/> is a type optimized for searching any in a set of bytes.</param>
/// <param name="minBufferBytes">Optional. The minimum number of bytes to maintain in the buffer.</param>
/// <param name="bufferStorage">
/// Optional. The backing array for the required buffer. If not passed, a buffer of 2 * minBufferChars will be allocated automatically.
/// <para>You might pass your own buffer in order to manage your own allocations, such as with <see cref="ArrayPool{T}"/>.</para>
/// </param>
/// <returns>An enumerator of subslices. Use foreach.</returns>
public static StreamEnumerator<byte> BytesAny(Stream stream, SearchValues<byte> separators)
public static StreamEnumerator<byte> BytesAny(Stream stream, SearchValues<byte> separators, int minBufferBytes = 1024, byte[]? bufferStorage = null)
{
var buffer = new Buffer<byte>(stream.Read, 1024);
bufferStorage ??= new byte[minBufferBytes * 2];
var buffer = new Buffer<byte>(stream.Read, minBufferBytes, bufferStorage);
return new StreamEnumerator<byte>(buffer, separators);
}
}
48 changes: 36 additions & 12 deletions Split/Split.Chars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,48 +39,72 @@ public static partial class Split
/// <summary>
/// Splits an incoming reader of chars into substrings, around a single-char separator.
/// </summary>
/// <param name="stream">The source reader of chars.</param>
/// <param name="reader">The source reader of chars.</param>
/// <param name="separator">The char on which to split the incoming reader.</param>
/// <param name="minBufferChars">Optional. The minimum number of chars to maintain in the buffer.</param>
/// <param name="bufferStorage">
/// Optional. The backing array for the required buffer. If not passed, a buffer of 2 * minBufferChars will be allocated automatically.
/// <para>You might pass your own buffer in order to manage your own allocations, such as with <see cref="ArrayPool{T}"/>.</para>
/// </param>
/// <returns>An enumerator of subslices. Use foreach.</returns>
public static StreamEnumerator<char> Chars(TextReader stream, char separator)
public static StreamEnumerator<char> Chars(TextReader reader, char separator, int minBufferChars = 1024, char[]? bufferStorage = null)
{
var buffer = new Buffer<char>(stream.Read, 1024);
bufferStorage ??= new char[minBufferChars * 2];
var buffer = new Buffer<char>(reader.Read, minBufferChars, bufferStorage);
return new StreamEnumerator<char>(buffer, separator);
}

/// <summary>
/// Split an incoming reader of chars into substrings, around a string (multi-char) separator.
/// </summary>
/// <param name="stream">The source reader of chars.</param>
/// <param name="reader">The source reader of chars.</param>
/// <param name="separator">The string on which to split the source reader.</param>
/// <param name="minBufferChars">Optional. The minimum number of chars to maintain in the buffer.</param>
/// <param name="bufferStorage">
/// Optional. The backing array for the required buffer. If not passed, a buffer of 2 * minBufferChars will be allocated automatically.
/// <para>You might pass your own buffer in order to manage your own allocations, such as with <see cref="ArrayPool{T}"/>.</para>
/// </param>
/// <returns>An enumerator of subslices. Use foreach.</returns>
public static StreamEnumerator<char> Chars(TextReader stream, ReadOnlySpan<char> separator)
public static StreamEnumerator<char> Chars(TextReader reader, ReadOnlySpan<char> separator, int minBufferChars = 1024, char[]? bufferStorage = null)
{
var buffer = new Buffer<char>(stream.Read, 1024);
bufferStorage ??= new char[minBufferChars * 2];
var buffer = new Buffer<char>(reader.Read, minBufferChars, bufferStorage);
return new StreamEnumerator<char>(buffer, separator, true);
}

/// <summary>
/// Split an incoming reader of chars into substrings, around any char in an array of separators.
/// </summary>
/// <param name="stream">The source reader of chars.</param>
/// <param name="reader">The source reader of chars.</param>
/// <param name="separators">The set of chars on which to split the source string. Any char in the collection will cause a split.</param>
/// <param name="minBufferChars">Optional. The minimum number of chars to maintain in the buffer.</param>
/// <param name="bufferStorage">
/// Optional. The backing array for the required buffer. If not passed, a buffer of 2 * minBufferChars will be allocated automatically.
/// <para>You might pass your own buffer in order to manage your own allocations, such as with <see cref="ArrayPool{T}"/>.</para>
/// </param>
/// <returns>An enumerator of subslices. Use foreach.</returns>
public static StreamEnumerator<char> CharsAny(TextReader stream, ReadOnlySpan<char> separators)
public static StreamEnumerator<char> CharsAny(TextReader reader, ReadOnlySpan<char> separators, int minBufferChars = 1024, char[]? bufferStorage = null)
{
var buffer = new Buffer<char>(stream.Read, 1024);
bufferStorage ??= new char[minBufferChars * 2];
var buffer = new Buffer<char>(reader.Read, minBufferChars, bufferStorage);
return new StreamEnumerator<char>(buffer, separators);
}

/// <summary>
/// Split an incoming reader of chars into substrings, around any char in <see cref="SearchValues{Char}"/>.
/// </summary>
/// <param name="stream">The source reader of chars.</param>
/// <param name="reader">The source reader of chars.</param>
/// <param name="separators"><see cref="SearchValues{Char}"/> is a type optimized for searching any in a set of chars.</param>
/// <param name="minBufferChars">Optional. The minimum number of chars to maintain in the buffer.</param>
/// <param name="bufferStorage">
/// Optional. The backing array for the required buffer. If not passed, a buffer of 2 * minBufferChars will be allocated automatically.
/// <para>You might pass your own buffer in order to manage your own allocations, such as with <see cref="ArrayPool{T}"/>.</para>
/// </param>
/// <returns>An enumerator of subslices. Use foreach.</returns>
public static StreamEnumerator<char> CharsAny(TextReader stream, SearchValues<char> separators)
public static StreamEnumerator<char> CharsAny(TextReader reader, SearchValues<char> separators, int minBufferChars = 1024, char[]? bufferStorage = null)
{
var buffer = new Buffer<char>(stream.Read, 1024);
bufferStorage ??= new char[minBufferChars * 2];
var buffer = new Buffer<char>(reader.Read, minBufferChars, bufferStorage);
return new StreamEnumerator<char>(buffer, separators);
}
}

0 comments on commit b99d992

Please sign in to comment.