Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion src/main/java/org/lmdbjava/BufferProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
package org.lmdbjava;

import static java.lang.Long.BYTES;
import static org.lmdbjava.DbiFlags.MDB_INTEGERKEY;
import static org.lmdbjava.DbiFlags.MDB_UNSIGNEDKEY;
import static org.lmdbjava.MaskedFlag.isSet;
import static org.lmdbjava.MaskedFlag.mask;

import java.util.Comparator;

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

return isSet(intFlag, MDB_INTEGERKEY) || isSet(intFlag, MDB_UNSIGNEDKEY) ? getUnsignedComparator() : getSignedComparator();
}

/**
* Get a suitable default {@link Comparator} to compare numeric key values as unsigned.
*
* @return a comparator that can be used (never null)
*/
protected abstract Comparator<T> getUnsignedComparator();
/**
* Get a suitable default {@link Comparator} to compare numeric key values as signed.
*
* @return a comparator that can be used (never null)
*/
protected abstract Comparator<T> getSignedComparator();
/**
* Deallocate a buffer that was previously provided by {@link #allocate()}.
*
Expand Down
50 changes: 38 additions & 12 deletions src/main/java/org/lmdbjava/ByteArrayProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,15 @@

package org.lmdbjava;

import static java.util.Objects.requireNonNull;
import static org.lmdbjava.Library.RUNTIME;
import jnr.ffi.Pointer;
import jnr.ffi.provider.MemoryManager;

import java.util.Arrays;
import java.util.Comparator;

import jnr.ffi.Pointer;
import jnr.ffi.provider.MemoryManager;
import static java.lang.Math.min;
import static java.util.Objects.requireNonNull;
import static org.lmdbjava.Library.RUNTIME;

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

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

private ByteArrayProxy() {
private static final Comparator<byte[]> signedComparator = ByteArrayProxy::compareArraysSigned;
private static final Comparator<byte[]> unsignedComparator = ByteArrayProxy::compareArrays;

private ByteArrayProxy() {
}

/**
Expand All @@ -60,7 +64,7 @@ public static int compareArrays(final byte[] o1, final byte[] o2) {
if (o1 == o2) {
return 0;
}
final int minLength = Math.min(o1.length, o2.length);
final int minLength = min(o1.length, o2.length);

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

/**
* Compare two byte arrays.
*
* @param b1 left operand (required)
* @param b2 right operand (required)
* @return as specified by {@link Comparable} interface
*/
@SuppressWarnings("PMD.CompareObjectsWithEquals")
public static int compareArraysSigned(final byte[] b1, final byte[] b2) {
requireNonNull(b1);
requireNonNull(b2);

if (b1 == b2) return 0;

for(int i = 0; i < min(b1.length, b2.length); ++i) {
if(b1[i] != b2[i]) return b1[i] - b2[i];
}

return b1.length - b2.length;
}

@Override
protected byte[] allocate() {
return new byte[0];
}

protected int compare(final byte[] o1, final byte[] o2) {
return compareArrays(o1, o2);
}

