Skip to content

Commit 68f0a44

Browse files
cdpretepretecd
andauthored
Regression: Add DbiFlags#MDB_UNSIGNEDKEY to allow to compare byte array, ByteBuffer and DirectBuffer keys as unsigned like in versions prior to 0.9.0 (#237)
Co-authored-by: Cosimo Damiano Prete <cosimo.prete.damiano@gmail.com>
1 parent b5dfb25 commit 68f0a44

File tree

11 files changed

+233
-114
lines changed

11 files changed

+233
-114
lines changed

src/main/java/org/lmdbjava/BufferProxy.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
package org.lmdbjava;
2222

2323
import static java.lang.Long.BYTES;
24+
import static org.lmdbjava.DbiFlags.MDB_INTEGERKEY;
25+
import static org.lmdbjava.DbiFlags.MDB_UNSIGNEDKEY;
26+
import static org.lmdbjava.MaskedFlag.isSet;
27+
import static org.lmdbjava.MaskedFlag.mask;
2428

2529
import java.util.Comparator;
2630

@@ -72,8 +76,24 @@ public abstract class BufferProxy<T> {
7276
* @param flags for the database
7377
* @return a comparator that can be used (never null)
7478
*/
75-
protected abstract Comparator<T> getComparator(DbiFlags... flags);
79+
protected Comparator<T> getComparator(DbiFlags... flags) {
80+
final int intFlag = mask(flags);
7681

82+
return isSet(intFlag, MDB_INTEGERKEY) || isSet(intFlag, MDB_UNSIGNEDKEY) ? getUnsignedComparator() : getSignedComparator();
83+
}
84+
85+
/**
86+
* Get a suitable default {@link Comparator} to compare numeric key values as unsigned.
87+
*
88+
* @return a comparator that can be used (never null)
89+
*/
90+
protected abstract Comparator<T> getUnsignedComparator();
91+
/**
92+
* Get a suitable default {@link Comparator} to compare numeric key values as signed.
93+
*
94+
* @return a comparator that can be used (never null)
95+
*/
96+
protected abstract Comparator<T> getSignedComparator();
7797
/**
7898
* Deallocate a buffer that was previously provided by {@link #allocate()}.
7999
*

src/main/java/org/lmdbjava/ByteArrayProxy.java

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@
2020

2121
package org.lmdbjava;
2222

23-
import static java.util.Objects.requireNonNull;
24-
import static org.lmdbjava.Library.RUNTIME;
23+
import jnr.ffi.Pointer;
24+
import jnr.ffi.provider.MemoryManager;
2525

2626
import java.util.Arrays;
2727
import java.util.Comparator;
2828

29-
import jnr.ffi.Pointer;
30-
import jnr.ffi.provider.MemoryManager;
29+
import static java.lang.Math.min;
30+
import static java.util.Objects.requireNonNull;
31+
import static org.lmdbjava.Library.RUNTIME;
3132

3233
/**
3334
* Byte array proxy.
@@ -43,7 +44,10 @@ public final class ByteArrayProxy extends BufferProxy<byte[]> {
4344

4445
private static final MemoryManager MEM_MGR = RUNTIME.getMemoryManager();
4546

46-
private ByteArrayProxy() {
47+
private static final Comparator<byte[]> signedComparator = ByteArrayProxy::compareArraysSigned;
48+
private static final Comparator<byte[]> unsignedComparator = ByteArrayProxy::compareArrays;
49+
50+
private ByteArrayProxy() {
4751
}
4852

4953
/**
@@ -60,7 +64,7 @@ public static int compareArrays(final byte[] o1, final byte[] o2) {
6064
if (o1 == o2) {
6165
return 0;
6266
}
63-
final int minLength = Math.min(o1.length, o2.length);
67+
final int minLength = min(o1.length, o2.length);
6468

6569
for (int i = 0; i < minLength; i++) {
6670
final int lw = Byte.toUnsignedInt(o1[i]);
@@ -74,15 +78,32 @@ public static int compareArrays(final byte[] o1, final byte[] o2) {
7478
return o1.length - o2.length;
7579
}
7680

81+
/**
82+
* Compare two byte arrays.
83+
*
84+
* @param b1 left operand (required)
85+
* @param b2 right operand (required)
86+
* @return as specified by {@link Comparable} interface
87+
*/
88+
@SuppressWarnings("PMD.CompareObjectsWithEquals")
89+
public static int compareArraysSigned(final byte[] b1, final byte[] b2) {
90+
requireNonNull(b1);
91+
requireNonNull(b2);
92+
93+
if (b1 == b2) return 0;
94+
95+
for(int i = 0; i < min(b1.length, b2.length); ++i) {
96+
if(b1[i] != b2[i]) return b1[i] - b2[i];
97+
}
98+
99+
return b1.length - b2.length;
100+
}
101+
77102
@Override
78103
protected byte[] allocate() {
79104
return new byte[0];
80105
}
81106

82-
protected int compare(final byte[] o1, final byte[] o2) {
83-
return compareArrays(o1, o2);
84-
}
85-
86107
@Override
87108
protected void deallocate(final byte[] buff) {
88109
// byte arrays cannot be allocated
@@ -94,8 +115,13 @@ protected byte[] getBytes(final byte[] buffer) {
94115
}
95116

96117
@Override
97-
protected Comparator<byte[]> getComparator(final DbiFlags... flags) {
98-
return this::compare;
118+
protected Comparator<byte[]> getSignedComparator() {
119+
return signedComparator;
120+
}
121+
122+
@Override
123+
protected Comparator<byte[]> getUnsignedComparator() {
124+
return unsignedComparator;
99125
}
100126

101127
@Override

src/main/java/org/lmdbjava/ByteBufProxy.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,17 @@
2020

2121
package org.lmdbjava;
2222

23-
import static io.netty.buffer.PooledByteBufAllocator.DEFAULT;
24-
import static java.lang.Class.forName;
25-
import static org.lmdbjava.UnsafeAccess.UNSAFE;
23+
import io.netty.buffer.ByteBuf;
24+
import io.netty.buffer.PooledByteBufAllocator;
25+
import jnr.ffi.Pointer;
2626

2727
import java.lang.reflect.Field;
2828
import java.util.Comparator;
2929

30-
import io.netty.buffer.ByteBuf;
31-
import io.netty.buffer.PooledByteBufAllocator;
32-
import jnr.ffi.Pointer;
30+
import static io.netty.buffer.PooledByteBufAllocator.DEFAULT;
31+
import static java.lang.Class.forName;
32+
import static java.util.Objects.requireNonNull;
33+
import static org.lmdbjava.UnsafeAccess.UNSAFE;
3334

3435
/**
3536
* A buffer proxy backed by Netty's {@link ByteBuf}.
@@ -51,6 +52,12 @@ public final class ByteBufProxy extends BufferProxy<ByteBuf> {
5152
private static final String FIELD_NAME_ADDRESS = "memoryAddress";
5253
private static final String FIELD_NAME_LENGTH = "length";
5354
private static final String NAME = "io.netty.buffer.PooledUnsafeDirectByteBuf";
55+
private static final Comparator<ByteBuf> comparator = (o1, o2) -> {
56+
requireNonNull(o1);
57+
requireNonNull(o2);
58+
59+
return o1.compareTo(o2);
60+
};
5461
private final long lengthOffset;
5562
private final long addressOffset;
5663

@@ -107,13 +114,14 @@ protected ByteBuf allocate() {
107114
throw new IllegalStateException("Netty buffer must be " + NAME);
108115
}
109116

110-
protected int compare(final ByteBuf o1, final ByteBuf o2) {
111-
return o1.compareTo(o2);
117+
@Override
118+
protected Comparator<ByteBuf> getSignedComparator() {
119+
return comparator;
112120
}
113121

114122
@Override
115-
protected Comparator<ByteBuf> getComparator(final DbiFlags... flags) {
116-
return this::compare;
123+
protected Comparator<ByteBuf> getUnsignedComparator() {
124+
return comparator;
117125
}
118126

119127
@Override

src/main/java/org/lmdbjava/ByteBufferProxy.java

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import static java.nio.ByteOrder.LITTLE_ENDIAN;
2828
import static java.util.Objects.requireNonNull;
2929
import static org.lmdbjava.DbiFlags.MDB_INTEGERKEY;
30+
import static org.lmdbjava.DbiFlags.MDB_UNSIGNEDKEY;
3031
import static org.lmdbjava.Env.SHOULD_CHECK;
3132
import static org.lmdbjava.MaskedFlag.isSet;
3233
import static org.lmdbjava.MaskedFlag.mask;
@@ -111,6 +112,14 @@ abstract static class AbstractByteBufferProxy extends BufferProxy<ByteBuffer> {
111112
protected static final String FIELD_NAME_ADDRESS = "address";
112113
protected static final String FIELD_NAME_CAPACITY = "capacity";
113114

115+
private static final Comparator<ByteBuffer> signedComparator = (o1, o2) -> {
116+
requireNonNull(o1);
117+
requireNonNull(o2);
118+
119+
return o1.compareTo(o2);
120+
};
121+
private static final Comparator<ByteBuffer> unsignedComparator = AbstractByteBufferProxy::compareBuff;
122+
114123
/**
115124
* A thread-safe pool for a given length. If the buffer found is valid (ie
116125
* not of a negative length) then that buffer is used. If no valid buffer is
@@ -193,22 +202,13 @@ protected final ByteBuffer allocate() {
193202
}
194203

195204
@Override
196-
protected Comparator<ByteBuffer> getComparator(final DbiFlags... flags) {
197-
final int flagInt = mask(flags);
198-
if (isSet(flagInt, MDB_INTEGERKEY)) {
199-
return this::compareCustom;
200-
}
201-
return this::compareDefault;
205+
protected Comparator<ByteBuffer> getSignedComparator() {
206+
return signedComparator;
202207
}
203208

204-
protected final int compareDefault(final ByteBuffer o1,
205-
final ByteBuffer o2) {
206-
return o1.compareTo(o2);
207-
}
208-
209-
protected final int compareCustom(final ByteBuffer o1,
210-
final ByteBuffer o2) {
211-
return compareBuff(o1, o2);
209+
@Override
210+
protected Comparator<ByteBuffer> getUnsignedComparator() {
211+
return unsignedComparator;
212212
}
213213

214214
@Override

src/main/java/org/lmdbjava/Cursor.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import jnr.ffi.Pointer;
4141
import jnr.ffi.byref.NativeLongByReference;
4242

43+
import java.util.Arrays;
44+
4345
/**
4446
* A cursor handle.
4547
*
@@ -120,7 +122,7 @@ public void delete(final PutFlags... f) {
120122
txn.checkReady();
121123
txn.checkWritesAllowed();
122124
}
123-
final int flags = mask(f);
125+
final int flags = mask(true, f);
124126
checkRc(LIB.mdb_cursor_del(ptrCursor, flags));
125127
}
126128

@@ -256,7 +258,7 @@ public boolean put(final T key, final T val, final PutFlags... op) {
256258
}
257259
kv.keyIn(key);
258260
kv.valIn(val);
259-
final int mask = mask(op);
261+
final int mask = mask(true, op);
260262
final int rc = LIB.mdb_cursor_put(ptrCursor, kv.pointerKey(),
261263
kv.pointerVal(), mask);
262264
if (rc == MDB_KEYEXIST) {
@@ -299,7 +301,7 @@ public void putMultiple(final T key, final T val, final int elements,
299301
txn.checkReady();
300302
txn.checkWritesAllowed();
301303
}
302-
final int mask = mask(op);
304+
final int mask = mask(true, op);
303305
if (SHOULD_CHECK && !isSet(mask, MDB_MULTIPLE)) {
304306
throw new IllegalArgumentException("Must set " + MDB_MULTIPLE + " flag");
305307
}
@@ -364,7 +366,7 @@ public T reserve(final T key, final int size, final PutFlags... op) {
364366
}
365367
kv.keyIn(key);
366368
kv.valIn(size);
367-
final int flags = mask(op) | MDB_RESERVE.getMask();
369+
final int flags = mask(true, op) | MDB_RESERVE.getMask();
368370
checkRc(LIB.mdb_cursor_put(ptrCursor, kv.pointerKey(), kv.pointerVal(),
369371
flags));
370372
kv.valOut();

src/main/java/org/lmdbjava/Dbi.java

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@
2020

2121
package org.lmdbjava;
2222

23+
import jnr.ffi.Pointer;
24+
import jnr.ffi.byref.IntByReference;
25+
import jnr.ffi.byref.PointerByReference;
26+
import org.lmdbjava.Library.ComparatorCallback;
27+
import org.lmdbjava.Library.MDB_stat;
28+
29+
import java.util.ArrayList;
30+
import java.util.Arrays;
31+
import java.util.Comparator;
32+
import java.util.List;
33+
2334
import static java.util.Objects.requireNonNull;
2435
import static jnr.ffi.Memory.allocateDirect;
2536
import static jnr.ffi.NativeType.ADDRESS;
@@ -36,17 +47,6 @@
3647
import static org.lmdbjava.PutFlags.MDB_RESERVE;
3748
import static org.lmdbjava.ResultCodeMapper.checkRc;
3849

39-
import java.util.ArrayList;
40-
import java.util.Arrays;
41-
import java.util.Comparator;
42-
import java.util.List;
43-
44-
import jnr.ffi.Pointer;
45-
import jnr.ffi.byref.IntByReference;
46-
import jnr.ffi.byref.PointerByReference;
47-
import org.lmdbjava.Library.ComparatorCallback;
48-
import org.lmdbjava.Library.MDB_stat;
49-
5050
/**
5151
* LMDB Database.
5252
*
@@ -64,10 +64,18 @@ public final class Dbi<T> {
6464
Dbi(final Env<T> env, final Txn<T> txn, final byte[] name,
6565
final Comparator<T> comparator, final boolean nativeCb,
6666
final BufferProxy<T> proxy, final DbiFlags... flags) {
67+
if (SHOULD_CHECK) {
68+
requireNonNull(txn);
69+
txn.checkReady();
70+
}
6771
this.env = env;
6872
this.name = name == null ? null : Arrays.copyOf(name, name.length);
69-
this.comparator = comparator;
70-
final int flagsMask = mask(flags);
73+
if(comparator == null) {
74+
this.comparator = proxy.getComparator(flags);
75+
} else {
76+
this.comparator = comparator;
77+
}
78+
final int flagsMask = mask(true, flags);
7179
final Pointer dbiPtr = allocateDirect(RUNTIME, ADDRESS);
7280
checkRc(LIB.mdb_dbi_open(txn.pointer(), name, flagsMask, dbiPtr));
7381
ptr = dbiPtr.getPointer(0);
@@ -377,7 +385,7 @@ public boolean put(final Txn<T> txn, final T key, final T val,
377385
}
378386
txn.kv().keyIn(key);
379387
txn.kv().valIn(val);
380-
final int mask = mask(flags);
388+
final int mask = mask(true, flags);
381389
final int rc = LIB.mdb_put(txn.pointer(), ptr, txn.kv().pointerKey(), txn
382390
.kv().pointerVal(), mask);
383391
if (rc == MDB_KEYEXIST) {
@@ -422,7 +430,7 @@ public T reserve(final Txn<T> txn, final T key, final int size,
422430
}
423431
txn.kv().keyIn(key);
424432
txn.kv().valIn(size);
425-
final int flags = mask(op) | MDB_RESERVE.getMask();
433+
final int flags = mask(true, op) | MDB_RESERVE.getMask();
426434
checkRc(LIB.mdb_put(txn.pointer(), ptr, txn.kv().pointerKey(), txn.kv()
427435
.pointerVal(), flags));
428436
txn.kv().valOut(); // marked as in,out in LMDB C docs

src/main/java/org/lmdbjava/DbiFlags.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,15 @@ public enum DbiFlags implements MaskedFlag {
6868
* similar to {@link #MDB_INTEGERKEY} keys.
6969
*/
7070
MDB_INTEGERDUP(0x20),
71+
/**
72+
* Compare the <b>numeric</b> keys in native byte order and as unsigned.
73+
*
74+
* <p>
75+
* This option is applied only to {@link java.nio.ByteBuffer}, {@link org.agrona.DirectBuffer} and byte array keys.
76+
* {@link io.netty.buffer.ByteBuf} keys are always compared in native byte order and as unsigned.
77+
* </p>
78+
*/
79+
MDB_UNSIGNEDKEY(0x30, false),
7180
/**
7281
* With {@link #MDB_DUPSORT}, use reverse string dups.
7382
*
@@ -86,14 +95,24 @@ public enum DbiFlags implements MaskedFlag {
8695
MDB_CREATE(0x4_0000);
8796

8897
private final int mask;
98+
private final boolean propagatedToLmdb;
8999

90-
DbiFlags(final int mask) {
100+
DbiFlags(final int mask, final boolean propagatedToLmdb) {
91101
this.mask = mask;
102+
this.propagatedToLmdb = propagatedToLmdb;
103+
}
104+
105+
DbiFlags(final int mask) {
106+
this(mask, true);
92107
}
93108

94109
@Override
95110
public int getMask() {
96111
return mask;
97112
}
98113

114+
@Override
115+
public boolean isPropagatedToLmdb() {
116+
return propagatedToLmdb;
117+
}
99118
}

0 commit comments

Comments
 (0)