Skip to content

Commit dd17a6c

Browse files
authored
Multirange Support (#73)
1 parent 8ae1b69 commit dd17a6c

File tree

12 files changed

+252
-17
lines changed

12 files changed

+252
-17
lines changed

examples/EdgeDB.Examples.FSharp/Program.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ open Serilog
33
open Microsoft.Extensions.Hosting
44
open Microsoft.Extensions.DependencyInjection
55
open Microsoft.Extensions.Logging
6+
open EdgeDB
67

78
Log.Logger <-
89
LoggerConfiguration()
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using EdgeDB.Binary.Protocol.Common.Descriptors;
2+
using EdgeDB.DataTypes;
3+
using EdgeDB.Models.DataTypes;
4+
5+
namespace EdgeDB.Binary.Codecs;
6+
7+
internal sealed class MultiRangeCodec<T> : BaseCodec<MultiRange<T>>, IWrappingCodec, ICacheableCodec
8+
where T : struct
9+
{
10+
private RangeCodec<T> _rangeCodec;
11+
12+
public MultiRangeCodec(in Guid id, ICodec<T> rangeInnerCodec, CodecMetadata? metadata) : base(in id, metadata)
13+
{
14+
_rangeCodec = new RangeCodec<T>(in id, rangeInnerCodec, metadata);
15+
}
16+
17+
public override void Serialize(ref PacketWriter writer, MultiRange<T> value, CodecContext context)
18+
{
19+
writer.Write(value.Length);
20+
21+
for (int i = 0; i != value.Length; i++)
22+
{
23+
writer.WriteToWithInt32Length(
24+
(ref PacketWriter innerWriter) => _rangeCodec.Serialize(ref innerWriter, value[i], context));
25+
}
26+
}
27+
28+
public override MultiRange<T> Deserialize(ref PacketReader reader, CodecContext context)
29+
{
30+
var length = reader.ReadInt32();
31+
32+
var elements = new Range<T>[length];
33+
34+
for (int i = 0; i != length; i++)
35+
{
36+
reader.Limit = reader.ReadInt32();
37+
elements[i] = _rangeCodec.Deserialize(ref reader, context);
38+
reader.Limit = -1;
39+
}
40+
41+
return new MultiRange<T>(elements);
42+
}
43+
44+
public ICodec InnerCodec
45+
{
46+
get => _rangeCodec;
47+
set
48+
{
49+
if (value is not RangeCodec<T> r)
50+
throw new ArgumentException($"Expected a range codec, but got {value}");
51+
52+
_rangeCodec = r;
53+
}
54+
}
55+
56+
public override string ToString()
57+
=> "multirange";
58+
}

src/EdgeDB.Net.Driver/Binary/Protocol/IProtocolProvider.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ internal interface IProtocolProvider
3030

3131
IReadOnlyDictionary<string, object?> ServerConfig { get; }
3232

33+
int? SuggestedPoolConcurrency { get; }
34+
3335
public static IProtocolProvider GetDefaultProvider(EdgeDBBinaryClient client)
3436
=> (_defaultProvider ??= Providers[ProtocolVersion.EdgeDBBinaryDefaultVersion].Factory)(client);
3537

src/EdgeDB.Net.Driver/Binary/Protocol/V1.0/V1ProtocolProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public V1ProtocolProvider(EdgeDBBinaryClient client)
2121
_client = client;
2222
}
2323

24-
public int SuggestedPoolConcurrency { get; private set; }
24+
public int? SuggestedPoolConcurrency { get; private set; }
2525

2626
public ref ReadOnlyMemory<byte> ServerKey
2727
=> ref _serverKey;

