Repository navigation
Expand file tree
/
Copy pathVectorData.cs
More file actions
131 lines (106 loc) · 4.83 KB
/
Copy pathVectorData.cs
File metadata and controls
131 lines (106 loc) · 4.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
using System.Buffers;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using StackExchange.Redis;
namespace NRedisStack.Search;
public abstract class VectorData<T> : VectorData, IDisposable where T : unmanaged
{
private protected VectorData()
{
}
public abstract Span<T> Span { get; }
internal sealed class VectorBytesData(int byteLength) : VectorData<T>
{
private byte[]? _oversized = ArrayPool<byte>.Shared.Rent(byteLength);
public override Span<T> Span => MemoryMarshal.Cast<byte, T>(Array.AsSpan(0, byteLength));
private byte[] Array => _oversized ?? ThrowDisposed();
static byte[] ThrowDisposed() => throw new ObjectDisposedException(nameof(VectorData));
public override void Dispose()
{
var tmp = _oversized;
_oversized = null;
if (tmp is not null) ArrayPool<byte>.Shared.Return(tmp);
}
public override RedisValue AsRedisValue() => (RedisValue)new ReadOnlyMemory<byte>(Array, 0, byteLength);
// *always* force; in 8.4, this is not required, but this is likely to change, so: avoid the problem immediately
internal override bool ForceParameter => true; // byteLength != 0 && Array[0] == (byte)'$';
}
public abstract void Dispose();
}
public abstract class VectorData
{
/// <summary>
/// Lease a vector that can hold values of <typeparamref name="T"/>.
/// No quantization occurs - the data is transmitted as the raw bytes of the corresponding size; for
/// example, to use FLOAT16 encoding, the <c>Half</c> type might be used.
/// </summary>
/// <param name="dimension">The number of values to be held.</param>
/// <typeparam name="T">The data type to be represented</typeparam>
public static VectorData<T> Lease<T>(int dimension) where T : unmanaged
{
if (dimension < 0) ThrowDimension();
if (!BitConverter.IsLittleEndian) ThrowBigEndian();
return new VectorData<T>.VectorBytesData(Unsafe.SizeOf<T>() * dimension);
static void ThrowDimension() => throw new ArgumentOutOfRangeException(nameof(dimension));
}
/// <summary>
/// Lease a vector that can hold values of <typeparamref name="T"/>, copying in the supplied values.
/// </summary>
public static VectorData<T> LeaseWithValues<T>(params ReadOnlySpan<T> values) where T : unmanaged
{
var lease = Lease<T>(values.Length);
values.CopyTo(lease.Span);
return lease;
}
private protected VectorData()
{
}
/// <summary>
/// Gets the underlying payload of this vector as a <see cref="RedisValue"/>.
/// </summary>
public abstract RedisValue AsRedisValue();
/// <summary>
/// A raw vector payload.
/// </summary>
public static VectorData Raw(ReadOnlyMemory<byte> bytes) => new VectorDataRaw(bytes);
/// <summary>
/// Represent a vector as a parameter to be supplied later.
/// </summary>
public static VectorData Parameter(string name) => new VectorParameter(name);
/*
/// <summary>
/// A vector of <see cref="Single"/> entries.
/// </summary>
public static implicit operator VectorData(float[] data) => new VectorDataSingle(data);
/// <inheritdoc cref="Create"/>
public static implicit operator VectorData(ReadOnlyMemory<float> vector) => new VectorDataSingle(vector);
*/
/// <inheritdoc cref="Parameter"/>
public static implicit operator VectorData(string name) => new VectorParameter(name);
internal virtual object GetSingleArg() => AsRedisValue();
/// <inheritdoc/>
public override string ToString() => GetType().Name;
private sealed class VectorDataRaw(ReadOnlyMemory<byte> bytes) : VectorData
{
public override RedisValue AsRedisValue() => (RedisValue)bytes;
// *always* force; in 8.4, this is not required, but this is likely to change, so: avoid the problem immediately
internal override bool ForceParameter => true; // !bytes.IsEmpty && bytes.Span[0] == (byte)'$';
}
private sealed class VectorParameter : VectorData
{
private readonly string name;
public VectorParameter(string name)
{
if (string.IsNullOrEmpty(name) || name[0] != '$') Throw();
this.name = name;
static void Throw() => throw new ArgumentException("Parameter tokens must start with the character '$'.");
}
public override string ToString() => name;
internal override object GetSingleArg() => name; // note type is string, not RedisValue
public override RedisValue AsRedisValue() => name;
}
private protected static void ThrowBigEndian() =>
throw new PlatformNotSupportedException("Big-endian CPUs are not currently supported for this operation");
internal virtual bool ForceParameter => false;
}