JPS mappings for incremental compilation refactoring: extract data caching functionality into a reusable wrapper; for MultiMaplet collections support configurable internal collection type

GitOrigin-RevId: a9fdaed773b1f58ebfb8cb984aefa006d12300c0
This commit is contained in:
Eugene Zhuravlev
2023-11-10 21:03:49 +01:00
committed by intellij-monorepo-bot
parent f2b323600b
commit 2b9519e024
15 changed files with 526 additions and 169 deletions

View File

@@ -0,0 +1,23 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.dependency;
import org.jetbrains.annotations.Nullable;
import java.io.Closeable;
import java.io.IOException;
public interface Maplet<K, V> extends Closeable {
boolean containsKey(final K key);
@Nullable
V get(final K key);
void put(K key, V value);
void remove(K key);
Iterable<K> getKeys();
@Override
void close() throws IOException;
}

View File

@@ -2,7 +2,11 @@
package org.jetbrains.jps.dependency;
public interface MapletFactory {
<K extends ExternalizableGraphElement, V extends ExternalizableGraphElement> MultiMaplet<K, V> createSetMultiMaplet(
<K, V> MultiMaplet<K, V> createSetMultiMaplet(
String storageName, Externalizer<K> keyExternalizer, Externalizer<V> valueExternalizer
);
<K, V> Maplet<K, V> createMaplet(
String storageName, Externalizer<K> keyExternalizer, Externalizer<V> valueExternalizer
);
}

View File

@@ -1,33 +1,41 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.dependency;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
public interface MultiMaplet<K, V> {
boolean containsKey(final K key);
import java.io.Closeable;
import java.io.IOException;
@Nullable
Iterable<V> get(final K key);
public interface MultiMaplet<K, V> extends Closeable {
boolean containsKey(K key);
void put(final K key, final Iterable<? extends V> values);
@NotNull
Iterable<V> get(K key);
void remove(final K key);
void put(K key, @NotNull Iterable<? extends V> values);
void appendValue(final K key, final V value);
void remove(K key);
default void appendValues(final K key, final Iterable<? extends V> values) {
void appendValue(K key, final V value);
default void appendValues(K key, @NotNull Iterable<? extends V> values) {
for (V value : values) {
appendValue(key, value);
}
}
void removeValue(final K key, final V value);
void removeValue(K key, V value);
default void removeValues(final K key, final Iterable<? extends V> values) {
default void removeValues(K key, @NotNull Iterable<? extends V> values) {
for (V value : values) {
removeValue(key, value);
}
}
@NotNull
Iterable<K> getKeys();
@Override
void close() throws IOException;
}

View File

@@ -7,7 +7,10 @@ import org.jetbrains.jps.dependency.java.JvmNodeElementExternalizer;
import org.jetbrains.jps.dependency.java.JvmNodeReferenceID;
import org.jetbrains.jps.javac.Iterators;
import java.util.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public abstract class BackDependencyIndexImpl implements BackDependencyIndex {
private final String myName;
@@ -38,8 +41,7 @@ public abstract class BackDependencyIndexImpl implements BackDependencyIndex {
@Override
public @NotNull Iterable<ReferenceID> getDependencies(@NotNull ReferenceID id) {
Iterable<ReferenceID> nodes = myMap.get(id);
return nodes != null? nodes : Collections.emptyList();
return myMap.get(id);
}
@Override

View File

@@ -0,0 +1,59 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.dependency.impl;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.dependency.Maplet;
import java.io.IOException;
public class CachingMaplet<K, V> implements Maplet<K, V> {
private static final Object NULL_OBJECT = new Object();
private final LoadingCache<K, V> myCache;
private final Maplet<K, V> myDelegate;
public CachingMaplet(Maplet<K, V> delegate, int maxCacheSize) {
myDelegate = delegate;
myCache = Caffeine.newBuilder().maximumSize(maxCacheSize).build(key -> {
V val = myDelegate.get(key);
//noinspection unchecked
return val != null? val : (V)NULL_OBJECT;
});
}
@Override
public boolean containsKey(K key) {
return myDelegate.containsKey(key);
}
@Override
public @Nullable V get(K key) {
V val = myCache.get(key);
return val == NULL_OBJECT? null : val;
}
@Override
public void put(K key, @NotNull V value) {
myCache.invalidate(key);
myDelegate.put(key, value);
}
@Override
public void remove(K key) {
myCache.invalidate(key);
myDelegate.remove(key);
}
@Override
public @NotNull Iterable<K> getKeys() {
return myDelegate.getKeys();
}
@Override
public void close() throws IOException {
myCache.invalidateAll();
myDelegate.close();
}
}

View File

@@ -0,0 +1,101 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.dependency.impl;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.dependency.MultiMaplet;
import org.jetbrains.jps.javac.Iterators;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
public class CachingMultiMaplet<K, V> implements MultiMaplet<K, V> {
private final LoadingCache<K, Iterable<V>> myCache;
private final MultiMaplet<K, V> myDelegate;
public CachingMultiMaplet(MultiMaplet<K, V> delegate, int maxCacheSize) {
myDelegate = delegate;
myCache = Caffeine.newBuilder().maximumSize(maxCacheSize).build(key -> {
return myDelegate.get(key);
});
}
@Override
public boolean containsKey(K key) {
return myDelegate.containsKey(key);
}
@Override
public @NotNull Iterable<V> get(K key) {
return myCache.get(key);
}
@Override
public void put(K key, @NotNull Iterable<? extends V> values) {
myCache.invalidate(key);
myDelegate.put(key, values);
}
@Override
public void remove(K key) {
myCache.invalidate(key);
myDelegate.remove(key);
}
@Override
public void appendValue(K key, V value) {
appendValues(key, Collections.singleton(value));
}
@Override
public void appendValues(K key, @NotNull Iterable<? extends V> values) {
if (!Iterators.isEmpty(values)) {
myCache.invalidate(key);
myDelegate.appendValues(key, values);
}
}
@Override
public void removeValue(K key, V value) {
removeValues(key, Collections.singleton(value));
}
@Override
public void removeValues(K key, @NotNull Iterable<? extends V> values) {
if (Iterators.isEmpty(values)) {
return;
}
Iterable<V> currentData = myCache.get(key);
Collection<V> collection = currentData instanceof Collection? (Collection<V>)currentData : Iterators.collect(currentData, new SmartList<>());
if (!collection.isEmpty()) {
boolean changes = false;
for (V value : values) {
changes |= collection.remove(value);
}
if (changes) {
myCache.invalidate(key);
if (collection.isEmpty()) {
myDelegate.remove(key);
}
else {
myDelegate.put(key, collection);
}
}
}
}
@Override
public @NotNull Iterable<K> getKeys() {
return myDelegate.getKeys();
}
@Override
public void close() throws IOException {
myCache.invalidateAll();
myDelegate.close();
}
}

View File

@@ -10,10 +10,11 @@ import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.builders.storage.BuildDataCorruptedException;
import org.jetbrains.jps.dependency.ExternalizableGraphElement;
import org.jetbrains.jps.dependency.Externalizer;
import org.jetbrains.jps.dependency.Maplet;
import org.jetbrains.jps.dependency.MapletFactory;
import org.jetbrains.jps.dependency.MultiMaplet;
import org.jetbrains.jps.javac.Iterators;
import java.io.*;
import java.nio.file.Path;
@@ -29,8 +30,13 @@ public final class Containers {
public static final MapletFactory MEMORY_CONTAINER_FACTORY = new MapletFactory() {
@Override
public <K extends ExternalizableGraphElement, V extends ExternalizableGraphElement> MultiMaplet<K, V> createSetMultiMaplet(String storageName, Externalizer<K> keyExternalizer, Externalizer<V> valueExternalizer) {
return new MemorySetMultiMaplet<>();
public <K, V> MultiMaplet<K, V> createSetMultiMaplet(String storageName, Externalizer<K> keyExternalizer, Externalizer<V> valueExternalizer) {
return new MemoryMultiMaplet<>(() -> (Set<V>)new HashSet<V>());
}
@Override
public <K, V> Maplet<K, V> createMaplet(String storageName, Externalizer<K> keyExternalizer, Externalizer<V> valueExternalizer) {
return new MemoryMaplet<>();
}
};
@@ -61,27 +67,37 @@ public final class Containers {
}
private static class PersistentMapletFactory implements MapletFactory, Closeable {
private static final int MAX_CACHE_SIZE = 1024; // todo: make configurable?
private final String myRootDirPath;
private final List<PersistentSetMultiMaplet<?, ?>> myContainers = new ArrayList<>();
private final List<MultiMaplet<?, ?>> myMultiMaplets = new ArrayList<>();
private final List<Maplet<?, ?>> myMaplets = new ArrayList<>();
PersistentMapletFactory(String rootDirPath) {
myRootDirPath = rootDirPath;
}
@Override
public <K extends ExternalizableGraphElement, V extends ExternalizableGraphElement> MultiMaplet<K, V> createSetMultiMaplet(String storageName, Externalizer<K> keyExternalizer, Externalizer<V> valueExternalizer) {
PersistentSetMultiMaplet<K, V> container = new PersistentSetMultiMaplet<>(
getMapFile(storageName), new ElementKeyDescriptor<>(keyExternalizer), new ElementDataExternalizer<>(valueExternalizer)
public <K, V> MultiMaplet<K, V> createSetMultiMaplet(String storageName, Externalizer<K> keyExternalizer, Externalizer<V> valueExternalizer) {
MultiMaplet<K, V> container = new CachingMultiMaplet<>(
new PersistentMultiMaplet<>(getMapFile(storageName), new ElementKeyDescriptor<>(keyExternalizer), new ElementDataExternalizer<>(valueExternalizer), () -> (Set<V>)new HashSet<V>()), MAX_CACHE_SIZE
);
myContainers.add(container);
myMultiMaplets.add(container);
return container;
}
@Override
public <K, V> Maplet<K, V> createMaplet(String storageName, Externalizer<K> keyExternalizer, Externalizer<V> valueExternalizer) {
Maplet<K, V> container = new CachingMaplet<>(
new PersistentMaplet<>(getMapFile(storageName), new ElementKeyDescriptor<>(keyExternalizer), new ElementDataExternalizer<>(valueExternalizer)), MAX_CACHE_SIZE
);
myMaplets.add(container);
return container;
}
@Override
public void close() {
Throwable ex = null;
for (PersistentSetMultiMaplet<?, ?> container : myContainers) {
for (Closeable container : Iterators.flat(myMultiMaplets, myMaplets)) {
try {
container.close();
}
@@ -91,7 +107,8 @@ public final class Containers {
}
}
}
myContainers.clear();
myMultiMaplets.clear();
myMaplets.clear();
if (ex instanceof IOException) {
throw new BuildDataCorruptedException((IOException)ex);
}
@@ -107,7 +124,7 @@ public final class Containers {
}
}
private static class ElementDataExternalizer<T extends ExternalizableGraphElement> implements DataExternalizer<T> {
private static class ElementDataExternalizer<T> implements DataExternalizer<T> {
private final Externalizer<T> myExternalizer;
ElementDataExternalizer(Externalizer<T> externalizer) {
@@ -116,16 +133,16 @@ public final class Containers {
@Override
public void save(@NotNull DataOutput out, T value) throws IOException {
myExternalizer.save(out, value);
myExternalizer.save(GraphDataOutput.wrap(out), value);
}
@Override
public T read(@NotNull DataInput in) throws IOException {
return myExternalizer.load(in);
return myExternalizer.load(GraphDataInput.wrap(in));
}
}
private static class ElementKeyDescriptor<T extends ExternalizableGraphElement> extends ElementDataExternalizer<T> implements KeyDescriptor<T> {
private static class ElementKeyDescriptor<T> extends ElementDataExternalizer<T> implements KeyDescriptor<T> {
ElementKeyDescriptor(Externalizer<T> externalizer) {
super(externalizer);
@@ -142,18 +159,4 @@ public final class Containers {
}
}
//private static <T> DataExternalizer<Collection<T>> asCollectionExternalizer(DataExternalizer<T> ext) {
// return new DataExternalizer<>() {
// @Override
// public void save(@NotNull DataOutput out, Collection<T> value) throws IOException {
// RW.writeCollection(out, value, v -> ext.save(out, v));
// }
//
// @Override
// public Collection<T> read(@NotNull DataInput in) throws IOException {
// return RW.readCollection(in, () -> ext.read(in), new HashSet<>());
// }
// };
//}
}

View File

@@ -95,4 +95,17 @@ public class GraphDataInput implements DataInput {
public static DataInput wrap(DataInput in) {
return new GraphDataInput(in);
}
public interface StringEnumerator {
String toString(int num) throws IOException;
}
public static DataInput wrap(DataInput in, StringEnumerator enumerator) {
return new GraphDataInput(in) {
@Override
public @NotNull String readUTF() throws IOException {
return enumerator.toString(readInt());
}
};
}
}

View File

@@ -89,4 +89,17 @@ public class GraphDataOutput implements DataOutput {
public static DataOutput wrap(DataOutput out) {
return new GraphDataOutput(out);
}
public interface StringEnumerator {
int toNumber(String str) throws IOException;
}
public static DataOutput wrap(DataOutput out, StringEnumerator enumerator) {
return new GraphDataOutput(out) {
@Override
public void writeUTF(@NotNull String s) throws IOException {
writeInt(enumerator.toNumber(s));
}
};
}
}

View File

@@ -10,7 +10,6 @@ import org.jetbrains.jps.dependency.java.JvmNodeReferenceID;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
abstract class GraphImpl implements Graph {
@@ -61,8 +60,7 @@ abstract class GraphImpl implements Graph {
@Override
public Iterable<NodeSource> getSources(@NotNull ReferenceID id) {
Iterable<NodeSource> nodeSources = myNodeToSourcesMap.get(id);
return nodeSources != null? nodeSources : Collections.emptyList();
return myNodeToSourcesMap.get(id);
}
@Override
@@ -77,8 +75,7 @@ abstract class GraphImpl implements Graph {
@Override
public Iterable<Node<?, ?>> getNodes(@NotNull NodeSource source) {
var nodes = mySourceToNodesMap.get(source);
return nodes != null? nodes : Collections.emptyList();
return mySourceToNodesMap.get(source);
}
public void close() throws IOException {

View File

@@ -0,0 +1,43 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.dependency.impl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.dependency.Maplet;
import java.util.HashMap;
import java.util.Map;
public final class MemoryMaplet<K, V> implements Maplet<K, V> {
private final Map<K, V> myMap = new HashMap<>();
@Override
public boolean containsKey(K key) {
return myMap.containsKey(key);
}
@Override
public @Nullable V get(K key) {
return myMap.get(key);
}
@Override
public void put(K key, V value) {
myMap.put(key, value);
}
@Override
public void remove(K key) {
myMap.remove(key);
}
@Override
public @NotNull Iterable<K> getKeys() {
return myMap.keySet();
}
@Override
public void close() {
myMap.clear();
}
}

View File

@@ -0,0 +1,81 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.dependency.impl;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jps.dependency.MultiMaplet;
import org.jetbrains.jps.javac.Iterators;
import java.util.*;
import java.util.function.Supplier;
public final class MemoryMultiMaplet<K, V, C extends Collection<V>> implements MultiMaplet<K, V> {
private final Map<K, C> myMap = new HashMap<>();
private final Supplier<? extends C> myCollectionFactory;
private final C myEmptyCollection;
public MemoryMultiMaplet(Supplier<? extends C> collectionFactory) {
myCollectionFactory = collectionFactory;
C col = collectionFactory.get();
//noinspection unchecked
myEmptyCollection = col instanceof List? (C)Collections.emptyList() : col instanceof Set? (C)Collections.emptySet() : col;
}
@Override
public boolean containsKey(K key) {
return myMap.containsKey(key);
}
@Override
public @NotNull Iterable<V> get(K key) {
C col = myMap.get(key);
return col != null? col : Collections.emptySet();
}
@Override
public void put(K key, @NotNull Iterable<? extends V> values) {
//noinspection unchecked
myMap.put(key, ensureCollection(values));
}
private C ensureCollection(Iterable<? extends V> seq) {
if (myEmptyCollection instanceof Set && seq instanceof Set) {
return (C)seq;
}
if (myEmptyCollection instanceof List && seq instanceof List) {
return (C)seq;
}
return Iterators.collect(seq, myCollectionFactory.get());
}
@Override
public void remove(K key) {
myMap.remove(key);
}
@Override
public void appendValue(K key, V value) {
C values = myMap.get(key);
if (values == null) {
myMap.put(key, values = myCollectionFactory.get());
}
values.add(value);
}
@Override
public void removeValue(K key, V value) {
C values = myMap.get(key);
if (values != null) {
values.remove(value);
}
}
@Override
public @NotNull Iterable<K> getKeys() {
return myMap.keySet();
}
@Override
public void close() {
myMap.clear();
}
}

View File

@@ -1,57 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.dependency.impl;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.dependency.MultiMaplet;
import org.jetbrains.jps.javac.Iterators;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public final class MemorySetMultiMaplet<K, V> implements MultiMaplet<K, V> {
private final Map<K, Set<V>> myMap = new HashMap<>();
@Override
public boolean containsKey(K key) {
return myMap.containsKey(key);
}
@Override
public @Nullable Iterable<V> get(K key) {
return myMap.get(key);
}
@Override
public void put(K key, Iterable<? extends V> values) {
myMap.put(key, Iterators.collect(values, new HashSet<>()));
}
@Override
public void remove(K key) {
myMap.remove(key);
}
@Override
public void appendValue(K key, V value) {
Set<V> values = myMap.get(key);
if (values == null) {
myMap.put(key, values = new HashSet<>());
}
values.add(value);
}
@Override
public void removeValue(K key, V value) {
Set<V> values = myMap.get(key);
if (values != null) {
values.remove(value);
}
}
@Override
public Iterable<K> getKeys() {
return myMap.keySet();
}
}

View File

@@ -0,0 +1,86 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.dependency.impl;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.KeyDescriptor;
import com.intellij.util.io.PersistentHashMap;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.builders.storage.BuildDataCorruptedException;
import org.jetbrains.jps.dependency.Maplet;
import java.io.IOException;
import java.nio.file.Path;
public final class PersistentMaplet<K, V> implements Maplet<K, V> {
private final PersistentHashMap<K, V> myMap;
public PersistentMaplet(Path mapFile, KeyDescriptor<K> keyDescriptor, DataExternalizer<V> valueExternalizer) {
try {
myMap = new PersistentHashMap<>(mapFile, keyDescriptor, valueExternalizer);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean containsKey(K key) {
try {
return myMap.containsMapping(key);
}
catch (IOException e) {
throw new BuildDataCorruptedException(e);
}
}
@Override
public @Nullable V get(K key) {
try {
return myMap.get(key);
}
catch (IOException e) {
throw new BuildDataCorruptedException(e);
}
}
@Override
public void put(K key, V value) {
try {
if (value == null) {
myMap.remove(key);
}
else {
myMap.put(key, value);
}
}
catch (IOException e) {
throw new BuildDataCorruptedException(e);
}
}
@Override
public void remove(K key) {
try {
myMap.remove(key);
}
catch (IOException e) {
throw new BuildDataCorruptedException(e);
}
}
@Override
public Iterable<K> getKeys() {
try {
return myMap.getAllKeysWithExistingMapping();
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void close() throws IOException{
myMap.close();
}
}

View File

@@ -1,14 +1,11 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.jps.dependency.impl;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.intellij.util.io.AppendablePersistentMap;
import com.intellij.util.io.DataExternalizer;
import com.intellij.util.io.KeyDescriptor;
import com.intellij.util.io.PersistentHashMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.builders.storage.BuildDataCorruptedException;
import org.jetbrains.jps.dependency.MultiMaplet;
import org.jetbrains.jps.javac.Iterators;
@@ -18,75 +15,48 @@ import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
public final class PersistentSetMultiMaplet<K, V> implements MultiMaplet<K, V> {
private static final Set<?> NULL_COLLECTION = Collections.emptySet();
private static final int CACHE_SIZE = 1024;
private final PersistentHashMap<K, Set<V>> myMap;
private final LoadingCache<K, Set<V>> myCache;
public final class PersistentMultiMaplet<K, V, C extends Collection<V>> implements MultiMaplet<K, V> {
private final PersistentHashMap<K, C> myMap;
private final DataExternalizer<V> myValuesExternalizer;
private final C myEmptyCollection;
private final Supplier<? extends C> myCollectionFactory;
public PersistentSetMultiMaplet(Path mapFile, KeyDescriptor<K> keyDescriptor, DataExternalizer<V> valueExternalizer) {
public PersistentMultiMaplet(Path mapFile, KeyDescriptor<K> keyDescriptor, DataExternalizer<V> valueExternalizer, Supplier<? extends C> collectionFactory) {
myCollectionFactory = collectionFactory;
try {
C col = collectionFactory.get();
//noinspection unchecked
myEmptyCollection = col instanceof List? (C)Collections.emptyList() : col instanceof Set? (C)Collections.emptySet() : col;
myValuesExternalizer = valueExternalizer;
myMap = new PersistentHashMap<>(mapFile, new KeyDescriptor<>() {
myMap = new PersistentHashMap<>(mapFile, keyDescriptor, new DataExternalizer<>() {
@Override
public int getHashCode(K value) {
return keyDescriptor.getHashCode(value);
}
@Override
public void save(@NotNull DataOutput out, K value) throws IOException {
keyDescriptor.save(GraphDataOutput.wrap(out), value);
}
@Override
public boolean isEqual(K val1, K val2) {
return keyDescriptor.isEqual(val1, val2);
}
@Override
public K read(@NotNull DataInput in) throws IOException {
return keyDescriptor.read(GraphDataInput.wrap(in));
}
}, new DataExternalizer<>() {
@Override
public void save(@NotNull DataOutput out, Set<V> value) throws IOException {
out = GraphDataOutput.wrap(out);
public void save(@NotNull DataOutput out, C value) throws IOException {
for (V v : value) {
valueExternalizer.save(out, v);
}
}
@Override
public Set<V> read(@NotNull DataInput in) throws IOException {
Set<V> acc = new HashSet<>();
public C read(@NotNull DataInput in) throws IOException {
C acc = myCollectionFactory.get();
final DataInputStream stream = (DataInputStream)in;
in = GraphDataInput.wrap(in);
while (stream.available() > 0) {
acc.add(valueExternalizer.read(in));
acc.add(valueExternalizer.read(stream));
}
return acc;
}
});
}
catch (IOException e) {
catch (Throwable e) {
throw new RuntimeException(e);
}
myCache = Caffeine.newBuilder().maximumSize(CACHE_SIZE).build(key -> {
try {
Set<V> collection = myMap.get(key);
//noinspection unchecked
return collection == null ? (Set<V>)NULL_COLLECTION : collection;
}
catch (IOException e) {
throw new BuildDataCorruptedException(e);
}
});
}
@Override
@@ -100,17 +70,21 @@ public final class PersistentSetMultiMaplet<K, V> implements MultiMaplet<K, V> {
}
@Override
public @Nullable Set<V> get(K key) {
final Set<V> collection = myCache.get(key);
return collection == NULL_COLLECTION ? null : collection;
public @NotNull C get(K key) {
try {
C col = myMap.get(key);
return col != null? col : myEmptyCollection;
}
catch (IOException e) {
throw new BuildDataCorruptedException(e);
}
}
@Override
public void put(K key, Iterable<? extends V> values) {
public void put(K key, @NotNull Iterable<? extends V> values) {
try {
myCache.invalidate(key);
//noinspection unchecked
Set<V> data = values instanceof Set? (Set<V>)values : Iterators.collect(values, new HashSet<>());
C data = ensureCollection(values);
if (data.isEmpty()) {
myMap.remove(key);
}
@@ -123,10 +97,19 @@ public final class PersistentSetMultiMaplet<K, V> implements MultiMaplet<K, V> {
}
}
private C ensureCollection(Iterable<? extends V> seq) {
if (myEmptyCollection instanceof Set && seq instanceof Set) {
return (C)seq;
}
if (myEmptyCollection instanceof List && seq instanceof List) {
return (C)seq;
}
return Iterators.collect(seq, myCollectionFactory.get());
}
@Override
public void remove(K key) {
try {
myCache.invalidate(key);
myMap.remove(key);
}
catch (IOException e) {
@@ -140,12 +123,11 @@ public final class PersistentSetMultiMaplet<K, V> implements MultiMaplet<K, V> {
}
@Override
public void appendValues(K key, Iterable<? extends V> values) {
public void appendValues(K key, @NotNull Iterable<? extends V> values) {
try {
myMap.appendData(key, new AppendablePersistentMap.ValueDataAppender() {
@Override
public void append(@NotNull DataOutput out) throws IOException {
out = GraphDataOutput.wrap(out);
for (V v : values) {
myValuesExternalizer.save(out, v);
}
@@ -163,16 +145,15 @@ public final class PersistentSetMultiMaplet<K, V> implements MultiMaplet<K, V> {
}
@Override
public void removeValues(K key, Iterable<? extends V> values) {
public void removeValues(K key, @NotNull Iterable<? extends V> values) {
try {
final Set<V> collection = myCache.get(key);
C collection = get(key);
if (!collection.isEmpty()) {
boolean changes = false;
for (V value : values) {
changes |= collection.remove(value);
}
if (changes) {
myCache.invalidate(key);
if (collection.isEmpty()) {
myMap.remove(key);
}
@@ -188,7 +169,7 @@ public final class PersistentSetMultiMaplet<K, V> implements MultiMaplet<K, V> {
}
@Override
public Iterable<K> getKeys() {
public @NotNull Iterable<K> getKeys() {
try {
return myMap.getAllKeysWithExistingMapping();
}
@@ -197,9 +178,9 @@ public final class PersistentSetMultiMaplet<K, V> implements MultiMaplet<K, V> {
}
}
@Override
public void close() {
try {
myCache.invalidateAll();
myMap.close();
}
catch (IOException e) {