Sync-base: some microoptimizations

This commit is contained in:
2025-04-28 15:39:47 +02:00
parent 1757034e0b
commit 81703a9406
9 changed files with 53 additions and 50 deletions

View File

@@ -8,11 +8,10 @@ import jakarta.inject.Singleton;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@Singleton @Singleton
public class JDataVersionedWrapperSerializer implements ObjectSerializer<JDataVersionedWrapper> { public class JDataVersionedWrapperSerializer {
@Inject @Inject
ObjectSerializer<JData> dataSerializer; ObjectSerializer<JData> dataSerializer;
@Override
public ByteString serialize(JDataVersionedWrapper obj) { public ByteString serialize(JDataVersionedWrapper obj) {
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
buffer.putLong(obj.version()); buffer.putLong(obj.version());
@@ -20,12 +19,10 @@ public class JDataVersionedWrapperSerializer implements ObjectSerializer<JDataVe
return ByteString.copyFrom(buffer).concat(dataSerializer.serialize(obj.data())); return ByteString.copyFrom(buffer).concat(dataSerializer.serialize(obj.data()));
} }
@Override public JDataVersionedWrapper deserialize(ByteBuffer data) {
public JDataVersionedWrapper deserialize(ByteString data) { var version = data.getLong();
var version = data.substring(0, Long.BYTES).asReadOnlyByteBuffer().getLong(); return new JDataVersionedWrapperLazy(version, data.remaining(),
var rawData = data.substring(Long.BYTES); () -> dataSerializer.deserialize(data)
return new JDataVersionedWrapperLazy(version, rawData.size(),
() -> dataSerializer.deserialize(rawData)
); );
} }
} }

View File

@@ -69,7 +69,7 @@ public final class JObjectKeyImpl implements JObjectKey {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(value); return value.hashCode();
} }
} }

View File

