diff --git a/libs/server/Objects/SortedSet/SortedSetObjectImpl.cs b/libs/server/Objects/SortedSet/SortedSetObjectImpl.cs index 600ebfec6d..5971d7b095 100644 --- a/libs/server/Objects/SortedSet/SortedSetObjectImpl.cs +++ b/libs/server/Objects/SortedSet/SortedSetObjectImpl.cs @@ -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 { @@ -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; @@ -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); @@ -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; } @@ -851,7 +854,8 @@ 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; @@ -859,17 +863,17 @@ private void GetRank(byte* input, int length, byte* output, bool ascending = tru 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(item.Item2).SequenceCompareTo(minValue.chars); - if (inRange < 0 || (inRange == 0 && minValue.exclusive)) + var inRange = new ReadOnlySpan(item.Item2).SequenceCompareTo(minValueChars); + if (inRange < 0 || (inRange == 0 && minValueExclusive)) continue; - var outRange = maxValue.chars == null ? -1 : new ReadOnlySpan(item.Item2).SequenceCompareTo(maxValue.chars); - if (outRange > 0 || (outRange == 0 && maxValue.exclusive)) + var outRange = maxValueChars == default ? -1 : new ReadOnlySpan(item.Item2).SequenceCompareTo(maxValueChars); + if (outRange > 0 || (outRange == 0 && maxValueExclusive)) break; if (rem) @@ -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 /// - /// - /// - /// - /// - private static bool TryParseParameter(byte[] val, out double valueDouble, out bool exclusive) + private static bool TryParseParameter(ReadOnlySpan 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; } /// /// Helper method to parse parameter when using Lexicographical ranges /// - /// - /// - /// - private static bool TryParseLexParameter(byte[] val, out (byte[] chars, bool exclusive) limit) + private static bool TryParseLexParameter(ReadOnlySpan val, out ReadOnlySpan 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(val)[1..].ToArray(), false); + case (byte)'[': + limitChars = val.Slice(1); + limitExclusive = false; return true; - case '(': - limit = (new Span(val)[1..].ToArray(), true); + case (byte)'(': + limitChars = val.Slice(1); + limitExclusive = true; return true; default: - limit = default; return false; } }