Commit 7cb8f3ce authored by Marc Gravell's avatar Marc Gravell

be evil, and make use of the innards of RedisValue to store array data (ref...

be evil, and make use of the innards of RedisValue to store array data (ref and count) when TypedRedisValue is multi-bulk
parent 50213afb
...@@ -174,8 +174,8 @@ protected virtual TypedRedisValue LRange(RedisClient client, RedisRequest reques ...@@ -174,8 +174,8 @@ protected virtual TypedRedisValue LRange(RedisClient client, RedisRequest reques
if (stop < 0) stop = 0; if (stop < 0) stop = 0;
else if (stop >= len) stop = len - 1; else if (stop >= len) stop = len - 1;
var arr = TypedRedisValue.Rent(checked((int)((stop - start) + 1))); var arr = TypedRedisValue.Rent(checked((int)((stop - start) + 1)), out var span);
LRange(client.Database, key, start, arr.MutableSpan); LRange(client.Database, key, start, span);
return arr; return arr;
} }
protected virtual void LRange(int database, RedisKey key, long start, Span<TypedRedisValue> arr) => throw new NotSupportedException(); protected virtual void LRange(int database, RedisKey key, long start, Span<TypedRedisValue> arr) => throw new NotSupportedException();
...@@ -221,17 +221,21 @@ protected virtual TypedRedisValue Config(RedisClient client, RedisRequest reques ...@@ -221,17 +221,21 @@ protected virtual TypedRedisValue Config(RedisClient client, RedisRequest reques
var matches = config.CountMatch(pattern); var matches = config.CountMatch(pattern);
if (matches == 0) return TypedRedisValue.EmptyArray; if (matches == 0) return TypedRedisValue.EmptyArray;
var arr = TypedRedisValue.Rent(2 * matches); var arr = TypedRedisValue.Rent(2 * matches, out var span);
int index = 0; int index = 0;
foreach (var pair in config.Wrapped) foreach (var pair in config.Wrapped)
{ {
if (IsMatch(pattern, pair.Key)) if (IsMatch(pattern, pair.Key))
{ {
arr[index++] = TypedRedisValue.BulkString(pair.Key); span[index++] = TypedRedisValue.BulkString(pair.Key);
arr[index++] = TypedRedisValue.BulkString(pair.Value); span[index++] = TypedRedisValue.BulkString(pair.Value);
} }
} }
if (index != arr.Length) throw new InvalidOperationException("Configuration CountMatch fail"); if (index != span.Length)
{
arr.Recycle(index);
throw new InvalidOperationException("Configuration CountMatch fail");
}
return arr; return arr;
} }
...@@ -410,11 +414,11 @@ protected virtual TypedRedisValue MemoryPurge(RedisClient client, RedisRequest r ...@@ -410,11 +414,11 @@ protected virtual TypedRedisValue MemoryPurge(RedisClient client, RedisRequest r
protected virtual TypedRedisValue Mget(RedisClient client, RedisRequest request) protected virtual TypedRedisValue Mget(RedisClient client, RedisRequest request)
{ {
int argCount = request.Count; int argCount = request.Count;
var arr = TypedRedisValue.Rent(argCount - 1); var arr = TypedRedisValue.Rent(argCount - 1, out var span);
var db = client.Database; var db = client.Database;
for (int i = 1; i < argCount; i++) for (int i = 1; i < argCount; i++)
{ {
arr[i - 1] = TypedRedisValue.BulkString(Get(db, request.GetKey(i))); span[i - 1] = TypedRedisValue.BulkString(Get(db, request.GetKey(i)));
} }
return arr; return arr;
} }
...@@ -443,10 +447,10 @@ protected virtual TypedRedisValue Quit(RedisClient client, RedisRequest request) ...@@ -443,10 +447,10 @@ protected virtual TypedRedisValue Quit(RedisClient client, RedisRequest request)
[RedisCommand(1, LockFree = true)] [RedisCommand(1, LockFree = true)]
protected virtual TypedRedisValue Role(RedisClient client, RedisRequest request) protected virtual TypedRedisValue Role(RedisClient client, RedisRequest request)
{ {
var arr = TypedRedisValue.Rent(3); var arr = TypedRedisValue.Rent(3, out var span);
arr[0] = TypedRedisValue.BulkString("master"); span[0] = TypedRedisValue.BulkString("master");
arr[1] = TypedRedisValue.Integer(0); span[1] = TypedRedisValue.Integer(0);
arr[2] = TypedRedisValue.EmptyArray; span[2] = TypedRedisValue.EmptyArray;
return arr; return arr;
} }
...@@ -470,7 +474,7 @@ protected virtual TypedRedisValue Unsubscribe(RedisClient client, RedisRequest r ...@@ -470,7 +474,7 @@ protected virtual TypedRedisValue Unsubscribe(RedisClient client, RedisRequest r
private TypedRedisValue SubscribeImpl(RedisClient client, RedisRequest request) private TypedRedisValue SubscribeImpl(RedisClient client, RedisRequest request)
{ {
var reply = TypedRedisValue.Rent(3 * (request.Count - 1)); var reply = TypedRedisValue.Rent(3 * (request.Count - 1), out var span);
int index = 0; int index = 0;
var mode = request.Command[0] == 'p' ? RedisChannel.PatternMode.Pattern : RedisChannel.PatternMode.Literal; var mode = request.Command[0] == 'p' ? RedisChannel.PatternMode.Pattern : RedisChannel.PatternMode.Literal;
for (int i = 1; i < request.Count; i++) for (int i = 1; i < request.Count; i++)
...@@ -485,9 +489,9 @@ private TypedRedisValue SubscribeImpl(RedisClient client, RedisRequest request) ...@@ -485,9 +489,9 @@ private TypedRedisValue SubscribeImpl(RedisClient client, RedisRequest request)
reply.Recycle(index); reply.Recycle(index);
return TypedRedisValue.Nil; return TypedRedisValue.Nil;
} }
reply[index++] = TypedRedisValue.BulkString(request.Command); span[index++] = TypedRedisValue.BulkString(request.Command);
reply[index++] = TypedRedisValue.BulkString((byte[])channel); span[index++] = TypedRedisValue.BulkString((byte[])channel);
reply[index++] = TypedRedisValue.Integer(count); span[index++] = TypedRedisValue.Integer(count);
} }
return reply; return reply;
} }
...@@ -500,9 +504,9 @@ protected virtual TypedRedisValue Time(RedisClient client, RedisRequest request) ...@@ -500,9 +504,9 @@ protected virtual TypedRedisValue Time(RedisClient client, RedisRequest request)
var ticks = delta.Ticks; var ticks = delta.Ticks;
var seconds = ticks / TimeSpan.TicksPerSecond; var seconds = ticks / TimeSpan.TicksPerSecond;
var micros = (ticks % TimeSpan.TicksPerSecond) / (TimeSpan.TicksPerMillisecond / 1000); var micros = (ticks % TimeSpan.TicksPerSecond) / (TimeSpan.TicksPerMillisecond / 1000);
var reply = TypedRedisValue.Rent(2); var reply = TypedRedisValue.Rent(2, out var span);
reply[0] = TypedRedisValue.BulkString(seconds); span[0] = TypedRedisValue.BulkString(seconds);
reply[1] = TypedRedisValue.BulkString(micros); span[1] = TypedRedisValue.BulkString(micros);
return reply; return reply;
} }
protected virtual DateTime Time() => DateTime.UtcNow; protected virtual DateTime Time() => DateTime.UtcNow;
......
...@@ -344,11 +344,13 @@ void WritePrefix(PipeWriter ooutput, char pprefix) ...@@ -344,11 +344,13 @@ void WritePrefix(PipeWriter ooutput, char pprefix)
} }
else else
{ {
var count = value.Length; var segment = value.Segment;
PhysicalConnection.WriteMultiBulkHeader(output, count); PhysicalConnection.WriteMultiBulkHeader(output, segment.Count);
for (int i = 0; i < count ; i++) var arr = segment.Array;
int offset = segment.Offset;
for (int i = 0; i < segment.Count; i++)
{ {
var item = value[i]; var item = arr[offset++];
if (item.IsNil) if (item.IsNil)
throw new InvalidOperationException("Array element cannot be nil, index " + i); throw new InvalidOperationException("Array element cannot be nil, index " + i);
...@@ -479,33 +481,33 @@ protected static TypedRedisValue CommandNotFound(string command) ...@@ -479,33 +481,33 @@ protected static TypedRedisValue CommandNotFound(string command)
[RedisCommand(1, LockFree = true)] [RedisCommand(1, LockFree = true)]
protected virtual TypedRedisValue Command(RedisClient client, RedisRequest request) protected virtual TypedRedisValue Command(RedisClient client, RedisRequest request)
{ {
var results = TypedRedisValue.Rent(_commands.Count); var results = TypedRedisValue.Rent(_commands.Count, out var span);
int index = 0; int index = 0;
foreach (var pair in _commands) foreach (var pair in _commands)
results[index++] = CommandInfo(pair.Value); span[index++] = CommandInfo(pair.Value);
return results; return results;
} }
[RedisCommand(-2, "command", "info", LockFree = true)] [RedisCommand(-2, "command", "info", LockFree = true)]
protected virtual TypedRedisValue CommandInfo(RedisClient client, RedisRequest request) protected virtual TypedRedisValue CommandInfo(RedisClient client, RedisRequest request)
{ {
var results = TypedRedisValue.Rent(request.Count - 2); var results = TypedRedisValue.Rent(request.Count - 2, out var span);
for (int i = 2; i < request.Count; i++) for (int i = 2; i < request.Count; i++)
{ {
results[i - 2] = _commands.TryGetValue(request.GetString(i), out var cmd) span[i - 2] = _commands.TryGetValue(request.GetString(i), out var cmd)
? CommandInfo(cmd) : TypedRedisValue.NullArray; ? CommandInfo(cmd) : TypedRedisValue.NullArray;
} }
return results; return results;
} }
private TypedRedisValue CommandInfo(RespCommand command) private TypedRedisValue CommandInfo(RespCommand command)
{ {
var arr = TypedRedisValue.Rent(6); var arr = TypedRedisValue.Rent(6, out var span);
arr[0] = TypedRedisValue.BulkString(command.Command); span[0] = TypedRedisValue.BulkString(command.Command);
arr[1] = TypedRedisValue.Integer(command.NetArity()); span[1] = TypedRedisValue.Integer(command.NetArity());
arr[2] = TypedRedisValue.EmptyArray; span[2] = TypedRedisValue.EmptyArray;
arr[3] = TypedRedisValue.Zero; span[3] = TypedRedisValue.Zero;
arr[4] = TypedRedisValue.Zero; span[4] = TypedRedisValue.Zero;
arr[5] = TypedRedisValue.Zero; span[5] = TypedRedisValue.Zero;
return arr; return arr;
} }
} }
......
...@@ -8,8 +8,12 @@ namespace StackExchange.Redis ...@@ -8,8 +8,12 @@ namespace StackExchange.Redis
/// </summary> /// </summary>
public readonly struct TypedRedisValue public readonly struct TypedRedisValue
{ {
internal static TypedRedisValue Rent(int count) internal static TypedRedisValue Rent(int count, out Span<TypedRedisValue> span)
=> new TypedRedisValue(ArrayPool<TypedRedisValue>.Shared.Rent(count), count); {
var arr = ArrayPool<TypedRedisValue>.Shared.Rent(count);
span = new Span<TypedRedisValue>(arr, 0, count);
return new TypedRedisValue(arr, count);
}
/// <summary> /// <summary>
/// An invalid empty value that has no type /// An invalid empty value that has no type
...@@ -23,29 +27,15 @@ internal static TypedRedisValue Rent(int count) ...@@ -23,29 +27,15 @@ internal static TypedRedisValue Rent(int count)
/// <summary> /// <summary>
/// Returns whether this value represents a null array /// Returns whether this value represents a null array
/// </summary> /// </summary>
public bool IsNullArray => Type == ResultType.MultiBulk && _oversizedItems == null; public bool IsNullArray => Type == ResultType.MultiBulk && _value.DirectObject == null;
/// <summary>
/// The type of value being represented
/// </summary>
public ResultType Type { get; }
private readonly RedisValue _value; private readonly RedisValue _value;
private readonly TypedRedisValue[] _oversizedItems;
/// <summary> /// <summary>
/// Gets items from an array by index /// The type of value being represented
/// </summary>
public TypedRedisValue this[int index]
{
get => _oversizedItems[index];
internal set => _oversizedItems[index] = value;
}
/// <summary>
/// Gets the length of the value as an array
/// </summary> /// </summary>
public int Length { get; } public ResultType Type { get; }
/// <summary> /// <summary>
/// Initialize a TypedRedisValue from a value and optionally a type /// Initialize a TypedRedisValue from a value and optionally a type
/// </summary> /// </summary>
...@@ -53,8 +43,6 @@ private TypedRedisValue(RedisValue value, ResultType? type = null) ...@@ -53,8 +43,6 @@ private TypedRedisValue(RedisValue value, ResultType? type = null)
{ {
Type = type ?? (value.IsInteger ? ResultType.Integer : ResultType.BulkString); Type = type ?? (value.IsInteger ? ResultType.Integer : ResultType.BulkString);
_value = value; _value = value;
Length = default;
_oversizedItems = default;
} }
/// <summary> /// <summary>
...@@ -81,8 +69,28 @@ public static TypedRedisValue SimpleString(string value) ...@@ -81,8 +69,28 @@ public static TypedRedisValue SimpleString(string value)
/// <summary> /// <summary>
/// Gets the array elements as a span /// Gets the array elements as a span
/// </summary> /// </summary>
public ReadOnlySpan<TypedRedisValue> Span => new ReadOnlySpan<TypedRedisValue>(_oversizedItems, 0, Length); public ReadOnlySpan<TypedRedisValue> Span
internal Span<TypedRedisValue> MutableSpan => new Span<TypedRedisValue>(_oversizedItems, 0, Length); {
get
{
if (Type != ResultType.MultiBulk) return default;
var arr = (TypedRedisValue[])_value.DirectObject;
if (arr == null) return default;
var length = (int)_value.DirectInt64;
return new ReadOnlySpan<TypedRedisValue>(arr, 0, length);
}
}
public ArraySegment<TypedRedisValue> Segment
{
get
{
if (Type != ResultType.MultiBulk) return default;
var arr = (TypedRedisValue[])_value.DirectObject;
if (arr == null) return default;
var length = (int)_value.DirectInt64;
return new ArraySegment<TypedRedisValue>(arr, 0, length);
}
}
/// <summary> /// <summary>
/// Initialize a TypedRedisValue that represents an integer /// Initialize a TypedRedisValue that represents an integer
...@@ -119,17 +127,15 @@ private TypedRedisValue(TypedRedisValue[] oversizedItems, int count) ...@@ -119,17 +127,15 @@ private TypedRedisValue(TypedRedisValue[] oversizedItems, int count)
if (count < 0 || count > oversizedItems.Length) throw new ArgumentOutOfRangeException(nameof(count)); if (count < 0 || count > oversizedItems.Length) throw new ArgumentOutOfRangeException(nameof(count));
if (count == 0) oversizedItems = Array.Empty<TypedRedisValue>(); if (count == 0) oversizedItems = Array.Empty<TypedRedisValue>();
} }
_value = default; _value = new RedisValue(oversizedItems, count);
Type = ResultType.MultiBulk; Type = ResultType.MultiBulk;
Length = count;
_oversizedItems = oversizedItems;
} }
internal void Recycle(int limit = -1) internal void Recycle(int limit = -1)
{ {
var arr = _oversizedItems; var arr = _value.DirectObject as TypedRedisValue[];
if (arr != null) if (arr != null)
{ {
if (limit < 0) limit = Length; if (limit < 0) limit = (int)_value.DirectInt64;
for (int i = 0; i < limit; i++) for (int i = 0; i < limit; i++)
{ {
arr[i].Recycle(); arr[i].Recycle();
...@@ -141,7 +147,7 @@ internal void Recycle(int limit = -1) ...@@ -141,7 +147,7 @@ internal void Recycle(int limit = -1)
/// <summary> /// <summary>
/// Get the underlying value assuming that it is a valid type with a meaningful value /// Get the underlying value assuming that it is a valid type with a meaningful value
/// </summary> /// </summary>
internal RedisValue AsRedisValue() => _value; internal RedisValue AsRedisValue() => Type == ResultType.MultiBulk ? default :_value;
/// <summary> /// <summary>
/// Obtain the value as a string /// Obtain the value as a string
...@@ -156,7 +162,7 @@ public override string ToString() ...@@ -156,7 +162,7 @@ public override string ToString()
case ResultType.Error: case ResultType.Error:
return $"{Type}:{_value}"; return $"{Type}:{_value}";
case ResultType.MultiBulk: case ResultType.MultiBulk:
return $"{Type}:[{Length}]"; return $"{Type}:[{Span.Length}]";
default: default:
return Type.ToString(); return Type.ToString();
} }
......
...@@ -25,10 +25,20 @@ private RedisValue(long overlappedValue64, ReadOnlyMemory<byte> memory, object o ...@@ -25,10 +25,20 @@ private RedisValue(long overlappedValue64, ReadOnlyMemory<byte> memory, object o
_objectOrSentinel = objectOrSentinel; _objectOrSentinel = objectOrSentinel;
} }
internal RedisValue(object obj, long val)
{ // this creates a bodged RedisValue which should **never**
// be seen directly; the contents are ... unexpected
_overlappedValue64 = val;
_objectOrSentinel = obj;
_memory = default;
}
internal object DirectObject => _objectOrSentinel;
internal long DirectInt64 => _overlappedValue64;
private readonly static object Sentinel_Integer = new object(); private readonly static object Sentinel_Integer = new object();
private readonly static object Sentinel_Raw = new object(); private readonly static object Sentinel_Raw = new object();
private readonly static object Sentinel_Double = new object(); private readonly static object Sentinel_Double = new object();
/// <summary> /// <summary>
/// Obtain this value as an object - to be used alongside Unbox /// Obtain this value as an object - to be used alongside Unbox
/// </summary> /// </summary>
......
...@@ -7,10 +7,15 @@ static class Program ...@@ -7,10 +7,15 @@ static class Program
{ {
static async Task Main() static async Task Main()
{ {
//using (var pool = new DedicatedThreadPoolPipeScheduler(minWorkers: 10, maxWorkers: 10,
// priority: System.Threading.ThreadPriority.Highest))
using (var resp = new MemoryCacheRedisServer(Console.Out)) using (var resp = new MemoryCacheRedisServer(Console.Out))
using (var socket = new RespSocketServer(resp)) using (var socket = new RespSocketServer(resp))
{ {
socket.Listen(new IPEndPoint(IPAddress.Loopback, 6378)); //var options = new PipeOptions(readerScheduler: pool, writerScheduler: pool, useSynchronizationContext: false);
socket.Listen(new IPEndPoint(IPAddress.Loopback, 6378)
//, sendOptions: options, receiveOptions: options
);
await resp.Shutdown; await resp.Shutdown;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment