From 68d6d79663864c256d045ef0df5dd6b6ec65222f Mon Sep 17 00:00:00 2001 From: slorello89 <42971704+slorello89@users.noreply.github.com> Date: Mon, 18 Apr 2022 15:43:34 -0400 Subject: [PATCH 01/14] WIP --- .../APITypes/BitfieldCommand.cs | 20 ++ .../APITypes/BitfieldOverflowHandling.cs | 33 +++ .../APITypes/BitfieldSubCommand.cs | 279 ++++++++++++++++++ .../APITypes/Signedness.cs | 28 ++ src/StackExchange.Redis/Enums/RedisCommand.cs | 2 + .../Interfaces/IDatabase.cs | 50 ++++ .../Interfaces/IDatabaseAsync.cs | 48 +++ src/StackExchange.Redis/Message.cs | 1 + .../PublicAPI.Unshipped.txt | 36 ++- src/StackExchange.Redis/RedisDatabase.cs | 56 ++++ src/StackExchange.Redis/RedisLiterals.cs | 7 + 11 files changed, 559 insertions(+), 1 deletion(-) create mode 100644 src/StackExchange.Redis/APITypes/BitfieldCommand.cs create mode 100644 src/StackExchange.Redis/APITypes/BitfieldOverflowHandling.cs create mode 100644 src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs create mode 100644 src/StackExchange.Redis/APITypes/Signedness.cs diff --git a/src/StackExchange.Redis/APITypes/BitfieldCommand.cs b/src/StackExchange.Redis/APITypes/BitfieldCommand.cs new file mode 100644 index 000000000..41755b08a --- /dev/null +++ b/src/StackExchange.Redis/APITypes/BitfieldCommand.cs @@ -0,0 +1,20 @@ +namespace StackExchange.Redis; + +/// +/// Possible commands to use on a bitfield. +/// +public enum BitfieldCommand +{ + /// + /// retrieves the specified integer from the bitfield. + /// + GET, + /// + /// Set's the bitfield. + /// + SET, + /// + /// Increments the bitfield. + /// + INCRBY +} diff --git a/src/StackExchange.Redis/APITypes/BitfieldOverflowHandling.cs b/src/StackExchange.Redis/APITypes/BitfieldOverflowHandling.cs new file mode 100644 index 000000000..061b0e025 --- /dev/null +++ b/src/StackExchange.Redis/APITypes/BitfieldOverflowHandling.cs @@ -0,0 +1,33 @@ +using System; + +namespace StackExchange.Redis; + +/// +/// Defines the overflow behavior of a BITFIELD command. +/// +public enum BitfieldOverflowHandling +{ + /// + /// Wraps around to the most negative value of signed integers, or zero for unsigned integers + /// + Wrap, + /// + /// Uses saturation arithmetic, stopping at the highest possible value for overflows, and the lowest possible value for underflows. + /// + Saturate, + /// + /// If an overflow is encountered, associated subcommand fails, and the result will be NULL. + /// + Fail +} + +internal static class BitfieldOverflowHandlingExtensions +{ + internal static RedisValue AsRedisValue(this BitfieldOverflowHandling handling) => handling switch + { + BitfieldOverflowHandling.Fail => RedisLiterals.FAIL, + BitfieldOverflowHandling.Saturate => RedisLiterals.SAT, + BitfieldOverflowHandling.Wrap => RedisLiterals.WRAP, + _ => throw new ArgumentOutOfRangeException(nameof(handling)) + }; +} diff --git a/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs b/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs new file mode 100644 index 000000000..559f8638c --- /dev/null +++ b/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs @@ -0,0 +1,279 @@ +using System; +using System.Collections.Generic; + +namespace StackExchange.Redis; + +/// +/// A subcommand for a bitfield. +/// +public abstract class BitfieldSubCommand +{ + internal abstract int NumArgs { get; } + internal abstract void AddArgs(IList args); + internal virtual bool IsReadonly => false; + /// + /// The encoding of the sub-command. This might be a signed or unsigned integer. + /// + public BitfieldEncoding Encoding { get; } + /// + /// The offset into the bitfield the subcommand will traverse. + /// + public BitfieldOffset Offset { get; } + + internal BitfieldSubCommand(BitfieldEncoding encoding, BitfieldOffset offset) + { + Encoding = encoding; + Offset = offset; + } + + internal BitfieldSubCommand(string encoding, string offset) + { + Encoding = BitfieldEncoding.Parse(encoding); + Offset = BitfieldOffset.Parse(offset); + } + +} + +/// +/// Represents a Bitfield GET, which returns the specified bitfield. +/// +public sealed class BitfieldGet : BitfieldSubCommand +{ + /// + /// Initializes a bitfield get subcommand + /// + /// the encoding of the subcommand. + /// The offset into the bitfield of the subcommand + public BitfieldGet(BitfieldEncoding encoding, BitfieldOffset offset) : base(encoding, offset) + { + } + + /// + /// Initializes a bitfield get subcommand + /// + /// the encoding of the subcommand. + /// The offset into the bitfield of the subcommand + public BitfieldGet(string encoding, string offset) : base(encoding, offset) + { + } + + internal override bool IsReadonly => true; + + internal override int NumArgs => 3; + + internal override void AddArgs(IList args) + { + args.Add(RedisLiterals.GET); + args.Add(Encoding.AsRedisValue); + args.Add(Offset.AsRedisValue); + } +} + +/// +/// Bitfield sub-command which set's the specified range of bits to the specified value. +/// +public sealed class BitfieldSet : BitfieldSubCommand +{ + /// + /// The value to set. + /// + public long Value { get; } + + /// + /// Initializes a sub-command for a Bitfield Set. + /// + /// The number's encoding. + /// The offset into the bitfield to set. + /// The value to set. + public BitfieldSet(BitfieldEncoding encoding, BitfieldOffset offset, long value) : base(encoding, offset) + { + Value = value; + } + + /// + /// Initializes a sub-command for a Bitfield Set. + /// + /// The number's encoding. + /// The offset into the bitfield to set. + /// The value to set. + public BitfieldSet(string encoding, string offset, long value) : base(encoding, offset) + { + Value = value; + } + + internal override int NumArgs => 4; + + internal override void AddArgs(IList args) + { + args.Add(RedisLiterals.SET); + args.Add(Encoding.AsRedisValue); + args.Add(Offset.AsRedisValue); + args.Add(Value); + } +} + +/// +/// Bitfield sub-command which increments the number at the specified range of bits by the provided value +/// +public sealed class BitfieldIncrby : BitfieldSubCommand +{ + /// + /// The value to set. + /// + public long Increment { get; } + + public BitfieldOverflowHandling OverflowHandling { get; } + + /// + /// Initializes a sub-command for a Bitfield Set. + /// + /// The number's encoding. + /// The offset into the bitfield to set. + /// The value to set. + /// How overflows will be handled when incrementing. + public BitfieldIncrby(BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap) : base(encoding, offset) + { + Increment = increment; + OverflowHandling = overflowHandling; + } + + /// + /// Initializes a sub-command for a Bitfield Set. + /// + /// The number's encoding. + /// The offset into the bitfield to set. + /// The value to set. + public BitfieldIncrby(string encoding, string offset, long increment) : base(encoding, offset) + { + Increment = increment; + } + + internal override int NumArgs => 4; + + internal override void AddArgs(IList args) + { + if (OverflowHandling != BitfieldOverflowHandling.Wrap) + { + args.Add(RedisLiterals.OVERFLOW); + args.Add(OverflowHandling.AsRedisValue()); + } + args.Add(RedisLiterals.INCRBY); + args.Add(Encoding.AsRedisValue); + args.Add(Offset.AsRedisValue); + args.Add(Increment); + } +} + + + +/// +/// An offset into a bitfield. This is either a literal offset (number of bits from the beginning of the bitfield) or an +/// encoding based offset, based off the encoding of the sub-command. +/// +public readonly struct BitfieldOffset +{ + /// + /// Returns the BitfieldOffset as a RedisValue + /// + internal RedisValue AsRedisValue => $"{(ByEncoding ? "#" : string.Empty)}{Offset}"; + + /// + /// Whether or not the BitfieldOffset will work off of the sub-commands integer encoding. + /// + public bool ByEncoding { get; } + + /// + /// The number of either bits or encoded integers to offset into the bitfield. + /// + public long Offset { get; } + + /// + /// Initializes a bitfield offset + /// + /// Whether or not the BitfieldOffset will work off of the sub-commands integer encoding. + /// The number of either bits or encoded integers to offset into the bitfield. + public BitfieldOffset(bool byEncoding, long offset) + { + ByEncoding = byEncoding; + Offset = offset; + } + + internal static BitfieldOffset Parse(string str) + { + if (str.IsNullOrEmpty()) + { + throw new ArgumentException($"Cannot parse {nameof(BitfieldOffset)} from an empty or null string.", nameof(str)); + } + + long offset; + + if (str[0] == '#') + { + if (long.TryParse(str.Substring(1), out offset)) + { + return new BitfieldOffset(true, offset); + } + } + else + { + if (long.TryParse(str, out offset)) + { + return new BitfieldOffset(false, offset); + } + } + + throw new ArgumentException($"{str} could not be parsed into a {nameof(BitfieldOffset)}.", nameof(str)); + } +} + +/// +/// The encoding that a sub-command should use. This is either a signed or unsigned integer. +/// +public readonly struct BitfieldEncoding +{ + internal RedisValue AsRedisValue => $"{Signedness.SignChar()}{Size}"; + /// + /// The signedness of the integer. + /// + public Signedness Signedness { get; } + /// + /// The size of the integer. + /// + public byte Size { get; } + + /// + /// Initializes the BitfieldEncoding. + /// + /// The encoding's + /// The size of the integer. + public BitfieldEncoding(Signedness signedness, byte size) + { + Signedness = signedness; + Size = size; + } + + internal static BitfieldEncoding Parse(string str) + { + if (str.IsNullOrEmpty()) + { + throw new ArgumentException($"Cannot parse {nameof(BitfieldEncoding)} from an empty or null String", nameof(str)); + } + + if (!byte.TryParse(str.Substring(1), out byte size)) + { + throw new ArgumentException($"Could not parse {nameof(BitfieldEncoding)} from {str}", nameof(str)); + } + + if (char.ToLowerInvariant('i') == char.ToLowerInvariant(str[0])) + { + return new BitfieldEncoding(Signedness.Signed, size); + } + + if (char.ToLowerInvariant('u') == char.ToLowerInvariant(str[0])) + { + return new BitfieldEncoding(Signedness.Unsigned, size); + } + + throw new ArgumentException($"Could not parse {nameof(BitfieldEncoding)} from {str}", nameof(str)); + } +} diff --git a/src/StackExchange.Redis/APITypes/Signedness.cs b/src/StackExchange.Redis/APITypes/Signedness.cs new file mode 100644 index 000000000..d81a4a16b --- /dev/null +++ b/src/StackExchange.Redis/APITypes/Signedness.cs @@ -0,0 +1,28 @@ +using System; + +namespace StackExchange.Redis; + +/// +/// Represents an integers signedness +/// +public enum Signedness +{ + /// + /// An integer with no sign bit. + /// + Unsigned, + /// + /// An integer with a sign bit. + /// + Signed +} + +internal static class SignednessExtensions +{ + internal static char SignChar(this Signedness sign) => sign switch + { + Signedness.Signed => 'i', + Signedness.Unsigned => 'u', + _ => throw new ArgumentOutOfRangeException(nameof(sign)) + }; +} diff --git a/src/StackExchange.Redis/Enums/RedisCommand.cs b/src/StackExchange.Redis/Enums/RedisCommand.cs index f7ceb8a1b..e3022f826 100644 --- a/src/StackExchange.Redis/Enums/RedisCommand.cs +++ b/src/StackExchange.Redis/Enums/RedisCommand.cs @@ -11,6 +11,8 @@ internal enum RedisCommand BGREWRITEAOF, BGSAVE, BITCOUNT, + BITFIELD, + BITFIELD_RO, BITOP, BITPOS, BLPOP, diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 4c42fa498..183f774d3 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2361,6 +2361,56 @@ IEnumerable SortedSetScan(RedisKey key, /// https://redis.io/commands/bitcount long StringBitCount(RedisKey key, long start = 0, long end = -1, CommandFlags flags = CommandFlags.None); + /// + /// Pulls a single number out of a bitfield. Will execute a BITFIELD_RO if possible. + /// + /// The key for the string. + /// The encoding of the number. + /// The offset into the bitfield to pull the number from. + /// The Commands for the operation. + /// The number of the given at the provided . + /// https://redis.io/commands/bitfield + /// https://redis.io/commands/bitfield_ro + long StringBitfieldGet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None); + + /// + /// Sets a single number number in a bitfield at the provided to the provided, in the given . + /// + /// The key for the string. + /// The encoding of the number. + /// The offset into the bitfield to pull the number from. + /// the value to set the bitfield to. + /// The Commands for the operation. + /// The previous value as am at the provided . + /// https://redis.io/commands/bitfield + long StringBitfieldSet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None); + + + /// + /// increments a single number number in a bitfield at the provided to the . + /// + /// The key for the string. + /// The encoding of the number. + /// The offset into the bitfield to pull the number from. + /// the value to increment the bitfield by. + /// The way integer overflows are handled. + /// The Commands for the operation. + /// The new value of the given at the provided after the incrby is applied, represented as an . Returns if the operation fails. + /// https://redis.io/commands/bitfield + long? StringBitfieldIncrement(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None); + + + /// + /// Executes a set of Bitfield against the bitfield at the provided . Will run as a BITFIELD_RO if all operations are read-only and the command is available. + /// + /// The key of the string. + /// The subcommands to execute against the bitfield. + /// The flags for this operation. + /// An array of numbers corresponding to the result of each sub-command. For increment subcommands, these can be null. + /// https://redis.io/commands/bitfield + /// https://redis.io/commands/bitfield_ro + long?[] StringBitfield(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None); + /// /// Perform a bitwise operation between multiple keys (containing string values) and store the result in the destination key. /// The BITOP command supports four bitwise operations; note that NOT is a unary operator: the second key should be omitted in this case diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index a54681ce5..cb777b8c7 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -2315,6 +2315,54 @@ IAsyncEnumerable SortedSetScanAsync(RedisKey key, /// https://redis.io/commands/bitcount Task StringBitCountAsync(RedisKey key, long start = 0, long end = -1, CommandFlags flags = CommandFlags.None); + /// + /// Pulls a single number out of a bitfield. Will execute a BITFIELD_RO if possible. + /// + /// The key for the string. + /// The encoding of the number. + /// The offset into the bitfield to pull the number from. + /// The Commands for the operation. + /// The number of the given at the provided . + /// https://redis.io/commands/bitfield + /// https://redis.io/commands/bitfield_ro + Task StringBitfieldGetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None); + + /// + /// Sets a single number number in a bitfield at the provided to the provided, in the given . + /// + /// The key for the string. + /// The encoding of the number. + /// The offset into the bitfield to pull the number from. + /// the value to set the bitfield to. + /// The Commands for the operation. + /// The previous value as am at the provided . + /// https://redis.io/commands/bitfield + Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None); + + /// + /// increments a single number number in a bitfield at the provided to the . + /// + /// The key for the string. + /// The encoding of the number. + /// The offset into the bitfield to pull the number from. + /// the value to increment the bitfield by. + /// The way integer overflows are handled. + /// The Commands for the operation. + /// The new value of the given at the provided after the incrby is applied, represented as an . Returns if the operation fails. + /// https://redis.io/commands/bitfield + Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None); + + /// + /// Executes a set of Bitfield against the bitfield at the provided . Will run as a BITFIELD_RO if all operations are read-only and the command is available. + /// + /// The key of the string. + /// The subcommands to execute against the bitfield. + /// The flags for this operation. + /// An array of numbers corresponding to the result of each sub-command. For increment subcommands, these can be null. + /// https://redis.io/commands/bitfield + /// https://redis.io/commands/bitfield_ro + Task StringBitfieldAsync(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None); + /// /// Perform a bitwise operation between multiple keys (containing string values) and store the result in the destination key. /// The BITOP command supports four bitwise operations; note that NOT is a unary operator: the second key should be omitted in this case diff --git a/src/StackExchange.Redis/Message.cs b/src/StackExchange.Redis/Message.cs index 7efd5fe6c..2de10474f 100644 --- a/src/StackExchange.Redis/Message.cs +++ b/src/StackExchange.Redis/Message.cs @@ -330,6 +330,7 @@ public static bool IsPrimaryOnly(RedisCommand command) { case RedisCommand.APPEND: case RedisCommand.BITOP: + case RedisCommand.BITFIELD: case RedisCommand.BLPOP: case RedisCommand.BRPOP: case RedisCommand.BRPOPLPUSH: diff --git a/src/StackExchange.Redis/PublicAPI.Unshipped.txt b/src/StackExchange.Redis/PublicAPI.Unshipped.txt index 5f282702b..981a4c756 100644 --- a/src/StackExchange.Redis/PublicAPI.Unshipped.txt +++ b/src/StackExchange.Redis/PublicAPI.Unshipped.txt @@ -1 +1,35 @@ - \ No newline at end of file +StackExchange.Redis.BitfieldEncoding +StackExchange.Redis.BitfieldEncoding.BitfieldEncoding() -> void +StackExchange.Redis.BitfieldEncoding.BitfieldEncoding(StackExchange.Redis.Signedness signedness, byte size) -> void +StackExchange.Redis.BitfieldEncoding.Signedness.get -> StackExchange.Redis.Signedness +StackExchange.Redis.BitfieldEncoding.Size.get -> byte +StackExchange.Redis.BitfieldGet +StackExchange.Redis.BitfieldGet.BitfieldGet(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset) -> void +StackExchange.Redis.BitfieldGet.BitfieldGet(string! encoding, string! offset) -> void +StackExchange.Redis.BitfieldIncrby +StackExchange.Redis.BitfieldIncrby.BitfieldIncrby(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment) -> void +StackExchange.Redis.BitfieldIncrby.BitfieldIncrby(string! encoding, string! offset, long increment) -> void +StackExchange.Redis.BitfieldIncrby.Increment.get -> long +StackExchange.Redis.BitfieldOffset +StackExchange.Redis.BitfieldOffset.BitfieldOffset() -> void +StackExchange.Redis.BitfieldOffset.BitfieldOffset(bool byEncoding, long offset) -> void +StackExchange.Redis.BitfieldOffset.ByEncoding.get -> bool +StackExchange.Redis.BitfieldOffset.Offset.get -> long +StackExchange.Redis.BitfieldSet +StackExchange.Redis.BitfieldSet.BitfieldSet(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value) -> void +StackExchange.Redis.BitfieldSet.BitfieldSet(string! encoding, string! offset, long value) -> void +StackExchange.Redis.BitfieldSet.Value.get -> long +StackExchange.Redis.BitfieldSubCommand +StackExchange.Redis.BitfieldSubCommand.Encoding.get -> StackExchange.Redis.BitfieldEncoding +StackExchange.Redis.BitfieldSubCommand.Offset.get -> StackExchange.Redis.BitfieldOffset +StackExchange.Redis.IDatabase.StringBitfield(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldSubCommand![]! subcommands, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long?[]! +StackExchange.Redis.IDatabase.StringBitfieldGet(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabase.StringBitfieldIncrement(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long? +StackExchange.Redis.IDatabase.StringBitfieldSet(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabaseAsync.StringBitfieldAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldSubCommand![]! subcommands, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StringBitfieldGetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StringBitfieldIncrementAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StringBitfieldSetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.Signedness +StackExchange.Redis.Signedness.Signed = 1 -> StackExchange.Redis.Signedness +StackExchange.Redis.Signedness.Unsigned = 0 -> StackExchange.Redis.Signedness \ No newline at end of file diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index 500fcc963..9a76b0854 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -1,6 +1,7 @@ using System; using System.Buffers; using System.Collections.Generic; +using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; @@ -2721,12 +2722,40 @@ public long StringBitCount(RedisKey key, long start = 0, long end = -1, CommandF return ExecuteSync(msg, ResultProcessor.Int64); } + public long StringBitfieldGet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, + CommandFlags flags = CommandFlags.None) => + throw new NotImplementedException(); + + public long StringBitfieldSet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, + CommandFlags flags = CommandFlags.None) => + throw new NotImplementedException(); + + public long? StringBitfieldIncrement(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, + BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) => + throw new NotImplementedException(); + + public long?[] StringBitfield(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None) => throw new NotImplementedException(); + public Task StringBitCountAsync(RedisKey key, long start = 0, long end = -1, CommandFlags flags = CommandFlags.None) { var msg = Message.Create(Database, flags, RedisCommand.BITCOUNT, key, start, end); return ExecuteAsync(msg, ResultProcessor.Int64); } + public Task StringBitfieldGetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, + CommandFlags flags = CommandFlags.None) => + throw new NotImplementedException(); + + public Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, + CommandFlags flags = CommandFlags.None) => + throw new NotImplementedException(); + + public Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, + BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) => + throw new NotImplementedException(); + + public Task StringBitfieldAsync(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None) => throw new NotImplementedException(); + public long StringBitOperation(Bitwise operation, RedisKey destination, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None) { var msg = GetStringBitOperationMessage(operation, destination, first, second, flags); @@ -3057,6 +3086,33 @@ public Task StringSetRangeAsync(RedisKey key, long offset, RedisValu _ => throw new ArgumentException("Expiry time must be either Utc or Local", nameof(when)), }; + private Message GetBitfieldMessage(in RedisKey key, BitfieldSubCommand subcommand, CommandFlags flags) + { + var args = new List(subcommand.NumArgs); + subcommand.AddArgs(args); + RedisCommand command = RedisCommand.BITFIELD; + if (subcommand is BitfieldGet && multiplexer.GetServer(multiplexer.EndPoints[0]).Version >= RedisFeatures.v6_2_0) + { + command = RedisCommand.BITFIELD_RO; + } + + return Message.Create(Database, flags, command, key, args.ToArray()); + } + + private Message GetBitfieldMessage(in RedisKey key, BitfieldSubCommand[] subCommands, CommandFlags flags) + { + var args = new List(subCommands.Sum(sc => sc.NumArgs)); + var canBeReadonly = subCommands.All(sc => sc.IsReadonly); + var command = canBeReadonly && multiplexer.GetServer(multiplexer.EndPoints[0]).Version >= RedisFeatures.v6_2_0 ? RedisCommand.BITFIELD_RO : RedisCommand.BITFIELD; + + foreach (var subcommand in subCommands) + { + subcommand.AddArgs(args); + } + + return Message.Create(Database, flags, command, key, args.ToArray()); + } + private Message GetCopyMessage(in RedisKey sourceKey, RedisKey destinationKey, int destinationDatabase, bool replace, CommandFlags flags) => destinationDatabase switch { diff --git a/src/StackExchange.Redis/RedisLiterals.cs b/src/StackExchange.Redis/RedisLiterals.cs index 3b405ae8b..3dfa62e3c 100644 --- a/src/StackExchange.Redis/RedisLiterals.cs +++ b/src/StackExchange.Redis/RedisLiterals.cs @@ -69,6 +69,7 @@ public static readonly RedisValue HISTORY = "HISTORY", ID = "ID", IDLETIME = "IDLETIME", + INCRBY = "INCRBY", KEEPTTL = "KEEPTTL", KILL = "KILL", LATEST = "LATEST", @@ -90,6 +91,7 @@ public static readonly RedisValue NX = "NX", OBJECT = "OBJECT", OR = "OR", + OVERFLOW = "OVERFLOW", PAUSE = "PAUSE", PERSIST = "PERSIST", PING = "PING", @@ -161,6 +163,11 @@ public static readonly RedisValue m = "m", mi = "mi", + //Bitfield literals + FAIL = "FAIL", + SAT = "SAT", + WRAP = "WRAP", + // misc (config, etc) databases = "databases", master = "master", From 1695639f351079ed804219a9eb5dd8c22a329dc8 Mon Sep 17 00:00:00 2001 From: slorello89 <42971704+slorello89@users.noreply.github.com> Date: Tue, 19 Apr 2022 09:51:47 -0400 Subject: [PATCH 02/14] Bitfield feature --- .../APITypes/BitfieldCommand.cs | 20 --- .../APITypes/BitfieldSubCommand.cs | 3 + .../KeyspaceIsolation/DatabaseWrapper.cs | 12 ++ .../KeyspaceIsolation/WrapperBase.cs | 13 ++ src/StackExchange.Redis/PublicAPI.Shipped.txt | 40 ++++++ .../PublicAPI.Unshipped.txt | 36 +---- src/StackExchange.Redis/RawResult.cs | 3 + src/StackExchange.Redis/RedisDatabase.cs | 60 ++++++--- src/StackExchange.Redis/ResultProcessor.cs | 41 ++++++ tests/StackExchange.Redis.Tests/Bitfield.cs | 123 ++++++++++++++++++ 10 files changed, 276 insertions(+), 75 deletions(-) delete mode 100644 src/StackExchange.Redis/APITypes/BitfieldCommand.cs create mode 100644 tests/StackExchange.Redis.Tests/Bitfield.cs diff --git a/src/StackExchange.Redis/APITypes/BitfieldCommand.cs b/src/StackExchange.Redis/APITypes/BitfieldCommand.cs deleted file mode 100644 index 41755b08a..000000000 --- a/src/StackExchange.Redis/APITypes/BitfieldCommand.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace StackExchange.Redis; - -/// -/// Possible commands to use on a bitfield. -/// -public enum BitfieldCommand -{ - /// - /// retrieves the specified integer from the bitfield. - /// - GET, - /// - /// Set's the bitfield. - /// - SET, - /// - /// Increments the bitfield. - /// - INCRBY -} diff --git a/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs b/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs index 559f8638c..f31b5d96a 100644 --- a/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs +++ b/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs @@ -122,6 +122,9 @@ public sealed class BitfieldIncrby : BitfieldSubCommand /// public long Increment { get; } + /// + /// Determines how overflows are handled for the bitfield. + /// public BitfieldOverflowHandling OverflowHandling { get; } /// diff --git a/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs b/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs index 7def559b2..ac0fec907 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs @@ -568,6 +568,18 @@ public long StringAppend(RedisKey key, RedisValue value, CommandFlags flags = Co public long StringBitCount(RedisKey key, long start = 0, long end = -1, CommandFlags flags = CommandFlags.None) => Inner.StringBitCount(ToInner(key), start, end, flags); + public long StringBitfieldGet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldGet(ToInner(key), encoding, offset, flags); + + public long StringBitfieldSet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldSet(ToInner(key), encoding, offset, value, flags); + + public long? StringBitfieldIncrement(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldIncrement(ToInner(key), encoding, offset, increment, overflowHandling, flags); + + public long?[] StringBitfield(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfield(key, subcommands, flags); + public long StringBitOperation(Bitwise operation, RedisKey destination, RedisKey[] keys, CommandFlags flags = CommandFlags.None) => Inner.StringBitOperation(operation, ToInner(destination), ToInner(keys), flags); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs b/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs index 5a1437e33..bea205502 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs @@ -585,6 +585,19 @@ public Task StringAppendAsync(RedisKey key, RedisValue value, CommandFlags public Task StringBitCountAsync(RedisKey key, long start = 0, long end = -1, CommandFlags flags = CommandFlags.None) => Inner.StringBitCountAsync(ToInner(key), start, end, flags); + public Task StringBitfieldGetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, + CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldGetAsync(ToInner(key), encoding, offset, flags); + + public Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldSetAsync(ToInner(key), encoding, offset, value, flags); + + public Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldIncrementAsync(ToInner(key), encoding, offset, increment, overflowHandling, flags); + + public Task StringBitfieldAsync(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldAsync(ToInner(key), subcommands, flags); + public Task StringBitOperationAsync(Bitwise operation, RedisKey destination, RedisKey[] keys, CommandFlags flags = CommandFlags.None) => Inner.StringBitOperationAsync(operation, ToInner(destination), ToInner(keys), flags); diff --git a/src/StackExchange.Redis/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI.Shipped.txt index 7b4dfb2df..8739d2d16 100644 --- a/src/StackExchange.Redis/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI.Shipped.txt @@ -66,6 +66,35 @@ StackExchange.Redis.BacklogPolicy.AbortPendingOnConnectionFailure.init -> void StackExchange.Redis.BacklogPolicy.BacklogPolicy() -> void StackExchange.Redis.BacklogPolicy.QueueWhileDisconnected.get -> bool StackExchange.Redis.BacklogPolicy.QueueWhileDisconnected.init -> void +StackExchange.Redis.BitfieldEncoding +StackExchange.Redis.BitfieldEncoding.BitfieldEncoding() -> void +StackExchange.Redis.BitfieldEncoding.BitfieldEncoding(StackExchange.Redis.Signedness signedness, byte size) -> void +StackExchange.Redis.BitfieldEncoding.Signedness.get -> StackExchange.Redis.Signedness +StackExchange.Redis.BitfieldEncoding.Size.get -> byte +StackExchange.Redis.BitfieldGet +StackExchange.Redis.BitfieldGet.BitfieldGet(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset) -> void +StackExchange.Redis.BitfieldGet.BitfieldGet(string! encoding, string! offset) -> void +StackExchange.Redis.BitfieldIncrby +StackExchange.Redis.BitfieldIncrby.BitfieldIncrby(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap) -> void +StackExchange.Redis.BitfieldIncrby.BitfieldIncrby(string! encoding, string! offset, long increment) -> void +StackExchange.Redis.BitfieldIncrby.Increment.get -> long +StackExchange.Redis.BitfieldIncrby.OverflowHandling.get -> StackExchange.Redis.BitfieldOverflowHandling +StackExchange.Redis.BitfieldOffset +StackExchange.Redis.BitfieldOffset.BitfieldOffset() -> void +StackExchange.Redis.BitfieldOffset.BitfieldOffset(bool byEncoding, long offset) -> void +StackExchange.Redis.BitfieldOffset.ByEncoding.get -> bool +StackExchange.Redis.BitfieldOffset.Offset.get -> long +StackExchange.Redis.BitfieldOverflowHandling +StackExchange.Redis.BitfieldOverflowHandling.Fail = 2 -> StackExchange.Redis.BitfieldOverflowHandling +StackExchange.Redis.BitfieldOverflowHandling.Saturate = 1 -> StackExchange.Redis.BitfieldOverflowHandling +StackExchange.Redis.BitfieldOverflowHandling.Wrap = 0 -> StackExchange.Redis.BitfieldOverflowHandling +StackExchange.Redis.BitfieldSet +StackExchange.Redis.BitfieldSet.BitfieldSet(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value) -> void +StackExchange.Redis.BitfieldSet.BitfieldSet(string! encoding, string! offset, long value) -> void +StackExchange.Redis.BitfieldSet.Value.get -> long +StackExchange.Redis.BitfieldSubCommand +StackExchange.Redis.BitfieldSubCommand.Encoding.get -> StackExchange.Redis.BitfieldEncoding +StackExchange.Redis.BitfieldSubCommand.Offset.get -> StackExchange.Redis.BitfieldOffset StackExchange.Redis.Bitwise StackExchange.Redis.Bitwise.And = 0 -> StackExchange.Redis.Bitwise StackExchange.Redis.Bitwise.Not = 3 -> StackExchange.Redis.Bitwise @@ -681,6 +710,10 @@ StackExchange.Redis.IDatabase.StringBitCount(StackExchange.Redis.RedisKey key, l StackExchange.Redis.IDatabase.StringBitOperation(StackExchange.Redis.Bitwise operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey first, StackExchange.Redis.RedisKey second = default(StackExchange.Redis.RedisKey), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringBitOperation(StackExchange.Redis.Bitwise operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey[]! keys, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringBitPosition(StackExchange.Redis.RedisKey key, bool bit, long start = 0, long end = -1, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabase.StringBitfield(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldSubCommand![]! subcommands, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long?[]! +StackExchange.Redis.IDatabase.StringBitfieldGet(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabase.StringBitfieldIncrement(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long? +StackExchange.Redis.IDatabase.StringBitfieldSet(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringDecrement(StackExchange.Redis.RedisKey key, double value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> double StackExchange.Redis.IDatabase.StringDecrement(StackExchange.Redis.RedisKey key, long value = 1, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringGet(StackExchange.Redis.RedisKey key, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> StackExchange.Redis.RedisValue @@ -889,6 +922,10 @@ StackExchange.Redis.IDatabaseAsync.StreamReadGroupAsync(StackExchange.Redis.Stre StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringAppendAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitCountAsync(StackExchange.Redis.RedisKey key, long start = 0, long end = -1, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StringBitfieldAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldSubCommand![]! subcommands, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StringBitfieldGetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StringBitfieldIncrementAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StringBitfieldSetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitOperationAsync(StackExchange.Redis.Bitwise operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey first, StackExchange.Redis.RedisKey second = default(StackExchange.Redis.RedisKey), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitOperationAsync(StackExchange.Redis.Bitwise operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey[]! keys, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitPositionAsync(StackExchange.Redis.RedisKey key, bool bit, long start = 0, long end = -1, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! @@ -1354,6 +1391,9 @@ StackExchange.Redis.ShutdownMode StackExchange.Redis.ShutdownMode.Always = 2 -> StackExchange.Redis.ShutdownMode StackExchange.Redis.ShutdownMode.Default = 0 -> StackExchange.Redis.ShutdownMode StackExchange.Redis.ShutdownMode.Never = 1 -> StackExchange.Redis.ShutdownMode +StackExchange.Redis.Signedness +StackExchange.Redis.Signedness.Signed = 1 -> StackExchange.Redis.Signedness +StackExchange.Redis.Signedness.Unsigned = 0 -> StackExchange.Redis.Signedness StackExchange.Redis.SlotRange StackExchange.Redis.SlotRange.CompareTo(StackExchange.Redis.SlotRange other) -> int StackExchange.Redis.SlotRange.Equals(StackExchange.Redis.SlotRange other) -> bool diff --git a/src/StackExchange.Redis/PublicAPI.Unshipped.txt b/src/StackExchange.Redis/PublicAPI.Unshipped.txt index 981a4c756..5f282702b 100644 --- a/src/StackExchange.Redis/PublicAPI.Unshipped.txt +++ b/src/StackExchange.Redis/PublicAPI.Unshipped.txt @@ -1,35 +1 @@ -StackExchange.Redis.BitfieldEncoding -StackExchange.Redis.BitfieldEncoding.BitfieldEncoding() -> void -StackExchange.Redis.BitfieldEncoding.BitfieldEncoding(StackExchange.Redis.Signedness signedness, byte size) -> void -StackExchange.Redis.BitfieldEncoding.Signedness.get -> StackExchange.Redis.Signedness -StackExchange.Redis.BitfieldEncoding.Size.get -> byte -StackExchange.Redis.BitfieldGet -StackExchange.Redis.BitfieldGet.BitfieldGet(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset) -> void -StackExchange.Redis.BitfieldGet.BitfieldGet(string! encoding, string! offset) -> void -StackExchange.Redis.BitfieldIncrby -StackExchange.Redis.BitfieldIncrby.BitfieldIncrby(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment) -> void -StackExchange.Redis.BitfieldIncrby.BitfieldIncrby(string! encoding, string! offset, long increment) -> void -StackExchange.Redis.BitfieldIncrby.Increment.get -> long -StackExchange.Redis.BitfieldOffset -StackExchange.Redis.BitfieldOffset.BitfieldOffset() -> void -StackExchange.Redis.BitfieldOffset.BitfieldOffset(bool byEncoding, long offset) -> void -StackExchange.Redis.BitfieldOffset.ByEncoding.get -> bool -StackExchange.Redis.BitfieldOffset.Offset.get -> long -StackExchange.Redis.BitfieldSet -StackExchange.Redis.BitfieldSet.BitfieldSet(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value) -> void -StackExchange.Redis.BitfieldSet.BitfieldSet(string! encoding, string! offset, long value) -> void -StackExchange.Redis.BitfieldSet.Value.get -> long -StackExchange.Redis.BitfieldSubCommand -StackExchange.Redis.BitfieldSubCommand.Encoding.get -> StackExchange.Redis.BitfieldEncoding -StackExchange.Redis.BitfieldSubCommand.Offset.get -> StackExchange.Redis.BitfieldOffset -StackExchange.Redis.IDatabase.StringBitfield(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldSubCommand![]! subcommands, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long?[]! -StackExchange.Redis.IDatabase.StringBitfieldGet(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long -StackExchange.Redis.IDatabase.StringBitfieldIncrement(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long? -StackExchange.Redis.IDatabase.StringBitfieldSet(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long -StackExchange.Redis.IDatabaseAsync.StringBitfieldAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldSubCommand![]! subcommands, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StringBitfieldGetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StringBitfieldIncrementAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StringBitfieldSetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.Signedness -StackExchange.Redis.Signedness.Signed = 1 -> StackExchange.Redis.Signedness -StackExchange.Redis.Signedness.Unsigned = 0 -> StackExchange.Redis.Signedness \ No newline at end of file + \ No newline at end of file diff --git a/src/StackExchange.Redis/RawResult.cs b/src/StackExchange.Redis/RawResult.cs index 919054411..592eccf48 100644 --- a/src/StackExchange.Redis/RawResult.cs +++ b/src/StackExchange.Redis/RawResult.cs @@ -256,6 +256,9 @@ internal bool GetBoolean() [MethodImpl(MethodImplOptions.AggressiveInlining)] internal double?[]? GetItemsAsDoubles() => this.ToArray((in RawResult x) => x.TryGetDouble(out double val) ? val : null); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal long?[]? GetItemsAsInt64s() => this.ToArray((in RawResult x) => x.TryGetInt64(out long val) ? val : null); + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal RedisKey[]? GetItemsAsKeys() => this.ToArray((in RawResult x) => x.AsRedisKey()); diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index 9a76b0854..fe7601cfc 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -2722,19 +2722,29 @@ public long StringBitCount(RedisKey key, long start = 0, long end = -1, CommandF return ExecuteSync(msg, ResultProcessor.Int64); } - public long StringBitfieldGet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, - CommandFlags flags = CommandFlags.None) => - throw new NotImplementedException(); + public long StringBitfieldGet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None) + { + var msg = GetBitfieldMessage(key, new BitfieldGet(encoding, offset), flags); + return ExecuteSync(msg, ResultProcessor.Int64); + } - public long StringBitfieldSet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, - CommandFlags flags = CommandFlags.None) => - throw new NotImplementedException(); + public long StringBitfieldSet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None) + { + var msg = GetBitfieldMessage(key, new BitfieldSet(encoding, offset, value), flags); + return ExecuteSync(msg, ResultProcessor.Int64); + } - public long? StringBitfieldIncrement(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, - BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) => - throw new NotImplementedException(); + public long? StringBitfieldIncrement(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) + { + var msg = GetBitfieldMessage(key, new BitfieldIncrby(encoding, offset, increment, overflowHandling), flags); + return ExecuteSync(msg, ResultProcessor.NullableInt64); + } - public long?[] StringBitfield(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None) => throw new NotImplementedException(); + public long?[] StringBitfield(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None) + { + var msg = GetBitfieldMessage(key, subcommands, flags); + return ExecuteSync(msg, ResultProcessor.NullableInt64Array, defaultValue: Array.Empty()); + } public Task StringBitCountAsync(RedisKey key, long start = 0, long end = -1, CommandFlags flags = CommandFlags.None) { @@ -2742,19 +2752,29 @@ public Task StringBitCountAsync(RedisKey key, long start = 0, long end = - return ExecuteAsync(msg, ResultProcessor.Int64); } - public Task StringBitfieldGetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, - CommandFlags flags = CommandFlags.None) => - throw new NotImplementedException(); + public Task StringBitfieldGetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None) + { + var msg = GetBitfieldMessage(key, new BitfieldGet(encoding, offset), flags); + return ExecuteAsync(msg, ResultProcessor.Int64); + } - public Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, - CommandFlags flags = CommandFlags.None) => - throw new NotImplementedException(); + public Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None) + { + var msg = GetBitfieldMessage(key, new BitfieldSet(encoding, offset, value), flags); + return ExecuteAsync(msg, ResultProcessor.Int64); + } - public Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, - BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) => - throw new NotImplementedException(); + public Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) + { + var msg = GetBitfieldMessage(key, new BitfieldIncrby(encoding, offset, increment, overflowHandling), flags); + return ExecuteAsync(msg, ResultProcessor.NullableInt64); + } - public Task StringBitfieldAsync(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None) => throw new NotImplementedException(); + public Task StringBitfieldAsync(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None) + { + var msg = GetBitfieldMessage(key, subcommands, flags); + return ExecuteAsync(msg, ResultProcessor.NullableInt64Array, defaultValue: Array.Empty()); + } public long StringBitOperation(Bitwise operation, RedisKey destination, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None) { diff --git a/src/StackExchange.Redis/ResultProcessor.cs b/src/StackExchange.Redis/ResultProcessor.cs index fedaa1982..219574a5d 100644 --- a/src/StackExchange.Redis/ResultProcessor.cs +++ b/src/StackExchange.Redis/ResultProcessor.cs @@ -64,6 +64,9 @@ public static readonly ResultProcessor public static readonly ResultProcessor NullableDoubleArray = new NullableDoubleArrayProcessor(); + public static readonly ResultProcessor + NullableInt64Array = new NullableInt64ArrayProcessor(); + public static readonly ResultProcessor NullableInt64 = new NullableInt64Processor(); @@ -1125,6 +1128,14 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes SetResult(message, i64); return true; } + break; + case ResultType.MultiBulk: + if (result.GetItems()[0].TryGetInt64(out i64)) + { + SetResult(message, i64); + return true; + } + break; } return false; @@ -1148,6 +1159,21 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes } } + + private sealed class NullableInt64ArrayProcessor : ResultProcessor + { + protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result) + { + if (result.Type == ResultType.MultiBulk && !result.IsNull) + { + var arr = result.GetItemsAsInt64s()!; + SetResult(message, arr); + return true; + } + return false; + } + } + private sealed class NullableDoubleArrayProcessor : ResultProcessor { protected override bool SetResultCore(PhysicalConnection connection, Message message, in RawResult result) @@ -1208,6 +1234,21 @@ protected override bool SetResultCore(PhysicalConnection connection, Message mes SetResult(message, i64); return true; } + break; + case ResultType.MultiBulk: + var item = result.GetItems()[0]; + if (item.IsNull) + { + SetResult(message, null); + return true; + } + + if (item.TryGetInt64(out i64)) + { + SetResult(message, i64); + return true; + } + break; } return false; diff --git a/tests/StackExchange.Redis.Tests/Bitfield.cs b/tests/StackExchange.Redis.Tests/Bitfield.cs new file mode 100644 index 000000000..0f52a191c --- /dev/null +++ b/tests/StackExchange.Redis.Tests/Bitfield.cs @@ -0,0 +1,123 @@ +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace StackExchange.Redis.Tests; + +[Collection(SharedConnectionFixture.Key)] +public class Bitfield : TestBase +{ + public Bitfield(ITestOutputHelper output, SharedConnectionFixture fixture) : base(output, fixture) { } + + [Fact] + public void TestBitfieldHappyPath() + { + + using var conn = Create(require: RedisFeatures.v3_2_0); + var db = conn.GetDatabase(); + RedisKey key = Me(); + db.KeyDelete(key); + + var encoding = new BitfieldEncoding(Signedness.Signed, 10); + var offset = new BitfieldOffset(true, 1); + + // should be the old value + var setResult = db.StringBitfieldSet(key, encoding, offset, -255); + var getResult = db.StringBitfieldGet(key, encoding, offset); + var incrementResult = db.StringBitfieldIncrement(key, encoding, offset, -10); + Assert.Equal(0, setResult); + Assert.Equal(-255, getResult); + Assert.Equal(-265, incrementResult); + + encoding = new BitfieldEncoding(Signedness.Unsigned, 18); + offset = new BitfieldOffset(false, 22); + + setResult = db.StringBitfieldSet(key, encoding, offset, 262123); + getResult = db.StringBitfieldGet(key, encoding, offset); + incrementResult = db.StringBitfieldIncrement(key, encoding, offset, 20); + + Assert.Equal(0, setResult); + Assert.Equal(262123, getResult); + Assert.Equal(262143, incrementResult); + } + + [Fact] + public async Task TestBitfieldHappyPathAsync() + { + + using var conn = Create(require: RedisFeatures.v3_2_0); + var db = conn.GetDatabase(); + RedisKey key = Me(); + db.KeyDelete(key); + + var encoding = new BitfieldEncoding(Signedness.Signed, 10); + var offset = new BitfieldOffset(true, 1); + + // should be the old value + var setResult = await db.StringBitfieldSetAsync(key, encoding, offset, -255); + var getResult = await db.StringBitfieldGetAsync(key, encoding, offset); + var incrementResult = await db.StringBitfieldIncrementAsync(key, encoding, offset, -10); + Assert.Equal(0, setResult); + Assert.Equal(-255, getResult); + Assert.Equal(-265, incrementResult); + + encoding = new BitfieldEncoding(Signedness.Unsigned, 18); + offset = new BitfieldOffset(false, 22); + + setResult = await db.StringBitfieldSetAsync(key, encoding, offset, 262123); + getResult = await db.StringBitfieldGetAsync(key, encoding, offset); + incrementResult = await db.StringBitfieldIncrementAsync(key, encoding, offset, 20); + + Assert.Equal(0, setResult); + Assert.Equal(262123, getResult); + Assert.Equal(262143, incrementResult); + } + + [Fact] + public async Task TestBitfieldMulti() + { + using var conn = Create(require: RedisFeatures.v3_2_0); + var db = conn.GetDatabase(); + RedisKey key = Me(); + db.KeyDelete(key); + + var set1 = new BitfieldSet(new BitfieldEncoding(Signedness.Unsigned, 3), new BitfieldOffset(false, 5), 7); + var get1 = new BitfieldGet(new BitfieldEncoding(Signedness.Unsigned, 3), new BitfieldOffset(false, 5)); + var incr1 = new BitfieldIncrby(new BitfieldEncoding(Signedness.Unsigned, 3), new BitfieldOffset(false, 5), -1); + + var set2 = new BitfieldSet(new BitfieldEncoding(Signedness.Signed, 45), new BitfieldOffset(true, 1), 17592186044415); + var get2 = new BitfieldGet(new BitfieldEncoding(Signedness.Signed, 45), new BitfieldOffset(true, 1)); + var incr2 = new BitfieldIncrby(new BitfieldEncoding(Signedness.Signed, 45), new BitfieldOffset(true, 1), 1, BitfieldOverflowHandling.Fail); + + var subcommands = new BitfieldSubCommand[] {set1, get1, incr1, set2, get2, incr2}; + var res = await db.StringBitfieldAsync(key, subcommands); + + Assert.Equal(0, res[0]); + Assert.Equal(7, res[1]); + Assert.Equal(6, res[2]); + Assert.Equal(0, res[3]); + Assert.Equal(17592186044415, res[4]); + Assert.Null(res[5]); + } + + [Fact] + public async Task TestOverflows() + { + using var conn = Create(require: RedisFeatures.v3_2_0); + var db = conn.GetDatabase(); + RedisKey key = Me(); + db.KeyDelete(key); + + var encoding = new BitfieldEncoding(Signedness.Signed, 3); + var offset = new BitfieldOffset(true, 3); + + await db.StringBitfieldSetAsync(key, encoding, offset, 3); + var incrFail = await db.StringBitfieldIncrementAsync(key, encoding, offset, 1, BitfieldOverflowHandling.Fail); + Assert.Null(incrFail); + var incrWrap = await db.StringBitfieldIncrementAsync(key, encoding, offset, 1); + Assert.Equal(-4, incrWrap); + await db.StringBitfieldSetAsync(key, encoding, offset, 3); + var incrSat = await db.StringBitfieldIncrementAsync(key, encoding, offset, 1, BitfieldOverflowHandling.Saturate); + Assert.Equal(3, incrSat); + } +} From 566071322c272a2e279214238fabe89ab7c46433 Mon Sep 17 00:00:00 2001 From: slorello89 <42971704+slorello89@users.noreply.github.com> Date: Tue, 19 Apr 2022 09:54:37 -0400 Subject: [PATCH 03/14] accurate reporting of num args for incr --- src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs b/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs index f31b5d96a..45dbe6993 100644 --- a/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs +++ b/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs @@ -151,7 +151,7 @@ public BitfieldIncrby(string encoding, string offset, long increment) : base(enc Increment = increment; } - internal override int NumArgs => 4; + internal override int NumArgs => OverflowHandling == BitfieldOverflowHandling.Wrap ? 4 : 6; internal override void AddArgs(IList args) { From bdb9128ed505d8b690354a3170ff0a2540f2f652 Mon Sep 17 00:00:00 2001 From: slorello89 <42971704+slorello89@users.noreply.github.com> Date: Tue, 19 Apr 2022 09:57:38 -0400 Subject: [PATCH 04/14] removing unused parsing methods --- .../APITypes/BitfieldSubCommand.cs | 89 ------------------- src/StackExchange.Redis/PublicAPI.Shipped.txt | 3 - 2 files changed, 92 deletions(-) diff --git a/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs b/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs index 45dbe6993..029be1f2c 100644 --- a/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs +++ b/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs @@ -26,12 +26,6 @@ internal BitfieldSubCommand(BitfieldEncoding encoding, BitfieldOffset offset) Offset = offset; } - internal BitfieldSubCommand(string encoding, string offset) - { - Encoding = BitfieldEncoding.Parse(encoding); - Offset = BitfieldOffset.Parse(offset); - } - } /// @@ -48,15 +42,6 @@ public BitfieldGet(BitfieldEncoding encoding, BitfieldOffset offset) : base(enco { } - /// - /// Initializes a bitfield get subcommand - /// - /// the encoding of the subcommand. - /// The offset into the bitfield of the subcommand - public BitfieldGet(string encoding, string offset) : base(encoding, offset) - { - } - internal override bool IsReadonly => true; internal override int NumArgs => 3; @@ -90,17 +75,6 @@ public BitfieldSet(BitfieldEncoding encoding, BitfieldOffset offset, long value) Value = value; } - /// - /// Initializes a sub-command for a Bitfield Set. - /// - /// The number's encoding. - /// The offset into the bitfield to set. - /// The value to set. - public BitfieldSet(string encoding, string offset, long value) : base(encoding, offset) - { - Value = value; - } - internal override int NumArgs => 4; internal override void AddArgs(IList args) @@ -140,17 +114,6 @@ public BitfieldIncrby(BitfieldEncoding encoding, BitfieldOffset offset, long inc OverflowHandling = overflowHandling; } - /// - /// Initializes a sub-command for a Bitfield Set. - /// - /// The number's encoding. - /// The offset into the bitfield to set. - /// The value to set. - public BitfieldIncrby(string encoding, string offset, long increment) : base(encoding, offset) - { - Increment = increment; - } - internal override int NumArgs => OverflowHandling == BitfieldOverflowHandling.Wrap ? 4 : 6; internal override void AddArgs(IList args) @@ -200,33 +163,6 @@ public BitfieldOffset(bool byEncoding, long offset) ByEncoding = byEncoding; Offset = offset; } - - internal static BitfieldOffset Parse(string str) - { - if (str.IsNullOrEmpty()) - { - throw new ArgumentException($"Cannot parse {nameof(BitfieldOffset)} from an empty or null string.", nameof(str)); - } - - long offset; - - if (str[0] == '#') - { - if (long.TryParse(str.Substring(1), out offset)) - { - return new BitfieldOffset(true, offset); - } - } - else - { - if (long.TryParse(str, out offset)) - { - return new BitfieldOffset(false, offset); - } - } - - throw new ArgumentException($"{str} could not be parsed into a {nameof(BitfieldOffset)}.", nameof(str)); - } } /// @@ -254,29 +190,4 @@ public BitfieldEncoding(Signedness signedness, byte size) Signedness = signedness; Size = size; } - - internal static BitfieldEncoding Parse(string str) - { - if (str.IsNullOrEmpty()) - { - throw new ArgumentException($"Cannot parse {nameof(BitfieldEncoding)} from an empty or null String", nameof(str)); - } - - if (!byte.TryParse(str.Substring(1), out byte size)) - { - throw new ArgumentException($"Could not parse {nameof(BitfieldEncoding)} from {str}", nameof(str)); - } - - if (char.ToLowerInvariant('i') == char.ToLowerInvariant(str[0])) - { - return new BitfieldEncoding(Signedness.Signed, size); - } - - if (char.ToLowerInvariant('u') == char.ToLowerInvariant(str[0])) - { - return new BitfieldEncoding(Signedness.Unsigned, size); - } - - throw new ArgumentException($"Could not parse {nameof(BitfieldEncoding)} from {str}", nameof(str)); - } } diff --git a/src/StackExchange.Redis/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI.Shipped.txt index 8739d2d16..fc8e8b321 100644 --- a/src/StackExchange.Redis/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI.Shipped.txt @@ -73,10 +73,8 @@ StackExchange.Redis.BitfieldEncoding.Signedness.get -> StackExchange.Redis.Signe StackExchange.Redis.BitfieldEncoding.Size.get -> byte StackExchange.Redis.BitfieldGet StackExchange.Redis.BitfieldGet.BitfieldGet(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset) -> void -StackExchange.Redis.BitfieldGet.BitfieldGet(string! encoding, string! offset) -> void StackExchange.Redis.BitfieldIncrby StackExchange.Redis.BitfieldIncrby.BitfieldIncrby(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap) -> void -StackExchange.Redis.BitfieldIncrby.BitfieldIncrby(string! encoding, string! offset, long increment) -> void StackExchange.Redis.BitfieldIncrby.Increment.get -> long StackExchange.Redis.BitfieldIncrby.OverflowHandling.get -> StackExchange.Redis.BitfieldOverflowHandling StackExchange.Redis.BitfieldOffset @@ -90,7 +88,6 @@ StackExchange.Redis.BitfieldOverflowHandling.Saturate = 1 -> StackExchange.Redis StackExchange.Redis.BitfieldOverflowHandling.Wrap = 0 -> StackExchange.Redis.BitfieldOverflowHandling StackExchange.Redis.BitfieldSet StackExchange.Redis.BitfieldSet.BitfieldSet(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value) -> void -StackExchange.Redis.BitfieldSet.BitfieldSet(string! encoding, string! offset, long value) -> void StackExchange.Redis.BitfieldSet.Value.get -> long StackExchange.Redis.BitfieldSubCommand StackExchange.Redis.BitfieldSubCommand.Encoding.get -> StackExchange.Redis.BitfieldEncoding From 7554d6dc9ed66b65dcbb8a023878ee8885fdcc4c Mon Sep 17 00:00:00 2001 From: slorello89 <42971704+slorello89@users.noreply.github.com> Date: Tue, 19 Apr 2022 10:26:07 -0400 Subject: [PATCH 05/14] some formatting comment updates --- .../APITypes/BitfieldSubCommand.cs | 28 +++++++++++-------- .../Interfaces/IDatabase.cs | 4 +-- .../Interfaces/IDatabaseAsync.cs | 4 +-- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs b/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs index 029be1f2c..055c91dea 100644 --- a/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs +++ b/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs @@ -4,17 +4,21 @@ namespace StackExchange.Redis; /// -/// A subcommand for a bitfield. +/// An abstract subcommand for a bitfield. /// public abstract class BitfieldSubCommand { internal abstract int NumArgs { get; } + internal abstract void AddArgs(IList args); + internal virtual bool IsReadonly => false; + /// - /// The encoding of the sub-command. This might be a signed or unsigned integer. + /// The encoding of the sub-command. A signed or unsigned integer of a given size. /// public BitfieldEncoding Encoding { get; } + /// /// The offset into the bitfield the subcommand will traverse. /// @@ -29,14 +33,14 @@ internal BitfieldSubCommand(BitfieldEncoding encoding, BitfieldOffset offset) } /// -/// Represents a Bitfield GET, which returns the specified bitfield. +/// Represents a Bitfield GET, which returns the number stored in the specified offset of a bitfield at the given encoding. /// public sealed class BitfieldGet : BitfieldSubCommand { /// - /// Initializes a bitfield get subcommand + /// Initializes a bitfield GET subcommand /// - /// the encoding of the subcommand. + /// The encoding of the subcommand. /// The offset into the bitfield of the subcommand public BitfieldGet(BitfieldEncoding encoding, BitfieldOffset offset) : base(encoding, offset) { @@ -55,7 +59,7 @@ internal override void AddArgs(IList args) } /// -/// Bitfield sub-command which set's the specified range of bits to the specified value. +/// Bitfield subcommand which SETs the specified range of bits to the specified value. /// public sealed class BitfieldSet : BitfieldSubCommand { @@ -65,7 +69,7 @@ public sealed class BitfieldSet : BitfieldSubCommand public long Value { get; } /// - /// Initializes a sub-command for a Bitfield Set. + /// Initializes a subcommand for a Bitfield SET. /// /// The number's encoding. /// The offset into the bitfield to set. @@ -87,12 +91,12 @@ internal override void AddArgs(IList args) } /// -/// Bitfield sub-command which increments the number at the specified range of bits by the provided value +/// Bitfield subcommand INCRBY, which increments the number at the specified range of bits by the provided value /// public sealed class BitfieldIncrby : BitfieldSubCommand { /// - /// The value to set. + /// The value to increment by. /// public long Increment { get; } @@ -102,7 +106,7 @@ public sealed class BitfieldIncrby : BitfieldSubCommand public BitfieldOverflowHandling OverflowHandling { get; } /// - /// Initializes a sub-command for a Bitfield Set. + /// Initializes a sub-command for a Bitfield INCRBY. /// /// The number's encoding. /// The offset into the bitfield to set. @@ -166,15 +170,17 @@ public BitfieldOffset(bool byEncoding, long offset) } /// -/// The encoding that a sub-command should use. This is either a signed or unsigned integer. +/// The encoding that a sub-command should use. This is either a signed or unsigned integer of a specified length. /// public readonly struct BitfieldEncoding { internal RedisValue AsRedisValue => $"{Signedness.SignChar()}{Size}"; + /// /// The signedness of the integer. /// public Signedness Signedness { get; } + /// /// The size of the integer. /// diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 183f774d3..b342e55bb 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2362,7 +2362,7 @@ IEnumerable SortedSetScan(RedisKey key, long StringBitCount(RedisKey key, long start = 0, long end = -1, CommandFlags flags = CommandFlags.None); /// - /// Pulls a single number out of a bitfield. Will execute a BITFIELD_RO if possible. + /// Pulls a single number out of a bitfield of the provided at the given offset. Will execute a BITFIELD_RO if possible. /// /// The key for the string. /// The encoding of the number. @@ -2387,7 +2387,7 @@ IEnumerable SortedSetScan(RedisKey key, /// - /// increments a single number number in a bitfield at the provided to the . + /// increments a single number number in a bitfield at the provided in the provided by the given . /// /// The key for the string. /// The encoding of the number. diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index cb777b8c7..d1fe47ef5 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -2316,7 +2316,7 @@ IAsyncEnumerable SortedSetScanAsync(RedisKey key, Task StringBitCountAsync(RedisKey key, long start = 0, long end = -1, CommandFlags flags = CommandFlags.None); /// - /// Pulls a single number out of a bitfield. Will execute a BITFIELD_RO if possible. + /// Pulls a single number out of a bitfield of the provided at the given offset. Will execute a BITFIELD_RO if possible. /// /// The key for the string. /// The encoding of the number. @@ -2340,7 +2340,7 @@ IAsyncEnumerable SortedSetScanAsync(RedisKey key, Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None); /// - /// increments a single number number in a bitfield at the provided to the . + /// Increments a single number number in a bitfield at the provided as if it were in the given by the given . /// /// The key for the string. /// The encoding of the number. From c0fe65de7268499c49bca06309eda62fb9521319 Mon Sep 17 00:00:00 2001 From: slorello89 <42971704+slorello89@users.noreply.github.com> Date: Tue, 19 Apr 2022 12:38:27 -0400 Subject: [PATCH 06/14] moving enums --- .../{APITypes => Enums}/BitfieldOverflowHandling.cs | 0 src/StackExchange.Redis/{APITypes => Enums}/Signedness.cs | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/StackExchange.Redis/{APITypes => Enums}/BitfieldOverflowHandling.cs (100%) rename src/StackExchange.Redis/{APITypes => Enums}/Signedness.cs (100%) diff --git a/src/StackExchange.Redis/APITypes/BitfieldOverflowHandling.cs b/src/StackExchange.Redis/Enums/BitfieldOverflowHandling.cs similarity index 100% rename from src/StackExchange.Redis/APITypes/BitfieldOverflowHandling.cs rename to src/StackExchange.Redis/Enums/BitfieldOverflowHandling.cs diff --git a/src/StackExchange.Redis/APITypes/Signedness.cs b/src/StackExchange.Redis/Enums/Signedness.cs similarity index 100% rename from src/StackExchange.Redis/APITypes/Signedness.cs rename to src/StackExchange.Redis/Enums/Signedness.cs From 3928113934dbc024d4e686532b318a62b9dbef80 Mon Sep 17 00:00:00 2001 From: slorello89 <42971704+slorello89@users.noreply.github.com> Date: Tue, 19 Apr 2022 12:44:42 -0400 Subject: [PATCH 07/14] using new remarks pattern nick introduced --- src/StackExchange.Redis/Interfaces/IDatabase.cs | 12 ++++++------ src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 1262821e6..1ee79ccf4 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2500,8 +2500,8 @@ IEnumerable SortedSetScan(RedisKey key, /// The offset into the bitfield to pull the number from. /// The Commands for the operation. /// The number of the given at the provided . - /// https://redis.io/commands/bitfield - /// https://redis.io/commands/bitfield_ro + /// + /// long StringBitfieldGet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None); /// @@ -2513,7 +2513,7 @@ IEnumerable SortedSetScan(RedisKey key, /// the value to set the bitfield to. /// The Commands for the operation. /// The previous value as am at the provided . - /// https://redis.io/commands/bitfield + /// long StringBitfieldSet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None); @@ -2527,7 +2527,7 @@ IEnumerable SortedSetScan(RedisKey key, /// The way integer overflows are handled. /// The Commands for the operation. /// The new value of the given at the provided after the incrby is applied, represented as an . Returns if the operation fails. - /// https://redis.io/commands/bitfield + /// long? StringBitfieldIncrement(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None); @@ -2538,8 +2538,8 @@ IEnumerable SortedSetScan(RedisKey key, /// The subcommands to execute against the bitfield. /// The flags for this operation. /// An array of numbers corresponding to the result of each sub-command. For increment subcommands, these can be null. - /// https://redis.io/commands/bitfield - /// https://redis.io/commands/bitfield_ro + /// + /// long?[] StringBitfield(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None); /// diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index 8cf8f9ee7..945c6ee18 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -2453,8 +2453,8 @@ IAsyncEnumerable SortedSetScanAsync(RedisKey key, /// The offset into the bitfield to pull the number from. /// The Commands for the operation. /// The number of the given at the provided . - /// https://redis.io/commands/bitfield - /// https://redis.io/commands/bitfield_ro + /// + /// Task StringBitfieldGetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None); /// @@ -2466,7 +2466,7 @@ IAsyncEnumerable SortedSetScanAsync(RedisKey key, /// the value to set the bitfield to. /// The Commands for the operation. /// The previous value as am at the provided . - /// https://redis.io/commands/bitfield + /// Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None); /// @@ -2479,7 +2479,7 @@ IAsyncEnumerable SortedSetScanAsync(RedisKey key, /// The way integer overflows are handled. /// The Commands for the operation. /// The new value of the given at the provided after the incrby is applied, represented as an . Returns if the operation fails. - /// https://redis.io/commands/bitfield + /// Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None); /// @@ -2489,8 +2489,8 @@ IAsyncEnumerable SortedSetScanAsync(RedisKey key, /// The subcommands to execute against the bitfield. /// The flags for this operation. /// An array of numbers corresponding to the result of each sub-command. For increment subcommands, these can be null. - /// https://redis.io/commands/bitfield - /// https://redis.io/commands/bitfield_ro + /// + /// Task StringBitfieldAsync(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None); /// From b3c16c4cc04bae7672824b4043f24643cecc00d8 Mon Sep 17 00:00:00 2001 From: slorello89 <42971704+slorello89@users.noreply.github.com> Date: Tue, 19 Apr 2022 16:45:51 -0400 Subject: [PATCH 08/14] properly checking feature flag --- src/StackExchange.Redis/RedisDatabase.cs | 40 +++++++++++++----------- src/StackExchange.Redis/RedisFeatures.cs | 5 +++ 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index f9a30d416..3552f1f10 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -2748,26 +2748,26 @@ public long StringBitCount(RedisKey key, long start = 0, long end = -1, CommandF public long StringBitfieldGet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, new BitfieldGet(encoding, offset), flags); - return ExecuteSync(msg, ResultProcessor.Int64); + var msg = GetBitfieldMessage(key, new BitfieldGet(encoding, offset), flags, out ServerEndPoint? server); + return ExecuteSync(msg, ResultProcessor.Int64, server); } public long StringBitfieldSet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, new BitfieldSet(encoding, offset, value), flags); - return ExecuteSync(msg, ResultProcessor.Int64); + var msg = GetBitfieldMessage(key, new BitfieldSet(encoding, offset, value), flags, out ServerEndPoint? server); + return ExecuteSync(msg, ResultProcessor.Int64, server); } public long? StringBitfieldIncrement(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, new BitfieldIncrby(encoding, offset, increment, overflowHandling), flags); - return ExecuteSync(msg, ResultProcessor.NullableInt64); + var msg = GetBitfieldMessage(key, new BitfieldIncrby(encoding, offset, increment, overflowHandling), flags, out ServerEndPoint? server); + return ExecuteSync(msg, ResultProcessor.NullableInt64, server); } public long?[] StringBitfield(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, subcommands, flags); - return ExecuteSync(msg, ResultProcessor.NullableInt64Array, defaultValue: Array.Empty()); + var msg = GetBitfieldMessage(key, subcommands, flags, out ServerEndPoint? server); + return ExecuteSync(msg, ResultProcessor.NullableInt64Array, defaultValue: Array.Empty(), server: server); } public Task StringBitCountAsync(RedisKey key, long start = 0, long end = -1, CommandFlags flags = CommandFlags.None) @@ -2778,26 +2778,26 @@ public Task StringBitCountAsync(RedisKey key, long start = 0, long end = - public Task StringBitfieldGetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, new BitfieldGet(encoding, offset), flags); - return ExecuteAsync(msg, ResultProcessor.Int64); + var msg = GetBitfieldMessage(key, new BitfieldGet(encoding, offset), flags, out ServerEndPoint? server); + return ExecuteAsync(msg, ResultProcessor.Int64, server); } public Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, new BitfieldSet(encoding, offset, value), flags); + var msg = GetBitfieldMessage(key, new BitfieldSet(encoding, offset, value), flags, out ServerEndPoint? server); return ExecuteAsync(msg, ResultProcessor.Int64); } public Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, new BitfieldIncrby(encoding, offset, increment, overflowHandling), flags); - return ExecuteAsync(msg, ResultProcessor.NullableInt64); + var msg = GetBitfieldMessage(key, new BitfieldIncrby(encoding, offset, increment, overflowHandling), flags, out ServerEndPoint? server); + return ExecuteAsync(msg, ResultProcessor.NullableInt64, server); } public Task StringBitfieldAsync(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, subcommands, flags); - return ExecuteAsync(msg, ResultProcessor.NullableInt64Array, defaultValue: Array.Empty()); + var msg = GetBitfieldMessage(key, subcommands, flags, out ServerEndPoint? server); + return ExecuteAsync(msg, ResultProcessor.NullableInt64Array, defaultValue: Array.Empty(), server: server); } public long StringBitOperation(Bitwise operation, RedisKey destination, RedisKey first, RedisKey second, CommandFlags flags = CommandFlags.None) @@ -3130,12 +3130,13 @@ public Task StringSetRangeAsync(RedisKey key, long offset, RedisValu _ => throw new ArgumentException("Expiry time must be either Utc or Local", nameof(when)), }; - private Message GetBitfieldMessage(in RedisKey key, BitfieldSubCommand subcommand, CommandFlags flags) + private Message GetBitfieldMessage(in RedisKey key, BitfieldSubCommand subcommand, CommandFlags flags, out ServerEndPoint? server) { + var features = GetFeatures(key, flags, out server); var args = new List(subcommand.NumArgs); subcommand.AddArgs(args); RedisCommand command = RedisCommand.BITFIELD; - if (subcommand is BitfieldGet && multiplexer.GetServer(multiplexer.EndPoints[0]).Version >= RedisFeatures.v6_2_0) + if (subcommand is BitfieldGet && features.ReadOnlyBitfield) { command = RedisCommand.BITFIELD_RO; } @@ -3143,11 +3144,12 @@ private Message GetBitfieldMessage(in RedisKey key, BitfieldSubCommand subcomman return Message.Create(Database, flags, command, key, args.ToArray()); } - private Message GetBitfieldMessage(in RedisKey key, BitfieldSubCommand[] subCommands, CommandFlags flags) + private Message GetBitfieldMessage(in RedisKey key, BitfieldSubCommand[] subCommands, CommandFlags flags, out ServerEndPoint? server) { + var features = GetFeatures(key, flags, out server); var args = new List(subCommands.Sum(sc => sc.NumArgs)); var canBeReadonly = subCommands.All(sc => sc.IsReadonly); - var command = canBeReadonly && multiplexer.GetServer(multiplexer.EndPoints[0]).Version >= RedisFeatures.v6_2_0 ? RedisCommand.BITFIELD_RO : RedisCommand.BITFIELD; + var command = canBeReadonly &&features.ReadOnlyBitfield ? RedisCommand.BITFIELD_RO : RedisCommand.BITFIELD; foreach (var subcommand in subCommands) { diff --git a/src/StackExchange.Redis/RedisFeatures.cs b/src/StackExchange.Redis/RedisFeatures.cs index a2dcee19e..1aa80e122 100644 --- a/src/StackExchange.Redis/RedisFeatures.cs +++ b/src/StackExchange.Redis/RedisFeatures.cs @@ -242,6 +242,11 @@ public RedisFeatures(Version version) /// public bool KeyTouch => Version >= v3_2_1; + /// + /// Is BITFIELD_RO available? + /// + internal bool ReadOnlyBitfield => Version > v6_2_0; + /// /// Does the server prefer 'replica' terminology - 'REPLICAOF', etc? /// From d3cc041bec284d8e0064964fb3c6e41108808b43 Mon Sep 17 00:00:00 2001 From: slorello89 <42971704+slorello89@users.noreply.github.com> Date: Wed, 27 Apr 2022 21:28:08 -0400 Subject: [PATCH 09/14] decreasing public-api surface area, using a builder. --- .../APITypes/BitfieldCommandBuilder.cs | 159 ++++++++++++++ .../APITypes/BitfieldSubCommand.cs | 199 ------------------ .../Interfaces/IDatabase.cs | 7 +- .../Interfaces/IDatabaseAsync.cs | 7 +- .../KeyspaceIsolation/DatabaseWrapper.cs | 4 +- .../KeyspaceIsolation/WrapperBase.cs | 4 +- src/StackExchange.Redis/PublicAPI.Shipped.txt | 21 +- src/StackExchange.Redis/RedisDatabase.cs | 51 +---- tests/StackExchange.Redis.Tests/Bitfield.cs | 20 +- 9 files changed, 199 insertions(+), 273 deletions(-) create mode 100644 src/StackExchange.Redis/APITypes/BitfieldCommandBuilder.cs delete mode 100644 src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs diff --git a/src/StackExchange.Redis/APITypes/BitfieldCommandBuilder.cs b/src/StackExchange.Redis/APITypes/BitfieldCommandBuilder.cs new file mode 100644 index 000000000..4c7e14697 --- /dev/null +++ b/src/StackExchange.Redis/APITypes/BitfieldCommandBuilder.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace StackExchange.Redis; + +/// +/// Builder for bitfield commands that take multiple sub-commands. +/// +public class BitfieldCommandBuilder +{ + private readonly LinkedList _args = new LinkedList(); + private bool _eligibleForReadOnly; + + /// + /// Builds a subcommand for a Bitfield GET, which returns the number stored in the specified offset of a bitfield at the given encoding. + /// + /// The encoding for the subcommand. + /// The offset into the bitfield for the subcommand. + public BitfieldCommandBuilder BitfieldGet(BitfieldEncoding encoding, BitfieldOffset offset) + { + _eligibleForReadOnly = true; + _args.AddLast(RedisLiterals.GET); + _args.AddLast(encoding.RedisValue); + _args.AddLast(offset.RedisValue); + return this; + } + + /// + /// Builds a Bitfield subcommand which SETs the specified range of bits to the specified value. + /// + /// The encoding of the subcommand. + /// The offset of the subcommand. + /// The value to set. + public BitfieldCommandBuilder BitfieldSet(BitfieldEncoding encoding, BitfieldOffset offset, long value) + { + _eligibleForReadOnly = false; + _args.AddLast(RedisLiterals.SET); + _args.AddLast(encoding.RedisValue); + _args.AddLast(offset.RedisValue); + _args.AddLast(value); + return this; + } + + /// + /// Builds a subcommand for Bitfield INCRBY, which increments the number at the specified range of bits by the provided value + /// + /// The number's encoding. + /// The offset into the bitfield to increment. + /// The value to increment by. + /// How overflows will be handled when incrementing. + public BitfieldCommandBuilder BitfieldIncrby(BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap) + { + _eligibleForReadOnly = false; + if (overflowHandling != BitfieldOverflowHandling.Wrap) + { + _args.AddLast(RedisLiterals.OVERFLOW); + _args.AddLast(overflowHandling.AsRedisValue()); + } + + _args.AddLast(RedisLiterals.INCRBY); + _args.AddLast(encoding.RedisValue); + _args.AddLast(offset.RedisValue); + _args.AddLast(increment); + return this; + } + + internal BitfieldCommandMessage Build(int db, RedisKey key, CommandFlags flags, RedisBase redisBase, out ServerEndPoint? server) + { + var features = redisBase.GetFeatures(key, flags, out server); + var command = _eligibleForReadOnly && features.ReadOnlyBitfield ? RedisCommand.BITFIELD_RO : RedisCommand.BITFIELD; + return new BitfieldCommandMessage(db, flags, key, command, _args); + } +} + +internal class BitfieldCommandMessage : Message +{ + private readonly LinkedList _args; + private readonly RedisKey _key; + public BitfieldCommandMessage(int db, CommandFlags flags, RedisKey key, RedisCommand command, LinkedList args) : base(db, flags, command) + { + _key = key; + _args = args; + } + + public override int ArgCount => 1 + _args.Count; + + protected override void WriteImpl(PhysicalConnection physical) + { + physical.WriteHeader(Command, ArgCount); + physical.Write(_key); + foreach (var arg in _args) + { + physical.WriteBulkString(arg); + } + } +} + +/// +/// An offset into a bitfield. This is either a literal offset (number of bits from the beginning of the bitfield) or an +/// encoding based offset, based off the encoding of the sub-command. +/// +public readonly struct BitfieldOffset +{ + /// + /// Returns the BitfieldOffset as a RedisValue + /// + internal RedisValue RedisValue => $"{(ByEncoding ? "#" : string.Empty)}{Offset}"; + + /// + /// Whether or not the BitfieldOffset will work off of the sub-commands integer encoding. + /// + public bool ByEncoding { get; } + + /// + /// The number of either bits or encoded integers to offset into the bitfield. + /// + public long Offset { get; } + + /// + /// Initializes a bitfield offset + /// + /// Whether or not the BitfieldOffset will work off of the sub-commands integer encoding. + /// The number of either bits or encoded integers to offset into the bitfield. + public BitfieldOffset(bool byEncoding, long offset) + { + ByEncoding = byEncoding; + Offset = offset; + } +} + +/// +/// The encoding that a sub-command should use. This is either a signed or unsigned integer of a specified length. +/// +public readonly struct BitfieldEncoding +{ + internal RedisValue RedisValue => $"{Signedness.SignChar()}{Size}"; + + /// + /// The signedness of the integer. + /// + public Signedness Signedness { get; } + + /// + /// The size of the integer. + /// + public byte Size { get; } + + /// + /// Initializes the BitfieldEncoding. + /// + /// The encoding's + /// The size of the integer. + public BitfieldEncoding(Signedness signedness, byte size) + { + Signedness = signedness; + Size = size; + } +} diff --git a/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs b/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs deleted file mode 100644 index 055c91dea..000000000 --- a/src/StackExchange.Redis/APITypes/BitfieldSubCommand.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace StackExchange.Redis; - -/// -/// An abstract subcommand for a bitfield. -/// -public abstract class BitfieldSubCommand -{ - internal abstract int NumArgs { get; } - - internal abstract void AddArgs(IList args); - - internal virtual bool IsReadonly => false; - - /// - /// The encoding of the sub-command. A signed or unsigned integer of a given size. - /// - public BitfieldEncoding Encoding { get; } - - /// - /// The offset into the bitfield the subcommand will traverse. - /// - public BitfieldOffset Offset { get; } - - internal BitfieldSubCommand(BitfieldEncoding encoding, BitfieldOffset offset) - { - Encoding = encoding; - Offset = offset; - } - -} - -/// -/// Represents a Bitfield GET, which returns the number stored in the specified offset of a bitfield at the given encoding. -/// -public sealed class BitfieldGet : BitfieldSubCommand -{ - /// - /// Initializes a bitfield GET subcommand - /// - /// The encoding of the subcommand. - /// The offset into the bitfield of the subcommand - public BitfieldGet(BitfieldEncoding encoding, BitfieldOffset offset) : base(encoding, offset) - { - } - - internal override bool IsReadonly => true; - - internal override int NumArgs => 3; - - internal override void AddArgs(IList args) - { - args.Add(RedisLiterals.GET); - args.Add(Encoding.AsRedisValue); - args.Add(Offset.AsRedisValue); - } -} - -/// -/// Bitfield subcommand which SETs the specified range of bits to the specified value. -/// -public sealed class BitfieldSet : BitfieldSubCommand -{ - /// - /// The value to set. - /// - public long Value { get; } - - /// - /// Initializes a subcommand for a Bitfield SET. - /// - /// The number's encoding. - /// The offset into the bitfield to set. - /// The value to set. - public BitfieldSet(BitfieldEncoding encoding, BitfieldOffset offset, long value) : base(encoding, offset) - { - Value = value; - } - - internal override int NumArgs => 4; - - internal override void AddArgs(IList args) - { - args.Add(RedisLiterals.SET); - args.Add(Encoding.AsRedisValue); - args.Add(Offset.AsRedisValue); - args.Add(Value); - } -} - -/// -/// Bitfield subcommand INCRBY, which increments the number at the specified range of bits by the provided value -/// -public sealed class BitfieldIncrby : BitfieldSubCommand -{ - /// - /// The value to increment by. - /// - public long Increment { get; } - - /// - /// Determines how overflows are handled for the bitfield. - /// - public BitfieldOverflowHandling OverflowHandling { get; } - - /// - /// Initializes a sub-command for a Bitfield INCRBY. - /// - /// The number's encoding. - /// The offset into the bitfield to set. - /// The value to set. - /// How overflows will be handled when incrementing. - public BitfieldIncrby(BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap) : base(encoding, offset) - { - Increment = increment; - OverflowHandling = overflowHandling; - } - - internal override int NumArgs => OverflowHandling == BitfieldOverflowHandling.Wrap ? 4 : 6; - - internal override void AddArgs(IList args) - { - if (OverflowHandling != BitfieldOverflowHandling.Wrap) - { - args.Add(RedisLiterals.OVERFLOW); - args.Add(OverflowHandling.AsRedisValue()); - } - args.Add(RedisLiterals.INCRBY); - args.Add(Encoding.AsRedisValue); - args.Add(Offset.AsRedisValue); - args.Add(Increment); - } -} - - - -/// -/// An offset into a bitfield. This is either a literal offset (number of bits from the beginning of the bitfield) or an -/// encoding based offset, based off the encoding of the sub-command. -/// -public readonly struct BitfieldOffset -{ - /// - /// Returns the BitfieldOffset as a RedisValue - /// - internal RedisValue AsRedisValue => $"{(ByEncoding ? "#" : string.Empty)}{Offset}"; - - /// - /// Whether or not the BitfieldOffset will work off of the sub-commands integer encoding. - /// - public bool ByEncoding { get; } - - /// - /// The number of either bits or encoded integers to offset into the bitfield. - /// - public long Offset { get; } - - /// - /// Initializes a bitfield offset - /// - /// Whether or not the BitfieldOffset will work off of the sub-commands integer encoding. - /// The number of either bits or encoded integers to offset into the bitfield. - public BitfieldOffset(bool byEncoding, long offset) - { - ByEncoding = byEncoding; - Offset = offset; - } -} - -/// -/// The encoding that a sub-command should use. This is either a signed or unsigned integer of a specified length. -/// -public readonly struct BitfieldEncoding -{ - internal RedisValue AsRedisValue => $"{Signedness.SignChar()}{Size}"; - - /// - /// The signedness of the integer. - /// - public Signedness Signedness { get; } - - /// - /// The size of the integer. - /// - public byte Size { get; } - - /// - /// Initializes the BitfieldEncoding. - /// - /// The encoding's - /// The size of the integer. - public BitfieldEncoding(Signedness signedness, byte size) - { - Signedness = signedness; - Size = size; - } -} diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 1ee79ccf4..2c8111562 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2532,15 +2532,16 @@ IEnumerable SortedSetScan(RedisKey key, /// - /// Executes a set of Bitfield against the bitfield at the provided . Will run as a BITFIELD_RO if all operations are read-only and the command is available. + /// Executes a set of Bitfield subcommands as constructed by the against the bitfield at the provided . + /// Will run as a BITFIELD_RO if all operations are read-only and the command is available. /// /// The key of the string. - /// The subcommands to execute against the bitfield. + /// The subcommands to execute against the bitfield. /// The flags for this operation. /// An array of numbers corresponding to the result of each sub-command. For increment subcommands, these can be null. /// /// - long?[] StringBitfield(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None); + long?[] StringBitfield(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None); /// /// Perform a bitwise operation between multiple keys (containing string values) and store the result in the destination key. diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index 945c6ee18..64027c4c8 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -2483,15 +2483,16 @@ IAsyncEnumerable SortedSetScanAsync(RedisKey key, Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None); /// - /// Executes a set of Bitfield against the bitfield at the provided . Will run as a BITFIELD_RO if all operations are read-only and the command is available. + /// Executes a set of Bitfield subcommands as constructed by the against the bitfield at the provided . + /// Will run as a BITFIELD_RO if all operations are read-only and the command is available. /// /// The key of the string. - /// The subcommands to execute against the bitfield. + /// The subcommands to execute against the bitfield. /// The flags for this operation. /// An array of numbers corresponding to the result of each sub-command. For increment subcommands, these can be null. /// /// - Task StringBitfieldAsync(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None); + Task StringBitfieldAsync(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None); /// /// Perform a bitwise operation between multiple keys (containing string values) and store the result in the destination key. diff --git a/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs b/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs index ae5c272b0..6760525ec 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/DatabaseWrapper.cs @@ -583,8 +583,8 @@ public long StringBitfieldSet(RedisKey key, BitfieldEncoding encoding, BitfieldO public long? StringBitfieldIncrement(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) => Inner.StringBitfieldIncrement(ToInner(key), encoding, offset, increment, overflowHandling, flags); - public long?[] StringBitfield(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None) => - Inner.StringBitfield(key, subcommands, flags); + public long?[] StringBitfield(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfield(key, builder, flags); public long StringBitOperation(Bitwise operation, RedisKey destination, RedisKey[] keys, CommandFlags flags = CommandFlags.None) => Inner.StringBitOperation(operation, ToInner(destination), ToInner(keys), flags); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs b/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs index 1108b4a50..e24d11d04 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/WrapperBase.cs @@ -601,8 +601,8 @@ public Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding public Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) => Inner.StringBitfieldIncrementAsync(ToInner(key), encoding, offset, increment, overflowHandling, flags); - public Task StringBitfieldAsync(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None) => - Inner.StringBitfieldAsync(ToInner(key), subcommands, flags); + public Task StringBitfieldAsync(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldAsync(ToInner(key), builder, flags); public Task StringBitOperationAsync(Bitwise operation, RedisKey destination, RedisKey[] keys, CommandFlags flags = CommandFlags.None) => Inner.StringBitOperationAsync(operation, ToInner(destination), ToInner(keys), flags); diff --git a/src/StackExchange.Redis/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI.Shipped.txt index ec39ec063..4b335efe3 100644 --- a/src/StackExchange.Redis/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI.Shipped.txt @@ -66,17 +66,16 @@ StackExchange.Redis.BacklogPolicy.AbortPendingOnConnectionFailure.init -> void StackExchange.Redis.BacklogPolicy.BacklogPolicy() -> void StackExchange.Redis.BacklogPolicy.QueueWhileDisconnected.get -> bool StackExchange.Redis.BacklogPolicy.QueueWhileDisconnected.init -> void +StackExchange.Redis.BitfieldCommandBuilder +StackExchange.Redis.BitfieldCommandBuilder.BitfieldCommandBuilder() -> void +StackExchange.Redis.BitfieldCommandBuilder.BitfieldGet(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset) -> StackExchange.Redis.BitfieldCommandBuilder! +StackExchange.Redis.BitfieldCommandBuilder.BitfieldIncrby(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap) -> StackExchange.Redis.BitfieldCommandBuilder! +StackExchange.Redis.BitfieldCommandBuilder.BitfieldSet(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value) -> StackExchange.Redis.BitfieldCommandBuilder! StackExchange.Redis.BitfieldEncoding StackExchange.Redis.BitfieldEncoding.BitfieldEncoding() -> void StackExchange.Redis.BitfieldEncoding.BitfieldEncoding(StackExchange.Redis.Signedness signedness, byte size) -> void StackExchange.Redis.BitfieldEncoding.Signedness.get -> StackExchange.Redis.Signedness StackExchange.Redis.BitfieldEncoding.Size.get -> byte -StackExchange.Redis.BitfieldGet -StackExchange.Redis.BitfieldGet.BitfieldGet(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset) -> void -StackExchange.Redis.BitfieldIncrby -StackExchange.Redis.BitfieldIncrby.BitfieldIncrby(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap) -> void -StackExchange.Redis.BitfieldIncrby.Increment.get -> long -StackExchange.Redis.BitfieldIncrby.OverflowHandling.get -> StackExchange.Redis.BitfieldOverflowHandling StackExchange.Redis.BitfieldOffset StackExchange.Redis.BitfieldOffset.BitfieldOffset() -> void StackExchange.Redis.BitfieldOffset.BitfieldOffset(bool byEncoding, long offset) -> void @@ -86,12 +85,6 @@ StackExchange.Redis.BitfieldOverflowHandling StackExchange.Redis.BitfieldOverflowHandling.Fail = 2 -> StackExchange.Redis.BitfieldOverflowHandling StackExchange.Redis.BitfieldOverflowHandling.Saturate = 1 -> StackExchange.Redis.BitfieldOverflowHandling StackExchange.Redis.BitfieldOverflowHandling.Wrap = 0 -> StackExchange.Redis.BitfieldOverflowHandling -StackExchange.Redis.BitfieldSet -StackExchange.Redis.BitfieldSet.BitfieldSet(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value) -> void -StackExchange.Redis.BitfieldSet.Value.get -> long -StackExchange.Redis.BitfieldSubCommand -StackExchange.Redis.BitfieldSubCommand.Encoding.get -> StackExchange.Redis.BitfieldEncoding -StackExchange.Redis.BitfieldSubCommand.Offset.get -> StackExchange.Redis.BitfieldOffset StackExchange.Redis.Bitwise StackExchange.Redis.Bitwise.And = 0 -> StackExchange.Redis.Bitwise StackExchange.Redis.Bitwise.Not = 3 -> StackExchange.Redis.Bitwise @@ -709,7 +702,7 @@ StackExchange.Redis.IDatabase.StringBitCount(StackExchange.Redis.RedisKey key, l StackExchange.Redis.IDatabase.StringBitOperation(StackExchange.Redis.Bitwise operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey first, StackExchange.Redis.RedisKey second = default(StackExchange.Redis.RedisKey), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringBitOperation(StackExchange.Redis.Bitwise operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey[]! keys, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringBitPosition(StackExchange.Redis.RedisKey key, bool bit, long start = 0, long end = -1, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long -StackExchange.Redis.IDatabase.StringBitfield(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldSubCommand![]! subcommands, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long?[]! +StackExchange.Redis.IDatabase.StringBitfield(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldCommandBuilder! builder, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long?[]! StackExchange.Redis.IDatabase.StringBitfieldGet(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringBitfieldIncrement(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long? StackExchange.Redis.IDatabase.StringBitfieldSet(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long @@ -923,7 +916,7 @@ StackExchange.Redis.IDatabaseAsync.StreamReadGroupAsync(StackExchange.Redis.Stre StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey key, int maxLength, bool useApproximateMaxLength = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringAppendAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitCountAsync(StackExchange.Redis.RedisKey key, long start = 0, long end = -1, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StringBitfieldAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldSubCommand![]! subcommands, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StringBitfieldAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldCommandBuilder! builder, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitfieldGetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitfieldIncrementAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitfieldSetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index 3552f1f10..8419c51ff 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -2748,25 +2748,25 @@ public long StringBitCount(RedisKey key, long start = 0, long end = -1, CommandF public long StringBitfieldGet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, new BitfieldGet(encoding, offset), flags, out ServerEndPoint? server); + var msg = new BitfieldCommandBuilder().BitfieldGet(encoding, offset).Build(Database, key, flags, this, out ServerEndPoint? server); return ExecuteSync(msg, ResultProcessor.Int64, server); } public long StringBitfieldSet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, new BitfieldSet(encoding, offset, value), flags, out ServerEndPoint? server); + var msg = new BitfieldCommandBuilder().BitfieldSet(encoding, offset, value).Build(Database, key, flags, this, out ServerEndPoint? server); return ExecuteSync(msg, ResultProcessor.Int64, server); } public long? StringBitfieldIncrement(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, new BitfieldIncrby(encoding, offset, increment, overflowHandling), flags, out ServerEndPoint? server); + var msg = new BitfieldCommandBuilder().BitfieldIncrby(encoding, offset, increment, overflowHandling).Build(Database, key, flags, this, out ServerEndPoint? server); return ExecuteSync(msg, ResultProcessor.NullableInt64, server); } - public long?[] StringBitfield(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None) + public long?[] StringBitfield(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, subcommands, flags, out ServerEndPoint? server); + var msg = builder.Build(Database, key, flags, this, out ServerEndPoint? server); return ExecuteSync(msg, ResultProcessor.NullableInt64Array, defaultValue: Array.Empty(), server: server); } @@ -2778,25 +2778,25 @@ public Task StringBitCountAsync(RedisKey key, long start = 0, long end = - public Task StringBitfieldGetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, new BitfieldGet(encoding, offset), flags, out ServerEndPoint? server); + var msg = new BitfieldCommandBuilder().BitfieldGet(encoding, offset).Build(Database, key, flags, this, out ServerEndPoint? server); return ExecuteAsync(msg, ResultProcessor.Int64, server); } public Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, new BitfieldSet(encoding, offset, value), flags, out ServerEndPoint? server); - return ExecuteAsync(msg, ResultProcessor.Int64); + var msg = new BitfieldCommandBuilder().BitfieldSet(encoding, offset, value).Build(Database, key, flags, this, out ServerEndPoint? server); + return ExecuteAsync(msg, ResultProcessor.Int64, server); } public Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, new BitfieldIncrby(encoding, offset, increment, overflowHandling), flags, out ServerEndPoint? server); + var msg = new BitfieldCommandBuilder().BitfieldIncrby(encoding, offset, increment, overflowHandling).Build(Database, key, flags, this, out ServerEndPoint? server); return ExecuteAsync(msg, ResultProcessor.NullableInt64, server); } - public Task StringBitfieldAsync(RedisKey key, BitfieldSubCommand[] subcommands, CommandFlags flags = CommandFlags.None) + public Task StringBitfieldAsync(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None) { - var msg = GetBitfieldMessage(key, subcommands, flags, out ServerEndPoint? server); + var msg = builder.Build(Database, key, flags, this, out ServerEndPoint? server); return ExecuteAsync(msg, ResultProcessor.NullableInt64Array, defaultValue: Array.Empty(), server: server); } @@ -3130,35 +3130,6 @@ public Task StringSetRangeAsync(RedisKey key, long offset, RedisValu _ => throw new ArgumentException("Expiry time must be either Utc or Local", nameof(when)), }; - private Message GetBitfieldMessage(in RedisKey key, BitfieldSubCommand subcommand, CommandFlags flags, out ServerEndPoint? server) - { - var features = GetFeatures(key, flags, out server); - var args = new List(subcommand.NumArgs); - subcommand.AddArgs(args); - RedisCommand command = RedisCommand.BITFIELD; - if (subcommand is BitfieldGet && features.ReadOnlyBitfield) - { - command = RedisCommand.BITFIELD_RO; - } - - return Message.Create(Database, flags, command, key, args.ToArray()); - } - - private Message GetBitfieldMessage(in RedisKey key, BitfieldSubCommand[] subCommands, CommandFlags flags, out ServerEndPoint? server) - { - var features = GetFeatures(key, flags, out server); - var args = new List(subCommands.Sum(sc => sc.NumArgs)); - var canBeReadonly = subCommands.All(sc => sc.IsReadonly); - var command = canBeReadonly &&features.ReadOnlyBitfield ? RedisCommand.BITFIELD_RO : RedisCommand.BITFIELD; - - foreach (var subcommand in subCommands) - { - subcommand.AddArgs(args); - } - - return Message.Create(Database, flags, command, key, args.ToArray()); - } - private Message GetCopyMessage(in RedisKey sourceKey, RedisKey destinationKey, int destinationDatabase, bool replace, CommandFlags flags) => destinationDatabase switch { diff --git a/tests/StackExchange.Redis.Tests/Bitfield.cs b/tests/StackExchange.Redis.Tests/Bitfield.cs index 0f52a191c..5a1c9e460 100644 --- a/tests/StackExchange.Redis.Tests/Bitfield.cs +++ b/tests/StackExchange.Redis.Tests/Bitfield.cs @@ -81,16 +81,16 @@ public async Task TestBitfieldMulti() RedisKey key = Me(); db.KeyDelete(key); - var set1 = new BitfieldSet(new BitfieldEncoding(Signedness.Unsigned, 3), new BitfieldOffset(false, 5), 7); - var get1 = new BitfieldGet(new BitfieldEncoding(Signedness.Unsigned, 3), new BitfieldOffset(false, 5)); - var incr1 = new BitfieldIncrby(new BitfieldEncoding(Signedness.Unsigned, 3), new BitfieldOffset(false, 5), -1); - - var set2 = new BitfieldSet(new BitfieldEncoding(Signedness.Signed, 45), new BitfieldOffset(true, 1), 17592186044415); - var get2 = new BitfieldGet(new BitfieldEncoding(Signedness.Signed, 45), new BitfieldOffset(true, 1)); - var incr2 = new BitfieldIncrby(new BitfieldEncoding(Signedness.Signed, 45), new BitfieldOffset(true, 1), 1, BitfieldOverflowHandling.Fail); - - var subcommands = new BitfieldSubCommand[] {set1, get1, incr1, set2, get2, incr2}; - var res = await db.StringBitfieldAsync(key, subcommands); + var builder = new BitfieldCommandBuilder() + .BitfieldSet(new BitfieldEncoding(Signedness.Unsigned, 3), new BitfieldOffset(false, 5), 7) + .BitfieldGet(new BitfieldEncoding(Signedness.Unsigned, 3), new BitfieldOffset(false, 5)) + .BitfieldIncrby(new BitfieldEncoding(Signedness.Unsigned, 3), new BitfieldOffset(false, 5), -1) + .BitfieldSet(new BitfieldEncoding(Signedness.Signed, 45), new BitfieldOffset(true, 1), 17592186044415) + .BitfieldGet(new BitfieldEncoding(Signedness.Signed, 45), new BitfieldOffset(true, 1)) + .BitfieldIncrby(new BitfieldEncoding(Signedness.Signed, 45), new BitfieldOffset(true, 1), 1, + BitfieldOverflowHandling.Fail); + + var res = await db.StringBitfieldAsync(key, builder); Assert.Equal(0, res[0]); Assert.Equal(7, res[1]); From 9dae591532defe145d97bf579bea147725128975 Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Sun, 15 May 2022 21:07:49 -0400 Subject: [PATCH 10/14] Fix spacing --- tests/StackExchange.Redis.Tests/Bitfield.cs | 2 -- tests/StackExchange.Redis.Tests/Strings.cs | 1 - 2 files changed, 3 deletions(-) diff --git a/tests/StackExchange.Redis.Tests/Bitfield.cs b/tests/StackExchange.Redis.Tests/Bitfield.cs index 5a1c9e460..54f0f7c1f 100644 --- a/tests/StackExchange.Redis.Tests/Bitfield.cs +++ b/tests/StackExchange.Redis.Tests/Bitfield.cs @@ -12,7 +12,6 @@ public Bitfield(ITestOutputHelper output, SharedConnectionFixture fixture) : bas [Fact] public void TestBitfieldHappyPath() { - using var conn = Create(require: RedisFeatures.v3_2_0); var db = conn.GetDatabase(); RedisKey key = Me(); @@ -44,7 +43,6 @@ public void TestBitfieldHappyPath() [Fact] public async Task TestBitfieldHappyPathAsync() { - using var conn = Create(require: RedisFeatures.v3_2_0); var db = conn.GetDatabase(); RedisKey key = Me(); diff --git a/tests/StackExchange.Redis.Tests/Strings.cs b/tests/StackExchange.Redis.Tests/Strings.cs index 54dfcce0d..6aee44764 100644 --- a/tests/StackExchange.Redis.Tests/Strings.cs +++ b/tests/StackExchange.Redis.Tests/Strings.cs @@ -651,7 +651,6 @@ public async Task HashStringLengthAsync() Assert.Equal(0, await resNonExistingAsync); } - [Fact] public void HashStringLength() { From 7c6f62f34d2788027a3ab07c959b35bf5d4a0790 Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Sun, 15 May 2022 21:44:41 -0400 Subject: [PATCH 11/14] Simplify a bit, update docs --- .../APITypes/BitfieldCommandBuilder.cs | 70 +++++++++---------- .../Enums/BitfieldOverflowHandling.cs | 2 +- src/StackExchange.Redis/Enums/Signedness.cs | 28 -------- .../Interfaces/IDatabase.cs | 51 +++++++------- .../Interfaces/IDatabaseAsync.cs | 49 +++++++------ src/StackExchange.Redis/PublicAPI.Shipped.txt | 10 +-- src/StackExchange.Redis/RedisDatabase.cs | 12 ++-- tests/StackExchange.Redis.Tests/Bitfield.cs | 22 +++--- 8 files changed, 111 insertions(+), 133 deletions(-) delete mode 100644 src/StackExchange.Redis/Enums/Signedness.cs diff --git a/src/StackExchange.Redis/APITypes/BitfieldCommandBuilder.cs b/src/StackExchange.Redis/APITypes/BitfieldCommandBuilder.cs index 4c7e14697..660b2387d 100644 --- a/src/StackExchange.Redis/APITypes/BitfieldCommandBuilder.cs +++ b/src/StackExchange.Redis/APITypes/BitfieldCommandBuilder.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; namespace StackExchange.Redis; @@ -17,7 +15,7 @@ public class BitfieldCommandBuilder /// /// The encoding for the subcommand. /// The offset into the bitfield for the subcommand. - public BitfieldCommandBuilder BitfieldGet(BitfieldEncoding encoding, BitfieldOffset offset) + public BitfieldCommandBuilder Get(BitfieldEncoding encoding, BitfieldOffset offset) { _eligibleForReadOnly = true; _args.AddLast(RedisLiterals.GET); @@ -32,7 +30,7 @@ public BitfieldCommandBuilder BitfieldGet(BitfieldEncoding encoding, BitfieldOff /// The encoding of the subcommand. /// The offset of the subcommand. /// The value to set. - public BitfieldCommandBuilder BitfieldSet(BitfieldEncoding encoding, BitfieldOffset offset, long value) + public BitfieldCommandBuilder Set(BitfieldEncoding encoding, BitfieldOffset offset, long value) { _eligibleForReadOnly = false; _args.AddLast(RedisLiterals.SET); @@ -49,7 +47,7 @@ public BitfieldCommandBuilder BitfieldSet(BitfieldEncoding encoding, BitfieldOff /// The offset into the bitfield to increment. /// The value to increment by. /// How overflows will be handled when incrementing. - public BitfieldCommandBuilder BitfieldIncrby(BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap) + public BitfieldCommandBuilder Incrby(BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap) { _eligibleForReadOnly = false; if (overflowHandling != BitfieldOverflowHandling.Wrap) @@ -97,63 +95,63 @@ protected override void WriteImpl(PhysicalConnection physical) } /// -/// An offset into a bitfield. This is either a literal offset (number of bits from the beginning of the bitfield) or an -/// encoding based offset, based off the encoding of the sub-command. +/// The encoding that a sub-command should use. This is either a signed or unsigned integer of a specified length. /// -public readonly struct BitfieldOffset +public readonly struct BitfieldEncoding { - /// - /// Returns the BitfieldOffset as a RedisValue - /// - internal RedisValue RedisValue => $"{(ByEncoding ? "#" : string.Empty)}{Offset}"; + internal RedisValue RedisValue => $"{(IsSigned ? 'i' : 'u')}{Size}"; /// - /// Whether or not the BitfieldOffset will work off of the sub-commands integer encoding. + /// Whether the integer is signed or not. /// - public bool ByEncoding { get; } + public bool IsSigned { get; } /// - /// The number of either bits or encoded integers to offset into the bitfield. + /// The size of the integer. /// - public long Offset { get; } + public byte Size { get; } /// - /// Initializes a bitfield offset + /// Initializes the BitfieldEncoding. /// - /// Whether or not the BitfieldOffset will work off of the sub-commands integer encoding. - /// The number of either bits or encoded integers to offset into the bitfield. - public BitfieldOffset(bool byEncoding, long offset) + /// Whether the encoding is signed. + /// The size of the integer. + public BitfieldEncoding(bool isSigned, byte size) { - ByEncoding = byEncoding; - Offset = offset; + IsSigned = isSigned; + Size = size; } } /// -/// The encoding that a sub-command should use. This is either a signed or unsigned integer of a specified length. +/// An offset into a bitfield. This is either a literal offset (number of bits from the beginning of the bitfield) or an +/// encoding based offset, based off the encoding of the sub-command. /// -public readonly struct BitfieldEncoding +public readonly struct BitfieldOffset { - internal RedisValue RedisValue => $"{Signedness.SignChar()}{Size}"; + /// + /// Returns the BitfieldOffset as a RedisValue. + /// + internal RedisValue RedisValue => $"{(ByEncoding ? "#" : string.Empty)}{Offset}"; /// - /// The signedness of the integer. + /// Whether or not the BitfieldOffset will work off of the sub-commands integer encoding. /// - public Signedness Signedness { get; } + public bool ByEncoding { get; } /// - /// The size of the integer. + /// The number of either bits or encoded integers to offset into the bitfield. /// - public byte Size { get; } + public long Offset { get; } /// - /// Initializes the BitfieldEncoding. + /// Initializes a bitfield offset /// - /// The encoding's - /// The size of the integer. - public BitfieldEncoding(Signedness signedness, byte size) + /// Whether or not the BitfieldOffset will work off of the sub-commands integer encoding. + /// The number of either bits or encoded integers to offset into the bitfield. + public BitfieldOffset(bool byEncoding, long offset) { - Signedness = signedness; - Size = size; + ByEncoding = byEncoding; + Offset = offset; } } diff --git a/src/StackExchange.Redis/Enums/BitfieldOverflowHandling.cs b/src/StackExchange.Redis/Enums/BitfieldOverflowHandling.cs index 061b0e025..df9275f34 100644 --- a/src/StackExchange.Redis/Enums/BitfieldOverflowHandling.cs +++ b/src/StackExchange.Redis/Enums/BitfieldOverflowHandling.cs @@ -18,7 +18,7 @@ public enum BitfieldOverflowHandling /// /// If an overflow is encountered, associated subcommand fails, and the result will be NULL. /// - Fail + Fail, } internal static class BitfieldOverflowHandlingExtensions diff --git a/src/StackExchange.Redis/Enums/Signedness.cs b/src/StackExchange.Redis/Enums/Signedness.cs deleted file mode 100644 index d81a4a16b..000000000 --- a/src/StackExchange.Redis/Enums/Signedness.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace StackExchange.Redis; - -/// -/// Represents an integers signedness -/// -public enum Signedness -{ - /// - /// An integer with no sign bit. - /// - Unsigned, - /// - /// An integer with a sign bit. - /// - Signed -} - -internal static class SignednessExtensions -{ - internal static char SignChar(this Signedness sign) => sign switch - { - Signedness.Signed => 'i', - Signedness.Unsigned => 'u', - _ => throw new ArgumentOutOfRangeException(nameof(sign)) - }; -} diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 08e9081a3..d1280d712 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2548,56 +2548,59 @@ IEnumerable SortedSetScan(RedisKey key, long StringBitCount(RedisKey key, long start = 0, long end = -1, StringIndexType indexType = StringIndexType.Byte, CommandFlags flags = CommandFlags.None); /// - /// Pulls a single number out of a bitfield of the provided at the given offset. Will execute a BITFIELD_RO if possible. + /// Executes a set of Bitfield subcommands as constructed by the against the bitfield at the provided . + /// Will run as a BITFIELD_RO if all operations are read-only and the command is available. + /// + /// The key of the string. + /// The subcommands to execute against the bitfield. + /// The flags to use for this operation. + /// An array of numbers corresponding to the result of each sub-command. For increment subcommands, these can be null. + /// + /// , + /// + /// + long?[] StringBitfield(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None); + + /// + /// Pulls a single number out of a bitfield of the provided at the given offset. + /// Will execute a BITFIELD_RO if possible. /// /// The key for the string. /// The encoding of the number. /// The offset into the bitfield to pull the number from. - /// The Commands for the operation. + /// The flags to use for this operation. /// The number of the given at the provided . - /// - /// + /// + /// , + /// + /// long StringBitfieldGet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None); /// - /// Sets a single number number in a bitfield at the provided to the provided, in the given . + /// Sets a single number in a bitfield at the provided to the provided, in the given . /// /// The key for the string. /// The encoding of the number. /// The offset into the bitfield to pull the number from. /// the value to set the bitfield to. - /// The Commands for the operation. - /// The previous value as am at the provided . + /// The flags to use for this operation. + /// The previous value as an at the provided . /// long StringBitfieldSet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None); - /// - /// increments a single number number in a bitfield at the provided in the provided by the given . + /// Increments a single number in a bitfield at the provided in the provided by the given . /// /// The key for the string. /// The encoding of the number. /// The offset into the bitfield to pull the number from. /// the value to increment the bitfield by. /// The way integer overflows are handled. - /// The Commands for the operation. - /// The new value of the given at the provided after the incrby is applied, represented as an . Returns if the operation fails. + /// The flags to use for this operation. + /// The new value of the given at the provided after the INCRBY is applied, represented as an . Returns if the operation fails. /// long? StringBitfieldIncrement(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None); - - /// - /// Executes a set of Bitfield subcommands as constructed by the against the bitfield at the provided . - /// Will run as a BITFIELD_RO if all operations are read-only and the command is available. - /// - /// The key of the string. - /// The subcommands to execute against the bitfield. - /// The flags for this operation. - /// An array of numbers corresponding to the result of each sub-command. For increment subcommands, these can be null. - /// - /// - long?[] StringBitfield(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None); - /// /// Perform a bitwise operation between multiple keys (containing string values) and store the result in the destination key. /// The BITOP command supports four bitwise operations; note that NOT is a unary operator: the second key should be omitted in this case diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index 65be7fd5c..eb14d4691 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -2500,54 +2500,59 @@ IAsyncEnumerable SortedSetScanAsync(RedisKey key, Task StringBitCountAsync(RedisKey key, long start = 0, long end = -1, StringIndexType indexType = StringIndexType.Byte, CommandFlags flags = CommandFlags.None); /// - /// Pulls a single number out of a bitfield of the provided at the given offset. Will execute a BITFIELD_RO if possible. + /// Executes a set of Bitfield subcommands as constructed by the against the bitfield at the provided . + /// Will run as a BITFIELD_RO if all operations are read-only and the command is available. + /// + /// The key of the string. + /// The subcommands to execute against the bitfield. + /// The flags to use for this operation. + /// An array of numbers corresponding to the result of each sub-command. For increment subcommands, these can be null. + /// + /// , + /// + /// + Task StringBitfieldAsync(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None); + + /// + /// Pulls a single number out of a bitfield of the provided at the given offset. + /// Will execute a BITFIELD_RO if possible. /// /// The key for the string. /// The encoding of the number. /// The offset into the bitfield to pull the number from. - /// The Commands for the operation. + /// The flags to use for this operation. /// The number of the given at the provided . - /// - /// + /// + /// , + /// + /// Task StringBitfieldGetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None); /// - /// Sets a single number number in a bitfield at the provided to the provided, in the given . + /// Sets a single number in a bitfield at the provided to the provided, in the given . /// /// The key for the string. /// The encoding of the number. /// The offset into the bitfield to pull the number from. /// the value to set the bitfield to. - /// The Commands for the operation. - /// The previous value as am at the provided . + /// The flags to use for this operation. + /// The previous value as an at the provided . /// Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None); /// - /// Increments a single number number in a bitfield at the provided as if it were in the given by the given . + /// Increments a single number in a bitfield at the provided in the provided by the given . /// /// The key for the string. /// The encoding of the number. /// The offset into the bitfield to pull the number from. /// the value to increment the bitfield by. /// The way integer overflows are handled. - /// The Commands for the operation. - /// The new value of the given at the provided after the incrby is applied, represented as an . Returns if the operation fails. + /// The flags to use for this operation. + /// The new value of the given at the provided after the INCRBY is applied, represented as an . Returns if the operation fails. /// Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None); - /// - /// Executes a set of Bitfield subcommands as constructed by the against the bitfield at the provided . - /// Will run as a BITFIELD_RO if all operations are read-only and the command is available. - /// - /// The key of the string. - /// The subcommands to execute against the bitfield. - /// The flags for this operation. - /// An array of numbers corresponding to the result of each sub-command. For increment subcommands, these can be null. - /// - /// - Task StringBitfieldAsync(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None); - /// /// Perform a bitwise operation between multiple keys (containing string values) and store the result in the destination key. /// The BITOP command supports four bitwise operations; note that NOT is a unary operator: the second key should be omitted in this case diff --git a/src/StackExchange.Redis/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI.Shipped.txt index cea26bd70..012dfbd9a 100644 --- a/src/StackExchange.Redis/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI.Shipped.txt @@ -68,13 +68,13 @@ StackExchange.Redis.BacklogPolicy.QueueWhileDisconnected.get -> bool StackExchange.Redis.BacklogPolicy.QueueWhileDisconnected.init -> void StackExchange.Redis.BitfieldCommandBuilder StackExchange.Redis.BitfieldCommandBuilder.BitfieldCommandBuilder() -> void -StackExchange.Redis.BitfieldCommandBuilder.BitfieldGet(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset) -> StackExchange.Redis.BitfieldCommandBuilder! -StackExchange.Redis.BitfieldCommandBuilder.BitfieldIncrby(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap) -> StackExchange.Redis.BitfieldCommandBuilder! -StackExchange.Redis.BitfieldCommandBuilder.BitfieldSet(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value) -> StackExchange.Redis.BitfieldCommandBuilder! +StackExchange.Redis.BitfieldCommandBuilder.Get(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset) -> StackExchange.Redis.BitfieldCommandBuilder! +StackExchange.Redis.BitfieldCommandBuilder.Incrby(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap) -> StackExchange.Redis.BitfieldCommandBuilder! +StackExchange.Redis.BitfieldCommandBuilder.Set(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value) -> StackExchange.Redis.BitfieldCommandBuilder! StackExchange.Redis.BitfieldEncoding StackExchange.Redis.BitfieldEncoding.BitfieldEncoding() -> void -StackExchange.Redis.BitfieldEncoding.BitfieldEncoding(StackExchange.Redis.Signedness signedness, byte size) -> void -StackExchange.Redis.BitfieldEncoding.Signedness.get -> StackExchange.Redis.Signedness +StackExchange.Redis.BitfieldEncoding.BitfieldEncoding(bool isSigned, byte size) -> void +StackExchange.Redis.BitfieldEncoding.IsSigned.get -> bool StackExchange.Redis.BitfieldEncoding.Size.get -> byte StackExchange.Redis.BitfieldOffset StackExchange.Redis.BitfieldOffset.BitfieldOffset() -> void diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index 1996694f2..3d36781a5 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -2866,37 +2866,37 @@ public Task StringBitCountAsync(RedisKey key, long start = 0, long end = - public long StringBitfieldGet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None) { - var msg = new BitfieldCommandBuilder().BitfieldGet(encoding, offset).Build(Database, key, flags, this, out ServerEndPoint? server); + var msg = new BitfieldCommandBuilder().Get(encoding, offset).Build(Database, key, flags, this, out ServerEndPoint? server); return ExecuteSync(msg, ResultProcessor.Int64, server); } public Task StringBitfieldGetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None) { - var msg = new BitfieldCommandBuilder().BitfieldGet(encoding, offset).Build(Database, key, flags, this, out ServerEndPoint? server); + var msg = new BitfieldCommandBuilder().Get(encoding, offset).Build(Database, key, flags, this, out ServerEndPoint? server); return ExecuteAsync(msg, ResultProcessor.Int64, server); } public long StringBitfieldSet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None) { - var msg = new BitfieldCommandBuilder().BitfieldSet(encoding, offset, value).Build(Database, key, flags, this, out ServerEndPoint? server); + var msg = new BitfieldCommandBuilder().Set(encoding, offset, value).Build(Database, key, flags, this, out ServerEndPoint? server); return ExecuteSync(msg, ResultProcessor.Int64, server); } public Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None) { - var msg = new BitfieldCommandBuilder().BitfieldSet(encoding, offset, value).Build(Database, key, flags, this, out ServerEndPoint? server); + var msg = new BitfieldCommandBuilder().Set(encoding, offset, value).Build(Database, key, flags, this, out ServerEndPoint? server); return ExecuteAsync(msg, ResultProcessor.Int64, server); } public long? StringBitfieldIncrement(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) { - var msg = new BitfieldCommandBuilder().BitfieldIncrby(encoding, offset, increment, overflowHandling).Build(Database, key, flags, this, out ServerEndPoint? server); + var msg = new BitfieldCommandBuilder().Incrby(encoding, offset, increment, overflowHandling).Build(Database, key, flags, this, out ServerEndPoint? server); return ExecuteSync(msg, ResultProcessor.NullableInt64, server); } public Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) { - var msg = new BitfieldCommandBuilder().BitfieldIncrby(encoding, offset, increment, overflowHandling).Build(Database, key, flags, this, out ServerEndPoint? server); + var msg = new BitfieldCommandBuilder().Incrby(encoding, offset, increment, overflowHandling).Build(Database, key, flags, this, out ServerEndPoint? server); return ExecuteAsync(msg, ResultProcessor.NullableInt64, server); } diff --git a/tests/StackExchange.Redis.Tests/Bitfield.cs b/tests/StackExchange.Redis.Tests/Bitfield.cs index 54f0f7c1f..5cada2413 100644 --- a/tests/StackExchange.Redis.Tests/Bitfield.cs +++ b/tests/StackExchange.Redis.Tests/Bitfield.cs @@ -17,7 +17,7 @@ public void TestBitfieldHappyPath() RedisKey key = Me(); db.KeyDelete(key); - var encoding = new BitfieldEncoding(Signedness.Signed, 10); + var encoding = new BitfieldEncoding(isSigned: true, 10); var offset = new BitfieldOffset(true, 1); // should be the old value @@ -28,7 +28,7 @@ public void TestBitfieldHappyPath() Assert.Equal(-255, getResult); Assert.Equal(-265, incrementResult); - encoding = new BitfieldEncoding(Signedness.Unsigned, 18); + encoding = new BitfieldEncoding(isSigned: false, 18); offset = new BitfieldOffset(false, 22); setResult = db.StringBitfieldSet(key, encoding, offset, 262123); @@ -48,7 +48,7 @@ public async Task TestBitfieldHappyPathAsync() RedisKey key = Me(); db.KeyDelete(key); - var encoding = new BitfieldEncoding(Signedness.Signed, 10); + var encoding = new BitfieldEncoding(isSigned: true, 10); var offset = new BitfieldOffset(true, 1); // should be the old value @@ -59,7 +59,7 @@ public async Task TestBitfieldHappyPathAsync() Assert.Equal(-255, getResult); Assert.Equal(-265, incrementResult); - encoding = new BitfieldEncoding(Signedness.Unsigned, 18); + encoding = new BitfieldEncoding(isSigned: false, 18); offset = new BitfieldOffset(false, 22); setResult = await db.StringBitfieldSetAsync(key, encoding, offset, 262123); @@ -80,12 +80,12 @@ public async Task TestBitfieldMulti() db.KeyDelete(key); var builder = new BitfieldCommandBuilder() - .BitfieldSet(new BitfieldEncoding(Signedness.Unsigned, 3), new BitfieldOffset(false, 5), 7) - .BitfieldGet(new BitfieldEncoding(Signedness.Unsigned, 3), new BitfieldOffset(false, 5)) - .BitfieldIncrby(new BitfieldEncoding(Signedness.Unsigned, 3), new BitfieldOffset(false, 5), -1) - .BitfieldSet(new BitfieldEncoding(Signedness.Signed, 45), new BitfieldOffset(true, 1), 17592186044415) - .BitfieldGet(new BitfieldEncoding(Signedness.Signed, 45), new BitfieldOffset(true, 1)) - .BitfieldIncrby(new BitfieldEncoding(Signedness.Signed, 45), new BitfieldOffset(true, 1), 1, + .Set(new BitfieldEncoding(isSigned: false, 3), new BitfieldOffset(false, 5), 7) + .Get(new BitfieldEncoding(isSigned: false, 3), new BitfieldOffset(false, 5)) + .Incrby(new BitfieldEncoding(isSigned: false, 3), new BitfieldOffset(false, 5), -1) + .Set(new BitfieldEncoding(isSigned: true, 45), new BitfieldOffset(true, 1), 17592186044415) + .Get(new BitfieldEncoding(isSigned: true, 45), new BitfieldOffset(true, 1)) + .Incrby(new BitfieldEncoding(isSigned: true, 45), new BitfieldOffset(true, 1), 1, BitfieldOverflowHandling.Fail); var res = await db.StringBitfieldAsync(key, builder); @@ -106,7 +106,7 @@ public async Task TestOverflows() RedisKey key = Me(); db.KeyDelete(key); - var encoding = new BitfieldEncoding(Signedness.Signed, 3); + var encoding = new BitfieldEncoding(isSigned: true, 3); var offset = new BitfieldOffset(true, 3); await db.StringBitfieldSetAsync(key, encoding, offset, 3); From 5f072907195489b56e819df68fef59cae557774b Mon Sep 17 00:00:00 2001 From: Nick Craver Date: Sun, 15 May 2022 22:40:11 -0400 Subject: [PATCH 12/14] Save all the files dammit --- src/StackExchange.Redis/PublicAPI.Shipped.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/StackExchange.Redis/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI.Shipped.txt index 012dfbd9a..bcf2de82d 100644 --- a/src/StackExchange.Redis/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI.Shipped.txt @@ -1419,9 +1419,6 @@ StackExchange.Redis.ShutdownMode StackExchange.Redis.ShutdownMode.Always = 2 -> StackExchange.Redis.ShutdownMode StackExchange.Redis.ShutdownMode.Default = 0 -> StackExchange.Redis.ShutdownMode StackExchange.Redis.ShutdownMode.Never = 1 -> StackExchange.Redis.ShutdownMode -StackExchange.Redis.Signedness -StackExchange.Redis.Signedness.Signed = 1 -> StackExchange.Redis.Signedness -StackExchange.Redis.Signedness.Unsigned = 0 -> StackExchange.Redis.Signedness StackExchange.Redis.SlotRange StackExchange.Redis.SlotRange.CompareTo(StackExchange.Redis.SlotRange other) -> int StackExchange.Redis.SlotRange.Equals(StackExchange.Redis.SlotRange other) -> bool From 1392d2018e0a21ea445746beadb7a390c0246689 Mon Sep 17 00:00:00 2001 From: slorello89 Date: Fri, 1 Dec 2023 09:34:43 -0500 Subject: [PATCH 13/14] api updates per marcs comments --- .../APITypes/BitfieldCommandBuilder.cs | 157 ------------ .../APITypes/BitfieldOperation.cs | 224 ++++++++++++++++++ .../Interfaces/IDatabase.cs | 42 ++-- .../Interfaces/IDatabaseAsync.cs | 44 ++-- .../KeyspaceIsolation/KeyPrefixed.cs | 19 +- .../KeyspaceIsolation/KeyPrefixedDatabase.cs | 18 +- .../PublicAPI/PublicAPI.Shipped.txt | 23 -- .../PublicAPI/PublicAPI.Unshipped.txt | 17 ++ src/StackExchange.Redis/RedisDatabase.cs | 32 +-- tests/StackExchange.Redis.Tests/Bitfield.cs | 101 +++++--- 10 files changed, 388 insertions(+), 289 deletions(-) delete mode 100644 src/StackExchange.Redis/APITypes/BitfieldCommandBuilder.cs create mode 100644 src/StackExchange.Redis/APITypes/BitfieldOperation.cs diff --git a/src/StackExchange.Redis/APITypes/BitfieldCommandBuilder.cs b/src/StackExchange.Redis/APITypes/BitfieldCommandBuilder.cs deleted file mode 100644 index 1b6f2dfc0..000000000 --- a/src/StackExchange.Redis/APITypes/BitfieldCommandBuilder.cs +++ /dev/null @@ -1,157 +0,0 @@ -using System.Collections.Generic; - -namespace StackExchange.Redis; - -/// -/// Builder for bitfield commands that take multiple sub-commands. -/// -public class BitfieldCommandBuilder -{ - private readonly LinkedList _args = new LinkedList(); - private bool _eligibleForReadOnly; - - /// - /// Builds a subcommand for a Bitfield GET, which returns the number stored in the specified offset of a bitfield at the given encoding. - /// - /// The encoding for the subcommand. - /// The offset into the bitfield for the subcommand. - public BitfieldCommandBuilder Get(BitfieldEncoding encoding, BitfieldOffset offset) - { - _eligibleForReadOnly = true; - _args.AddLast(RedisLiterals.GET); - _args.AddLast(encoding.RedisValue); - _args.AddLast(offset.RedisValue); - return this; - } - - /// - /// Builds a Bitfield subcommand which SETs the specified range of bits to the specified value. - /// - /// The encoding of the subcommand. - /// The offset of the subcommand. - /// The value to set. - public BitfieldCommandBuilder Set(BitfieldEncoding encoding, BitfieldOffset offset, long value) - { - _eligibleForReadOnly = false; - _args.AddLast(RedisLiterals.SET); - _args.AddLast(encoding.RedisValue); - _args.AddLast(offset.RedisValue); - _args.AddLast(value); - return this; - } - - /// - /// Builds a subcommand for Bitfield INCRBY, which increments the number at the specified range of bits by the provided value - /// - /// The number's encoding. - /// The offset into the bitfield to increment. - /// The value to increment by. - /// How overflows will be handled when incrementing. - public BitfieldCommandBuilder Incrby(BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap) - { - _eligibleForReadOnly = false; - if (overflowHandling != BitfieldOverflowHandling.Wrap) - { - _args.AddLast(RedisLiterals.OVERFLOW); - _args.AddLast(overflowHandling.AsRedisValue()); - } - - _args.AddLast(RedisLiterals.INCRBY); - _args.AddLast(encoding.RedisValue); - _args.AddLast(offset.RedisValue); - _args.AddLast(increment); - return this; - } - - internal BitfieldCommandMessage Build(int db, RedisKey key, CommandFlags flags, RedisBase redisBase, out ServerEndPoint? server) - { - var features = redisBase.GetFeatures(key, flags, _eligibleForReadOnly ? RedisCommand.BITFIELD_RO : RedisCommand.BITFIELD, out server); - var command = _eligibleForReadOnly && features.ReadOnlyBitfield ? RedisCommand.BITFIELD_RO : RedisCommand.BITFIELD; - return new BitfieldCommandMessage(db, flags, key, command, _args); - } -} - -internal class BitfieldCommandMessage : Message -{ - private readonly LinkedList _args; - private readonly RedisKey _key; - public BitfieldCommandMessage(int db, CommandFlags flags, RedisKey key, RedisCommand command, LinkedList args) : base(db, flags, command) - { - _key = key; - _args = args; - } - - public override int ArgCount => 1 + _args.Count; - - protected override void WriteImpl(PhysicalConnection physical) - { - physical.WriteHeader(Command, ArgCount); - physical.Write(_key); - foreach (var arg in _args) - { - physical.WriteBulkString(arg); - } - } -} - -/// -/// The encoding that a sub-command should use. This is either a signed or unsigned integer of a specified length. -/// -public readonly struct BitfieldEncoding -{ - internal RedisValue RedisValue => $"{(IsSigned ? 'i' : 'u')}{Size}"; - - /// - /// Whether the integer is signed or not. - /// - public bool IsSigned { get; } - - /// - /// The size of the integer. - /// - public byte Size { get; } - - /// - /// Initializes the BitfieldEncoding. - /// - /// Whether the encoding is signed. - /// The size of the integer. - public BitfieldEncoding(bool isSigned, byte size) - { - IsSigned = isSigned; - Size = size; - } -} - -/// -/// An offset into a bitfield. This is either a literal offset (number of bits from the beginning of the bitfield) or an -/// encoding based offset, based off the encoding of the sub-command. -/// -public readonly struct BitfieldOffset -{ - /// - /// Returns the BitfieldOffset as a RedisValue. - /// - internal RedisValue RedisValue => $"{(ByEncoding ? "#" : string.Empty)}{Offset}"; - - /// - /// Whether or not the BitfieldOffset will work off of the sub-commands integer encoding. - /// - public bool ByEncoding { get; } - - /// - /// The number of either bits or encoded integers to offset into the bitfield. - /// - public long Offset { get; } - - /// - /// Initializes a bitfield offset - /// - /// Whether or not the BitfieldOffset will work off of the sub-commands integer encoding. - /// The number of either bits or encoded integers to offset into the bitfield. - public BitfieldOffset(bool byEncoding, long offset) - { - ByEncoding = byEncoding; - Offset = offset; - } -} diff --git a/src/StackExchange.Redis/APITypes/BitfieldOperation.cs b/src/StackExchange.Redis/APITypes/BitfieldOperation.cs new file mode 100644 index 000000000..311502857 --- /dev/null +++ b/src/StackExchange.Redis/APITypes/BitfieldOperation.cs @@ -0,0 +1,224 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace StackExchange.Redis; + +/// +/// Represents a single Bitfield Operation. +/// +public struct BitfieldOperation +{ + private static string CreateOffset(bool offsetByBit, long offset) => $"{(offsetByBit ? string.Empty : "#")}{offset}"; + private static readonly string[] Encodings = Enumerable.Range(0, 127).Select(x => // 0? + { + var size = x % 64; + var signedness = x < 65 ? "i" : "u"; + return $"{signedness}{size}"; + }).ToArray(); + + private static RedisValue CreateEncoding(bool unsigned, byte size) + { + if (size == 0) + { + throw new ArgumentException("Invalid encoding, size must be non-zero", nameof(size)); + } + + if (unsigned && size > 63) + { + throw new ArgumentException( + $"Invalid Encoding, unsigned bitfield operations support a maximum size of 63, provided size: {size}", nameof(size)); + } + + if (size > 64) + { + throw new ArgumentException( + $"Invalid Encoding, signed bitfield operations support a maximum size of 64, provided size: {size}", nameof(size)); + } + + return Encodings[size + (!unsigned ? 0 : 64)]; + } + + internal string Offset; + internal long? Value; + internal BitFieldSubCommand SubCommand; + internal RedisValue Encoding; + internal BitfieldOverflowHandling? BitfieldOverflowHandling; + + /// + /// Creates a Get Bitfield Subcommand struct to retrieve a single integer from the bitfield. + /// + /// The offset into the bitfield to address. + /// The width of the encoding to interpret the bitfield width. + /// Whether or not to offset into the bitfield by bits vs encoding. + /// Whether or not to interpret the number gotten as an unsigned integer. + /// + public static BitfieldOperation Get(long offset, byte width, bool offsetByBit = true, bool unsigned = false) + { + var offsetValue = CreateOffset(offsetByBit, offset); + return new BitfieldOperation + { + Offset = offsetValue, + Value = null, + SubCommand = BitFieldSubCommand.Get, + Encoding = CreateEncoding(unsigned, width) + }; + } + + /// + /// Creates a Set Bitfield SubCommand to set a single integer from the bitfield. + /// + /// The offset into the bitfield to address. + /// The width of the encoding to interpret the bitfield width. + /// The value to set the addressed bits to. + /// Whether or not to offset into the bitfield by bits vs encoding. + /// Whether or not to interpret the number gotten as an unsigned integer. + /// + public static BitfieldOperation Set(long offset, byte width, long value, bool offsetByBit = true, bool unsigned = false) + { + var offsetValue = CreateOffset(offsetByBit, offset); + return new BitfieldOperation + { + Offset = offsetValue, + Value = value, + SubCommand = BitFieldSubCommand.Set, + Encoding = CreateEncoding(unsigned, width) + }; + } + + /// + /// Creates an Increment Bitfield SubCommand to increment a single integer from the bitfield. + /// + /// The offset into the bitfield to address. + /// The width of the encoding to interpret the bitfield width. + /// The value to set the addressed bits to. + /// Whether or not to offset into the bitfield by bits vs encoding. + /// Whether or not to interpret the number gotten as an unsigned integer. + /// How to handle overflows. + /// + public static BitfieldOperation Increment(long offset, byte width, long increment, bool offsetByBit = true, bool unsigned = false, BitfieldOverflowHandling overflowHandling = Redis.BitfieldOverflowHandling.Wrap) + { + var offsetValue = CreateOffset(offsetByBit, offset); + return new BitfieldOperation + { + Offset = offsetValue, + Value = increment, + SubCommand = BitFieldSubCommand.Increment, + Encoding = CreateEncoding(unsigned, width), + BitfieldOverflowHandling = overflowHandling + }; + } + + internal IEnumerable EnumerateArgs() + { + if (SubCommand != BitFieldSubCommand.Get) + { + if (BitfieldOverflowHandling is not null && BitfieldOverflowHandling != Redis.BitfieldOverflowHandling.Wrap) + { + yield return RedisLiterals.OVERFLOW; + yield return BitfieldOverflowHandling.Value.AsRedisValue(); + } + } + + yield return SubCommand.AsRedisValue(); + yield return Encoding; + yield return Offset; + if (SubCommand != BitFieldSubCommand.Get) + { + if (Value is null) + { + throw new ArgumentNullException($"Value must not be null for {SubCommand.AsRedisValue()} commands"); + } + + yield return Value; + } + } + + internal int NumArgs() + { + var numArgs = 3; + if (SubCommand != BitFieldSubCommand.Get) + { + numArgs += BitfieldOverflowHandling is not null && BitfieldOverflowHandling != Redis.BitfieldOverflowHandling.Wrap ? 3 : 1; + } + + return numArgs; + } +} + +internal static class BitfieldOperationExtensions +{ + internal static BitfieldCommandMessage BuildMessage(this BitfieldOperation[] subCommands, int db, RedisKey key, + CommandFlags flags, RedisBase redisBase, out ServerEndPoint? server) + { + var eligibleForReadOnly = subCommands.All(x => x.SubCommand == BitFieldSubCommand.Get); + var features = redisBase.GetFeatures(key, flags, eligibleForReadOnly ? RedisCommand.BITFIELD_RO : RedisCommand.BITFIELD, out server); + var command = eligibleForReadOnly && features.ReadOnlyBitfield ? RedisCommand.BITFIELD_RO : RedisCommand.BITFIELD; + return new BitfieldCommandMessage(db, flags, key, command, subCommands.SelectMany(x=>x.EnumerateArgs()).ToArray()); + } + + internal static BitfieldCommandMessage BuildMessage(this BitfieldOperation subCommand, int db, RedisKey key, + CommandFlags flags, RedisBase redisBase, out ServerEndPoint? server) + { + var eligibleForReadOnly = subCommand.SubCommand == BitFieldSubCommand.Get; + var features = redisBase.GetFeatures(key, flags, eligibleForReadOnly ? RedisCommand.BITFIELD_RO : RedisCommand.BITFIELD, out server); + var command = eligibleForReadOnly && features.ReadOnlyBitfield ? RedisCommand.BITFIELD_RO : RedisCommand.BITFIELD; + return new BitfieldCommandMessage(db, flags, key, command, subCommand.EnumerateArgs().ToArray()); + } +} + +/// +/// Bitfield subcommands. +/// +public enum BitFieldSubCommand +{ + /// + /// Subcommand to get the bitfield value. + /// + Get, + + /// + /// Subcommand to set the bitfield value. + /// + Set, + + /// + /// Subcommand to increment the bitfield value + /// + Increment +} + +internal static class BitfieldSubCommandExtensions +{ + internal static RedisValue AsRedisValue(this BitFieldSubCommand subCommand) => + subCommand switch + { + BitFieldSubCommand.Get => RedisLiterals.GET, + BitFieldSubCommand.Set => RedisLiterals.SET, + BitFieldSubCommand.Increment => RedisLiterals.INCRBY, + _ => throw new ArgumentOutOfRangeException(nameof(subCommand)) + }; +} + +internal class BitfieldCommandMessage : Message +{ + private readonly IEnumerable _args; + private readonly RedisKey _key; + public BitfieldCommandMessage(int db, CommandFlags flags, RedisKey key, RedisCommand command, RedisValue[] args) : base(db, flags, command) + { + _key = key; + _args = args; + } + + public override int ArgCount => 1 + _args.Count(); + + protected override void WriteImpl(PhysicalConnection physical) + { + physical.WriteHeader(Command, ArgCount); + physical.Write(_key); + foreach (var arg in _args) + { + physical.WriteBulkString(arg); + } + } +} diff --git a/src/StackExchange.Redis/Interfaces/IDatabase.cs b/src/StackExchange.Redis/Interfaces/IDatabase.cs index 99f8d2f4b..87ccf925e 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabase.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabase.cs @@ -2595,58 +2595,64 @@ IEnumerable SortedSetScan(RedisKey key, long StringBitCount(RedisKey key, long start = 0, long end = -1, StringIndexType indexType = StringIndexType.Byte, CommandFlags flags = CommandFlags.None); /// - /// Executes a set of Bitfield subcommands as constructed by the against the bitfield at the provided . + /// Executes a set of Bitfield subcommands as constructed by the against the bitfield at the provided . /// Will run as a BITFIELD_RO if all operations are read-only and the command is available. /// /// The key of the string. - /// The subcommands to execute against the bitfield. + /// The subcommands to execute against the bitfield. /// The flags to use for this operation. /// An array of numbers corresponding to the result of each sub-command. For increment subcommands, these can be null. /// /// , /// /// - long?[] StringBitfield(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None); + long?[] StringBitfield(RedisKey key, BitfieldOperation[] subCommands, CommandFlags flags = CommandFlags.None); /// - /// Pulls a single number out of a bitfield of the provided at the given offset. + /// Pulls a single number out of a bitfield of the provided encoding at the given offset. /// Will execute a BITFIELD_RO if possible. /// /// The key for the string. - /// The encoding of the number. /// The offset into the bitfield to pull the number from. + /// The width of the encoding to interpret the bitfield width. + /// Whether or not to offset into the bitfield by bits vs encoding. + /// Whether or not to interpret the number gotten as an unsigned integer. /// The flags to use for this operation. - /// The number of the given at the provided . + /// The number of the given encoding at the provided . /// /// , /// /// - long StringBitfieldGet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None); + long StringBitfieldGet(RedisKey key, long offset, byte width, bool offsetByBit = true, bool unsigned = false, CommandFlags flags = CommandFlags.None); /// - /// Sets a single number in a bitfield at the provided to the provided, in the given . + /// Sets a single number in a bitfield at the provided offset to the provided, in the given encoding. /// /// The key for the string. - /// The encoding of the number. - /// The offset into the bitfield to pull the number from. - /// the value to set the bitfield to. + /// The offset into the bitfield to address. + /// The width of the encoding to interpret the bitfield width. + /// The value to set the addressed bits to. + /// Whether or not to offset into the bitfield by bits vs encoding. + /// Whether or not to interpret the number gotten as an unsigned integer. /// The flags to use for this operation. /// The previous value as an at the provided . /// - long StringBitfieldSet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None); + long StringBitfieldSet(RedisKey key, long offset, byte width, long value, bool offsetByBit = true, bool unsigned = false, CommandFlags flags = CommandFlags.None); /// - /// Increments a single number in a bitfield at the provided in the provided by the given . + /// Increments a single number in a bitfield at the provided in the provided encoding by the given . /// /// The key for the string. - /// The encoding of the number. - /// The offset into the bitfield to pull the number from. - /// the value to increment the bitfield by. - /// The way integer overflows are handled. + /// The offset into the bitfield to address. + /// The width of the encoding to interpret the bitfield width. + /// The value to set the addressed bits to. + /// Whether or not to offset into the bitfield by bits vs encoding. + /// Whether or not to interpret the number gotten as an unsigned integer. + /// How to handle overflows. /// The flags to use for this operation. /// The new value of the given at the provided after the INCRBY is applied, represented as an . Returns if the operation fails. /// - long? StringBitfieldIncrement(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None); + long? StringBitfieldIncrement(RedisKey key, long offset, byte width, long increment, bool offsetByBit = true, bool unsigned = false, BitfieldOverflowHandling overflowHandling = Redis.BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None); /// /// Perform a bitwise operation between multiple keys (containing string values) and store the result in the destination key. diff --git a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs index c50f0d4f4..269f40815 100644 --- a/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs +++ b/src/StackExchange.Redis/Interfaces/IDatabaseAsync.cs @@ -2548,58 +2548,64 @@ IAsyncEnumerable SortedSetScanAsync(RedisKey key, Task StringBitCountAsync(RedisKey key, long start = 0, long end = -1, StringIndexType indexType = StringIndexType.Byte, CommandFlags flags = CommandFlags.None); /// - /// Executes a set of Bitfield subcommands as constructed by the against the bitfield at the provided . + /// Executes a set of Bitfield subcommands as constructed by the against the bitfield at the provided . /// Will run as a BITFIELD_RO if all operations are read-only and the command is available. /// /// The key of the string. - /// The subcommands to execute against the bitfield. + /// The subcommands to execute against the bitfield. /// The flags to use for this operation. /// An array of numbers corresponding to the result of each sub-command. For increment subcommands, these can be null. /// /// , /// /// - Task StringBitfieldAsync(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None); + Task StringBitfieldAsync(RedisKey key, BitfieldOperation[] subCommands, CommandFlags flags = CommandFlags.None); /// - /// Pulls a single number out of a bitfield of the provided at the given offset. + /// Pulls a single number out of a bitfield of the provided encoding at the given offset. /// Will execute a BITFIELD_RO if possible. /// /// The key for the string. - /// The encoding of the number. - /// The offset into the bitfield to pull the number from. + /// The offset into the bitfield to address. + /// The width of the encoding to interpret the bitfield width. + /// Whether or not to offset into the bitfield by bits vs encoding. + /// Whether or not to interpret the number gotten as an unsigned integer. /// The flags to use for this operation. - /// The number of the given at the provided . + /// The number of the given encoding at the provided . /// /// , /// /// - Task StringBitfieldGetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None); + Task StringBitfieldGetAsync(RedisKey key, long offset, byte width, bool offsetByBit = true, bool unsigned = false, CommandFlags flags = CommandFlags.None); /// - /// Sets a single number in a bitfield at the provided to the provided, in the given . + /// Sets a single number in a bitfield at the provided to the provided, in the given encoding. /// /// The key for the string. - /// The encoding of the number. - /// The offset into the bitfield to pull the number from. - /// the value to set the bitfield to. + /// The offset into the bitfield to address. + /// The width of the encoding to interpret the bitfield width. + /// The value to set the addressed bits to. + /// Whether or not to offset into the bitfield by bits vs encoding. + /// Whether or not to interpret the number gotten as an unsigned integer. /// The flags to use for this operation. /// The previous value as an at the provided . /// - Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None); + Task StringBitfieldSetAsync(RedisKey key, long offset, byte width, long value, bool offsetByBit = true, bool unsigned = false, CommandFlags flags = CommandFlags.None); /// - /// Increments a single number in a bitfield at the provided in the provided by the given . + /// Increments a single number in a bitfield at the provided in the provided encoding by the given . /// /// The key for the string. - /// The encoding of the number. - /// The offset into the bitfield to pull the number from. - /// the value to increment the bitfield by. - /// The way integer overflows are handled. + /// The offset into the bitfield to address. + /// The width of the encoding to interpret the bitfield width. + /// The value to set the addressed bits to. + /// Whether or not to offset into the bitfield by bits vs encoding. + /// Whether or not to interpret the number gotten as an unsigned integer. + /// How to handle overflows. /// The flags to use for this operation. /// The new value of the given at the provided after the INCRBY is applied, represented as an . Returns if the operation fails. /// - Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None); + Task StringBitfieldIncrementAsync(RedisKey key, long offset, byte width, long increment, bool offsetByBit = true, bool unsigned = false, BitfieldOverflowHandling overflowHandling = Redis.BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None); /// /// Perform a bitwise operation between multiple keys (containing string values) and store the result in the destination key. diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs index 19bf83110..70293d7ca 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixed.cs @@ -630,22 +630,21 @@ public Task StringAppendAsync(RedisKey key, RedisValue value, CommandFlags public Task StringBitCountAsync(RedisKey key, long start, long end, CommandFlags flags) => Inner.StringBitCountAsync(ToInner(key), start, end, flags); - + public Task StringBitCountAsync(RedisKey key, long start = 0, long end = -1, StringIndexType indexType = StringIndexType.Byte, CommandFlags flags = CommandFlags.None) => Inner.StringBitCountAsync(ToInner(key), start, end, indexType, flags); - public Task StringBitfieldGetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, - CommandFlags flags = CommandFlags.None) => - Inner.StringBitfieldGetAsync(ToInner(key), encoding, offset, flags); + public Task StringBitfieldGetAsync(RedisKey key, long offset, byte width, bool offsetByBit = true, bool unsigned = false, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldGetAsync(ToInner(key), offset, width, offsetByBit, unsigned, flags); - public Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None) => - Inner.StringBitfieldSetAsync(ToInner(key), encoding, offset, value, flags); + public Task StringBitfieldSetAsync(RedisKey key, long offset, byte width, long value, bool offsetByBit = true, bool unsigned = false, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldSetAsync(ToInner(key), offset, width, value, offsetByBit, unsigned, flags); - public Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) => - Inner.StringBitfieldIncrementAsync(ToInner(key), encoding, offset, increment, overflowHandling, flags); + public Task StringBitfieldIncrementAsync(RedisKey key, long offset, byte width, long increment, bool offsetByBit = true, bool unsigned = false, BitfieldOverflowHandling overflowHandling = Redis.BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldIncrementAsync(ToInner(key), offset, width, increment, offsetByBit, unsigned, overflowHandling, flags); - public Task StringBitfieldAsync(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None) => - Inner.StringBitfieldAsync(ToInner(key), builder, flags); + public Task StringBitfieldAsync(RedisKey key, BitfieldOperation[] subCommands, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldAsync(ToInner(key), subCommands, flags); public Task StringBitOperationAsync(Bitwise operation, RedisKey destination, RedisKey[] keys, CommandFlags flags = CommandFlags.None) => Inner.StringBitOperationAsync(operation, ToInner(destination), ToInner(keys), flags); diff --git a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs index 0c9d6c7f7..fe1eec523 100644 --- a/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs +++ b/src/StackExchange.Redis/KeyspaceIsolation/KeyPrefixedDatabase.cs @@ -614,21 +614,21 @@ public long StringAppend(RedisKey key, RedisValue value, CommandFlags flags = Co public long StringBitCount(RedisKey key, long start, long end, CommandFlags flags) => Inner.StringBitCount(ToInner(key), start, end, flags); - + public long StringBitCount(RedisKey key, long start = 0, long end = -1, StringIndexType indexType = StringIndexType.Byte, CommandFlags flags = CommandFlags.None) => Inner.StringBitCount(ToInner(key), start, end, indexType, flags); - public long StringBitfieldGet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None) => - Inner.StringBitfieldGet(ToInner(key), encoding, offset, flags); + public long StringBitfieldGet(RedisKey key, long offset, byte width, bool offsetByBit = true, bool unsigned = false, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldGet(ToInner(key), offset, width, offsetByBit, unsigned, flags); - public long StringBitfieldSet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None) => - Inner.StringBitfieldSet(ToInner(key), encoding, offset, value, flags); + public long StringBitfieldSet(RedisKey key, long offset, byte width, long value, bool offsetByBit = true, bool unsigned = false, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldSet(ToInner(key), offset, width, value, offsetByBit, unsigned, flags); - public long? StringBitfieldIncrement(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) => - Inner.StringBitfieldIncrement(ToInner(key), encoding, offset, increment, overflowHandling, flags); + public long? StringBitfieldIncrement(RedisKey key, long offset, byte width, long increment, bool offsetByBit = true, bool unsigned = false, BitfieldOverflowHandling overflowHandling = Redis.BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfieldIncrement(ToInner(key), offset, width, increment, offsetByBit, unsigned, overflowHandling, flags); - public long?[] StringBitfield(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None) => - Inner.StringBitfield(key, builder, flags); + public long?[] StringBitfield(RedisKey key, BitfieldOperation[] subCommands, CommandFlags flags = CommandFlags.None) => + Inner.StringBitfield(ToInner(key), subCommands, flags); public long StringBitOperation(Bitwise operation, RedisKey destination, RedisKey[] keys, CommandFlags flags = CommandFlags.None) => Inner.StringBitOperation(operation, ToInner(destination), ToInner(keys), flags); diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt index ffda71467..871d55de1 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Shipped.txt @@ -65,21 +65,6 @@ StackExchange.Redis.BacklogPolicy.AbortPendingOnConnectionFailure.init -> void StackExchange.Redis.BacklogPolicy.BacklogPolicy() -> void StackExchange.Redis.BacklogPolicy.QueueWhileDisconnected.get -> bool StackExchange.Redis.BacklogPolicy.QueueWhileDisconnected.init -> void -StackExchange.Redis.BitfieldCommandBuilder -StackExchange.Redis.BitfieldCommandBuilder.BitfieldCommandBuilder() -> void -StackExchange.Redis.BitfieldCommandBuilder.Get(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset) -> StackExchange.Redis.BitfieldCommandBuilder! -StackExchange.Redis.BitfieldCommandBuilder.Incrby(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap) -> StackExchange.Redis.BitfieldCommandBuilder! -StackExchange.Redis.BitfieldCommandBuilder.Set(StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value) -> StackExchange.Redis.BitfieldCommandBuilder! -StackExchange.Redis.BitfieldEncoding -StackExchange.Redis.BitfieldEncoding.BitfieldEncoding() -> void -StackExchange.Redis.BitfieldEncoding.BitfieldEncoding(bool isSigned, byte size) -> void -StackExchange.Redis.BitfieldEncoding.IsSigned.get -> bool -StackExchange.Redis.BitfieldEncoding.Size.get -> byte -StackExchange.Redis.BitfieldOffset -StackExchange.Redis.BitfieldOffset.BitfieldOffset() -> void -StackExchange.Redis.BitfieldOffset.BitfieldOffset(bool byEncoding, long offset) -> void -StackExchange.Redis.BitfieldOffset.ByEncoding.get -> bool -StackExchange.Redis.BitfieldOffset.Offset.get -> long StackExchange.Redis.BitfieldOverflowHandling StackExchange.Redis.BitfieldOverflowHandling.Fail = 2 -> StackExchange.Redis.BitfieldOverflowHandling StackExchange.Redis.BitfieldOverflowHandling.Saturate = 1 -> StackExchange.Redis.BitfieldOverflowHandling @@ -733,10 +718,6 @@ StackExchange.Redis.IDatabase.StreamTrim(StackExchange.Redis.RedisKey key, int m StackExchange.Redis.IDatabase.StringAppend(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringBitCount(StackExchange.Redis.RedisKey key, long start, long end, StackExchange.Redis.CommandFlags flags) -> long StackExchange.Redis.IDatabase.StringBitCount(StackExchange.Redis.RedisKey key, long start = 0, long end = -1, StackExchange.Redis.StringIndexType indexType = StackExchange.Redis.StringIndexType.Byte, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long -StackExchange.Redis.IDatabase.StringBitfield(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldCommandBuilder! builder, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long?[]! -StackExchange.Redis.IDatabase.StringBitfieldGet(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long -StackExchange.Redis.IDatabase.StringBitfieldIncrement(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long? -StackExchange.Redis.IDatabase.StringBitfieldSet(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringBitOperation(StackExchange.Redis.Bitwise operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey first, StackExchange.Redis.RedisKey second = default(StackExchange.Redis.RedisKey), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringBitOperation(StackExchange.Redis.Bitwise operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey[]! keys, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long StackExchange.Redis.IDatabase.StringBitPosition(StackExchange.Redis.RedisKey key, bool bit, long start, long end, StackExchange.Redis.CommandFlags flags) -> long @@ -966,10 +947,6 @@ StackExchange.Redis.IDatabaseAsync.StreamTrimAsync(StackExchange.Redis.RedisKey StackExchange.Redis.IDatabaseAsync.StringAppendAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.RedisValue value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitCountAsync(StackExchange.Redis.RedisKey key, long start, long end, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitCountAsync(StackExchange.Redis.RedisKey key, long start = 0, long end = -1, StackExchange.Redis.StringIndexType indexType = StackExchange.Redis.StringIndexType.Byte, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StringBitfieldAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldCommandBuilder! builder, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StringBitfieldGetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StringBitfieldIncrementAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long increment, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! -StackExchange.Redis.IDatabaseAsync.StringBitfieldSetAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldEncoding encoding, StackExchange.Redis.BitfieldOffset offset, long value, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitOperationAsync(StackExchange.Redis.Bitwise operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey first, StackExchange.Redis.RedisKey second = default(StackExchange.Redis.RedisKey), StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitOperationAsync(StackExchange.Redis.Bitwise operation, StackExchange.Redis.RedisKey destination, StackExchange.Redis.RedisKey[]! keys, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IDatabaseAsync.StringBitPositionAsync(StackExchange.Redis.RedisKey key, bool bit, long start, long end, StackExchange.Redis.CommandFlags flags) -> System.Threading.Tasks.Task! diff --git a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt index eff457070..36fe35ed2 100644 --- a/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt +++ b/src/StackExchange.Redis/PublicAPI/PublicAPI.Unshipped.txt @@ -1,9 +1,23 @@ abstract StackExchange.Redis.RedisResult.ToString(out string? type) -> string? override sealed StackExchange.Redis.RedisResult.ToString() -> string! override StackExchange.Redis.Role.Master.Replica.ToString() -> string! +StackExchange.Redis.BitfieldOperation +StackExchange.Redis.BitfieldOperation.BitfieldOperation() -> void +StackExchange.Redis.BitFieldSubCommand +StackExchange.Redis.BitFieldSubCommand.Get = 0 -> StackExchange.Redis.BitFieldSubCommand +StackExchange.Redis.BitFieldSubCommand.Increment = 2 -> StackExchange.Redis.BitFieldSubCommand +StackExchange.Redis.BitFieldSubCommand.Set = 1 -> StackExchange.Redis.BitFieldSubCommand StackExchange.Redis.ClientInfo.Protocol.get -> StackExchange.Redis.RedisProtocol? StackExchange.Redis.ConfigurationOptions.Protocol.get -> StackExchange.Redis.RedisProtocol? StackExchange.Redis.ConfigurationOptions.Protocol.set -> void +StackExchange.Redis.IDatabase.StringBitfield(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldOperation[]! subCommands, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long?[]! +StackExchange.Redis.IDatabase.StringBitfieldGet(StackExchange.Redis.RedisKey key, long offset, byte width, bool offsetByBit = true, bool unsigned = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabase.StringBitfieldIncrement(StackExchange.Redis.RedisKey key, long offset, byte width, long increment, bool offsetByBit = true, bool unsigned = false, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long? +StackExchange.Redis.IDatabase.StringBitfieldSet(StackExchange.Redis.RedisKey key, long offset, byte width, long value, bool offsetByBit = true, bool unsigned = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> long +StackExchange.Redis.IDatabaseAsync.StringBitfieldAsync(StackExchange.Redis.RedisKey key, StackExchange.Redis.BitfieldOperation[]! subCommands, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StringBitfieldGetAsync(StackExchange.Redis.RedisKey key, long offset, byte width, bool offsetByBit = true, bool unsigned = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StringBitfieldIncrementAsync(StackExchange.Redis.RedisKey key, long offset, byte width, long increment, bool offsetByBit = true, bool unsigned = false, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! +StackExchange.Redis.IDatabaseAsync.StringBitfieldSetAsync(StackExchange.Redis.RedisKey key, long offset, byte width, long value, bool offsetByBit = true, bool unsigned = false, StackExchange.Redis.CommandFlags flags = StackExchange.Redis.CommandFlags.None) -> System.Threading.Tasks.Task! StackExchange.Redis.IServer.Protocol.get -> StackExchange.Redis.RedisProtocol StackExchange.Redis.RedisFeatures.ClientId.get -> bool StackExchange.Redis.RedisFeatures.Equals(StackExchange.Redis.RedisFeatures other) -> bool @@ -25,6 +39,9 @@ StackExchange.Redis.ResultType.Null = 8 -> StackExchange.Redis.ResultType StackExchange.Redis.ResultType.Push = 37 -> StackExchange.Redis.ResultType StackExchange.Redis.ResultType.Set = 21 -> StackExchange.Redis.ResultType StackExchange.Redis.ResultType.VerbatimString = 12 -> StackExchange.Redis.ResultType +static StackExchange.Redis.BitfieldOperation.Get(long offset, byte width, bool offsetByBit = true, bool unsigned = false) -> StackExchange.Redis.BitfieldOperation +static StackExchange.Redis.BitfieldOperation.Increment(long offset, byte width, long increment, bool offsetByBit = true, bool unsigned = false, StackExchange.Redis.BitfieldOverflowHandling overflowHandling = StackExchange.Redis.BitfieldOverflowHandling.Wrap) -> StackExchange.Redis.BitfieldOperation +static StackExchange.Redis.BitfieldOperation.Set(long offset, byte width, long value, bool offsetByBit = true, bool unsigned = false) -> StackExchange.Redis.BitfieldOperation static StackExchange.Redis.RedisResult.Create(StackExchange.Redis.RedisResult![]! values, StackExchange.Redis.ResultType resultType) -> StackExchange.Redis.RedisResult! static StackExchange.Redis.RedisResult.Create(StackExchange.Redis.RedisValue[]! values, StackExchange.Redis.ResultType resultType) -> StackExchange.Redis.RedisResult! virtual StackExchange.Redis.RedisResult.Length.get -> int diff --git a/src/StackExchange.Redis/RedisDatabase.cs b/src/StackExchange.Redis/RedisDatabase.cs index 4a2f2a80f..6f4fbd4fd 100644 --- a/src/StackExchange.Redis/RedisDatabase.cs +++ b/src/StackExchange.Redis/RedisDatabase.cs @@ -2920,51 +2920,51 @@ public Task StringBitCountAsync(RedisKey key, long start = 0, long end = - return ExecuteAsync(msg, ResultProcessor.Int64); } - public long?[] StringBitfield(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None) + public long?[] StringBitfield(RedisKey key, BitfieldOperation[] subCommands, CommandFlags flags = CommandFlags.None) { - var msg = builder.Build(Database, key, flags, this, out ServerEndPoint? server); + var msg = subCommands.BuildMessage(Database, key, flags, this, out var server); return ExecuteSync(msg, ResultProcessor.NullableInt64Array, defaultValue: Array.Empty(), server: server); } - public Task StringBitfieldAsync(RedisKey key, BitfieldCommandBuilder builder, CommandFlags flags = CommandFlags.None) + public Task StringBitfieldAsync(RedisKey key, BitfieldOperation[] subCommands, CommandFlags flags = CommandFlags.None) { - var msg = builder.Build(Database, key, flags, this, out ServerEndPoint? server); + var msg = subCommands.BuildMessage(Database, key, flags, this, out var server); return ExecuteAsync(msg, ResultProcessor.NullableInt64Array, defaultValue: Array.Empty(), server: server); } - public long StringBitfieldGet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None) + public long StringBitfieldGet(RedisKey key, long offset, byte width, bool offsetByBit = true, bool unsigned = false, CommandFlags flags = CommandFlags.None) { - var msg = new BitfieldCommandBuilder().Get(encoding, offset).Build(Database, key, flags, this, out ServerEndPoint? server); + var msg = BitfieldOperation.Get(offset, width, offsetByBit, unsigned).BuildMessage(Database, key, flags, this, out var server); return ExecuteSync(msg, ResultProcessor.Int64, server); } - public Task StringBitfieldGetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, CommandFlags flags = CommandFlags.None) + public Task StringBitfieldGetAsync(RedisKey key, long offset, byte width, bool offsetByBit = true, bool unsigned = false, CommandFlags flags = CommandFlags.None) { - var msg = new BitfieldCommandBuilder().Get(encoding, offset).Build(Database, key, flags, this, out ServerEndPoint? server); + var msg = BitfieldOperation.Get(offset, width, offsetByBit, unsigned).BuildMessage(Database, key, flags, this, out var server); return ExecuteAsync(msg, ResultProcessor.Int64, server); } - public long StringBitfieldSet(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None) + public long StringBitfieldSet(RedisKey key, long offset, byte width, long value, bool offsetByBit = true, bool unsigned = false, CommandFlags flags = CommandFlags.None) { - var msg = new BitfieldCommandBuilder().Set(encoding, offset, value).Build(Database, key, flags, this, out ServerEndPoint? server); + var msg = BitfieldOperation.Set(offset, width, value, offsetByBit, unsigned).BuildMessage(Database, key, flags, this, out var server); return ExecuteSync(msg, ResultProcessor.Int64, server); } - public Task StringBitfieldSetAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long value, CommandFlags flags = CommandFlags.None) + public Task StringBitfieldSetAsync(RedisKey key, long offset, byte width, long value, bool offsetByBit = true, bool unsigned = false, CommandFlags flags = CommandFlags.None) { - var msg = new BitfieldCommandBuilder().Set(encoding, offset, value).Build(Database, key, flags, this, out ServerEndPoint? server); + var msg = BitfieldOperation.Set(offset, width, value, offsetByBit, unsigned).BuildMessage(Database, key, flags, this, out var server); return ExecuteAsync(msg, ResultProcessor.Int64, server); } - public long? StringBitfieldIncrement(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) + public long? StringBitfieldIncrement(RedisKey key, long offset, byte width, long increment, bool offsetByBit = true, bool unsigned = false, BitfieldOverflowHandling overflowHandling = Redis.BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) { - var msg = new BitfieldCommandBuilder().Incrby(encoding, offset, increment, overflowHandling).Build(Database, key, flags, this, out ServerEndPoint? server); + var msg = BitfieldOperation.Increment(offset,width, increment, offsetByBit, unsigned, overflowHandling).BuildMessage(Database, key, flags, this, out var server); return ExecuteSync(msg, ResultProcessor.NullableInt64, server); } - public Task StringBitfieldIncrementAsync(RedisKey key, BitfieldEncoding encoding, BitfieldOffset offset, long increment, BitfieldOverflowHandling overflowHandling = BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) + public Task StringBitfieldIncrementAsync(RedisKey key, long offset, byte width, long increment, bool offsetByBit = true, bool unsigned = false, BitfieldOverflowHandling overflowHandling = Redis.BitfieldOverflowHandling.Wrap, CommandFlags flags = CommandFlags.None) { - var msg = new BitfieldCommandBuilder().Incrby(encoding, offset, increment, overflowHandling).Build(Database, key, flags, this, out ServerEndPoint? server); + var msg = BitfieldOperation.Increment(offset,width, increment, offsetByBit, unsigned, overflowHandling).BuildMessage(Database, key, flags, this, out var server); return ExecuteAsync(msg, ResultProcessor.NullableInt64, server); } diff --git a/tests/StackExchange.Redis.Tests/Bitfield.cs b/tests/StackExchange.Redis.Tests/Bitfield.cs index 5cada2413..517c53bb7 100644 --- a/tests/StackExchange.Redis.Tests/Bitfield.cs +++ b/tests/StackExchange.Redis.Tests/Bitfield.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -17,23 +18,28 @@ public void TestBitfieldHappyPath() RedisKey key = Me(); db.KeyDelete(key); - var encoding = new BitfieldEncoding(isSigned: true, 10); - var offset = new BitfieldOffset(true, 1); + var offset = 1; + byte width = 10; + var byBit = false; + var unsigned = false; + // should be the old value - var setResult = db.StringBitfieldSet(key, encoding, offset, -255); - var getResult = db.StringBitfieldGet(key, encoding, offset); - var incrementResult = db.StringBitfieldIncrement(key, encoding, offset, -10); + var setResult = db.StringBitfieldSet(key, offset, width, -255, byBit, unsigned); + var getResult = db.StringBitfieldGet(key, offset, width, byBit, unsigned); + var incrementResult = db.StringBitfieldIncrement(key, offset, width, -10, byBit, unsigned); Assert.Equal(0, setResult); Assert.Equal(-255, getResult); Assert.Equal(-265, incrementResult); - encoding = new BitfieldEncoding(isSigned: false, 18); - offset = new BitfieldOffset(false, 22); + width = 18; + unsigned = true; + offset = 22; + byBit = true; - setResult = db.StringBitfieldSet(key, encoding, offset, 262123); - getResult = db.StringBitfieldGet(key, encoding, offset); - incrementResult = db.StringBitfieldIncrement(key, encoding, offset, 20); + setResult = db.StringBitfieldSet(key, offset, width, 262123, byBit, unsigned); + getResult = db.StringBitfieldGet(key, offset, width, byBit, unsigned); + incrementResult = db.StringBitfieldIncrement(key, offset, width, 20, byBit, unsigned); Assert.Equal(0, setResult); Assert.Equal(262123, getResult); @@ -48,23 +54,27 @@ public async Task TestBitfieldHappyPathAsync() RedisKey key = Me(); db.KeyDelete(key); - var encoding = new BitfieldEncoding(isSigned: true, 10); - var offset = new BitfieldOffset(true, 1); + var offset = 1; + byte width = 10; + var byBit = false; + var unsigned = false; // should be the old value - var setResult = await db.StringBitfieldSetAsync(key, encoding, offset, -255); - var getResult = await db.StringBitfieldGetAsync(key, encoding, offset); - var incrementResult = await db.StringBitfieldIncrementAsync(key, encoding, offset, -10); + var setResult = await db.StringBitfieldSetAsync(key, offset, width, -255, byBit, unsigned); + var getResult = await db.StringBitfieldGetAsync(key, offset, width, byBit, unsigned); + var incrementResult = await db.StringBitfieldIncrementAsync(key, offset, width, -10, byBit, unsigned); Assert.Equal(0, setResult); Assert.Equal(-255, getResult); Assert.Equal(-265, incrementResult); - encoding = new BitfieldEncoding(isSigned: false, 18); - offset = new BitfieldOffset(false, 22); + width = 18; + unsigned = true; + offset = 22; + byBit = true; - setResult = await db.StringBitfieldSetAsync(key, encoding, offset, 262123); - getResult = await db.StringBitfieldGetAsync(key, encoding, offset); - incrementResult = await db.StringBitfieldIncrementAsync(key, encoding, offset, 20); + setResult = await db.StringBitfieldSetAsync(key, offset, width, 262123, byBit, unsigned); + getResult = await db.StringBitfieldGetAsync(key, offset, width, byBit, unsigned); + incrementResult = await db.StringBitfieldIncrementAsync(key, offset, width, 20, byBit, unsigned); Assert.Equal(0, setResult); Assert.Equal(262123, getResult); @@ -79,16 +89,17 @@ public async Task TestBitfieldMulti() RedisKey key = Me(); db.KeyDelete(key); - var builder = new BitfieldCommandBuilder() - .Set(new BitfieldEncoding(isSigned: false, 3), new BitfieldOffset(false, 5), 7) - .Get(new BitfieldEncoding(isSigned: false, 3), new BitfieldOffset(false, 5)) - .Incrby(new BitfieldEncoding(isSigned: false, 3), new BitfieldOffset(false, 5), -1) - .Set(new BitfieldEncoding(isSigned: true, 45), new BitfieldOffset(true, 1), 17592186044415) - .Get(new BitfieldEncoding(isSigned: true, 45), new BitfieldOffset(true, 1)) - .Incrby(new BitfieldEncoding(isSigned: true, 45), new BitfieldOffset(true, 1), 1, - BitfieldOverflowHandling.Fail); + var subCommands = new[] + { + BitfieldOperation.Set(5, 3, 7, true, true), + BitfieldOperation.Get(5, 3, true, true), + BitfieldOperation.Increment(5, 3, -1, true, true), + BitfieldOperation.Set(1, 45, 17592186044415, false, false), + BitfieldOperation.Get(1, 45, false, false), + BitfieldOperation.Increment(1, 45, 1, false, false, BitfieldOverflowHandling.Fail) + }; - var res = await db.StringBitfieldAsync(key, builder); + var res = await db.StringBitfieldAsync(key, subCommands); Assert.Equal(0, res[0]); Assert.Equal(7, res[1]); @@ -106,16 +117,32 @@ public async Task TestOverflows() RedisKey key = Me(); db.KeyDelete(key); - var encoding = new BitfieldEncoding(isSigned: true, 3); - var offset = new BitfieldOffset(true, 3); + var offset = 3; + byte width = 3; + var byBit = false; + var unsigned = false; - await db.StringBitfieldSetAsync(key, encoding, offset, 3); - var incrFail = await db.StringBitfieldIncrementAsync(key, encoding, offset, 1, BitfieldOverflowHandling.Fail); + await db.StringBitfieldSetAsync(key, offset, width, 3, byBit, unsigned); + var incrFail = await db.StringBitfieldIncrementAsync(key, offset, width, 1, byBit, unsigned, BitfieldOverflowHandling.Fail); Assert.Null(incrFail); - var incrWrap = await db.StringBitfieldIncrementAsync(key, encoding, offset, 1); + var incrWrap = await db.StringBitfieldIncrementAsync(key, offset, width, 1, byBit, unsigned); Assert.Equal(-4, incrWrap); - await db.StringBitfieldSetAsync(key, encoding, offset, 3); - var incrSat = await db.StringBitfieldIncrementAsync(key, encoding, offset, 1, BitfieldOverflowHandling.Saturate); + await db.StringBitfieldSetAsync(key, offset, width, 3, byBit, unsigned); + var incrSat = await db.StringBitfieldIncrementAsync(key, offset, width, 1, byBit, unsigned, BitfieldOverflowHandling.Saturate); Assert.Equal(3, incrSat); } + + [Fact] + public void PreflightValidation() + { + using var conn = Create(require: RedisFeatures.v3_2_0); + var db = conn.GetDatabase(); + RedisKey key = Me(); + db.KeyDelete(key); + + Assert.Throws(()=> db.StringBitfieldGet(key, 0, 0, false, false)); + Assert.Throws(()=> db.StringBitfieldGet(key, 64, 0, false, true)); + Assert.Throws(()=> db.StringBitfieldGet(key, 65, 0, false, false)); + Assert.Throws(() => db.StringBitfield(key, new[] { new BitfieldOperation() { Offset = "0", SubCommand = BitFieldSubCommand.Set, Encoding = "i5" } })); + } } From a00aba5a0e6b0a5031c569514c05072eb3ed4a14 Mon Sep 17 00:00:00 2001 From: slorello89 Date: Fri, 1 Dec 2023 10:00:55 -0500 Subject: [PATCH 14/14] encoding -> string --- src/StackExchange.Redis/APITypes/BitfieldOperation.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/StackExchange.Redis/APITypes/BitfieldOperation.cs b/src/StackExchange.Redis/APITypes/BitfieldOperation.cs index 311502857..1802ceaa4 100644 --- a/src/StackExchange.Redis/APITypes/BitfieldOperation.cs +++ b/src/StackExchange.Redis/APITypes/BitfieldOperation.cs @@ -17,7 +17,7 @@ public struct BitfieldOperation return $"{signedness}{size}"; }).ToArray(); - private static RedisValue CreateEncoding(bool unsigned, byte size) + private static string CreateEncoding(bool unsigned, byte size) { if (size == 0) { @@ -42,7 +42,7 @@ private static RedisValue CreateEncoding(bool unsigned, byte size) internal string Offset; internal long? Value; internal BitFieldSubCommand SubCommand; - internal RedisValue Encoding; + internal string Encoding; internal BitfieldOverflowHandling? BitfieldOverflowHandling; ///