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
if (stop < 0) stop = 0;
else if (stop >= len) stop = len - 1;
var arr = TypedRedisValue.Rent(checked((int)((stop - start) + 1)));
LRange(client.Database, key, start, arr.MutableSpan);
var arr = TypedRedisValue.Rent(checked((int)((stop - start) + 1)), out var span);
LRange(client.Database, key, start, span);
return arr;
}
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
var matches = config.CountMatch(pattern);
if (matches == 0) return TypedRedisValue.EmptyArray;
var arr = TypedRedisValue.Rent(2 * matches);
var arr = TypedRedisValue.Rent(2 * matches, out var span);
int index = 0;
foreach (var pair in config.Wrapped)
{
if (IsMatch(pattern, pair.Key))
{
arr[index++] = TypedRedisValue.BulkString(pair.Key);
arr[index++] = TypedRedisValue.BulkString(pair.Value);
span[index++] = TypedRedisValue.BulkString(pair.Key);
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;
}
......@@ -410,11 +414,11 @@ protected virtual TypedRedisValue MemoryPurge(RedisClient client, RedisRequest r
protected virtual TypedRedisValue Mget(RedisClient client, RedisRequest request)
{
int argCount = request.Count;
var arr = TypedRedisValue.Rent(argCount - 1);
var arr = TypedRedisValue.Rent(argCount - 1, out var span);
var db = client.Database;
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;
}
......@@ -443,10 +447,10 @@ protected virtual TypedRedisValue Quit(RedisClient client, RedisRequest request)
[RedisCommand(1, LockFree = true)]
protected virtual TypedRedisValue Role(RedisClient client, RedisRequest request)
{
var arr = TypedRedisValue.Rent(3);
arr[0] = TypedRedisValue.BulkString("master");
arr[1] = TypedRedisValue.Integer(0);
arr[2] = TypedRedisValue.EmptyArray;
var arr = TypedRedisValue.Rent(3, out var span);
span[0] = TypedRedisValue.BulkString("master");
span[1] = TypedRedisValue.Integer(0);
span[2] = TypedRedisValue.EmptyArray;
return arr;
}
......@@ -470,7 +474,7 @@ protected virtual TypedRedisValue Unsubscribe(RedisClient client, RedisRequest r
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;
var mode = request.Command[0] == 'p' ? RedisChannel.PatternMode.Pattern : RedisChannel.PatternMode.Literal;
for (int i = 1; i < request.Count; i++)
......@@ -485,9 +489,9 @@ private TypedRedisValue SubscribeImpl(RedisClient client, RedisRequest request)
reply.Recycle(index);
return TypedRedisValue.Nil;
}
reply[index++] = TypedRedisValue.BulkString(request.Command);
reply[index++] = TypedRedisValue.BulkString((byte[])channel);
reply[index++] = TypedRedisValue.Integer(count);
span[index++] = TypedRedisValue.BulkString(request.Command);
span[index++] = TypedRedisValue.BulkString((byte[])channel);
span[index++] = TypedRedisValue.Integer(count);
}
return reply;
}
......@@ -500,9 +504,9 @@ protected virtual TypedRedisValue Time(RedisClient client, RedisRequest request)
var ticks = delta.Ticks;
var seconds = ticks / TimeSpan.TicksPerSecond;
var micros = (ticks % TimeSpan.TicksPerSecond) / (TimeSpan.TicksPerMillisecond / 1000);
var reply = TypedRedisValue.Rent(2);
reply[0] = TypedRedisValue.BulkString(seconds);
reply[1] = TypedRedisValue.BulkString(micros);
var reply = TypedRedisValue.Rent(2, out var span);
span[0] = TypedRedisValue.BulkString(seconds);
span[1] = TypedRedisValue.BulkString(micros);
return reply;
}
protected virtual DateTime Time() => DateTime.UtcNow;
......
......@@ -344,11 +344,13 @@ void WritePrefix(PipeWriter ooutput, char pprefix)
}
else
{
var count = value.Length;
PhysicalConnection.WriteMultiBulkHeader(output, count);
for (int i = 0; i < count ; i++)
var segment = value.Segment;
PhysicalConnection.WriteMultiBulkHeader(output, segment.Count);
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)
throw new InvalidOperationException("Array element cannot be nil, index " + i);
......@@ -479,33 +481,33 @@ protected static TypedRedisValue CommandNotFound(string command)
[RedisCommand(1, LockFree = true)]
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;
foreach (var pair in _commands)
results[index++] = CommandInfo(pair.Value);
span[index++] = CommandInfo(pair.Value);
return results;
}
[RedisCommand(-2, "command", "info", LockFree = true)]
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++)
{
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;
}
return results;
}
private TypedRedisValue CommandInfo(RespCommand command)
{
var arr = TypedRedisValue.Rent(6);
arr[0] = TypedRedisValue.BulkString(command.Command);
arr[1] = TypedRedisValue.Integer(command.NetArity());
arr[2] = TypedRedisValue.EmptyArray;
arr[3] = TypedRedisValue.Zero;
arr[4] = TypedRedisValue.Zero;
arr[5] = TypedRedisValue.Zero;
var arr = TypedRedisValue.Rent(6, out var span);
span[0] = TypedRedisValue.BulkString(command.Command);
span[1] = TypedRedisValue.Integer(command.NetArity());
span[2] = TypedRedisValue.EmptyArray;
span[3] = TypedRedisValue.Zero;
span[4] = TypedRedisValue.Zero;
span[5] = TypedRedisValue.Zero;
return arr;
}
}
......
......@@ -8,8 +8,12 @@ namespace StackExchange.Redis
/// </summary>
public readonly struct TypedRedisValue
{
internal static TypedRedisValue Rent(int count)
=> new TypedRedisValue(ArrayPool<TypedRedisValue>.Shared.Rent(count), count);
internal static TypedRedisValue Rent(int count, out Span<TypedRedisValue> span)
{
var arr = ArrayPool<TypedRedisValue>.Shared.Rent(count);
span = new Span<TypedRedisValue>(arr, 0, count);
return new TypedRedisValue(arr, count);
}
/// <summary>
/// An invalid empty value that has no type
......@@ -23,28 +27,14 @@ internal static TypedRedisValue Rent(int count)
/// <summary>
/// Returns whether this value represents a null array
/// </summary>
public bool IsNullArray => Type == ResultType.MultiBulk && _oversizedItems == null;
/// <summary>
/// The type of value being represented
/// </summary>
public ResultType Type { get; }
public bool IsNullArray => Type == ResultType.MultiBulk && _value.DirectObject == null;
private readonly RedisValue _value;
private readonly TypedRedisValue[] _oversizedItems;
/// <summary>
/// Gets items from an array by index
/// </summary>
public TypedRedisValue this[int index]
{
get => _oversizedItems[index];
internal set => _oversizedItems[index] = value;
}
/// <summary>
/// Gets the length of the value as an array
/// The type of value being represented
/// </summary>
public int Length { get; }
public ResultType Type { get; }
/// <summary>
/// Initialize a TypedRedisValue from a value and optionally a type
......@@ -53,8 +43,6 @@ private TypedRedisValue(RedisValue value, ResultType? type = null)
{
Type = type ?? (value.IsInteger ? ResultType.Integer : ResultType.BulkString);
_value = value;
Length = default;
_oversizedItems = default;
}
/// <summary>
......@@ -81,8 +69,28 @@ public static TypedRedisValue SimpleString(string value)
/// <summary>
/// Gets the array elements as a span
/// </summary>
public ReadOnlySpan<TypedRedisValue> Span => new ReadOnlySpan<TypedRedisValue>(_oversizedItems, 0, Length);
internal Span<TypedRedisValue> MutableSpan => new Span<TypedRedisValue>(_oversizedItems, 0, Length);
public ReadOnlySpan<TypedRedisValue> Span
{
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>
/// Initialize a TypedRedisValue that represents an integer
......@@ -119,17 +127,15 @@ private TypedRedisValue(TypedRedisValue[] oversizedItems, int count)
if (count < 0 || count > oversizedItems.Length) throw new ArgumentOutOfRangeException(nameof(count));
if (count == 0) oversizedItems = Array.Empty<TypedRedisValue>();
}
_value = default;
_value = new RedisValue(oversizedItems, count);
Type = ResultType.MultiBulk;
Length = count;
_oversizedItems = oversizedItems;
}
internal void Recycle(int limit = -1)
{
var arr = _oversizedItems;
var arr = _value.DirectObject as TypedRedisValue[];
if (arr != null)
{
if (limit < 0) limit = Length;
if (limit < 0) limit = (int)_value.DirectInt64;
for (int i = 0; i < limit; i++)
{
arr[i].Recycle();
......@@ -141,7 +147,7 @@ internal void Recycle(int limit = -1)
/// <summary>
/// Get the underlying value assuming that it is a valid type with a meaningful value
/// </summary>
internal RedisValue AsRedisValue() => _value;
internal RedisValue AsRedisValue() => Type == ResultType.MultiBulk ? default :_value;
/// <summary>
/// Obtain the value as a string
......@@ -156,7 +162,7 @@ public override string ToString()
case ResultType.Error:
return $"{Type}:{_value}";
case ResultType.MultiBulk:
return $"{Type}:[{Length}]";
return $"{Type}:[{Span.Length}]";
default:
return Type.ToString();
}
......
......@@ -25,6 +25,16 @@ private RedisValue(long overlappedValue64, ReadOnlyMemory<byte> memory, object o
_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_Raw = new object();
private readonly static object Sentinel_Double = new object();
......
......@@ -7,10 +7,15 @@ static class Program
{
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 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;
}
}
......
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