diff --git a/libs/common/RespLengthEncodingUtils.cs b/libs/common/RespLengthEncodingUtils.cs index a5ab9dea4f..c19cdd696b 100644 --- a/libs/common/RespLengthEncodingUtils.cs +++ b/libs/common/RespLengthEncodingUtils.cs @@ -17,7 +17,7 @@ public static class RespLengthEncodingUtils /// /// /// - public static (long length, byte payloadStart) DecodeLength(ref ReadOnlySpan buff) + public static (long Length, byte PayloadStart) DecodeLength(ref ReadOnlySpan buff) { // remove the value type byte var encoded = buff.Slice(1); diff --git a/libs/server/Resp/KeyAdminCommands.cs b/libs/server/Resp/KeyAdminCommands.cs index 06e2596683..0a947e1590 100644 --- a/libs/server/Resp/KeyAdminCommands.cs +++ b/libs/server/Resp/KeyAdminCommands.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. using System; +using System.Buffers; using System.Diagnostics; using Garnet.common; using Garnet.server.Auth; @@ -43,12 +44,11 @@ bool NetworkRESTORE(ref TGarnetApi storageApi) // return error if key already exists var keyExists = storageApi.EXISTS(key); - switch (keyExists) + if (keyExists is GarnetStatus.OK) { - case GarnetStatus.OK: - while (!RespWriteUtils.WriteDirect(CmdStrings.RESP_ERR_KEY_ALREADY_EXISTS, ref dcurr, dend)) - SendAndReset(); - return true; + while (!RespWriteUtils.WriteDirect(CmdStrings.RESP_ERR_KEY_ALREADY_EXISTS, ref dcurr, dend)) + SendAndReset(); + return true; } var valueSpan = value.ReadOnlySpan; @@ -128,24 +128,27 @@ bool NetworkDUMP(ref TGarnetApi storageApi) var status = storageApi.GET(key, out var value); - switch (status) + if (status is GarnetStatus.NOTFOUND) { - case GarnetStatus.NOTFOUND: - while (!RespWriteUtils.WriteDirect(CmdStrings.RESP_ERRNOTFOUND, ref dcurr, dend)) - SendAndReset(); - return true; + while (!RespWriteUtils.WriteDirect(CmdStrings.RESP_ERRNOTFOUND, ref dcurr, dend)) + SendAndReset(); + return true; } var encodedLength = RespLengthEncodingUtils.EncodeLength(value.ReadOnlySpan.Length); // Len of the dump (payload type + redis encoded payload len + payload len + rdb version + crc64) var len = 1 + encodedLength.Length + value.ReadOnlySpan.Length + 2 + 8; - var lengthInASCIIBytes = new Span(new byte[NumUtils.NumDigitsInLong(len)]); + Span lengthInASCIIBytes = stackalloc byte[NumUtils.NumDigitsInLong(len)]; var lengthInASCIIBytesLen = NumUtils.LongToSpanByte(len, lengthInASCIIBytes); // Total len (% + length of ascii bytes + CR LF + payload type + redis encoded payload len + payload len + rdb version + crc64 + CR LF) var totalLength = 1 + lengthInASCIIBytesLen + 2 + 1 + encodedLength.Length + value.ReadOnlySpan.Length + 2 + 8 + 2; - Span buffer = stackalloc byte[totalLength]; + + var buffer = totalLength <= 128 + ? stackalloc byte[totalLength] + : new byte[totalLength]; + var offset = 0; // Write RESP bulk string prefix and length @@ -159,8 +162,8 @@ bool NetworkDUMP(ref TGarnetApi storageApi) buffer[offset++] = 0x00; // length of the span - foreach (var b in encodedLength) - buffer[offset++] = b; + encodedLength.CopyTo(buffer[offset..]); + offset += encodedLength.Length; // copy value to buffer value.ReadOnlySpan.CopyTo(buffer[offset..]); @@ -182,7 +185,7 @@ bool NetworkDUMP(ref TGarnetApi storageApi) buffer[offset++] = 0x0D; // CR buffer[offset++] = 0x0A; // LF - while (!RespWriteUtils.WriteDirect(buffer, ref dcurr, dend)) + while (!RespWriteUtils.WriteDirect(buffer.Slice(0, totalLength), ref dcurr, dend)) SendAndReset(); return true; diff --git a/test/Garnet.test/Resp/ACL/RespCommandTests.cs b/test/Garnet.test/Resp/ACL/RespCommandTests.cs index c4c2ad3a2c..8dc10d1655 100644 --- a/test/Garnet.test/Resp/ACL/RespCommandTests.cs +++ b/test/Garnet.test/Resp/ACL/RespCommandTests.cs @@ -6439,8 +6439,8 @@ async Task DoRestoreAsync(GarnetClient client) var val = await client.ExecuteForStringResultAsync( "$7\r\nRESTORE\r\n"u8.ToArray(), [ - Encoding.UTF8.GetBytes($"foo-{count}"), - "0"u8.ToArray(), + Encoding.UTF8.GetBytes($"foo-{count}"), + "0"u8.ToArray(), payload ]);