2021-01-31 21:23:43 +01:00
|
|
|
package it.cavallium.dbengine.database.collections;
|
|
|
|
|
|
|
|
import it.cavallium.dbengine.client.CompositeSnapshot;
|
|
|
|
import it.cavallium.dbengine.database.LLDictionary;
|
2021-03-14 03:13:19 +01:00
|
|
|
import it.cavallium.dbengine.database.LLDictionaryResultType;
|
2021-01-31 21:23:43 +01:00
|
|
|
import it.cavallium.dbengine.database.LLRange;
|
|
|
|
import it.cavallium.dbengine.database.LLSnapshot;
|
2021-02-02 19:40:37 +01:00
|
|
|
import it.cavallium.dbengine.database.serialization.SerializerFixedBinaryLength;
|
2021-01-31 21:23:43 +01:00
|
|
|
import java.util.Arrays;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Map.Entry;
|
|
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
import reactor.core.publisher.Flux;
|
|
|
|
import reactor.core.publisher.Mono;
|
|
|
|
import reactor.util.function.Tuples;
|
|
|
|
|
|
|
|
// todo: implement optimized methods
|
|
|
|
public class DatabaseMapDictionaryDeep<T, U, US extends DatabaseStage<U>> implements DatabaseStageMap<T, U, US> {
|
|
|
|
|
|
|
|
public static final byte[] EMPTY_BYTES = new byte[0];
|
|
|
|
protected final LLDictionary dictionary;
|
|
|
|
protected final SubStageGetter<U, US> subStageGetter;
|
2021-02-01 12:19:17 +01:00
|
|
|
protected final SerializerFixedBinaryLength<T, byte[]> keySuffixSerializer;
|
2021-01-31 21:23:43 +01:00
|
|
|
protected final byte[] keyPrefix;
|
|
|
|
protected final int keySuffixLength;
|
|
|
|
protected final int keyExtLength;
|
|
|
|
protected final LLRange range;
|
|
|
|
|
2021-03-13 19:01:36 +01:00
|
|
|
private static byte[] incrementPrefix(byte[] key, int prefixLength) {
|
|
|
|
boolean remainder = true;
|
|
|
|
final byte ff = (byte) 0xFF;
|
|
|
|
for (int i = prefixLength - 1; i >= 0; i--) {
|
|
|
|
if (key[i] != ff) {
|
|
|
|
key[i]++;
|
|
|
|
remainder = false;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
key[i] = 0x00;
|
|
|
|
remainder = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (remainder) {
|
|
|
|
Arrays.fill(key, 0, prefixLength, (byte) 0xFF);
|
|
|
|
return Arrays.copyOf(key, key.length + 1);
|
|
|
|
} else {
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static byte[] firstRangeKey(byte[] prefixKey, int prefixLength, int suffixLength, int extLength) {
|
2021-01-31 21:23:43 +01:00
|
|
|
return fillKeySuffixAndExt(prefixKey, prefixLength, suffixLength, extLength, (byte) 0x00);
|
|
|
|
}
|
|
|
|
|
2021-03-13 19:01:36 +01:00
|
|
|
static byte[] nextRangeKey(byte[] prefixKey, int prefixLength, int suffixLength, int extLength) {
|
|
|
|
byte[] nonIncremented = fillKeySuffixAndExt(prefixKey, prefixLength, suffixLength, extLength, (byte) 0x00);
|
|
|
|
return incrementPrefix(nonIncremented, prefixLength);
|
2021-01-31 21:23:43 +01:00
|
|
|
}
|
|
|
|
|
2021-02-01 11:00:27 +01:00
|
|
|
protected static byte[] fillKeySuffixAndExt(byte[] prefixKey,
|
|
|
|
int prefixLength,
|
|
|
|
int suffixLength,
|
|
|
|
int extLength,
|
|
|
|
byte fillValue) {
|
2021-01-31 21:23:43 +01:00
|
|
|
assert prefixKey.length == prefixLength;
|
|
|
|
assert suffixLength > 0;
|
2021-02-02 15:36:11 +01:00
|
|
|
assert extLength >= 0;
|
2021-01-31 21:23:43 +01:00
|
|
|
byte[] result = Arrays.copyOf(prefixKey, prefixLength + suffixLength + extLength);
|
|
|
|
Arrays.fill(result, prefixLength, result.length, fillValue);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-03-13 19:01:36 +01:00
|
|
|
static byte[] firstRangeKey(byte[] prefixKey,
|
2021-02-01 11:00:27 +01:00
|
|
|
byte[] suffixKey,
|
|
|
|
int prefixLength,
|
|
|
|
int suffixLength,
|
|
|
|
int extLength) {
|
2021-01-31 21:23:43 +01:00
|
|
|
return fillKeyExt(prefixKey, suffixKey, prefixLength, suffixLength, extLength, (byte) 0x00);
|
|
|
|
}
|
|
|
|
|
2021-03-13 19:01:36 +01:00
|
|
|
static byte[] nextRangeKey(byte[] prefixKey,
|
2021-02-01 11:00:27 +01:00
|
|
|
byte[] suffixKey,
|
|
|
|
int prefixLength,
|
|
|
|
int suffixLength,
|
|
|
|
int extLength) {
|
2021-03-13 19:01:36 +01:00
|
|
|
byte[] nonIncremented = fillKeyExt(prefixKey, suffixKey, prefixLength, suffixLength, extLength, (byte) 0x00);
|
|
|
|
return incrementPrefix(nonIncremented, prefixLength + suffixLength);
|
2021-01-31 21:23:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected static byte[] fillKeyExt(byte[] prefixKey,
|
|
|
|
byte[] suffixKey,
|
|
|
|
int prefixLength,
|
|
|
|
int suffixLength,
|
|
|
|
int extLength,
|
|
|
|
byte fillValue) {
|
|
|
|
assert prefixKey.length == prefixLength;
|
|
|
|
assert suffixKey.length == suffixLength;
|
|
|
|
assert suffixLength > 0;
|
2021-02-02 15:36:11 +01:00
|
|
|
assert extLength >= 0;
|
2021-01-31 21:23:43 +01:00
|
|
|
byte[] result = Arrays.copyOf(prefixKey, prefixLength + suffixLength + extLength);
|
|
|
|
System.arraycopy(suffixKey, 0, result, prefixLength, suffixLength);
|
|
|
|
Arrays.fill(result, prefixLength + suffixLength, result.length, fillValue);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Use DatabaseMapDictionaryRange.simple instead
|
|
|
|
*/
|
|
|
|
@Deprecated
|
2021-02-02 19:40:37 +01:00
|
|
|
public static <T, U> DatabaseMapDictionaryDeep<T, U, DatabaseStageEntry<U>> simple(LLDictionary dictionary,
|
|
|
|
SerializerFixedBinaryLength<T, byte[]> keySerializer,
|
|
|
|
SubStageGetterSingle<U> subStageGetter) {
|
|
|
|
return new DatabaseMapDictionaryDeep<>(dictionary, EMPTY_BYTES, keySerializer, subStageGetter, 0);
|
2021-01-31 21:23:43 +01:00
|
|
|
}
|
|
|
|
|
2021-02-02 19:40:37 +01:00
|
|
|
public static <T, U, US extends DatabaseStage<U>> DatabaseMapDictionaryDeep<T, U, US> deepTail(LLDictionary dictionary,
|
2021-02-01 12:19:17 +01:00
|
|
|
SerializerFixedBinaryLength<T, byte[]> keySerializer,
|
2021-02-02 19:40:37 +01:00
|
|
|
int keyExtLength,
|
|
|
|
SubStageGetter<U, US> subStageGetter) {
|
|
|
|
return new DatabaseMapDictionaryDeep<>(dictionary, EMPTY_BYTES, keySerializer, subStageGetter, keyExtLength);
|
2021-01-31 21:23:43 +01:00
|
|
|
}
|
|
|
|
|
2021-02-02 19:40:37 +01:00
|
|
|
public static <T, U, US extends DatabaseStage<U>> DatabaseMapDictionaryDeep<T, U, US> deepIntermediate(LLDictionary dictionary,
|
2021-01-31 21:23:43 +01:00
|
|
|
byte[] prefixKey,
|
2021-02-02 19:40:37 +01:00
|
|
|
SerializerFixedBinaryLength<T, byte[]> keySuffixSerializer,
|
|
|
|
SubStageGetter<U, US> subStageGetter,
|
2021-01-31 21:23:43 +01:00
|
|
|
int keyExtLength) {
|
2021-02-02 19:40:37 +01:00
|
|
|
return new DatabaseMapDictionaryDeep<>(dictionary, prefixKey, keySuffixSerializer, subStageGetter, keyExtLength);
|
2021-01-31 21:23:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected DatabaseMapDictionaryDeep(LLDictionary dictionary,
|
|
|
|
byte[] prefixKey,
|
2021-02-02 19:40:37 +01:00
|
|
|
SerializerFixedBinaryLength<T, byte[]> keySuffixSerializer,
|
|
|
|
SubStageGetter<U, US> subStageGetter,
|
2021-01-31 21:23:43 +01:00
|
|
|
int keyExtLength) {
|
|
|
|
this.dictionary = dictionary;
|
|
|
|
this.subStageGetter = subStageGetter;
|
|
|
|
this.keySuffixSerializer = keySuffixSerializer;
|
|
|
|
this.keyPrefix = prefixKey;
|
2021-02-01 10:52:33 +01:00
|
|
|
this.keySuffixLength = keySuffixSerializer.getSerializedBinaryLength();
|
2021-01-31 21:23:43 +01:00
|
|
|
this.keyExtLength = keyExtLength;
|
2021-03-13 19:01:36 +01:00
|
|
|
byte[] firstKey = firstRangeKey(keyPrefix, keyPrefix.length, keySuffixLength, keyExtLength);
|
|
|
|
byte[] nextRangeKey = nextRangeKey(keyPrefix, keyPrefix.length, keySuffixLength, keyExtLength);
|
|
|
|
this.range = keyPrefix.length == 0 ? LLRange.all() : LLRange.of(firstKey, nextRangeKey);
|
2021-02-02 15:36:11 +01:00
|
|
|
assert subStageKeysConsistency(keyPrefix.length + keySuffixLength + keyExtLength);
|
2021-01-31 21:23:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressWarnings("unused")
|
|
|
|
protected boolean suffixKeyConsistency(int keySuffixLength) {
|
|
|
|
return this.keySuffixLength == keySuffixLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressWarnings("unused")
|
|
|
|
protected boolean extKeyConsistency(int keyExtLength) {
|
|
|
|
return this.keyExtLength == keyExtLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
@SuppressWarnings("unused")
|
|
|
|
protected boolean suffixAndExtKeyConsistency(int keySuffixAndExtLength) {
|
|
|
|
return this.keySuffixLength + this.keyExtLength == keySuffixAndExtLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Keep only suffix and ext
|
|
|
|
*/
|
|
|
|
protected byte[] stripPrefix(byte[] key) {
|
|
|
|
return Arrays.copyOfRange(key, this.keyPrefix.length, key.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove ext from full key
|
|
|
|
*/
|
|
|
|
protected byte[] removeExtFromFullKey(byte[] key) {
|
|
|
|
return Arrays.copyOf(key, keyPrefix.length + keySuffixLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add prefix to suffix
|
|
|
|
*/
|
|
|
|
protected byte[] toKeyWithoutExt(byte[] suffixKey) {
|
|
|
|
assert suffixKey.length == keySuffixLength;
|
|
|
|
byte[] result = Arrays.copyOf(keyPrefix, keyPrefix.length + keySuffixLength);
|
|
|
|
System.arraycopy(suffixKey, 0, result, keyPrefix.length, keySuffixLength);
|
2021-02-02 15:36:11 +01:00
|
|
|
assert result.length == keyPrefix.length + keySuffixLength;
|
2021-01-31 21:23:43 +01:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected LLSnapshot resolveSnapshot(@Nullable CompositeSnapshot snapshot) {
|
|
|
|
if (snapshot == null) {
|
|
|
|
return null;
|
|
|
|
} else {
|
|
|
|
return snapshot.getSnapshot(dictionary);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected LLRange toExtRange(byte[] keySuffix) {
|
2021-03-13 19:01:36 +01:00
|
|
|
byte[] first = firstRangeKey(keyPrefix, keySuffix, keyPrefix.length, keySuffixLength, keyExtLength);
|
|
|
|
byte[] end = nextRangeKey(keyPrefix, keySuffix, keyPrefix.length, keySuffixLength, keyExtLength);
|
2021-01-31 21:23:43 +01:00
|
|
|
return LLRange.of(first, end);
|
|
|
|
}
|
|
|
|
|
2021-02-24 16:43:07 +01:00
|
|
|
@Override
|
|
|
|
public Mono<Long> leavesCount(@Nullable CompositeSnapshot snapshot, boolean fast) {
|
|
|
|
return dictionary.sizeRange(resolveSnapshot(snapshot), range, fast);
|
|
|
|
}
|
|
|
|
|
2021-03-14 03:13:19 +01:00
|
|
|
@Override
|
|
|
|
public Mono<Boolean> isEmpty(@Nullable CompositeSnapshot snapshot) {
|
|
|
|
return dictionary.isRangeEmpty(resolveSnapshot(snapshot), range);
|
|
|
|
}
|
|
|
|
|
2021-02-02 15:36:11 +01:00
|
|
|
@SuppressWarnings("ReactiveStreamsUnusedPublisher")
|
2021-01-31 21:23:43 +01:00
|
|
|
@Override
|
|
|
|
public Mono<US> at(@Nullable CompositeSnapshot snapshot, T keySuffix) {
|
|
|
|
byte[] keySuffixData = serializeSuffix(keySuffix);
|
2021-03-14 13:08:03 +01:00
|
|
|
Flux<byte[]> keyFlux;
|
2021-03-14 13:24:46 +01:00
|
|
|
if (this.subStageGetter.needsDebuggingKeyFlux()) {
|
2021-03-14 13:08:03 +01:00
|
|
|
keyFlux = this.dictionary.getRangeKeys(resolveSnapshot(snapshot), toExtRange(keySuffixData));
|
|
|
|
} else {
|
|
|
|
keyFlux = Flux.empty();
|
|
|
|
}
|
2021-01-31 21:23:43 +01:00
|
|
|
return this.subStageGetter
|
2021-02-02 00:09:46 +01:00
|
|
|
.subStage(dictionary,
|
|
|
|
snapshot,
|
|
|
|
toKeyWithoutExt(keySuffixData),
|
2021-03-14 13:08:03 +01:00
|
|
|
keyFlux
|
2021-02-02 00:09:46 +01:00
|
|
|
);
|
2021-01-31 21:23:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Flux<Entry<T, US>> getAllStages(@Nullable CompositeSnapshot snapshot) {
|
2021-03-14 13:24:46 +01:00
|
|
|
if (this.subStageGetter.needsDebuggingKeyFlux()) {
|
2021-03-14 03:13:19 +01:00
|
|
|
return dictionary
|
|
|
|
.getRangeKeysGrouped(resolveSnapshot(snapshot), range, keyPrefix.length + keySuffixLength)
|
|
|
|
.flatMapSequential(rangeKeys -> {
|
2021-03-14 13:24:46 +01:00
|
|
|
assert this.subStageGetter.isMultiKey() || rangeKeys.size() == 1;
|
2021-03-14 03:13:19 +01:00
|
|
|
byte[] groupKeyWithExt = rangeKeys.get(0);
|
|
|
|
byte[] groupKeyWithoutExt = removeExtFromFullKey(groupKeyWithExt);
|
|
|
|
byte[] groupSuffix = this.stripPrefix(groupKeyWithoutExt);
|
|
|
|
assert subStageKeysConsistency(groupKeyWithExt.length);
|
|
|
|
return this.subStageGetter
|
|
|
|
.subStage(dictionary,
|
|
|
|
snapshot,
|
|
|
|
groupKeyWithoutExt,
|
2021-03-14 13:24:46 +01:00
|
|
|
Flux.fromIterable(rangeKeys)
|
2021-03-14 03:13:19 +01:00
|
|
|
)
|
|
|
|
.map(us -> Map.entry(this.deserializeSuffix(groupSuffix), us));
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
return dictionary
|
2021-03-14 13:24:46 +01:00
|
|
|
.getRangeKeyPrefixes(resolveSnapshot(snapshot), range, keyPrefix.length + keySuffixLength)
|
2021-03-14 18:51:42 +01:00
|
|
|
.flatMapSequential(groupKeyWithoutExt -> {
|
2021-03-14 13:24:46 +01:00
|
|
|
byte[] groupSuffix = this.stripPrefix(groupKeyWithoutExt);
|
2021-03-14 18:51:42 +01:00
|
|
|
assert subStageKeysConsistency(groupKeyWithoutExt.length + keyExtLength);
|
2021-03-14 03:13:19 +01:00
|
|
|
return this.subStageGetter
|
2021-03-14 13:24:46 +01:00
|
|
|
.subStage(dictionary,
|
|
|
|
snapshot,
|
|
|
|
groupKeyWithoutExt,
|
|
|
|
Flux.empty()
|
|
|
|
)
|
|
|
|
.map(us -> Map.entry(this.deserializeSuffix(groupSuffix), us));
|
|
|
|
});
|
2021-03-14 03:13:19 +01:00
|
|
|
}
|
2021-02-02 15:36:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private boolean subStageKeysConsistency(int totalKeyLength) {
|
|
|
|
if (subStageGetter instanceof SubStageGetterMapDeep) {
|
|
|
|
return totalKeyLength
|
|
|
|
== keyPrefix.length + keySuffixLength + ((SubStageGetterMapDeep<?, ?, ?>) subStageGetter).getKeyBinaryLength();
|
|
|
|
} else if (subStageGetter instanceof SubStageGetterMap) {
|
|
|
|
return totalKeyLength
|
|
|
|
== keyPrefix.length + keySuffixLength + ((SubStageGetterMap<?, ?>) subStageGetter).getKeyBinaryLength();
|
|
|
|
} else {
|
|
|
|
return true;
|
|
|
|
}
|
2021-01-31 21:23:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Flux<Entry<T, U>> setAllValuesAndGetPrevious(Flux<Entry<T, U>> entries) {
|
|
|
|
return getAllStages(null)
|
2021-03-14 03:13:19 +01:00
|
|
|
.flatMapSequential(stage -> stage.getValue().get(null).map(val -> Map.entry(stage.getKey(), val)))
|
2021-03-11 02:22:59 +01:00
|
|
|
.concatWith(clear().then(entries
|
2021-02-02 00:09:46 +01:00
|
|
|
.flatMap(entry -> at(null, entry.getKey()).map(us -> Tuples.of(us, entry.getValue())))
|
|
|
|
.flatMap(tuple -> tuple.getT1().set(tuple.getT2()))
|
2021-03-11 02:22:59 +01:00
|
|
|
.then(Mono.empty())));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Mono<Void> clear() {
|
2021-03-14 03:13:19 +01:00
|
|
|
if (range.isAll()) {
|
|
|
|
return dictionary
|
|
|
|
.clear();
|
|
|
|
} else if (range.isSingle()) {
|
|
|
|
return dictionary
|
|
|
|
.remove(range.getSingle(), LLDictionaryResultType.VOID)
|
|
|
|
.then();
|
|
|
|
} else {
|
|
|
|
return dictionary
|
|
|
|
.setRange(range, Flux.empty(), false)
|
|
|
|
.then();
|
|
|
|
}
|
2021-01-31 21:23:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//todo: temporary wrapper. convert the whole class to buffers
|
|
|
|
protected T deserializeSuffix(byte[] keySuffix) {
|
2021-02-02 15:36:11 +01:00
|
|
|
assert suffixKeyConsistency(keySuffix.length);
|
2021-02-01 12:19:17 +01:00
|
|
|
return keySuffixSerializer.deserialize(keySuffix);
|
2021-01-31 21:23:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//todo: temporary wrapper. convert the whole class to buffers
|
|
|
|
protected byte[] serializeSuffix(T keySuffix) {
|
2021-02-02 15:36:11 +01:00
|
|
|
byte[] suffixData = keySuffixSerializer.serialize(keySuffix);
|
|
|
|
assert suffixKeyConsistency(suffixData.length);
|
|
|
|
return suffixData;
|
2021-01-31 21:23:43 +01:00
|
|
|
}
|
|
|
|
}
|