src/EdgeDB.Net.Driver/Binary/Protocol/V2.0/Descriptors/DescriptorType.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ internal enum DescriptorType : byte
1313
Range = 9,
1414
Object = 10,
1515
Compound = 11,
16+
MultiRange = 12,
1617
TypeAnnotationText = 127
1718
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using EdgeDB.Binary.Protocol.Common.Descriptors;
2+
3+
namespace EdgeDB.Binary.Protocol.V2._0.Descriptors;
4+
5+
internal readonly struct MultiRangeDescriptor : ITypeDescriptor, IMetadataDescriptor
6+
{
7+
public readonly Guid Id;
8+
9+
public readonly string Name;
10+
11+
public readonly bool IsSchemaDefined;
12+
13+
public readonly ushort[] Ancestors;
14+
15+
public readonly ushort Type;
16+
public MultiRangeDescriptor(ref PacketReader reader, in Guid id)
17+
{
18+
Id = id;
19+
20+
Name = reader.ReadString();
21+
IsSchemaDefined = reader.ReadBoolean();
22+
23+
var ancestorsCount = reader.ReadUInt16();
24+
var ancestors = new ushort[ancestorsCount];
25+
26+
for (var i = 0; i != ancestorsCount; i++)
27+
{
28+
ancestors[i] = reader.ReadUInt16();
29+
}
30+
31+
Ancestors = ancestors;
32+
33+
Type = reader.ReadUInt16();
34+
}
35+
36+
37+
public CodecMetadata? GetMetadata(RelativeCodecDelegate relativeCodec,
38+
RelativeDescriptorDelegate relativeDescriptor)
39+
=> new(Name, IsSchemaDefined,
40+
IMetadataDescriptor.ConstructAncestors(Ancestors, relativeCodec, relativeDescriptor));
41+
42+
unsafe ref readonly Guid ITypeDescriptor.Id
43+
{
44+
get
45+
{
46+
fixed (Guid* ptr = &Id)
47+
return ref *ptr;
48+
}
49+
}
50+
}

src/EdgeDB.Net.Driver/Binary/Protocol/V2.0/V2ProtocolProvider.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public override ITypeDescriptor GetDescriptor(ref PacketReader reader)
4343
DescriptorType.Scalar => new ScalarTypeDescriptor(ref reader, in id),
4444
DescriptorType.Set => new SetDescriptor(ref reader, in id),
4545
DescriptorType.Tuple => new TupleTypeDescriptor(ref reader, in id),
46+
DescriptorType.MultiRange => new MultiRangeDescriptor(ref reader, in id),
4647
_ => throw new InvalidDataException($"No descriptor found for type {type}")
4748
};
4849
}
@@ -142,6 +143,12 @@ public override ITypeDescriptor GetDescriptor(ref PacketReader reader)
142143

143144
return new CompilableWrappingCodec(in range.Id, innerCodec, typeof(RangeCodec<>), metadata);
144145
}
146+
case MultiRangeDescriptor multirange:
147+
{
148+
ref var innerCodec = ref getRelativeCodec(multirange.Type)!;
149+
150+
return new CompilableWrappingCodec(in multirange.Id, innerCodec, typeof(MultiRangeCodec<>), metadata);
151+
}
145152
case ScalarTypeDescriptor scalar:
146153
throw new MissingCodecException(
147154
$"Could not find the scalar type {scalar.Id}. Please file a bug report with your query that caused this error.");

src/EdgeDB.Net.Driver/Clients/EdgeDBBinaryClient.cs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.Extensions.Logging;
77
using Microsoft.Extensions.Logging.Abstractions;
88
using System.Collections.Immutable;
9+
using System.Diagnostics;
910
using System.Reflection;
1011
using ProtocolExecuteResult = EdgeDB.Binary.Protocol.ExecuteResult;
1112

@@ -34,7 +35,9 @@ internal abstract class EdgeDBBinaryClient : BaseEdgeDBClient
3435
private Guid _stateDescriptorId;
3536

3637
internal byte[] ServerKey;
37-
internal int? SuggestedPoolConcurrency;
38+
39+
internal int? SuggestedPoolConcurrency
40+
=> _protocolProvider.SuggestedPoolConcurrency;
3841

3942
/// <summary>
4043
/// Creates a new binary client with the provided conection and config.
@@ -92,19 +95,6 @@ internal ref Guid StateDescriptorId
9295
protected CancellationToken DisconnectCancelToken
9396
=> Duplexer.DisconnectToken;
9497

95-
#region Events
96-
97-
/// <summary>
98-
/// Fired when the client disconnects.
99-
/// </summary>
100-
public event Func<ValueTask> OnDisconnect
101-
{
102-
add => OnDisconnectInternal.Add(c => value());
103-
remove => OnDisconnectInternal.Remove(c => value());
104-
}
105-
106-
#endregion
107-
10898
#region Client pool dispose
10999

