Skip to content

Commit 28008f9

Browse files
authored
bit set and string compatibility (#547)
1 parent bfa886f commit 28008f9

File tree

6 files changed

+123
-20
lines changed

6 files changed

+123
-20
lines changed

src/main/java/com/github/fppt/jedismock/datastructures/RMBitMap.java

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import java.util.BitSet;
66

77
public class RMBitMap extends StringCompatible {
8-
private static final long serialVersionUID = 1L;
98
private final BitSet bitSet;
109
private int size;
1110

@@ -16,7 +15,11 @@ public RMBitMap() {
1615

1716
public RMBitMap(byte[] data) {
1817
this.size = data.length;
19-
this.bitSet = BitSet.valueOf(data);
18+
byte[] reversed = new byte[data.length];
19+
for (int i = 0; i < data.length; i++) {
20+
reversed[i] = reverseBits(data[i]);
21+
}
22+
this.bitSet = BitSet.valueOf(reversed);
2023
}
2124

2225
public RMBitMap(int size, BitSet bitSet) {
@@ -46,4 +49,34 @@ public boolean getBit(int pos) {
4649
public void raiseTypeCastException() {
4750
throw new WrongValueTypeException("WRONGTYPE RMBitMap value is used in the wrong place");
4851
}
52+
53+
@Override
54+
public final Slice getAsSlice() {
55+
return Slice.create(toByteArray());
56+
}
57+
58+
/**
59+
* Produce little-endian bitewise representation of the bit set.
60+
*/
61+
private byte[] toByteArray() {
62+
long[] longs = bitSet.toLongArray();
63+
int nBytes = (bitSet.length() + 7) / 8;
64+
byte[] bytes = new byte[nBytes];
65+
int byteIndex = 0;
66+
for (long value : longs) {
67+
for (int i = 0; i < 8 && byteIndex < nBytes; i++) {
68+
bytes[byteIndex++] = reverseBits((byte) (value & 0xFF));
69+
value >>>= 8;
70+
}
71+
}
72+
return bytes;
73+
}
74+
75+
private static byte reverseBits(byte b) {
76+
int buf = b & 0xFF;
77+
buf = ((buf & 0xF0) >>> 4) | ((buf & 0x0F) << 4);
78+
buf = ((buf & 0xCC) >>> 2) | ((buf & 0x33) << 2);
79+
buf = ((buf & 0xAA) >>> 1) | ((buf & 0x55) << 1);
80+
return (byte) buf;
81+
}
4982
}

src/main/java/com/github/fppt/jedismock/datastructures/RMHyperLogLog.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22

33
import com.github.fppt.jedismock.exception.WrongValueTypeException;
44

5+
import java.io.ByteArrayOutputStream;
6+
import java.io.IOException;
7+
import java.io.ObjectOutputStream;
8+
import java.io.Serializable;
59
import java.util.Collection;
610
import java.util.HashSet;
711
import java.util.Set;
812

9-
public class RMHyperLogLog extends StringCompatible {
13+
public class RMHyperLogLog extends StringCompatible implements Serializable {
1014
private static final long serialVersionUID = 1L;
1115
private transient HashSet<Slice> storedData;
1216

@@ -60,4 +64,16 @@ private void readObject(java.io.ObjectInputStream s)
6064
storedData.add(Slice.create(buf));
6165
}
6266
}
67+
68+
@Override
69+
public final Slice getAsSlice() {
70+
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
71+
try {
72+
ObjectOutputStream outputStream = new ObjectOutputStream(byteOutputStream);
73+
outputStream.writeObject(this);
74+
return Slice.create(byteOutputStream.toByteArray());
75+
} catch (IOException exp) {
76+
throw new IllegalStateException(exp);
77+
}
78+
}
6379
}
Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,11 @@
11
package com.github.fppt.jedismock.datastructures;
22

3-
import java.io.ByteArrayOutputStream;
4-
import java.io.IOException;
5-
import java.io.ObjectOutputStream;
6-
import java.io.Serializable;
73

8-
public abstract class StringCompatible implements RMDataStructure, Serializable {
4+
public abstract class StringCompatible implements RMDataStructure {
95

106
@Override
117
public final String getTypeName() {
128
return "string";
139
}
1410

15-
@Override
16-
public final Slice getAsSlice() {
17-
ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
18-
try {
19-
ObjectOutputStream outputStream = new ObjectOutputStream(byteOutputStream);
20-
outputStream.writeObject(this);
21-
return Slice.create(byteOutputStream.toByteArray());
22-
} catch (IOException exp) {
23-
throw new IllegalStateException(exp);
24-
}
25-
}
2611
}

src/main/java/com/github/fppt/jedismock/operations/strings/SetRange.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ protected Slice response() {
3737
if (offset + value.length() < oldValue.length()) {
3838
newValue += oldValue.substring(offset + value.length());
3939
}
40-
base().putValue(key, Slice.create(newValue).extract(), null);
40+
if (!oldValue.equals(newValue)) {
41+
base().putValue(key, Slice.create(newValue).extract(), null);
42+
}
4143
return Response.integer(newValue.length());
4244
}
4345
}

src/test/java/com/github/fppt/jedismock/comparisontests/bitmaps/BitMapsOperationsTest.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import java.util.Arrays;
1010
import java.util.Collections;
1111
import java.util.List;
12+
import java.util.Random;
13+
import java.util.concurrent.ThreadLocalRandom;
1214

1315
import static org.assertj.core.api.Assertions.assertThat;
1416

@@ -55,6 +57,9 @@ void testValueAftersetbit(Jedis jedis) {
5557
assertThat(jedis.getbit("foo", 0L)).isTrue();
5658
jedis.setbit("foo", 1L, true);
5759
assertThat(jedis.getbit("foo", 0L)).isTrue();
60+
jedis.setbit("foo", 0L, false);
61+
assertThat(jedis.getbit("foo", 0L)).isFalse();
62+
5863
}
5964

6065
@TestTemplate
@@ -65,4 +70,47 @@ public void testStringAndBitmapGet(Jedis jedis) {
6570
assertThat(jedis.getbit("something2", 1)).isTrue();
6671
assertThat(jedis.getbit("something2", 41)).isTrue();
6772
}
73+
74+
@TestTemplate
75+
public void bitsToString(Jedis jedis) {
76+
jedis.setbit("bitmapsarestrings", 2, true);
77+
jedis.setbit("bitmapsarestrings", 3, true);
78+
jedis.setbit("bitmapsarestrings", 5, true);
79+
jedis.setbit("bitmapsarestrings", 10, true);
80+
jedis.setbit("bitmapsarestrings", 11, true);
81+
jedis.setbit("bitmapsarestrings", 14, true);
82+
String result = jedis.get("bitmapsarestrings");
83+
assertThat(result).isEqualTo("42");
84+
}
85+
86+
@TestTemplate
87+
public void stringToBits(Jedis jedis) {
88+
jedis.set("foo", "e");
89+
assertThat(jedis.getbit("foo", 0)).isFalse();
90+
assertThat(jedis.getbit("foo", 1)).isTrue();
91+
}
92+
93+
@TestTemplate
94+
public void testLongSetBit(Jedis jedis) {
95+
int len = 256 * 8;
96+
jedis.del("mykey");
97+
byte[] expectedBytes = new byte[256];
98+
Random random = new Random(42);
99+
for (int i = 0; i < 2000; i++) {
100+
int bitPosition = random.nextInt(len);
101+
boolean bitValue = random.nextBoolean();
102+
int byteIndex = bitPosition / 8;
103+
int bitIndex = 7 - (bitPosition % 8);
104+
if (bitValue) {
105+
expectedBytes[byteIndex] |= (byte) (1 << bitIndex);
106+
} else {
107+
expectedBytes[byteIndex] &= (byte) ~(1 << bitIndex);
108+
}
109+
jedis.setbit("mykey", bitPosition, bitValue);
110+
byte[] redisBytes = jedis.get("mykey".getBytes());
111+
byte[] expectedTruncated = new byte[redisBytes.length];
112+
System.arraycopy(expectedBytes, 0, expectedTruncated, 0, redisBytes.length);
113+
assertThat(redisBytes).isEqualTo(expectedTruncated);
114+
}
115+
}
68116
}

src/test/java/com/github/fppt/jedismock/comparisontests/strings/TestSetRange.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,23 @@ public void setRangeZeroPadding(Jedis jedis) {
4646
assertThat(l).isEqualTo(11);
4747
assertThat(jedis.get("key2")).isEqualTo(new String(new byte[6]) + "Redis");
4848
}
49+
50+
@TestTemplate
51+
public void setRangeAgainstNonExistent(Jedis jedis) {
52+
assertThat(jedis.setrange("mykey", 0, "foo"))
53+
.isEqualTo(3);
54+
assertThat(jedis.get("mykey")).isEqualTo("foo");
55+
}
56+
57+
@TestTemplate
58+
public void setRangeAgainstNonExistentNoOp(Jedis jedis) {
59+
assertThat(jedis.setrange("mykey", 0, "")).isEqualTo(0);
60+
assertThat(jedis.exists("mykey")).isFalse();
61+
}
62+
63+
@TestTemplate
64+
public void setRangeAgainstNonExistentPadding(Jedis jedis) {
65+
assertThat(jedis.setrange("mykey", 1, "foo")).isEqualTo(4);
66+
assertThat(jedis.get("mykey")).isEqualTo("\000foo");
67+
}
4968
}

0 commit comments

Comments
 (0)