Skip to content

Commit

Permalink
Spanify SortedSet range parsing (#345)
Browse files Browse the repository at this point in the history
  • Loading branch information
PaulusParssinen authored May 2, 2024
1 parent ca30044 commit 6b8df64
Showing 1 changed file with 47 additions and 39 deletions.
86 changes: 47 additions & 39 deletions libs/server/Objects/SortedSet/SortedSetObjectImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,10 @@ private void SortedSetCount(byte* input, int length, byte* output)
return;

//check if parameters are valid
if (!TryParseParameter(minParamByteArray, out var minValue, out var minExclusive) || !TryParseParameter(maxParamByteArray, out var maxValue, out var maxExclusive))
if (!TryParseParameter(minParamByteArray, out var minValue, out var minExclusive) ||
!TryParseParameter(maxParamByteArray, out var maxValue, out var maxExclusive))
{
count = Int32.MaxValue;
count = int.MaxValue;
}
else
{
Expand Down Expand Up @@ -399,7 +400,8 @@ private void SortedSetRange(byte* input, int length, ref SpanByteAndMemory outpu
return;
if (!RespReadUtils.ReadByteArrayWithLengthHeader(out var countLimit, ref input_currptr, input + length))
return;
if (TryParseParameter(offset, out var offsetLimit, out bool _) && TryParseParameter(countLimit, out var countLimitNumber, out bool _))
if (TryParseParameter(offset, out var offsetLimit, out _) &&
TryParseParameter(countLimit, out var countLimitNumber, out _))
{
options.Limit = ((int)offsetLimit, (int)countLimitNumber);
options.ValidLimit = true;
Expand All @@ -418,8 +420,8 @@ private void SortedSetRange(byte* input, int length, ref SpanByteAndMemory outpu

if (count >= 2 && ((!options.ByScore && !options.ByLex) || options.ByScore))
{

if (!TryParseParameter(minParamByteArray, out var minValue, out var minExclusive) | !TryParseParameter(maxParamByteArray, out var maxValue, out var maxExclusive))
if (!TryParseParameter(minParamByteArray, out var minValue, out var minExclusive) |
!TryParseParameter(maxParamByteArray, out var maxValue, out var maxExclusive))
{
while (!RespWriteUtils.WriteError("ERR max or min value is not a float value."u8, ref curr, end))
ObjectUtils.ReallocateOutput(ref output, ref isMemory, ref ptr, ref ptrHandle, ref curr, ref end);
Expand Down Expand Up @@ -667,7 +669,8 @@ private void SortedSetRemoveRangeByScore(byte* input, int length, byte* output)
if (!RespReadUtils.ReadByteArrayWithLengthHeader(out var maxParamByteArray, ref input_currptr, input + length))
return;

if (!TryParseParameter(minParamByteArray, out var minValue, out var minExclusive) || !TryParseParameter(maxParamByteArray, out double maxValue, out bool maxExclusive))
if (!TryParseParameter(minParamByteArray, out var minValue, out var minExclusive) ||
!TryParseParameter(maxParamByteArray, out var maxValue, out var maxExclusive))
{
_output->countDone = int.MaxValue;
}
Expand Down Expand Up @@ -851,25 +854,26 @@ private void GetRank(byte* input, int length, byte* output, bool ascending = tru
var elementsInLex = new List<(double, byte[])>();

// parse boundaries
if (!TryParseLexParameter(minParamByteArray, out var minValue) || !TryParseLexParameter(maxParamByteArray, out var maxValue))
if (!TryParseLexParameter(minParamByteArray, out var minValueChars, out bool minValueExclusive) ||
!TryParseLexParameter(maxParamByteArray, out var maxValueChars, out bool maxValueExclusive))
{
errorCode = int.MaxValue;
return elementsInLex;
}

try
{
var iterator = sortedSet.GetViewBetween((sortedSet.Min.Item1, minValue.chars), sortedSet.Max);
var iterator = sortedSet.GetViewBetween((sortedSet.Min.Item1, minValueChars.ToArray()), sortedSet.Max);

// using ToList method so we avoid the Invalid operation ex. when removing
foreach (var item in iterator.ToList())
{
var inRange = new ReadOnlySpan<byte>(item.Item2).SequenceCompareTo(minValue.chars);
if (inRange < 0 || (inRange == 0 && minValue.exclusive))
var inRange = new ReadOnlySpan<byte>(item.Item2).SequenceCompareTo(minValueChars);
if (inRange < 0 || (inRange == 0 && minValueExclusive))
continue;

var outRange = maxValue.chars == null ? -1 : new ReadOnlySpan<byte>(item.Item2).SequenceCompareTo(maxValue.chars);
if (outRange > 0 || (outRange == 0 && maxValue.exclusive))
var outRange = maxValueChars == default ? -1 : new ReadOnlySpan<byte>(item.Item2).SequenceCompareTo(maxValueChars);
if (outRange > 0 || (outRange == 0 && maxValueExclusive))
break;

if (rem)
Expand Down Expand Up @@ -1042,57 +1046,61 @@ private void PopMinOrMaxCount(byte* input, ref SpanByteAndMemory output, SortedS
/// Helper method to parse parameters min and max
/// in commands including +inf -inf
/// </summary>
/// <param name="val"></param>
/// <param name="valueDouble"></param>
/// <param name="exclusive"></param>
/// <returns></returns>
private static bool TryParseParameter(byte[] val, out double valueDouble, out bool exclusive)
private static bool TryParseParameter(ReadOnlySpan<byte> val, out double valueDouble, out bool exclusive)
{
exclusive = false;

// adjust for exclusion
if (val[0] == '(')
{
val = val.Slice(1);
exclusive = true;
}

if (Utf8Parser.TryParse(val, out valueDouble, out int bytesConsumed, default) &&
bytesConsumed == val.Length)
{
return true;
}

var strVal = Encoding.ASCII.GetString(val);
if (string.Equals("+inf", strVal, StringComparison.OrdinalIgnoreCase))
{
valueDouble = double.PositiveInfinity;
exclusive = false;
return true;
}
else if (string.Equals("-inf", strVal, StringComparison.OrdinalIgnoreCase))
{
valueDouble = double.NegativeInfinity;
exclusive = false;
return true;
}

// adjust for exclusion
if (val[0] == '(')
{
strVal = strVal[1..];
exclusive = true;
}

return double.TryParse(strVal, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out valueDouble);
return false;
}

/// <summary>
/// Helper method to parse parameter when using Lexicographical ranges
/// </summary>
/// <param name="val"></param>
/// <param name="limit"></param>
/// <returns></returns>
private static bool TryParseLexParameter(byte[] val, out (byte[] chars, bool exclusive) limit)
private static bool TryParseLexParameter(ReadOnlySpan<byte> val, out ReadOnlySpan<byte> limitChars, out bool limitExclusive)
{
switch ((char)val[0])
limitChars = default;
limitExclusive = false;

switch (val[0])
{
case '+':
case '-':
limit = (null, false);
case (byte)'+':
case (byte)'-':
return true;
case '[':
limit = (new Span<byte>(val)[1..].ToArray(), false);
case (byte)'[':
limitChars = val.Slice(1);
limitExclusive = false;
return true;
case '(':
limit = (new Span<byte>(val)[1..].ToArray(), true);
case (byte)'(':
limitChars = val.Slice(1);
limitExclusive = true;
return true;
default:
limit = default;
return false;
}
}
Expand Down

0 comments on commit 6b8df64

Please sign in to comment.