package it.cavallium.dbengine.database.collections; import io.netty5.buffer.api.Drop; import io.netty5.buffer.api.Owned; import io.netty5.util.Send; import it.cavallium.dbengine.client.BadBlock; import it.cavallium.dbengine.client.CompositeSnapshot; import it.cavallium.dbengine.database.Delta; import it.cavallium.dbengine.database.LLUtils; import io.netty5.buffer.api.internal.ResourceSupport; import it.cavallium.dbengine.database.UpdateReturnMode; import it.cavallium.dbengine.database.serialization.SerializationFunction; import it.cavallium.dbengine.utils.SimpleResource; import it.unimi.dsi.fastutil.objects.ObjectArraySet; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @SuppressWarnings("unused") public class DatabaseSingleBucket extends SimpleResource implements DatabaseStageEntry { private static final Logger logger = LogManager.getLogger(DatabaseSingleBucket.class); private final K key; private final DatabaseStageEntry>> bucketStage; public DatabaseSingleBucket(DatabaseStageEntry>> bucketStage, K key) { this.key = key; this.bucketStage = bucketStage; } private DatabaseSingleBucket(DatabaseStage>> bucketStage, K key) { this.key = key; this.bucketStage = (DatabaseStageEntry>>) bucketStage; } @Override public Mono get(@Nullable CompositeSnapshot snapshot) { return bucketStage.get(snapshot).flatMap(entries -> extractValueTransformation(entries)); } @Override public Mono getOrDefault(@Nullable CompositeSnapshot snapshot, Mono defaultValue) { return bucketStage.get(snapshot).flatMap(entries -> extractValueTransformation(entries)).switchIfEmpty(defaultValue); } @Override public Mono set(V value) { return this.update(prev -> value, UpdateReturnMode.NOTHING).then(); } @Override public Mono setAndGetPrevious(V value) { return this.update(prev -> value, UpdateReturnMode.GET_OLD_VALUE); } @Override public Mono setAndGetChanged(V value) { return this.updateAndGetDelta(prev -> value).map(delta -> LLUtils.isDeltaChanged(delta)); } @Override public Mono update(SerializationFunction<@Nullable V, @Nullable V> updater, UpdateReturnMode updateReturnMode) { return bucketStage .update(oldBucket -> { V oldValue = extractValue(oldBucket); V newValue = updater.apply(oldValue); if (newValue == null) { return this.removeValueOrDelete(oldBucket); } else { return this.insertValueOrCreate(oldBucket, newValue); } }, updateReturnMode) .flatMap(entries -> extractValueTransformation(entries)); } @Override public Mono> updateAndGetDelta(SerializationFunction<@Nullable V, @Nullable V> updater) { return bucketStage.updateAndGetDelta(oldBucket -> { V oldValue = extractValue(oldBucket); var result = updater.apply(oldValue); if (result == null) { return this.removeValueOrDelete(oldBucket); } else { return this.insertValueOrCreate(oldBucket, result); } }).transform(mono -> LLUtils.mapDelta(mono, entries -> extractValue(entries))); } @Override public Mono clear() { return this.update(prev -> null, UpdateReturnMode.NOTHING).then(); } @Override public Mono clearAndGetPrevious() { return this.update(prev -> null, UpdateReturnMode.GET_OLD_VALUE); } @Override public Mono clearAndGetStatus() { return this.updateAndGetDelta(prev -> null).map(delta -> LLUtils.isDeltaChanged(delta)); } @Override public Mono leavesCount(@Nullable CompositeSnapshot snapshot, boolean fast) { return this.get(snapshot).map(prev -> 1L).defaultIfEmpty(0L); } @Override public Mono isEmpty(@Nullable CompositeSnapshot snapshot) { return this.get(snapshot).map(prev -> true).defaultIfEmpty(true); } @Override public DatabaseStageEntry entry() { return this; } @Override public Flux badBlocks() { return bucketStage.badBlocks(); } private Mono extractValueTransformation(Set> entries) { return Mono.fromCallable(() -> extractValue(entries)); } @Nullable private V extractValue(Set> entries) { if (entries == null) return null; for (Entry entry : entries) { if (Objects.equals(entry.getKey(), key)) { return entry.getValue(); } } return null; } @NotNull private ObjectArraySet> insertValueOrCreate(@Nullable ObjectArraySet> entries, V value) { if (entries != null) { var clonedEntries = entries.clone(); var it = clonedEntries.iterator(); while (it.hasNext()) { var entry = it.next(); if (Objects.equals(entry.getKey(), key)) { it.remove(); break; } } clonedEntries.add(Map.entry(key, value)); return clonedEntries; } else { var oas = new ObjectArraySet>(1); oas.add(Map.entry(key, value)); return oas; } } @Nullable private ObjectArraySet> removeValueOrDelete(@Nullable ObjectArraySet> entries) { if (entries != null) { var clonedEntries = entries.clone(); var it = clonedEntries.iterator(); while (it.hasNext()) { var entry = it.next(); if (Objects.equals(entry.getKey(), key)) { it.remove(); break; } } if (clonedEntries.size() == 0) { return null; } else { return clonedEntries; } } else { return null; } } @Override protected void onClose() { try { if (bucketStage != null) { bucketStage.close(); } } catch (Throwable ex) { logger.error("Failed to close bucketStage", ex); } } }