Skip to content

Commit 6973fc9

Browse files
committed
Optimized intermediate buffers with stackalloc, spans and array pools
1 parent 0fd8306 commit 6973fc9

11 files changed

Lines changed: 662 additions & 87 deletions

File tree

src/FirebirdSql.Data.FirebirdClient/Client/Managed/DataProviderStreamWrapper.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515

1616
//$Authors = Jiri Cincura (jiri@cincura.net)
1717

18+
using System;
1819
using System.IO;
1920
using System.Runtime.CompilerServices;
21+
using System.Runtime.InteropServices;
2022
using System.Threading;
2123
using System.Threading.Tasks;
2224

@@ -36,12 +38,31 @@ public int Read(byte[] buffer, int offset, int count)
3638
{
3739
return _stream.Read(buffer, offset, count);
3840
}
41+
42+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
43+
public int Read(Span<byte> buffer, int offset, int count)
44+
{
45+
return _stream.Read(buffer[offset..(offset+count)]);
46+
}
47+
3948
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4049
public ValueTask<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default)
4150
{
4251
return new ValueTask<int>(_stream.ReadAsync(buffer, offset, count, cancellationToken));
4352
}
4453

54+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
55+
public ValueTask<int> ReadAsync(Memory<byte> buffer, int offset, int count, CancellationToken cancellationToken = default)
56+
{
57+
return _stream.ReadAsync(buffer.Slice(offset, count), cancellationToken);
58+
}
59+
60+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
61+
public void Write(ReadOnlySpan<byte> buffer)
62+
{
63+
_stream.Write(buffer);
64+
}
65+
4566
[MethodImpl(MethodImplOptions.AggressiveInlining)]
4667
public void Write(byte[] buffer, int offset, int count)
4768
{
@@ -53,6 +74,12 @@ public ValueTask WriteAsync(byte[] buffer, int offset, int count, CancellationTo
5374
return new ValueTask(_stream.WriteAsync(buffer, offset, count, cancellationToken));
5475
}
5576

77+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
78+
public ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, int offset, int count, CancellationToken cancellationToken = default)
79+
{
80+
return _stream.WriteAsync(buffer.Slice(offset, count), cancellationToken);
81+
}
82+
5683
[MethodImpl(MethodImplOptions.AggressiveInlining)]
5784
public void Flush()
5885
{

src/FirebirdSql.Data.FirebirdClient/Client/Managed/FirebirdNetworkHandlingWrapper.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,33 @@ public int Read(byte[] buffer, int offset, int count)
8787
var dataLength = ReadFromInputBuffer(buffer, offset, count);
8888
return dataLength;
8989
}
90+
91+
public int Read(Span<byte> buffer, int offset, int count)
92+
{
93+
if (_inputBuffer.Count < count) {
94+
var readBuffer = _readBuffer;
95+
int read;
96+
try {
97+
read = _dataProvider.Read(readBuffer, 0, readBuffer.Length);
98+
}
99+
catch (IOException) {
100+
IOFailed = true;
101+
throw;
102+
}
103+
if (read != 0) {
104+
if (_decryptor != null) {
105+
_decryptor.ProcessBytes(readBuffer, 0, read, readBuffer, 0);
106+
}
107+
if (_decompressor != null) {
108+
read = HandleDecompression(readBuffer, read);
109+
readBuffer = _compressionBuffer;
110+
}
111+
WriteToInputBuffer(readBuffer, read);
112+
}
113+
}
114+
var dataLength = ReadFromInputBuffer(buffer, offset, count);
115+
return dataLength;
116+
}
90117
public async ValueTask<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default)
91118
{
92119
if (_inputBuffer.Count < count)
@@ -120,6 +147,24 @@ public async ValueTask<int> ReadAsync(byte[] buffer, int offset, int count, Canc
120147
return dataLength;
121148
}
122149

150+
public async ValueTask<int> ReadAsync(Memory<byte> buffer, int offset, int count, CancellationToken cancellationToken = default)
151+
{
152+
var rented = new byte[count];
153+
try
154+
{
155+
var read = await ReadAsync(rented, 0, count, cancellationToken).ConfigureAwait(false);
156+
rented.AsSpan(0, read).CopyTo(buffer.Span.Slice(offset, read));
157+
return read;
158+
}
159+
finally { }
160+
}
161+
162+
public void Write(ReadOnlySpan<byte> buffer)
163+
{
164+
foreach (var b in buffer)
165+
_outputBuffer.Enqueue(b);
166+
}
167+
123168
public void Write(byte[] buffer, int offset, int count)
124169
{
125170
for (var i = 0; i < count; i++)
@@ -132,6 +177,14 @@ public ValueTask WriteAsync(byte[] buffer, int offset, int count, CancellationTo
132177
return ValueTask.CompletedTask;
133178
}
134179

180+
public ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, int offset, int count, CancellationToken cancellationToken = default)
181+
{
182+
var span = buffer.Span.Slice(offset, count);
183+
foreach (var b in span)
184+
_outputBuffer.Enqueue(b);
185+
return ValueTask2.CompletedTask;
186+
}
187+
135188
public void Flush()
136189
{
137190
var buffer = _outputBuffer.ToArray();
@@ -206,6 +259,15 @@ int ReadFromInputBuffer(byte[] buffer, int offset, int count)
206259
return read;
207260
}
208261

262+
int ReadFromInputBuffer(Span<byte> buffer, int offset, int count)
263+
{
264+
var read = Math.Min(count, _inputBuffer.Count);
265+
for (var i = 0; i < read; i++) {
266+
buffer[offset+i] = _inputBuffer.Dequeue();
267+
}
268+
return read;
269+
}
270+
209271
void WriteToInputBuffer(byte[] data, int count)
210272
{
211273
for (var i = 0; i < count; i++)

src/FirebirdSql.Data.FirebirdClient/Client/Managed/IDataProvider.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
//$Authors = Jiri Cincura (jiri@cincura.net)
1717

18+
using System;
1819
using System.Threading;
1920
using System.Threading.Tasks;
2021

@@ -23,10 +24,14 @@ namespace FirebirdSql.Data.Client.Managed;
2324
interface IDataProvider
2425
{
2526
int Read(byte[] buffer, int offset, int count);
27+
int Read(Span<byte> buffer, int offset, int count);
2628
ValueTask<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default);
29+
ValueTask<int> ReadAsync(Memory<byte> buffer, int offset, int count, CancellationToken cancellationToken = default);
2730

31+
void Write(ReadOnlySpan<byte> buffer);
2832
void Write(byte[] buffer, int offset, int count);
2933
ValueTask WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default);
34+
ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, int offset, int count, CancellationToken cancellationToken = default);
3035