@@ -2,11 +2,13 @@ package com.usatiuk.objects;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import com.google.protobuf.UnsafeByteOperations;
import com.usatiuk.utils.SerializationHelper; import com.usatiuk.utils.SerializationHelper;
import io.quarkus.arc.DefaultBean; import io.quarkus.arc.DefaultBean;
import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.ApplicationScoped;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
@ApplicationScoped @ApplicationScoped
@DefaultBean @DefaultBean
@@ -16,9 +18,8 @@ public class JavaDataSerializer implements ObjectSerializer<JData> {
return SerializationHelper.serialize(obj); return SerializationHelper.serialize(obj);
} }
@Override public JData deserialize(ByteBuffer data) {
public JData deserialize(ByteString data) { try (var is = UnsafeByteOperations.unsafeWrap(data).newInput()) {
try (var is = data.newInput()) {
return SerializationHelper.deserialize(is); return SerializationHelper.deserialize(is);
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);

View File

@@ -2,8 +2,10 @@ package com.usatiuk.objects;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import java.nio.ByteBuffer;
public interface ObjectSerializer<T> { public interface ObjectSerializer<T> {
ByteString serialize(T obj); ByteString serialize(T obj);
T deserialize(ByteString data); T deserialize(ByteBuffer data);
} }

View File

@@ -1,7 +1,5 @@
package com.usatiuk.objects.stores; package com.usatiuk.objects.stores;
import com.google.protobuf.ByteString;
import com.google.protobuf.UnsafeByteOperations;
import com.usatiuk.objects.JObjectKey; import com.usatiuk.objects.JObjectKey;
import com.usatiuk.objects.JObjectKeyMax; import com.usatiuk.objects.JObjectKeyMax;
import com.usatiuk.objects.JObjectKeyMin; import com.usatiuk.objects.JObjectKeyMin;
@@ -104,27 +102,27 @@ public class LmdbObjectPersistentStore implements ObjectPersistentStore {
} }
@Override @Override
public Snapshot<JObjectKey, ByteString> getSnapshot() { public Snapshot<JObjectKey, ByteBuffer> getSnapshot() {
var txn = _env.txnRead(); var txn = _env.txnRead();
try { try {
long commitId = readTxId(txn).orElseThrow(); long commitId = readTxId(txn).orElseThrow();
return new Snapshot<JObjectKey, ByteString>() { return new Snapshot<JObjectKey, ByteBuffer>() {
private final Txn<ByteBuffer> _txn = txn; private final Txn<ByteBuffer> _txn = txn;
private final long _id = commitId; private final long _id = commitId;
private boolean _closed = false; private boolean _closed = false;
@Override @Override
public CloseableKvIterator<JObjectKey, ByteString> getIterator(IteratorStart start, JObjectKey key) { public CloseableKvIterator<JObjectKey, ByteBuffer> getIterator(IteratorStart start, JObjectKey key) {
assert !_closed; assert !_closed;
return new KeyPredicateKvIterator<>(new LmdbKvIterator(_txn, start, key), start, key, (k) -> !k.value().equals(DB_VER_OBJ_NAME_STR)); return new KeyPredicateKvIterator<>(new LmdbKvIterator(_txn, start, key), start, key, (k) -> !k.value().equals(DB_VER_OBJ_NAME_STR));
} }
@Nonnull @Nonnull
@Override @Override
public Optional<ByteString> readObject(JObjectKey name) { public Optional<ByteBuffer> readObject(JObjectKey name) {
assert !_closed; assert !_closed;
var got = _db.get(_txn, name.toByteBuffer()); var got = _db.get(_txn, name.toByteBuffer());
var ret = Optional.ofNullable(got).map(UnsafeByteOperations::unsafeWrap); var ret = Optional.ofNullable(got).map(ByteBuffer::asReadOnlyBuffer);
return ret; return ret;
} }
@@ -197,7 +195,7 @@ public class LmdbObjectPersistentStore implements ObjectPersistentStore {
return _root.toFile().getUsableSpace(); return _root.toFile().getUsableSpace();
} }
private class LmdbKvIterator extends ReversibleKvIterator<JObjectKey, ByteString> { private class LmdbKvIterator extends ReversibleKvIterator<JObjectKey, ByteBuffer> {
private static final Cleaner CLEANER = Cleaner.create(); private static final Cleaner CLEANER = Cleaner.create();
private final Txn<ByteBuffer> _txn; // Managed by the snapshot private final Txn<ByteBuffer> _txn; // Managed by the snapshot
private final Cursor<ByteBuffer> _cursor; private final Cursor<ByteBuffer> _cursor;
@@ -352,14 +350,13 @@ public class LmdbObjectPersistentStore implements ObjectPersistentStore {
} }
@Override @Override
protected Pair<JObjectKey, ByteString> nextImpl() { protected Pair<JObjectKey, ByteBuffer> nextImpl() {
if (!_hasNext) { if (!_hasNext) {
throw new NoSuchElementException("No more elements"); throw new NoSuchElementException("No more elements");
} }
// TODO: Right now with java serialization it doesn't matter, it's all copied to arrays anyway // TODO: Right now with java serialization it doesn't matter, it's all copied to arrays anyway
var val = _cursor.val(); var val = _cursor.val();
var bs = UnsafeByteOperations.unsafeWrap(val); var ret = Pair.of(JObjectKey.fromByteBuffer(_cursor.key()), val.asReadOnlyBuffer());
var ret = Pair.of(JObjectKey.fromByteBuffer(_cursor.key()), bs);
if (_goingForward) if (_goingForward)
_hasNext = _cursor.next(); _hasNext = _cursor.next();
else else

View File

@@ -4,6 +4,7 @@ import com.google.protobuf.ByteString;
import com.usatiuk.objects.JObjectKey; import com.usatiuk.objects.JObjectKey;
import com.usatiuk.objects.iterators.CloseableKvIterator; import com.usatiuk.objects.iterators.CloseableKvIterator;
import com.usatiuk.objects.iterators.IteratorStart; import com.usatiuk.objects.iterators.IteratorStart;
import com.usatiuk.objects.iterators.MappingKvIterator;
import com.usatiuk.objects.iterators.NavigableMapKvIterator; import com.usatiuk.objects.iterators.NavigableMapKvIterator;
import com.usatiuk.objects.snapshot.Snapshot; import com.usatiuk.objects.snapshot.Snapshot;
import io.quarkus.arc.properties.IfBuildProperty; import io.quarkus.arc.properties.IfBuildProperty;
@@ -11,6 +12,7 @@ import jakarta.enterprise.context.ApplicationScoped;
import org.pcollections.TreePMap; import org.pcollections.TreePMap;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -22,21 +24,21 @@ public class MemoryObjectPersistentStore implements ObjectPersistentStore {
private long _lastCommitId = 0; private long _lastCommitId = 0;
@Override @Override
public Snapshot<JObjectKey, ByteString> getSnapshot() { public Snapshot<JObjectKey, ByteBuffer> getSnapshot() {
synchronized (this) { synchronized (this) {
return new Snapshot<JObjectKey, ByteString>() { return new Snapshot<JObjectKey, ByteBuffer>() {
private final TreePMap<JObjectKey, ByteString> _objects = MemoryObjectPersistentStore.this._objects; private final TreePMap<JObjectKey, ByteString> _objects = MemoryObjectPersistentStore.this._objects;
private final long _lastCommitId = MemoryObjectPersistentStore.this._lastCommitId; private final long _lastCommitId = MemoryObjectPersistentStore.this._lastCommitId;
@Override @Override
public CloseableKvIterator<JObjectKey, ByteString> getIterator(IteratorStart start, JObjectKey key) { public CloseableKvIterator<JObjectKey, ByteBuffer> getIterator(IteratorStart start, JObjectKey key) {
return new NavigableMapKvIterator<>(_objects, start, key); return new MappingKvIterator<>(new NavigableMapKvIterator<>(_objects, start, key), ByteString::asReadOnlyByteBuffer);
} }
@Nonnull @Nonnull
@Override @Override
public Optional<ByteString> readObject(JObjectKey name) { public Optional<ByteBuffer> readObject(JObjectKey name) {
return Optional.ofNullable(_objects.get(name)); return Optional.ofNullable(_objects.get(name)).map(ByteString::asReadOnlyByteBuffer);
} }
@Override @Override

View File

@@ -5,12 +5,13 @@ import com.usatiuk.objects.JObjectKey;
import com.usatiuk.objects.snapshot.Snapshot; import com.usatiuk.objects.snapshot.Snapshot;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
import java.util.Optional; import java.util.Optional;
// Persistent storage of objects // Persistent storage of objects
// All changes are written as sequential transactions // All changes are written as sequential transactions
public interface ObjectPersistentStore { public interface ObjectPersistentStore {
Snapshot<JObjectKey, ByteString> getSnapshot(); Snapshot<JObjectKey, ByteBuffer> getSnapshot();
Runnable prepareTx(TxManifestRaw names, long txId); Runnable prepareTx(TxManifestRaw names, long txId);

View File

@@ -2,6 +2,7 @@ package com.usatiuk.objects.stores;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import com.usatiuk.objects.JDataVersionedWrapper; import com.usatiuk.objects.JDataVersionedWrapper;
import com.usatiuk.objects.JDataVersionedWrapperSerializer;
import com.usatiuk.objects.JObjectKey; import com.usatiuk.objects.JObjectKey;
import com.usatiuk.objects.ObjectSerializer; import com.usatiuk.objects.ObjectSerializer;
import com.usatiuk.objects.iterators.CloseableKvIterator; import com.usatiuk.objects.iterators.CloseableKvIterator;
@@ -13,19 +14,20 @@ import jakarta.inject.Inject;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.nio.ByteBuffer;
import java.util.Optional; import java.util.Optional;
@ApplicationScoped @ApplicationScoped
public class SerializingObjectPersistentStore { public class SerializingObjectPersistentStore {
@Inject @Inject
ObjectSerializer<JDataVersionedWrapper> serializer; JDataVersionedWrapperSerializer serializer;
@Inject @Inject
ObjectPersistentStore delegateStore; ObjectPersistentStore delegateStore;
public Snapshot<JObjectKey, JDataVersionedWrapper> getSnapshot() { public Snapshot<JObjectKey, JDataVersionedWrapper> getSnapshot() {
return new Snapshot<JObjectKey, JDataVersionedWrapper>() { return new Snapshot<JObjectKey, JDataVersionedWrapper>() {
private final Snapshot<JObjectKey, ByteString> _backing = delegateStore.getSnapshot(); private final Snapshot<JObjectKey, ByteBuffer> _backing = delegateStore.getSnapshot();
@Override @Override
public CloseableKvIterator<JObjectKey, JDataVersionedWrapper> getIterator(IteratorStart start, JObjectKey key) { public CloseableKvIterator<JObjectKey, JDataVersionedWrapper> getIterator(IteratorStart start, JObjectKey key) {

View File

@@ -13,6 +13,7 @@ import org.apache.commons.lang3.tuple.Pair;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.RepeatedTest;
import java.nio.ByteBuffer;
import java.util.List; import java.util.List;
class Profiles { class Profiles {
@@ -46,49 +47,49 @@ public class LmdbKvIteratorTest {
try (var snapshot = store.getSnapshot()) { try (var snapshot = store.getSnapshot()) {
var iterator = snapshot.getIterator(IteratorStart.GE, JObjectKey.of("")); var iterator = snapshot.getIterator(IteratorStart.GE, JObjectKey.of(""));
Just.checkIterator(iterator, List.of(Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Just.checkIterator(iterator, List.of(Pair.of(JObjectKey.of(Long.toString(1)), ByteBuffer.wrap(new byte[]{2})),
Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(2)), ByteBuffer.wrap(new byte[]{3})),
Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})))); Pair.of(JObjectKey.of(Long.toString(3)), ByteBuffer.wrap(new byte[]{4}))));
Assertions.assertFalse(iterator.hasNext()); Assertions.assertFalse(iterator.hasNext());
iterator.close(); iterator.close();
iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(3))); iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(3)));
Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4}))); Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(3)), ByteBuffer.wrap(new byte[]{4})));
Assertions.assertFalse(iterator.hasNext()); Assertions.assertFalse(iterator.hasNext());
iterator.close(); iterator.close();
iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(2))); iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(2)));
Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4}))); Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(2)), ByteBuffer.wrap(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteBuffer.wrap(new byte[]{4})));
Assertions.assertFalse(iterator.hasNext()); Assertions.assertFalse(iterator.hasNext());
iterator.close(); iterator.close();
iterator = snapshot.getIterator(IteratorStart.GE, JObjectKey.of(Long.toString(2))); iterator = snapshot.getIterator(IteratorStart.GE, JObjectKey.of(Long.toString(2)));
Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4}))); Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(2)), ByteBuffer.wrap(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteBuffer.wrap(new byte[]{4})));
Assertions.assertFalse(iterator.hasNext()); Assertions.assertFalse(iterator.hasNext());
iterator.close(); iterator.close();
iterator = snapshot.getIterator(IteratorStart.GT, JObjectKey.of(Long.toString(2))); iterator = snapshot.getIterator(IteratorStart.GT, JObjectKey.of(Long.toString(2)));
Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4}))); Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(3)), ByteBuffer.wrap(new byte[]{4})));
Assertions.assertFalse(iterator.hasNext()); Assertions.assertFalse(iterator.hasNext());
iterator.close(); iterator.close();
iterator = snapshot.getIterator(IteratorStart.LT, JObjectKey.of(Long.toString(3))); iterator = snapshot.getIterator(IteratorStart.LT, JObjectKey.of(Long.toString(3)));
Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4}))); Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(2)), ByteBuffer.wrap(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteBuffer.wrap(new byte[]{4})));
Assertions.assertFalse(iterator.hasNext()); Assertions.assertFalse(iterator.hasNext());
iterator.close(); iterator.close();
iterator = snapshot.getIterator(IteratorStart.LT, JObjectKey.of(Long.toString(2))); iterator = snapshot.getIterator(IteratorStart.LT, JObjectKey.of(Long.toString(2)));
Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4}))); Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteBuffer.wrap(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteBuffer.wrap(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteBuffer.wrap(new byte[]{4})));
Assertions.assertFalse(iterator.hasNext()); Assertions.assertFalse(iterator.hasNext());
iterator.close(); iterator.close();
iterator = snapshot.getIterator(IteratorStart.LT, JObjectKey.of(Long.toString(1))); iterator = snapshot.getIterator(IteratorStart.LT, JObjectKey.of(Long.toString(1)));
Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4}))); Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteBuffer.wrap(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteBuffer.wrap(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteBuffer.wrap(new byte[]{4})));
Assertions.assertFalse(iterator.hasNext()); Assertions.assertFalse(iterator.hasNext());
iterator.close(); iterator.close();
iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(1))); iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(1)));
Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4}))); Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteBuffer.wrap(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteBuffer.wrap(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteBuffer.wrap(new byte[]{4})));
Assertions.assertFalse(iterator.hasNext()); Assertions.assertFalse(iterator.hasNext());
iterator.close(); iterator.close();
@@ -101,7 +102,7 @@ public class LmdbKvIteratorTest {
iterator.close(); iterator.close();
iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(0))); iterator = snapshot.getIterator(IteratorStart.LE, JObjectKey.of(Long.toString(0)));
Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4}))); Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteBuffer.wrap(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteBuffer.wrap(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteBuffer.wrap(new byte[]{4})));
Assertions.assertFalse(iterator.hasNext()); Assertions.assertFalse(iterator.hasNext());
iterator.close(); iterator.close();
@@ -111,11 +112,11 @@ public class LmdbKvIteratorTest {
Assertions.assertEquals(JObjectKey.of(Long.toString(1)), iterator.peekPrevKey()); Assertions.assertEquals(JObjectKey.of(Long.toString(1)), iterator.peekPrevKey());
Assertions.assertEquals(JObjectKey.of(Long.toString(2)), iterator.peekNextKey()); Assertions.assertEquals(JObjectKey.of(Long.toString(2)), iterator.peekNextKey());
Assertions.assertEquals(JObjectKey.of(Long.toString(1)), iterator.peekPrevKey()); Assertions.assertEquals(JObjectKey.of(Long.toString(1)), iterator.peekPrevKey());
Just.checkIterator(iterator.reversed(), Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2}))); Just.checkIterator(iterator.reversed(), Pair.of(JObjectKey.of(Long.toString(1)), ByteBuffer.wrap(new byte[]{2})));
Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteString.copyFrom(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4}))); Just.checkIterator(iterator, Pair.of(JObjectKey.of(Long.toString(1)), ByteBuffer.wrap(new byte[]{2})), Pair.of(JObjectKey.of(Long.toString(2)), ByteBuffer.wrap(new byte[]{3})), Pair.of(JObjectKey.of(Long.toString(3)), ByteBuffer.wrap(new byte[]{4})));
Assertions.assertEquals(Pair.of(JObjectKey.of(Long.toString(3)), ByteString.copyFrom(new byte[]{4})), iterator.prev()); Assertions.assertEquals(Pair.of(JObjectKey.of(Long.toString(3)), ByteBuffer.wrap(new byte[]{4})), iterator.prev());
Assertions.assertEquals(Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), iterator.prev()); Assertions.assertEquals(Pair.of(JObjectKey.of(Long.toString(2)), ByteBuffer.wrap(new byte[]{3})), iterator.prev());
Assertions.assertEquals(Pair.of(JObjectKey.of(Long.toString(2)), ByteString.copyFrom(new byte[]{3})), iterator.next()); Assertions.assertEquals(Pair.of(JObjectKey.of(Long.toString(2)), ByteBuffer.wrap(new byte[]{3})), iterator.next());
iterator.close(); iterator.close();
} }