@Override
protected void deallocate(final byte[] buff) {
// byte arrays cannot be allocated
Expand All @@ -94,8 +115,13 @@ protected byte[] getBytes(final byte[] buffer) {
}

@Override
protected Comparator<byte[]> getComparator(final DbiFlags... flags) {
return this::compare;
protected Comparator<byte[]> getSignedComparator() {
return signedComparator;
}

@Override
protected Comparator<byte[]> getUnsignedComparator() {
return unsignedComparator;
}

@Override
Expand Down
28 changes: 18 additions & 10 deletions src/main/java/org/lmdbjava/ByteBufProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,17 @@

package org.lmdbjava;

import static io.netty.buffer.PooledByteBufAllocator.DEFAULT;
import static java.lang.Class.forName;
import static org.lmdbjava.UnsafeAccess.UNSAFE;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import jnr.ffi.Pointer;

import java.lang.reflect.Field;
import java.util.Comparator;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator;
import jnr.ffi.Pointer;
import static io.netty.buffer.PooledByteBufAllocator.DEFAULT;
import static java.lang.Class.forName;
import static java.util.Objects.requireNonNull;
import static org.lmdbjava.UnsafeAccess.UNSAFE;

/**
* A buffer proxy backed by Netty's {@link ByteBuf}.
Expand All @@ -51,6 +52,12 @@ public final class ByteBufProxy extends BufferProxy<ByteBuf> {
private static final String FIELD_NAME_ADDRESS = "memoryAddress";
private static final String FIELD_NAME_LENGTH = "length";
private static final String NAME = "io.netty.buffer.PooledUnsafeDirectByteBuf";
private static final Comparator<ByteBuf> comparator = (o1, o2) -> {
requireNonNull(o1);
requireNonNull(o2);

return o1.compareTo(o2);
};
private final long lengthOffset;
private final long addressOffset;

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

protected int compare(final ByteBuf o1, final ByteBuf o2) {
return o1.compareTo(o2);
@Override
protected Comparator<ByteBuf> getSignedComparator() {
return comparator;
}

@Override
protected Comparator<ByteBuf> getComparator(final DbiFlags... flags) {
return this::compare;
protected Comparator<ByteBuf> getUnsignedComparator() {
return comparator;
}

@Override
Expand Down
28 changes: 14 additions & 14 deletions src/main/java/org/lmdbjava/ByteBufferProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import static java.nio.ByteOrder.LITTLE_ENDIAN;
import static java.util.Objects.requireNonNull;
import static org.lmdbjava.DbiFlags.MDB_INTEGERKEY;
import static org.lmdbjava.DbiFlags.MDB_UNSIGNEDKEY;
import static org.lmdbjava.Env.SHOULD_CHECK;
import static org.lmdbjava.MaskedFlag.isSet;
import static org.lmdbjava.MaskedFlag.mask;
Expand Down Expand Up @@ -111,6 +112,14 @@ abstract static class AbstractByteBufferProxy extends BufferProxy<ByteBuffer> {
protected static final String FIELD_NAME_ADDRESS = "address";
protected static final String FIELD_NAME_CAPACITY = "capacity";

private static final Comparator<ByteBuffer> signedComparator = (o1, o2) -> {
requireNonNull(o1);
requireNonNull(o2);

return o1.compareTo(o2);
};
private static final Comparator<ByteBuffer> unsignedComparator = AbstractByteBufferProxy::compareBuff;

/**
* A thread-safe pool for a given length. If the buffer found is valid (ie
* not of a negative length) then that buffer is used. If no valid buffer is
Expand Down Expand Up @@ -193,22 +202,13 @@ protected final ByteBuffer allocate() {
}

@Override
protected Comparator<ByteBuffer> getComparator(final DbiFlags... flags) {
final int flagInt = mask(flags);
if (isSet(flagInt, MDB_INTEGERKEY)) {
return this::compareCustom;
}
return this::compareDefault;
protected Comparator<ByteBuffer> getSignedComparator() {
return signedComparator;
}

protected final int compareDefault(final ByteBuffer o1,
final ByteBuffer o2) {
return o1.compareTo(o2);
}

protected final int compareCustom(final ByteBuffer o1,
final ByteBuffer o2) {
return compareBuff(o1, o2);
@Override
protected Comparator<ByteBuffer> getUnsignedComparator() {
return unsignedComparator;
}

@Override
Expand Down
10 changes: 6 additions & 4 deletions src/main/java/org/lmdbjava/Cursor.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
import jnr.ffi.Pointer;
import jnr.ffi.byref.NativeLongByReference;

import java.util.Arrays;

/**
* A cursor handle.
*
Expand Down Expand Up @@ -120,7 +122,7 @@ public void delete(final PutFlags... f) {
txn.checkReady();
txn.checkWritesAllowed();
}
final int flags = mask(f);
final int flags = mask(true, f);
checkRc(LIB.mdb_cursor_del(ptrCursor, flags));
}

Expand Down Expand Up @@ -256,7 +258,7 @@ public boolean put(final T key, final T val, final PutFlags... op) {
}
kv.keyIn(key);
kv.valIn(val);
final int mask = mask(op);
final int mask = mask(true, op);
final int rc = LIB.mdb_cursor_put(ptrCursor, kv.pointerKey(),
kv.pointerVal(), mask);
if (rc == MDB_KEYEXIST) {
Expand Down Expand Up @@ -299,7 +301,7 @@ public void putMultiple(final T key, final T val, final int elements,
txn.checkReady();
txn.checkWritesAllowed();
}
final int mask = mask(op);
final int mask = mask(true, op);
if (SHOULD_CHECK && !isSet(mask, MDB_MULTIPLE)) {
throw new IllegalArgumentException("Must set " + MDB_MULTIPLE + " flag");
}
Expand Down Expand Up @@ -364,7 +366,7 @@ public T reserve(final T key, final int size, final PutFlags... op) {
}
kv.keyIn(key);
kv.valIn(size);
final int flags = mask(op) | MDB_RESERVE.getMask();
final int flags = mask(true, op) | MDB_RESERVE.getMask();
checkRc(LIB.mdb_cursor_put(ptrCursor, kv.pointerKey(), kv.pointerVal(),
flags));
kv.valOut();
Expand Down
38 changes: 23 additions & 15 deletions src/main/java/org/lmdbjava/Dbi.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@

package org.lmdbjava;

import jnr.ffi.Pointer;
import jnr.ffi.byref.IntByReference;
import jnr.ffi.byref.PointerByReference;
import org.lmdbjava.Library.ComparatorCallback;
import org.lmdbjava.Library.MDB_stat;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import static java.util.Objects.requireNonNull;
import static jnr.ffi.Memory.allocateDirect;
import static jnr.ffi.NativeType.ADDRESS;
Expand All @@ -36,17 +47,6 @@
import static org.lmdbjava.PutFlags.MDB_RESERVE;
import static org.lmdbjava.ResultCodeMapper.checkRc;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import jnr.ffi.Pointer;
import jnr.ffi.byref.IntByReference;
import jnr.ffi.byref.PointerByReference;
import org.lmdbjava.Library.ComparatorCallback;
import org.lmdbjava.Library.MDB_stat;

/**
* LMDB Database.
*
Expand All @@ -64,10 +64,18 @@ public final class Dbi<T> {
Dbi(final Env<T> env, final Txn<T> txn, final byte[] name,
final Comparator<T> comparator, final boolean nativeCb,
final BufferProxy<T> proxy, final DbiFlags... flags) {
if (SHOULD_CHECK) {
requireNonNull(txn);
txn.checkReady();
}
this.env = env;
this.name = name == null ? null : Arrays.copyOf(name, name.length);
this.comparator = comparator;
final int flagsMask = mask(flags);
if(comparator == null) {
this.comparator = proxy.getComparator(flags);
} else {
this.comparator = comparator;
}
final int flagsMask = mask(true, flags);
final Pointer dbiPtr = allocateDirect(RUNTIME, ADDRESS);
checkRc(LIB.mdb_dbi_open(txn.pointer(), name, flagsMask, dbiPtr));
ptr = dbiPtr.getPointer(0);
Expand Down Expand Up @@ -377,7 +385,7 @@ public boolean put(final Txn<T> txn, final T key, final T val,
}
txn.kv().keyIn(key);
txn.kv().valIn(val);
final int mask = mask(flags);
final int mask = mask(true, flags);
final int rc = LIB.mdb_put(txn.pointer(), ptr, txn.kv().pointerKey(), txn
.kv().pointerVal(), mask);
if (rc == MDB_KEYEXIST) {
Expand Down Expand Up @@ -422,7 +430,7 @@ public T reserve(final Txn<T> txn, final T key, final int size,
}
txn.kv().keyIn(key);
txn.kv().valIn(size);
final int flags = mask(op) | MDB_RESERVE.getMask();
final int flags = mask(true, op) | MDB_RESERVE.getMask();
checkRc(LIB.mdb_put(txn.pointer(), ptr, txn.kv().pointerKey(), txn.kv()
.pointerVal(), flags));
txn.kv().valOut(); // marked as in,out in LMDB C docs
Expand Down
21 changes: 20 additions & 1 deletion src/main/java/org/lmdbjava/DbiFlags.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ public enum DbiFlags implements MaskedFlag {
* similar to {@link #MDB_INTEGERKEY} keys.
*/
MDB_INTEGERDUP(0x20),
/**
* Compare the <b>numeric</b> keys in native byte order and as unsigned.
*
* <p>
* This option is applied only to {@link java.nio.ByteBuffer}, {@link org.agrona.DirectBuffer} and byte array keys.
* {@link io.netty.buffer.ByteBuf} keys are always compared in native byte order and as unsigned.
* </p>
*/
MDB_UNSIGNEDKEY(0x30, false),
/**
* With {@link #MDB_DUPSORT}, use reverse string dups.
*
Expand All @@ -86,14 +95,24 @@ public enum DbiFlags implements MaskedFlag {
MDB_CREATE(0x4_0000);

private final int mask;
private final boolean propagatedToLmdb;

DbiFlags(final int mask) {
DbiFlags(final int mask, final boolean propagatedToLmdb) {
this.mask = mask;
this.propagatedToLmdb = propagatedToLmdb;
}

DbiFlags(final int mask) {
this(mask, true);
}

@Override
public int getMask() {
return mask;
}

@Override
public boolean isPropagatedToLmdb() {
return propagatedToLmdb;
}
}
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy