Skip to content

Commit 90a56af

Browse files
committed
Bring #2151 to post .NET 10
1 parent a491ccf commit 90a56af

6 files changed

Lines changed: 86 additions & 116 deletions

File tree

crates/bindings-csharp/Runtime/AuthCtx.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ namespace SpacetimeDB;
44

55
public sealed class AuthCtx
66
{
7+
private static byte[] jwtBuffer = new byte[0x10_000];
8+
79
private readonly bool _isInternal;
810
private readonly Lazy<JwtClaims?> _jwtLazy;
911

@@ -47,11 +49,12 @@ private static AuthCtx FromConnectionId(ConnectionId connectionId, Identity iden
4749
{
4850
var result = SpacetimeDB.Internal.FFI.get_jwt(ref connectionId, out var source);
4951
SpacetimeDB.Internal.FFI.CheckedStatus.Marshaller.ConvertToManaged(result);
50-
var bytes = SpacetimeDB.Internal.Module.Consume(source);
51-
if (bytes == null || bytes.Length == 0)
52+
using var stream = SpacetimeDB.Internal.Module.Consume(source, ref jwtBuffer);
53+
if (stream.Length == 0)
5254
{
5355
return null;
5456
}
57+
var bytes = stream.ToArray();
5558
var jwt = System.Text.Encoding.UTF8.GetString(bytes);
5659
return jwt != null ? new JwtClaims(jwt, identity) : null;
5760
}

crates/bindings-csharp/Runtime/Http.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ public sealed class HttpError(string message) : Exception(message)
155155
public sealed class HttpClient
156156
{
157157
private static readonly TimeSpan MaxTimeout = TimeSpan.FromMilliseconds(500);
158+
private static byte[] responseWireBuffer = new byte[0x10_000];
159+
private static byte[] responseBodyBuffer = new byte[0x10_000];
160+
private static byte[] errorWireBuffer = new byte[0x10_000];
158161

159162
/// <summary>
160163
/// Send a simple <c>GET</c> request to <paramref name="uri"/> with no headers.
@@ -341,10 +344,10 @@ out var out_
341344
{
342345
case Errno.OK:
343346
{
344-
var responseWireBytes = out_.A.Consume();
347+
var responseWireBytes = out_.A.Consume(ref responseWireBuffer).ToArray();
345348
var responseWire = FromBytes(new HttpResponseWire.BSATN(), responseWireBytes);
346349

347-
var body = new HttpBody(out_.B.Consume());
350+
var body = new HttpBody(out_.B.Consume(ref responseBodyBuffer).ToArray());
348351
var (statusCode, version, headers) = FromWireResponse(responseWire);
349352

350353
return Result<HttpResponse, HttpError>.Ok(
@@ -353,7 +356,7 @@ out var out_
353356
}
354357
case Errno.HTTP_ERROR:
355358
{
356-
var errorWireBytes = out_.A.Consume();
359+
var errorWireBytes = out_.A.Consume(ref errorWireBuffer).ToArray();
357360
var err = FromBytes(new SpacetimeDB.BSATN.String(), errorWireBytes);
358361
return Result<HttpResponse, HttpError>.Err(new HttpError(err));
359362
}

crates/bindings-csharp/Runtime/Internal/FFI.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ public enum Errno : short
5050
HTTP_ERROR = 21,
5151
}
5252

53+
internal static class ErrnoExtensions
54+
{
55+
public static void Check(this Errno status) => FFI.ErrnoHelpers.ThrowIfError(status);
56+
}
57+
5358
#pragma warning disable IDE1006 // Naming Styles - Not applicable to FFI stuff.
5459
internal static partial class FFI
5560
{

crates/bindings-csharp/Runtime/Internal/IIndex.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ out ReadOnlySpan<byte> rend
4444
}
4545

4646
protected IEnumerable<Row> DoFilter<Bounds>(Bounds bounds)
47-
where Bounds : IBTreeIndexBounds => new RawTableIter<Bounds>(indexId, bounds).Parse();
47+
where Bounds : IBTreeIndexBounds => new RawTableIter<Bounds>(indexId, bounds);
4848

4949
protected uint DoDelete<Bounds>(Bounds bounds)
5050
where Bounds : IBTreeIndexBounds
@@ -129,7 +129,7 @@ out var numDeleted
129129
new RW().Write(w, key);
130130
var point = s.ToArray();
131131

132-
using var e = new RawPointIter(indexId, point).Parse().GetEnumerator();
132+
using var e = new RawPointIter(indexId, point).GetEnumerator();
133133
if (!e.MoveNext())
134134
{
135135
return null;
@@ -192,7 +192,7 @@ out var numDeleted
192192
new RW().Write(w, key);
193193
var point = s.ToArray();
194194

195-
using var e = new RawPointIter(indexId, point).Parse().GetEnumerator();
195+
using var e = new RawPointIter(indexId, point).GetEnumerator();
196196
if (!e.MoveNext())
197197
{
198198
return null;
@@ -241,7 +241,7 @@ protected override void IterStart(out FFI.RowIter handle) =>
241241
new RW().Write(w, key);
242242
var point = s.ToArray();
243243

244-
using var e = new RawPointIter(indexId, point).Parse().GetEnumerator();
244+
using var e = new RawPointIter(indexId, point).GetEnumerator();
245245
if (!e.MoveNext())
246246
{
247247
return null;
@@ -280,7 +280,7 @@ protected override void IterStart(out FFI.RowIter handle) =>
280280
new RW().Write(w, key);
281281
var point = s.ToArray();
282282

283-
using var e = new RawPointIter(indexId, point).Parse().GetEnumerator();
283+
using var e = new RawPointIter(indexId, point).GetEnumerator();
284284
if (!e.MoveNext())
285285
{
286286
return null;
@@ -319,5 +319,5 @@ protected ulong DoCount()
319319
return count;
320320
}
321321

322-
protected IEnumerable<Row> DoIter() => new TableIter(tableId).Parse();
322+
protected IEnumerable<Row> DoIter() => new TableIter(tableId);
323323
}

crates/bindings-csharp/Runtime/Internal/ITable.cs

Lines changed: 35 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,26 @@
11
namespace SpacetimeDB.Internal;
22

33
using System.Buffers;
4+
using System.Collections;
45
using SpacetimeDB.BSATN;
56

6-
internal abstract class RawTableIterBase<T>
7+
internal abstract class RawTableIterBase<T> : IEnumerable<T>
78
where T : IStructuralReadWrite, new()
89
{
9-
public sealed class Enumerator(FFI.RowIter handle) : IDisposable
10-
{
11-
private const int InitialBufferSize = 1024;
12-
private byte[]? buffer = ArrayPool<byte>.Shared.Rent(InitialBufferSize);
13-
public ArraySegment<byte> Current { get; private set; } = ArraySegment<byte>.Empty;
14-
15-
public bool MoveNext()
16-
{
17-
if (handle == FFI.RowIter.INVALID)
18-
{
19-
return false;
20-
}
10+
private const int InitialBufferSize = 1024;
2111

22-
if (buffer is null)
23-
{
24-
return false;
25-
}
12+
protected abstract void IterStart(out FFI.RowIter handle);
2613

27-
uint buffer_len;
28-
while (true)
14+
public IEnumerator<T> GetEnumerator()
15+
{
16+
IterStart(out var handle);
17+
var buffer = ArrayPool<byte>.Shared.Rent(InitialBufferSize);
18+
try
19+
{
20+
while (handle != FFI.RowIter.INVALID)
2921
{
3022
var requested_len = (uint)buffer.Length;
31-
buffer_len = requested_len;
23+
var buffer_len = requested_len;
3224
var ret = FFI.row_iter_bsatn_advance(handle, buffer, ref buffer_len);
3325
if (ret == Errno.EXHAUSTED)
3426
{
@@ -38,82 +30,50 @@ public bool MoveNext()
3830
buffer_len = 0;
3931
}
4032
}
33+
4134
// On success, the only way `buffer_len == 0` is for the iterator to be exhausted.
4235
// This happens when the host iterator was empty from the start.
4336
System.Diagnostics.Debug.Assert(!(ret == Errno.OK && buffer_len == 0));
4437
switch (ret)
4538
{
46-
// Iterator advanced and may also be `EXHAUSTED`.
47-
// When `OK`, we'll need to advance the iterator in the next call to `MoveNext`.
48-
// In both cases, update `Current` to point at the valid range in the scratch `buffer`.
4939
case Errno.EXHAUSTED
5040
or Errno.OK:
51-
Current = new ArraySegment<byte>(buffer, 0, (int)buffer_len);
52-
return buffer_len != 0;
53-
// Couldn't find the iterator, error!
54-
case Errno.NO_SUCH_ITER:
55-
throw new NoSuchIterException();
56-
// The scratch `buffer` is too small to fit a row / chunk.
57-
// Grow `buffer` and try again.
58-
// The `buffer_len` will have been updated with the necessary size.
41+
{
42+
using var stream = new MemoryStream(
43+
buffer,
44+
0,
45+
(int)buffer_len,
46+
writable: false,
47+
publiclyVisible: true
48+
);
49+
using var reader = new BinaryReader(stream);
50+
while (stream.Position < stream.Length)
51+
{
52+
yield return IStructuralReadWrite.Read<T>(reader);
53+
}
54+
break;
55+
}
5956
case Errno.BUFFER_TOO_SMALL:
6057
ArrayPool<byte>.Shared.Return(buffer);
6158
buffer = ArrayPool<byte>.Shared.Rent((int)buffer_len);
62-
continue;
59+
break;
6360
default:
64-
throw new UnknownException(ret);
61+
ret.Check();
62+
break;
6563
}
6664
}
6765
}
68-
69-
public void Dispose()
66+
finally
7067
{
7168
if (handle != FFI.RowIter.INVALID)
7269
{
7370
FFI.row_iter_bsatn_close(handle);
74-
handle = FFI.RowIter.INVALID;
7571
}
76-
77-
if (buffer is not null)
78-
{
79-
ArrayPool<byte>.Shared.Return(buffer);
80-
buffer = null;
81-
}
82-
}
83-
84-
public void Reset()
85-
{
86-
throw new NotImplementedException();
72+
ArrayPool<byte>.Shared.Return(buffer);
8773
}
8874
}
8975

90-
protected abstract void IterStart(out FFI.RowIter handle);
91-
92-
// Note: using the GetEnumerator() duck-typing protocol instead of IEnumerable to avoid extra boxing.
93-
public Enumerator GetEnumerator()
94-
{
95-
IterStart(out var handle);
96-
return new(handle);
97-
}
98-
99-
public IEnumerable<T> Parse()
100-
{
101-
foreach (var chunk in this)
102-
{
103-
using var stream = new MemoryStream(
104-
chunk.Array!,
105-
chunk.Offset,
106-
chunk.Count,
107-
writable: false,
108-
publiclyVisible: true
109-
);
110-
using var reader = new BinaryReader(stream);
111-
while (stream.Position < stream.Length)
112-
{
113-
yield return IStructuralReadWrite.Read<T>(reader);
114-
}
115-
}
116-
}
76+
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
11777
}
11878

11979
public interface ITableView<View, T>
@@ -166,7 +126,7 @@ protected static ulong DoCount()
166126
return count;
167127
}
168128

169-
protected static IEnumerable<T> DoIter() => new RawTableIter(tableId).Parse();
129+
protected static IEnumerable<T> DoIter() => new RawTableIter(tableId);
170130

171131
protected static T DoInsert(T row)
172132
{

0 commit comments

Comments
 (0)