3136
void Flush();
3237
ValueTask FlushAsync(CancellationToken cancellationToken = default);

src/FirebirdSql.Data.FirebirdClient/Client/Managed/IXdrReader.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,18 @@ namespace FirebirdSql.Data.Client.Managed;
2727
interface IXdrReader
2828
{
2929
byte[] ReadBytes(byte[] buffer, int count);
30+
void ReadBytes(Span<byte> buffer, int count);
31+
ValueTask ReadBytesAsync(Memory<byte> buffer, int count, CancellationToken cancellationToken = default);
3032
ValueTask<byte[]> ReadBytesAsync(byte[] buffer, int count, CancellationToken cancellationToken = default);
3133

3234
byte[] ReadOpaque(int length);
35+
void ReadOpaque(Span<byte> buffer, int length);
36+
ValueTask ReadOpaqueAsync(Memory<byte> buffer, int length, CancellationToken cancellationToken = default);
3337
ValueTask<byte[]> ReadOpaqueAsync(int length, CancellationToken cancellationToken = default);
3438

3539
byte[] ReadBuffer();
40+
void ReadBuffer(Span<byte> buffer);
41+
ValueTask ReadBufferAsync(Memory<byte> buffer, CancellationToken cancellationToken = default);
3642
ValueTask<byte[]> ReadBufferAsync(CancellationToken cancellationToken = default);
3743

3844
string ReadString();

src/FirebirdSql.Data.FirebirdClient/Client/Managed/IXdrWriter.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,36 @@ interface IXdrWriter
2828
void Flush();
2929
ValueTask FlushAsync(CancellationToken cancellationToken = default);
3030

31+
void WriteBytes(ReadOnlySpan<byte> buffer);
3132
void WriteBytes(byte[] buffer, int count);
3233
ValueTask WriteBytesAsync(byte[] buffer, int count, CancellationToken cancellationToken = default);
3334

3435
void WriteOpaque(byte[] buffer);
36+
void WriteOpaque(ReadOnlySpan<byte> buffer);
37+
ValueTask WriteOpaqueAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default);
3538
ValueTask WriteOpaqueAsync(byte[] buffer, CancellationToken cancellationToken = default);
3639

3740
void WriteOpaque(byte[] buffer, int length);
41+
void WriteOpaque(ReadOnlySpan<byte> buffer, int length);
3842
ValueTask WriteOpaqueAsync(byte[] buffer, int length, CancellationToken cancellationToken = default);
43+
ValueTask WriteOpaqueAsync(ReadOnlyMemory<byte> buffer, int length, CancellationToken cancellationToken = default);
3944

4045
void WriteBuffer(byte[] buffer);
46+
void WriteBuffer(ReadOnlySpan<byte> buffer);
47+
ValueTask WriteBufferAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default);
4148
ValueTask WriteBufferAsync(byte[] buffer, CancellationToken cancellationToken = default);
4249

4350
void WriteBuffer(byte[] buffer, int length);
4451
ValueTask WriteBufferAsync(byte[] buffer, int length, CancellationToken cancellationToken = default);
4552

4653
void WriteBlobBuffer(byte[] buffer);
54+
void WriteBlobBuffer(ReadOnlySpan<byte> buffer);
55+
ValueTask WriteBlobBufferAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default);
4756
ValueTask WriteBlobBufferAsync(byte[] buffer, CancellationToken cancellationToken = default);
4857

4958
void WriteTyped(int type, byte[] buffer);
59+
void WriteTyped(int type, ReadOnlySpan<byte> buffer);
60+
ValueTask WriteTypedAsync(int type, ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default);
5061
ValueTask WriteTypedAsync(int type, byte[] buffer, CancellationToken cancellationToken = default);
5162

5263
void Write(string value);

0 commit comments

Comments
 (0)