CavalliumDBEngine/src/main/java/it/cavallium/dbengine/database/structures/LLFixedDeepSet.java
2020-12-07 22:15:18 +01:00

210 lines
6.9 KiB
Java

package it.cavallium.dbengine.database.structures;
import it.cavallium.dbengine.database.LLDeepDictionary;
import it.cavallium.dbengine.database.LLDictionaryResultType;
import it.cavallium.dbengine.database.LLKeyValueDatabaseStructure;
import it.cavallium.dbengine.database.LLSnapshot;
import it.cavallium.dbengine.database.LLUtils;
import it.unimi.dsi.fastutil.objects.ObjectSets.UnmodifiableSet;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import org.jetbrains.annotations.Nullable;
import org.warp.commonutils.type.Bytes;
import org.warp.commonutils.type.UnmodifiableIterableMap;
import org.warp.commonutils.type.UnmodifiableIterableSet;
import org.warp.commonutils.type.UnmodifiableMap;
/**
* A set in which keys and values must have a fixed size
*/
public class LLFixedDeepSet implements LLKeyValueDatabaseStructure {
private static final byte[] EMPTY_VALUE = new byte[0];
private static final Bytes EMPTY_VALUE_BYTES = new Bytes(EMPTY_VALUE);
private final LLDeepDictionary dictionary;
public LLFixedDeepSet(LLDeepDictionary dictionary) {
this.dictionary = dictionary;
}
private byte[][] generateEmptyArray(int length) {
byte[][] data = new byte[length][];
for (int i = 0; i < length; i++) {
data[i] = EMPTY_VALUE;
}
return data;
}
private Bytes[] generateEmptyBytesArray(int length) {
Bytes[] data = new Bytes[length];
for (int i = 0; i < length; i++) {
data[i] = EMPTY_VALUE_BYTES;
}
return data;
}
public UnmodifiableIterableSet<byte[]> get(@Nullable LLSnapshot snapshot, byte[] key1) throws IOException {
return dictionary.get(snapshot, key1).toUnmodifiableIterableKeysSet(byte[][]::new);
}
public boolean contains(@Nullable LLSnapshot snapshot, byte[] key1, byte[] value) throws IOException {
return dictionary.contains(snapshot, key1, value);
}
public boolean isEmpty(@Nullable LLSnapshot snapshot, byte[] key1) {
return dictionary.isEmpty(snapshot, key1);
}
public boolean add(byte[] key1, byte[] value, LLDeepSetItemResultType resultType) throws IOException {
Optional<byte[]> response = dictionary.put(key1, value, EMPTY_VALUE, resultType.getDictionaryResultType());
if (resultType == LLDeepSetItemResultType.VALUE_CHANGED) {
return LLUtils.responseToBoolean(response.orElseThrow());
}
return false;
}
public void addMulti(byte[] key1, byte[][] values) throws IOException {
dictionary.putMulti(key1, values, generateEmptyArray(values.length), LLDictionaryResultType.VOID, (x) -> {});
}
/**
* Note: this will remove previous elements because it replaces the entire set
*/
public void put(byte[] key1, UnmodifiableIterableSet<byte[]> values) throws IOException {
dictionary.put(key1, values.toUnmodifiableIterableMapSetValues(generateEmptyArray(values.size())));
}
public void putMulti(byte[][] keys1, UnmodifiableIterableSet<byte[]>[] values) throws IOException {
var fixedValues = new UnmodifiableIterableMap[values.length];
for (int i = 0; i < values.length; i++) {
fixedValues[i] = values[i].toUnmodifiableIterableMapSetValues(generateEmptyArray(values[i].size()));
}
//noinspection unchecked
dictionary.putMulti(keys1, fixedValues);
}
public void clear() throws IOException {
dictionary.clear();
}
public Optional<UnmodifiableIterableSet<byte[]>> clear(byte[] key1, LLDeepSetResultType resultType) throws IOException {
Optional<UnmodifiableIterableMap<byte[], byte[]>> response = dictionary.clear(key1, resultType.getDictionaryResultType());
if (response.isEmpty()) {
return Optional.empty();
} else {
return Optional.of(response.get().toUnmodifiableIterableKeysSet(byte[][]::new));
}
}
public boolean remove(byte[] key1, byte[] value, LLDeepSetItemResultType resultType) throws IOException {
Optional<byte[]> response = dictionary.remove(key1, value, resultType.getDictionaryResultType());
if (resultType == LLDeepSetItemResultType.VALUE_CHANGED) {
return LLUtils.responseToBoolean(response.orElseThrow());
}
return false;
}
public void forEach(@Nullable LLSnapshot snapshot, int parallelism, BiConsumer<byte[], UnmodifiableIterableSet<byte[]>> consumer) {
dictionary.forEach(snapshot, parallelism, (key1, entries) -> consumer.accept(key1, entries.toUnmodifiableIterableKeysSet(byte[][]::new)));
}
public void forEach(@Nullable LLSnapshot snapshot, int parallelism, byte[] key1, Consumer<byte[]> consumer) {
dictionary.forEach(snapshot, parallelism, key1, (value, empty) -> consumer.accept(value));
}
public void replaceAll(int parallelism, BiFunction<byte[], UnmodifiableIterableSet<byte[]>, Entry<byte[], UnmodifiableSet<Bytes>>> consumer) throws IOException {
dictionary.replaceAll(parallelism, true, (key1, entries) -> {
var result = consumer.apply(key1, entries.toUnmodifiableIterableKeysSet(byte[][]::new));
var resultItems = result.getValue().toArray(Bytes[]::new);
return Map.entry(result.getKey(), UnmodifiableMap.of(resultItems, generateEmptyArray(resultItems.length)));
});
}
public void replaceAll(int parallelism, byte[] key1, Function<byte[], byte[]> consumer) throws IOException {
dictionary.replaceAll(parallelism, true, key1, (value, empty) -> {
var changedValue = consumer.apply(value);
return Map.entry(changedValue, EMPTY_VALUE);
});
}
public long size(@Nullable LLSnapshot snapshot, boolean fast) throws IOException {
return dictionary.size(snapshot, fast);
}
public long exactSize(@Nullable LLSnapshot snapshot, byte[] key1) {
return dictionary.exactSize(snapshot, key1);
}
@Override
public String getDatabaseName() {
return dictionary.getDatabaseName();
}
public enum LLDeepSetResultType {
VOID,
VALUE_CHANGED,
PREVIOUS_VALUE;
public LLDictionaryResultType getDictionaryResultType() {
switch (this) {
case VOID:
return LLDictionaryResultType.VOID;
case VALUE_CHANGED:
return LLDictionaryResultType.VALUE_CHANGED;
case PREVIOUS_VALUE:
return LLDictionaryResultType.PREVIOUS_VALUE;
}
return LLDictionaryResultType.VOID;
}
}
public enum LLDeepSetItemResultType {
VOID,
VALUE_CHANGED;
public LLDictionaryResultType getDictionaryResultType() {
switch (this) {
case VOID:
return LLDictionaryResultType.VOID;
case VALUE_CHANGED:
return LLDictionaryResultType.VALUE_CHANGED;
}
return LLDictionaryResultType.VOID;
}
}
@Override
public String toString() {
return new StringJoiner(", ", LLFixedDeepSet.class.getSimpleName() + "[", "]")
.add("dictionary=" + dictionary)
.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LLFixedDeepSet llMap = (LLFixedDeepSet) o;
return Objects.equals(dictionary, llMap.dictionary);
}
@Override
public int hashCode() {
return Objects.hash(dictionary);
}
}