110100
/// <remarks />
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
using EdgeDB.DataTypes;
2+
using System.Collections;
3+
4+
namespace EdgeDB.Models.DataTypes;
5+
6+
/// <summary>
7+
/// Represents the <c>multirange</c> type in EdgeDB.
8+
/// </summary>
9+
/// <typeparam name="T">The inner type of the multirange.</typeparam>
10+
public readonly struct MultiRange<T> : IEnumerable<Range<T>>
11+
where T : struct
12+
{
13+
/// <summary>
14+
/// Gets the length of this multirange.
15+
/// </summary>
16+
public int Length
17+
=> _ranges.Length;
18+
19+
/// <summary>
20+
/// Gets a <see cref="Range{T}"/> element within this multirange.
21+
/// </summary>
22+
/// <param name="i"></param>
23+
public readonly ref Range<T> this[int i]
24+
=> ref _ranges[i];
25+
26+
private readonly Range<T>[] _ranges;
27+
28+
/// <summary>
29+
/// Constructs a new <see cref="MultiRange{T}"/>.
30+
/// </summary>
31+
/// <param name="set">A set of ranges to put within this multirange.</param>
32+
public MultiRange(HashSet<Range<T>> set)
33+
{
34+
_ranges = set.ToArray();
35+
}
36+
37+
internal MultiRange(Range<T>[] ranges)
38+
{
39+
_ranges = ranges;
40+
}
41+
42+
/// <summary>
43+
/// Returns a hashset that represents this multirange.
44+
/// </summary>
45+
/// <returns>A hashset, derived from the contents of this multirange.</returns>
46+
public HashSet<Range<T>> ToSet() => new(_ranges);
47+
48+
#region Enumerator
49+
private sealed class MultiRangeEnumerator : IEnumerator<Range<T>>
50+
{
51+
private readonly MultiRange<T> _multiRange;
52+
private int _index;
53+
54+
internal MultiRangeEnumerator(in MultiRange<T> multiRange)
55+
{
56+
_multiRange = multiRange;
57+
_index = -1;
58+
}
59+
60+
public bool MoveNext()
61+
{
62+
var index = _index + 1;
63+
if (index >= _multiRange.Length)
64+
{
65+
_index = _multiRange.Length;
66+
return false;
67+
}
68+
_index = index;
69+
return true;
70+
}
71+
72+
public void Reset() => _index = -1;
73+
74+
public Range<T> Current
75+
{
76+
get
77+
{
78+
var index = _index;
79+
80+
if (index >= _multiRange.Length)
81+
{
82+
if (index < 0)
83+
{
84+
throw new InvalidOperationException("Enumeration hasn't started");
85+
}
86+
else
87+
{
88+
throw new InvalidOperationException("The enumeration has finished");
89+
}
90+
}
91+
92+
return _multiRange[index];
93+
}
94+
}
95+
96+
public void Dispose()
97+
{ }
98+
99+
object IEnumerator.Current => Current;
100+
}
101+
#endregion
102+
103+
/// <inheritdoc />
104+
public IEnumerator<Range<T>> GetEnumerator() => new MultiRangeEnumerator(in this);
105+
106+
/// <inheritdoc />
107+
IEnumerator IEnumerable.GetEnumerator() => _ranges.GetEnumerator();
108+
}

tests/EdgeDB.Tests.Integration/ClientTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using EdgeDB.DataTypes;
2+
using EdgeDB.Models.DataTypes;
13
using EdgeDB.State;
24
using Microsoft.VisualStudio.TestTools.UnitTesting;
35
using System;
@@ -20,6 +22,22 @@ public ClientTests()
2022

2123
internal EdgeDBClient EdgeDB { get; set; }
2224

25+
[TestMethod]
26+
public async Task TestMultiRanges()
27+
{
28+
var multiRange = new MultiRange<int>(new[]
29+
{
30+
new Range<int>(-40, -20), new Range<int>(5, 10), new Range<int>(20, 50), new Range<int>(5000, 5001),
31+
});
32+
33+
var result = await EdgeDB.QueryRequiredSingleAsync<MultiRange<int>>("select <multirange<int32>>$arg",
34+
new {arg = multiRange});
35+
36+
Assert.AreEqual(result.Length, multiRange.Length);
37+
38+
Assert.IsTrue(multiRange.SequenceEqual(result));
39+
}
40+
2341
[TestMethod]
2442
public async Task TestNullableReturns()
2543
{

0 commit comments

Comments
 (0)