From b925816f773d2aba38614c9df7118765932ec54f Mon Sep 17 00:00:00 2001 From: psw0946 <31843235+psw0946@users.noreply.github.com> Date: Fri, 27 Dec 2024 18:42:43 +0900 Subject: [PATCH] Add support up to max unsigned integer in Bitfield offset (#2964) (#3099) * Add support up to max unsigned integer in Bitfield offset (#2964) * Add test for reactive command --- .../java/io/lettuce/core/BitFieldArgs.java | 45 +++++++++---------- .../commands/BitCommandIntegrationTests.java | 35 +++++++++++++++ .../BitReactiveCommandIntegrationTests.java | 9 ++++ 3 files changed, 65 insertions(+), 24 deletions(-) diff --git a/src/main/java/io/lettuce/core/BitFieldArgs.java b/src/main/java/io/lettuce/core/BitFieldArgs.java index 6b2741d038..b7f99d5117 100644 --- a/src/main/java/io/lettuce/core/BitFieldArgs.java +++ b/src/main/java/io/lettuce/core/BitFieldArgs.java @@ -70,7 +70,7 @@ private Builder() { * Create a new {@code GET} subcommand. * * @param bitFieldType the bit field type, must not be {@code null}. - * @param offset bitfield offset + * @param offset bitfield offset, supports up to {@code 2^32-1} using {@link Integer#toUnsignedString(int)}. * @return a new {@code GET} subcommand for the given {@code bitFieldType} and {@code offset}. */ public static BitFieldArgs get(BitFieldType bitFieldType, int offset) { @@ -93,7 +93,7 @@ public static BitFieldArgs get(BitFieldType bitFieldType, Offset offset) { * Create a new {@code SET} subcommand. * * @param bitFieldType the bit field type, must not be {@code null}. - * @param offset bitfield offset + * @param offset bitfield offset, supports up to {@code 2^32-1} using {@link Integer#toUnsignedString(int)}. * @param value the value * @return a new {@code SET} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}. */ @@ -118,7 +118,7 @@ public static BitFieldArgs set(BitFieldType bitFieldType, Offset offset, long va * Create a new {@code INCRBY} subcommand. * * @param bitFieldType the bit field type, must not be {@code null}. - * @param offset bitfield offset + * @param offset bitfield offset, supports up to {@code 2^32-1} using {@link Integer#toUnsignedString(int)}. * @param value the value * @return a new {@code INCRBY} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value} . */ @@ -177,7 +177,7 @@ public static BitFieldType unsigned(int bits) { /** * Creates a new {@link Offset} for the given {@code offset}. * - * @param offset zero-based offset. + * @param offset zero-based offset, supports up to {@code 2^32-1} using {@link Integer#toUnsignedString(int)}. * @return the {@link Offset}. * @since 4.3 */ @@ -233,7 +233,7 @@ public BitFieldArgs get(BitFieldType bitFieldType) { * Adds a new {@code GET} subcommand. * * @param bitFieldType the bit field type, must not be {@code null}. - * @param offset bitfield offset + * @param offset bitfield offset, supports up to {@code 2^32-1} using {@link Integer#toUnsignedString(int)}. * @return a new {@code GET} subcommand for the given {@code bitFieldType} and {@code offset}. */ public BitFieldArgs get(BitFieldType bitFieldType, int offset) { @@ -258,7 +258,7 @@ public BitFieldArgs get(BitFieldType bitFieldType, Offset offset) { /** * Adds a new {@code GET} subcommand using the field type of the previous command. * - * @param offset bitfield offset + * @param offset bitfield offset, supports up to {@code 2^32-1} using {@link Integer#toUnsignedString(int)}. * @return a new {@code GET} subcommand for the given {@code bitFieldType} and {@code offset}. * @throws IllegalStateException if no previous field type was found */ @@ -291,7 +291,7 @@ public BitFieldArgs set(BitFieldType bitFieldType, long value) { /** * Adds a new {@code SET} subcommand using the field type of the previous command. * - * @param offset bitfield offset + * @param offset bitfield offset, supports up to {@code 2^32-1} using {@link Integer#toUnsignedString(int)}. * @param value the value * @return a new {@code SET} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}. * @throws IllegalStateException if no previous field type was found @@ -304,7 +304,7 @@ public BitFieldArgs set(int offset, long value) { * Adds a new {@code SET} subcommand. * * @param bitFieldType the bit field type, must not be {@code null}. - * @param offset bitfield offset + * @param offset bitfield offset, supports up to {@code 2^32-1} using {@link Integer#toUnsignedString(int)}. * @param value the value * @return a new {@code SET} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}. */ @@ -353,7 +353,7 @@ public BitFieldArgs incrBy(BitFieldType bitFieldType, long value) { /** * Adds a new {@code INCRBY} subcommand using the field type of the previous command. * - * @param offset bitfield offset + * @param offset bitfield offset, supports up to {@code 2^32-1} using {@link Integer#toUnsignedString(int)}. * @param value the value * @return a new {@code INCRBY} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}. * @throws IllegalStateException if no previous field type was found @@ -366,7 +366,7 @@ public BitFieldArgs incrBy(int offset, long value) { * Adds a new {@code INCRBY} subcommand. * * @param bitFieldType the bit field type, must not be {@code null}. - * @param offset bitfield offset + * @param offset bitfield offset, supports up to {@code 2^32-1} using {@link Integer#toUnsignedString(int)}. * @param value the value * @return a new {@code INCRBY} subcommand for the given {@code bitFieldType}, {@code offset} and {@code value}. */ @@ -432,14 +432,13 @@ private static class Set extends SubCommand { private final boolean bitOffset; - private final long offset; + private final int offset; private final long value; private Set(BitFieldType bitFieldType, boolean bitOffset, int offset, long value) { LettuceAssert.notNull(bitFieldType, "BitFieldType must not be null"); - LettuceAssert.isTrue(offset > -1, "Offset must be greater or equal to 0"); this.bitFieldType = bitFieldType; this.bitOffset = bitOffset; @@ -453,9 +452,9 @@ void build(CommandArgs args) { args.add(CommandType.SET).add(bitFieldType.asString()); if (bitOffset) { - args.add("#" + offset); + args.add("#" + Integer.toUnsignedString(offset)); } else { - args.add(offset); + args.add(Integer.toUnsignedString(offset)); } args.add(value); @@ -477,7 +476,6 @@ private static class Get extends SubCommand { private Get(BitFieldType bitFieldType, boolean bitOffset, int offset) { LettuceAssert.notNull(bitFieldType, "BitFieldType must not be null"); - LettuceAssert.isTrue(offset > -1, "Offset must be greater or equal to 0"); this.bitFieldType = bitFieldType; this.bitOffset = bitOffset; @@ -490,9 +488,9 @@ void build(CommandArgs args) { args.add(CommandType.GET).add(bitFieldType.asString()); if (bitOffset) { - args.add("#" + offset); + args.add("#" + Integer.toUnsignedString(offset)); } else { - args.add(offset); + args.add(Integer.toUnsignedString(offset)); } } @@ -507,14 +505,13 @@ private static class IncrBy extends SubCommand { private final boolean bitOffset; - private final long offset; + private final int offset; private final long value; private IncrBy(BitFieldType bitFieldType, boolean offsetWidthMultiplier, int offset, long value) { LettuceAssert.notNull(bitFieldType, "BitFieldType must not be null"); - LettuceAssert.isTrue(offset > -1, "Offset must be greater or equal to 0"); this.bitFieldType = bitFieldType; this.bitOffset = offsetWidthMultiplier; @@ -528,9 +525,9 @@ void build(CommandArgs args) { args.add(CommandType.INCRBY).add(bitFieldType.asString()); if (bitOffset) { - args.add("#" + offset); + args.add("#" + Integer.toUnsignedString(offset)); } else { - args.add(offset); + args.add(Integer.toUnsignedString(offset)); } args.add(value); @@ -646,8 +643,8 @@ public String toString() { } /** - * Represents a bit field offset. See also Bits and - * positional offsets + * Represents a bit field offset. Offset supports up to {@code 2^32-1} using {@link Integer#toUnsignedString(int)}. See also + * Bits and positional offsets * * @since 4.3 */ @@ -681,7 +678,7 @@ public int getOffset() { @Override public String toString() { - return (multiplyByTypeWidth ? "#" : "") + offset; + return (multiplyByTypeWidth ? "#" : "") + Integer.toUnsignedString(offset); } } diff --git a/src/test/java/io/lettuce/core/commands/BitCommandIntegrationTests.java b/src/test/java/io/lettuce/core/commands/BitCommandIntegrationTests.java index 6bc667e9b4..16ae65c3f2 100644 --- a/src/test/java/io/lettuce/core/commands/BitCommandIntegrationTests.java +++ b/src/test/java/io/lettuce/core/commands/BitCommandIntegrationTests.java @@ -121,6 +121,9 @@ void bitfieldArgsTest() { assertThat(offset(5).toString()).isEqualTo("5"); assertThat(typeWidthBasedOffset(5).toString()).isEqualTo("#5"); + + assertThat(offset(-1).toString()).isEqualTo(Integer.toUnsignedString(-1)); + assertThat(typeWidthBasedOffset(-1).toString()).isEqualTo("#" + Integer.toUnsignedString(-1)); } @Test @@ -147,6 +150,18 @@ void bitfieldGetWithOffset() { assertThat(bitstring.get(key)).isEqualTo("10000000"); } + @Test + @EnabledOnCommand("BITFIELD") + void bitfieldGetWithUnsignedOffset() { + + long unsignedIntMax = (1L << 32) - 1; + BitFieldArgs bitFieldArgs = BitFieldArgs.Builder.set(signed(8), 0, 1).get(signed(1), (int) unsignedIntMax); + + List values = redis.bitfield(key, bitFieldArgs); + + assertThat(values).containsExactly(0L, 0L); + } + @Test @EnabledOnCommand("BITFIELD") void bitfieldSet() { @@ -171,6 +186,16 @@ void bitfieldWithOffsetSet() { assertThat(bitstring.get(key)).isEqualTo("1000000000000010"); } + @Test + @EnabledOnCommand("BITFIELD") + void bitfieldWithUnsignedOffsetSet() { + + long unsignedIntMax = (1L << 32) - 1; + redis.bitfield(key, BitFieldArgs.Builder.set(signed(1), offset((int) unsignedIntMax), 1)); + assertThat(bitstring.getbit(key, unsignedIntMax)).isEqualTo(1); + assertThat(bitstring.bitcount(key)).isEqualTo(1); + } + @Test @EnabledOnCommand("BITFIELD") void bitfieldIncrBy() { @@ -195,6 +220,16 @@ void bitfieldWithOffsetIncrBy() { assertThat(bitstring.get(key)).isEqualTo("0000000000000010"); } + @Test + @EnabledOnCommand("BITFIELD") + void bitfieldWithUnsignedOffsetIncryBy() { + + long unsignedIntMax = (1L << 32) - 1; + redis.bitfield(key, BitFieldArgs.Builder.incrBy(signed(1), offset((int) unsignedIntMax), 1)); + assertThat(bitstring.getbit(key, unsignedIntMax)).isEqualTo(1); + assertThat(bitstring.bitcount(key)).isEqualTo(1); + } + @Test @EnabledOnCommand("BITFIELD") void bitfieldOverflow() { diff --git a/src/test/java/io/lettuce/core/commands/reactive/BitReactiveCommandIntegrationTests.java b/src/test/java/io/lettuce/core/commands/reactive/BitReactiveCommandIntegrationTests.java index 648a074885..0db4fc4161 100644 --- a/src/test/java/io/lettuce/core/commands/reactive/BitReactiveCommandIntegrationTests.java +++ b/src/test/java/io/lettuce/core/commands/reactive/BitReactiveCommandIntegrationTests.java @@ -56,6 +56,15 @@ void bitfieldGetWithOffset() { assertThat(bitstring.get(key)).isEqualTo("10000000"); } + @Test + void bitfieldGetWithUnsignedOffset() { + + long unsignedIntMax = (1L << 32) - 1; + BitFieldArgs bitFieldArgs = BitFieldArgs.Builder.set(signed(8), 0, 1).get(signed(1), (int) unsignedIntMax); + + StepVerifier.create(reactive.bitfield(key, bitFieldArgs)).expectNext(Value.just(0L), Value.just(0L)).verifyComplete(); + } + @Test void bitfieldSet() {