diff --git a/pom.xml b/pom.xml
index efc324c..e3122bd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,6 +29,11 @@
argparse4j
0.9.0
+
+ it.unimi.dsi
+ fastutil-core
+ 8.5.12
+
com.github.seancfoley
ipaddress
diff --git a/src/main/java/it/cavallium/rockserver/core/Main.java b/src/main/java/it/cavallium/rockserver/core/Main.java
index 39bae4c..dcf1985 100644
--- a/src/main/java/it/cavallium/rockserver/core/Main.java
+++ b/src/main/java/it/cavallium/rockserver/core/Main.java
@@ -81,7 +81,7 @@ public class Main {
switch (url.getScheme()) {
case "unix" -> clientBuilder.setUnixSocket(UnixDomainSocketAddress.of(Path.of(url.getPath())));
- case "file" -> clientBuilder.setEmbeddedPath(Path.of(url.getPath()));
+ case "file" -> clientBuilder.setEmbeddedPath(Path.of((url.getAuthority() != null ? url.getAuthority() : "") + url.getPath()).normalize());
case "memory" -> clientBuilder.setEmbeddedInMemory(true);
case "rocksdb" -> clientBuilder.setAddress(new HostName(url.getHost()).asInetSocketAddress());
default -> throw new IllegalArgumentException("Invalid scheme: " + url.getScheme());
diff --git a/src/main/java/it/cavallium/rockserver/core/common/ColumnHashType.java b/src/main/java/it/cavallium/rockserver/core/common/ColumnHashType.java
new file mode 100644
index 0000000..9e9fc99
--- /dev/null
+++ b/src/main/java/it/cavallium/rockserver/core/common/ColumnHashType.java
@@ -0,0 +1,34 @@
+package it.cavallium.rockserver.core.common;
+
+import it.cavallium.rockserver.core.impl.ColumnInstance;
+import it.cavallium.rockserver.core.impl.XXHash32;
+import java.lang.foreign.MemorySegment;
+
+public enum ColumnHashType implements HashFunction {
+ XXHASH32(Integer.BYTES, (inputData, hashResult) -> XXHash32.getInstance()
+ .hash(inputData, 0, Math.toIntExact(inputData.byteSize()), 0, hashResult)),
+ XXHASH8(Byte.BYTES, (inputData, hashResult) -> {
+ var xxHash = XXHash32.getInstance().hash(Utils.toByteArray(inputData), 0, Math.toIntExact(inputData.byteSize()), 0);
+ hashResult.set(ColumnInstance.BIG_ENDIAN_BYTES, 0, (byte) xxHash);
+ }),
+ ALLSAME8(Byte.BYTES, (inputData, hashResult) -> {
+ hashResult.set(ColumnInstance.BIG_ENDIAN_BYTES, 0, (byte) 0);
+ });
+
+ private final int bytes;
+ private final HashFunction hashFunction;
+
+ ColumnHashType(int bytes, HashFunction hashFunction) {
+ this.bytes = bytes;
+ this.hashFunction = hashFunction;
+ }
+
+ public int bytesSize() {
+ return bytes;
+ }
+
+ public void hash(MemorySegment inputData, MemorySegment hashResult) {
+ hashFunction.hash(inputData, hashResult);
+ }
+
+}
diff --git a/src/main/java/it/cavallium/rockserver/core/common/ColumnSchema.java b/src/main/java/it/cavallium/rockserver/core/common/ColumnSchema.java
index 18938bd..42ef56c 100644
--- a/src/main/java/it/cavallium/rockserver/core/common/ColumnSchema.java
+++ b/src/main/java/it/cavallium/rockserver/core/common/ColumnSchema.java
@@ -1,18 +1,43 @@
package it.cavallium.rockserver.core.common;
-public record ColumnSchema(int[] keys, int variableLengthKeysCount, boolean hasValue) {
- public ColumnSchema {
- if (variableLengthKeysCount > keys.length) {
- throw new IllegalArgumentException("variable length keys count must be less or equal keysCount");
+import it.cavallium.rockserver.core.common.RocksDBException.RocksDBErrorType;
+import it.unimi.dsi.fastutil.ints.IntArrayList;
+import it.unimi.dsi.fastutil.ints.IntList;
+import it.unimi.dsi.fastutil.objects.ObjectList;
+import java.lang.foreign.MemorySegment;
+
+public record ColumnSchema(IntList keys, ObjectList variableTailKeys, boolean hasValue) {
+
+ public static ColumnSchema of(IntList fixedKeys, ObjectList variableTailKeys, boolean hasValue) {
+ IntList keys;
+ if (!variableTailKeys.isEmpty()) {
+ keys = new IntArrayList(fixedKeys.size() + variableTailKeys.size());
+ keys.addAll(fixedKeys);
+ for (ColumnHashType variableTailKey : variableTailKeys) {
+ keys.add(variableTailKey.bytesSize());
+ }
+ } else {
+ keys = fixedKeys;
}
- for (int i = 0; i < keys.length - variableLengthKeysCount; i++) {
- if (keys[i] <= 0) {
- throw new UnsupportedOperationException("Key length must be > 0");
+ return new ColumnSchema(keys, variableTailKeys, hasValue);
+ }
+
+ public ColumnSchema {
+ final int keysSize = keys.size();
+ final int variableKeysSize = variableTailKeys.size();
+ if (variableKeysSize > keysSize) {
+ throw new RocksDBException(RocksDBErrorType.KEYS_COUNT_MISMATCH, "variable length keys count must be less or equal keysCount");
+ }
+ for (int i = 0; i < keysSize - variableKeysSize; i++) {
+ if (keys.getInt(i) <= 0) {
+ throw new RocksDBException(RocksDBErrorType.KEY_LENGTH_MISMATCH, "Key length must be > 0");
}
}
- for (int i = keys.length - variableLengthKeysCount; i < keys.length; i++) {
- if (keys[i] <= 1) {
- throw new UnsupportedOperationException("Key hash length must be > 1");
+ for (int i = keysSize - variableKeysSize; i < keysSize; i++) {
+ var hash = variableTailKeys.get(i - (keysSize - variableKeysSize));
+ var keySize = keys.getInt(i);
+ if (keySize != hash.bytesSize()) {
+ throw new RocksDBException(RocksDBErrorType.KEY_HASH_SIZE_MISMATCH, "Key hash length of type " + hash + " must be " + hash.bytesSize() + ", but it's defined as " + keySize);
}
}
}
@@ -22,15 +47,39 @@ public record ColumnSchema(int[] keys, int variableLengthKeysCount, boolean hasV
* @return an array with the length of each key, variable-length keys must have the length of their hash
*/
@Override
- public int[] keys() {
+ public IntList keys() {
return keys;
}
/**
* The last n keys that are variable-length
*/
- @Override
public int variableLengthKeysCount() {
- return variableLengthKeysCount;
+ return variableTailKeys.size();
+ }
+
+ /**
+ * The first n keys that are fixed
+ */
+ public int fixedLengthKeysCount() {
+ return keys.size() - variableTailKeys.size();
+ }
+
+ /**
+ * The first n keys that are fixed
+ */
+ public int keysCount() {
+ return keys.size();
+ }
+
+ public int key(int i) {
+ return keys.getInt(i);
+ }
+
+ /**
+ * @param absoluteIndex index from the first fixed key
+ */
+ public ColumnHashType variableTailKey(int absoluteIndex) {
+ return variableTailKeys.get(absoluteIndex - fixedLengthKeysCount());
}
}
diff --git a/src/main/java/it/cavallium/rockserver/core/common/HashFunction.java b/src/main/java/it/cavallium/rockserver/core/common/HashFunction.java
new file mode 100644
index 0000000..e389ff0
--- /dev/null
+++ b/src/main/java/it/cavallium/rockserver/core/common/HashFunction.java
@@ -0,0 +1,9 @@
+package it.cavallium.rockserver.core.common;
+
+import java.lang.foreign.Arena;
+import java.lang.foreign.MemorySegment;
+
+public interface HashFunction {
+
+ void hash(MemorySegment inputData, MemorySegment hashResult);
+}
diff --git a/src/main/java/it/cavallium/rockserver/core/common/RocksDBException.java b/src/main/java/it/cavallium/rockserver/core/common/RocksDBException.java
index ce1dfff..22cddaf 100644
--- a/src/main/java/it/cavallium/rockserver/core/common/RocksDBException.java
+++ b/src/main/java/it/cavallium/rockserver/core/common/RocksDBException.java
@@ -5,7 +5,7 @@ public class RocksDBException extends RuntimeException {
private final RocksDBErrorType errorUniqueId;
public enum RocksDBErrorType {
- PUT_UNKNOWN_ERROR, PUT_2, UNEXPECTED_NULL_VALUE, PUT_1, PUT_3, GET_1, COLUMN_EXISTS, COLUMN_CREATE_FAIL, COLUMN_NOT_FOUND, COLUMN_DELETE_FAIL, CONFIG_ERROR, ROCKSDB_CONFIG_ERROR, VALUE_MUST_BE_NULL, DIRECTORY_DELETE, KEY_LENGTH_MISMATCH, UNSUPPORTED_HASH_SIZE, RAW_KEY_LENGTH_MISMATCH, KEYS_COUNT_MISMATCH, COMMIT_FAILED_TRY_AGAIN, COMMIT_FAILED, TX_NOT_FOUND, ROCKSDB_LOAD_ERROR
+ PUT_UNKNOWN_ERROR, PUT_2, UNEXPECTED_NULL_VALUE, PUT_1, PUT_3, GET_1, COLUMN_EXISTS, COLUMN_CREATE_FAIL, COLUMN_NOT_FOUND, COLUMN_DELETE_FAIL, CONFIG_ERROR, ROCKSDB_CONFIG_ERROR, VALUE_MUST_BE_NULL, DIRECTORY_DELETE, KEY_LENGTH_MISMATCH, UNSUPPORTED_HASH_SIZE, RAW_KEY_LENGTH_MISMATCH, KEYS_COUNT_MISMATCH, COMMIT_FAILED_TRY_AGAIN, COMMIT_FAILED, TX_NOT_FOUND, KEY_HASH_SIZE_MISMATCH, ROCKSDB_LOAD_ERROR
}
diff --git a/src/main/java/it/cavallium/rockserver/core/common/Utils.java b/src/main/java/it/cavallium/rockserver/core/common/Utils.java
index 69fd5e9..3270b7e 100644
--- a/src/main/java/it/cavallium/rockserver/core/common/Utils.java
+++ b/src/main/java/it/cavallium/rockserver/core/common/Utils.java
@@ -51,7 +51,7 @@ public class Utils {
}
@NotNull
- public static MemorySegment toMemorySegment(byte @Nullable [] array) {
+ public static MemorySegment toMemorySegment(byte... array) {
if (array != null) {
return MemorySegment.ofArray(array);
} else {
@@ -59,6 +59,19 @@ public class Utils {
}
}
+ @NotNull
+ public static MemorySegment toMemorySegmentSimple(int... array) {
+ if (array != null) {
+ var newArray = new byte[array.length];
+ for (int i = 0; i < array.length; i++) {
+ newArray[i] = (byte) array[i];
+ }
+ return MemorySegment.ofArray(newArray);
+ } else {
+ return MemorySegment.NULL;
+ }
+ }
+
public static byte[] toByteArray(MemorySegment memorySegment) {
return memorySegment.toArray(BIG_ENDIAN_BYTES);
}
diff --git a/src/main/java/it/cavallium/rockserver/core/impl/Bucket.java b/src/main/java/it/cavallium/rockserver/core/impl/Bucket.java
index 78f4786..3e64e33 100644
--- a/src/main/java/it/cavallium/rockserver/core/impl/Bucket.java
+++ b/src/main/java/it/cavallium/rockserver/core/impl/Bucket.java
@@ -1,6 +1,7 @@
package it.cavallium.rockserver.core.impl;
import static it.cavallium.rockserver.core.impl.ColumnInstance.BIG_ENDIAN_INT;
+import static it.cavallium.rockserver.core.impl.ColumnInstance.BIG_ENDIAN_INT_UNALIGNED;
import static java.lang.Math.toIntExact;
import it.cavallium.rockserver.core.common.Utils;
@@ -122,15 +123,16 @@ public class Bucket {
}
private int indexOf(MemorySegment[] bucketVariableKeys) {
- for (int i = 0; i < elements.size(); i++) {
+ nextElement: for (int i = 0; i < elements.size(); i++) {
var elem = elements.get(i);
var arrayKeys = elem.getKey();
assert arrayKeys.length == bucketVariableKeys.length;
for (int j = 0; j < arrayKeys.length; j++) {
- if (Utils.valueEquals(arrayKeys[j], bucketVariableKeys[j])) {
- return i;
+ if (!Utils.valueEquals(arrayKeys[j], bucketVariableKeys[j])) {
+ continue nextElement;
}
}
+ return i;
}
return -1;
}
@@ -157,7 +159,7 @@ public class Bucket {
offset += Integer.BYTES;
for (MemorySegment elementAtI : serializedElements) {
var elementSize = elementAtI.byteSize();
- segment.set(BIG_ENDIAN_INT, offset, toIntExact(elementSize));
+ segment.set(BIG_ENDIAN_INT_UNALIGNED, offset, toIntExact(elementSize));
offset += Integer.BYTES;
MemorySegment.copy(elementAtI, 0, segment, offset, elementSize);
offset += elementSize;
diff --git a/src/main/java/it/cavallium/rockserver/core/impl/ColumnInstance.java b/src/main/java/it/cavallium/rockserver/core/impl/ColumnInstance.java
index 681b884..834c760 100644
--- a/src/main/java/it/cavallium/rockserver/core/impl/ColumnInstance.java
+++ b/src/main/java/it/cavallium/rockserver/core/impl/ColumnInstance.java
@@ -69,7 +69,7 @@ public record ColumnInstance(ColumnFamilyHandle cfh, ColumnSchema schema, int fi
} else {
finalKey = arena.allocate(finalKeySizeBytes);
long offsetBytes = 0;
- for (int i = 0; i < schema.keys().length; i++) {
+ for (int i = 0; i < schema.keys().size(); i++) {
var computedKeyAtI = computeKeyAt(arena, i, keys);
var computedKeyAtISize = computedKeyAtI.byteSize();
MemorySegment.copy(computedKeyAtI, 0, finalKey, offsetBytes, computedKeyAtISize);
@@ -81,20 +81,18 @@ public record ColumnInstance(ColumnFamilyHandle cfh, ColumnSchema schema, int fi
}
private MemorySegment computeKeyAt(Arena arena, int i, MemorySegment[] keys) {
- if (i < schema.keys().length - schema.variableLengthKeysCount()) {
- if (keys[i].byteSize() != schema.keys()[i]) {
+ if (i < schema.keys().size() - schema.variableLengthKeysCount()) {
+ if (keys[i].byteSize() != schema.key(i)) {
throw new RocksDBException(RocksDBErrorType.KEY_LENGTH_MISMATCH,
- "Key at index " + i + " has a different length than expected! Expected: " + schema.keys()[i]
+ "Key at index " + i + " has a different length than expected! Expected: " + schema.key(i)
+ ", received: " + keys[i].byteSize());
}
return keys[i];
} else {
- if (schema.keys()[i] != Integer.BYTES) {
- throw new RocksDBException(RocksDBErrorType.UNSUPPORTED_HASH_SIZE,
- "Hash size different than 32-bit is currently unsupported");
- } else {
- return XXHash32.getInstance().hash(arena, keys[i], 0, 0, 0);
- }
+ var tailKey = schema.variableTailKey(i);
+ var hashResult = arena.allocate(tailKey.bytesSize());
+ tailKey.hash(keys[i], hashResult);
+ return hashResult;
}
}
@@ -107,9 +105,9 @@ public record ColumnInstance(ColumnFamilyHandle cfh, ColumnSchema schema, int fi
}
private void validateKeyCount(MemorySegment[] keys) {
- if (schema.keys().length != keys.length) {
+ if (schema.keys().size() != keys.length) {
throw new RocksDBException(RocksDBErrorType.KEYS_COUNT_MISMATCH,
- "Keys count must be equal to the column keys count. Expected: " + schema.keys().length
+ "Keys count must be equal to the column keys count. Expected: " + schema.keys().size()
+ ", got: " + keys.length);
}
}
@@ -172,9 +170,9 @@ public record ColumnInstance(ColumnFamilyHandle cfh, ColumnSchema schema, int fi
* Get only the variable-length keys
*/
public MemorySegment[] getBucketElementKeys(MemorySegment[] keys) {
- assert keys.length == schema.keys().length;
+ assert keys.length == schema.keys().size();
return Arrays.copyOfRange(keys,
- schema.keys().length - schema.variableLengthKeysCount(),
- schema.keys().length);
+ schema.keys().size() - schema.variableLengthKeysCount(),
+ schema.keys().size());
}
}
diff --git a/src/main/java/it/cavallium/rockserver/core/impl/EmbeddedDB.java b/src/main/java/it/cavallium/rockserver/core/impl/EmbeddedDB.java
index 576ed27..863daed 100644
--- a/src/main/java/it/cavallium/rockserver/core/impl/EmbeddedDB.java
+++ b/src/main/java/it/cavallium/rockserver/core/impl/EmbeddedDB.java
@@ -7,14 +7,11 @@ import static java.util.Objects.requireNonNullElse;
import static org.rocksdb.KeyMayExist.KeyMayExistEnum.kExistsWithValue;
import static org.rocksdb.KeyMayExist.KeyMayExistEnum.kExistsWithoutValue;
-import it.cavallium.rockserver.core.common.Callback.CallbackDelta;
-import it.cavallium.rockserver.core.common.Callback.CallbackPrevious;
-import it.cavallium.rockserver.core.common.Callback.CallbackVoid;
+import it.cavallium.rockserver.core.common.Callback;
import it.cavallium.rockserver.core.common.Callback.GetCallback;
import it.cavallium.rockserver.core.common.Callback.IteratorCallback;
import it.cavallium.rockserver.core.common.Callback.PutCallback;
import it.cavallium.rockserver.core.common.ColumnSchema;
-import it.cavallium.rockserver.core.common.Callback;
import it.cavallium.rockserver.core.common.Delta;
import it.cavallium.rockserver.core.common.RocksDBAPI;
import it.cavallium.rockserver.core.common.RocksDBException.RocksDBErrorType;
@@ -39,20 +36,18 @@ import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicReference;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.cliffc.high_scale_lib.NonBlockingHashMapLong;
-import org.github.gestalt.config.builder.GestaltBuilder;
-import org.github.gestalt.config.exceptions.GestaltException;
-import org.github.gestalt.config.source.ClassPathConfigSource;
-import org.github.gestalt.config.source.ClassPathConfigSourceBuilder;
-import org.github.gestalt.config.source.FileConfigSource;
-import org.github.gestalt.config.source.FileConfigSourceBuilder;
-import org.github.gestalt.config.source.StringConfigSourceBuilder;
import org.jetbrains.annotations.Nullable;
-import org.rocksdb.*;
+import org.rocksdb.ColumnFamilyDescriptor;
+import org.rocksdb.ReadOptions;
+import org.rocksdb.RocksDB;
+import org.rocksdb.RocksDBException;
+import org.rocksdb.RocksIterator;
import org.rocksdb.Status.Code;
+import org.rocksdb.Transaction;
+import org.rocksdb.WriteOptions;
public class EmbeddedDB implements RocksDBAPI, Closeable {
diff --git a/src/main/java/it/cavallium/rockserver/core/impl/XXHash32.java b/src/main/java/it/cavallium/rockserver/core/impl/XXHash32.java
index 122e1bf..5e0ffba 100644
--- a/src/main/java/it/cavallium/rockserver/core/impl/XXHash32.java
+++ b/src/main/java/it/cavallium/rockserver/core/impl/XXHash32.java
@@ -34,7 +34,7 @@ public abstract class XXHash32 {
* Compute the big-endian 32-bits hash of buf[off:off+len]
using seed
* seed
.
*/
- public abstract MemorySegment hash(Arena arena, MemorySegment buf, int off, int len, int seed);
+ public abstract void hash(MemorySegment buf, int off, int len, int seed, MemorySegment result);
@Override
public String toString() {
diff --git a/src/main/java/it/cavallium/rockserver/core/impl/XXHash32JavaSafe.java b/src/main/java/it/cavallium/rockserver/core/impl/XXHash32JavaSafe.java
index 6116286..a2640c4 100644
--- a/src/main/java/it/cavallium/rockserver/core/impl/XXHash32JavaSafe.java
+++ b/src/main/java/it/cavallium/rockserver/core/impl/XXHash32JavaSafe.java
@@ -36,8 +36,8 @@ public final class XXHash32JavaSafe extends XXHash32 {
public static final ByteOrder NATIVE_BYTE_ORDER = ByteOrder.nativeOrder();
public static final XXHash32 INSTANCE = new XXHash32JavaSafe();
- private static final OfInt INT_LE = ValueLayout.JAVA_INT.withOrder(ByteOrder.LITTLE_ENDIAN);
- private static final OfInt INT_BE = ValueLayout.JAVA_INT.withOrder(ByteOrder.BIG_ENDIAN);
+ private static final OfInt INT_LE = ValueLayout.JAVA_INT_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN);
+ private static final OfInt INT_BE = ValueLayout.JAVA_INT_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN);
private static final OfByte BYTE_BE = ValueLayout.JAVA_BYTE.withOrder(ByteOrder.BIG_ENDIAN);
@Override
@@ -104,7 +104,7 @@ public final class XXHash32JavaSafe extends XXHash32 {
}
@Override
- public MemorySegment hash(Arena arena, MemorySegment buf, int off, int len, int seed) {
+ public void hash(MemorySegment buf, int off, int len, int seed, MemorySegment result) {
checkRange(buf, off, len);
final int end = off + len;
@@ -163,7 +163,8 @@ public final class XXHash32JavaSafe extends XXHash32 {
h32 *= PRIME3;
h32 ^= h32 >>> 16;
- return arena.allocate(INT_BE, h32);
+ assert result.byteSize() >= Integer.BYTES;
+ result.set(INT_BE, 0, h32);
}
private static void checkRange(byte[] buf, int off) {
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index eebafa1..ee9bf6b 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -8,6 +8,7 @@ module rockserver.core {
requires high.scale.lib;
requires org.github.gestalt.core;
requires org.github.gestalt.hocon;
+ requires it.unimi.dsi.fastutil.core;
exports it.cavallium.rockserver.core.client;
exports it.cavallium.rockserver.core.common;
diff --git a/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBTest.java b/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBTest.java
index 2a5d455..8f05b6d 100644
--- a/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBTest.java
+++ b/src/test/java/it/cavallium/rockserver/core/impl/test/EmbeddedDBTest.java
@@ -1,20 +1,23 @@
package it.cavallium.rockserver.core.impl.test;
+import static it.cavallium.rockserver.core.common.Utils.toMemorySegmentSimple;
+
import it.cavallium.rockserver.core.client.EmbeddedConnection;
import it.cavallium.rockserver.core.common.Callback;
-import it.cavallium.rockserver.core.common.Callback.CallbackDelta;
+import it.cavallium.rockserver.core.common.ColumnHashType;
import it.cavallium.rockserver.core.common.ColumnSchema;
import it.cavallium.rockserver.core.common.Utils;
+import it.unimi.dsi.fastutil.ints.IntList;
+import it.unimi.dsi.fastutil.objects.ObjectList;
import org.junit.jupiter.api.Assertions;
import java.io.IOException;
import java.lang.foreign.MemorySegment;
-import java.util.concurrent.atomic.AtomicInteger;
class EmbeddedDBTest {
private EmbeddedConnection db;
- private long colId;
+ private long colId = 0L;
@org.junit.jupiter.api.BeforeEach
void setUp() throws IOException {
@@ -22,7 +25,21 @@ class EmbeddedDBTest {
System.setProperty("rockserver.core.print-config", "false");
}
db = new EmbeddedConnection(null, "test", null);
- this.colId = db.createColumn("put-1", new ColumnSchema(new int[]{1, 2, 1, Integer.BYTES, Integer.BYTES}, 2, true));
+ createStandardColumn();
+ }
+
+ private void createStandardColumn() {
+ createColumn(ColumnSchema.of(IntList.of(1, 2, 1),
+ ObjectList.of(ColumnHashType.XXHASH32, ColumnHashType.XXHASH32),
+ true
+ ));
+ }
+
+ private void createColumn(ColumnSchema schema) {
+ if (colId != 0L) {
+ db.deleteColumn(colId);
+ }
+ colId = db.createColumn("column-test", schema);
}
@org.junit.jupiter.api.AfterEach
@@ -32,13 +49,13 @@ class EmbeddedDBTest {
}
@org.junit.jupiter.api.Test
- void put() {
+ void putSameBucketSameKey() {
var key = new MemorySegment[] {
- Utils.toMemorySegment(new byte[] {3}),
- Utils.toMemorySegment(new byte[] {4, 6}),
- Utils.toMemorySegment(new byte[] {3}),
- Utils.toMemorySegment(new byte[] {1, 2, 3}),
- Utils.toMemorySegment(new byte[] {0, 0, 3, 6, 7, 8})
+ toMemorySegmentSimple(3),
+ toMemorySegmentSimple(4, 6),
+ toMemorySegmentSimple(3),
+ toMemorySegmentSimple(1, 2, 3),
+ toMemorySegmentSimple(0, 0, 3, 6, 7, 8)
};
var value1 = MemorySegment.ofArray(new byte[] {0, 0, 3});
var value2 = MemorySegment.ofArray(new byte[] {0, 0, 5});
@@ -52,6 +69,101 @@ class EmbeddedDBTest {
Assertions.assertTrue(Utils.valueEquals(delta.current(), value2));
}
+ @org.junit.jupiter.api.Test
+ void putSameBucketDifferentKey() {
+ createColumn(ColumnSchema.of(IntList.of(1, 2, 1), ObjectList.of(ColumnHashType.XXHASH32, ColumnHashType.ALLSAME8), true));
+
+ var lastKey1 = toMemorySegmentSimple(6, 7, 8);
+ var lastKey2 = toMemorySegmentSimple(6, 7, -48);
+
+ var key1 = new MemorySegment[] {
+ toMemorySegmentSimple(3),
+ toMemorySegmentSimple(4, 6),
+ toMemorySegmentSimple(3),
+ toMemorySegmentSimple(1, 2, 3),
+ lastKey1
+ };
+ var key2 = new MemorySegment[] {
+ toMemorySegmentSimple(3),
+ toMemorySegmentSimple(4, 6),
+ toMemorySegmentSimple(3),
+ toMemorySegmentSimple(1, 2, 3),
+ lastKey2
+ };
+
+ var value1 = MemorySegment.ofArray(new byte[] {0, 0, 3});
+ var value2 = MemorySegment.ofArray(new byte[] {0, 0, 5});
+
+ var delta = db.put(0, colId, key1, value1, Callback.delta());
+ Assertions.assertNull(delta.previous());
+ Assertions.assertTrue(Utils.valueEquals(delta.current(), value1));
+
+ delta = db.put(0, colId, key2, value2, Callback.delta());
+ Assertions.assertNull(delta.previous());
+ Assertions.assertTrue(Utils.valueEquals(delta.current(), value2));
+
+ delta = db.put(0, colId, key2, value1, Callback.delta());
+ Assertions.assertTrue(Utils.valueEquals(delta.previous(), value2));
+ Assertions.assertTrue(Utils.valueEquals(delta.current(), value1));
+ }
+
+ /**
+ * Some keys have same bucket, some not
+ */
+ @org.junit.jupiter.api.Test
+ void putMixedBucketMixedKey() {
+ createColumn(ColumnSchema.of(IntList.of(1, 2, 1), ObjectList.of(ColumnHashType.XXHASH32, ColumnHashType.XXHASH8), true));
+
+ var lastKey1 = toMemorySegmentSimple(6, 7, 8);
+ var collidingLastKey1 = toMemorySegmentSimple(6, 7, -48);
+ var lastKey2 = toMemorySegmentSimple(6, 7, 7);
+
+ var key1 = new MemorySegment[] {
+ toMemorySegmentSimple(3),
+ toMemorySegmentSimple(4, 6),
+ toMemorySegmentSimple(3),
+ toMemorySegmentSimple(1, 2, 3),
+ lastKey1
+ };
+ var collidingKey1 = new MemorySegment[] {
+ toMemorySegmentSimple(3),
+ toMemorySegmentSimple(4, 6),
+ toMemorySegmentSimple(3),
+ toMemorySegmentSimple(1, 2, 3),
+ collidingLastKey1
+ };
+ var key2 = new MemorySegment[] {
+ toMemorySegmentSimple(3),
+ toMemorySegmentSimple(4, 6),
+ toMemorySegmentSimple(3),
+ toMemorySegmentSimple(1, 2, 3),
+ lastKey2
+ };
+
+ var value1 = MemorySegment.ofArray(new byte[] {0, 0, 3});
+ var value2 = MemorySegment.ofArray(new byte[] {0, 0, 5});
+
+ var delta = db.put(0, colId, key1, value1, Callback.delta());
+ Assertions.assertNull(delta.previous());
+ Assertions.assertTrue(Utils.valueEquals(delta.current(), value1));
+
+ delta = db.put(0, colId, collidingKey1, value2, Callback.delta());
+ Assertions.assertNull(delta.previous());
+ Assertions.assertTrue(Utils.valueEquals(delta.current(), value2));
+
+ delta = db.put(0, colId, collidingKey1, value1, Callback.delta());
+ Assertions.assertTrue(Utils.valueEquals(delta.previous(), value2));
+ Assertions.assertTrue(Utils.valueEquals(delta.current(), value1));
+
+ delta = db.put(0, colId, key2, value1, Callback.delta());
+ Assertions.assertNull(delta.previous());
+ Assertions.assertTrue(Utils.valueEquals(delta.current(), value1));
+
+ delta = db.put(0, colId, key2, value2, Callback.delta());
+ Assertions.assertTrue(Utils.valueEquals(delta.previous(), value1));
+ Assertions.assertTrue(Utils.valueEquals(delta.current(), value2));
+ }
+
@org.junit.jupiter.api.Test
void get() {
throw new UnsupportedOperationException();
diff --git a/src/test/java/it/cavallium/rockserver/core/test/XXHash32Test.java b/src/test/java/it/cavallium/rockserver/core/test/XXHash32Test.java
index 4768464..077c01b 100644
--- a/src/test/java/it/cavallium/rockserver/core/test/XXHash32Test.java
+++ b/src/test/java/it/cavallium/rockserver/core/test/XXHash32Test.java
@@ -26,7 +26,8 @@ public class XXHash32Test {
ThreadLocalRandom.current().nextBytes(bytes);
var hash = safeXxhash32.hash(bytes, 0, bytes.length, Integer.MIN_VALUE);
var a = Arena.global();
- var result = myXxhash32.hash(a, a.allocateArray(OfByte.JAVA_BYTE, bytes), 0, bytes.length, Integer.MIN_VALUE);
+ var result = a.allocate(Integer.BYTES);
+ myXxhash32.hash(a.allocateArray(OfByte.JAVA_BYTE, bytes), 0, bytes.length, Integer.MIN_VALUE, result);
var resultInt = result.get(ColumnInstance.BIG_ENDIAN_INT, 0);
Assertions.assertEquals(hash, resultInt);
}
diff --git a/src/test/java/module-info.java b/src/test/java/module-info.java
index f43f685..1e6f645 100644
--- a/src/test/java/module-info.java
+++ b/src/test/java/module-info.java
@@ -2,6 +2,7 @@ module rockserver.core.test {
requires org.lz4.java;
requires rockserver.core;
requires org.junit.jupiter.api;
+ requires it.unimi.dsi.fastutil.core;
opens it.cavallium.rockserver.core.test;
opens it.cavallium.rockserver.core.impl.test;
}
\ No newline at end of file