aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-07-14 19:39:45 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-07-15 14:37:36 +0200
commitfcac626303cc092246ffc2db8fd3d04695787f53 (patch)
tree77da1c00ad73b4b9c5536dfec35baefc58dbb183 /subprojects/store
parentfix: ConstantLiteral to PConstraint (diff)
downloadrefinery-fcac626303cc092246ffc2db8fd3d04695787f53.tar.gz
refinery-fcac626303cc092246ffc2db8fd3d04695787f53.tar.zst
refinery-fcac626303cc092246ffc2db8fd3d04695787f53.zip
feat: base indexer for store
Diffstat (limited to 'subprojects/store')
-rw-r--r--subprojects/store/build.gradle.kts5
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/Cursors.java53
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/AnyInterpretation.java2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/BaseIndexer.java135
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/IndexedVersionedInterpretation.java50
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/NullaryVersionedInterpretation.java29
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/UnaryVersionedInterpretation.java50
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java32
9 files changed, 349 insertions, 9 deletions
diff --git a/subprojects/store/build.gradle.kts b/subprojects/store/build.gradle.kts
index 2c485020..d653f01d 100644
--- a/subprojects/store/build.gradle.kts
+++ b/subprojects/store/build.gradle.kts
@@ -8,3 +8,8 @@ plugins {
8 id("tools.refinery.gradle.java-library") 8 id("tools.refinery.gradle.java-library")
9 id("tools.refinery.gradle.jmh") 9 id("tools.refinery.gradle.jmh")
10} 10}
11
12dependencies {
13 implementation(libs.eclipseCollections)
14 implementation(libs.eclipseCollections.api)
15}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/Cursors.java b/subprojects/store/src/main/java/tools/refinery/store/map/Cursors.java
index 0a94d449..1080a248 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/map/Cursors.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/Cursors.java
@@ -14,6 +14,10 @@ public final class Cursors {
14 return new Empty<>(); 14 return new Empty<>();
15 } 15 }
16 16
17 public static <K, V> Cursor<K, V> singleton(K key, V value) {
18 return new Singleton<>(key, value);
19 }
20
17 private static class Empty<K, V> implements Cursor<K, V> { 21 private static class Empty<K, V> implements Cursor<K, V> {
18 private boolean terminated = false; 22 private boolean terminated = false;
19 23
@@ -38,4 +42,53 @@ public final class Cursors {
38 return false; 42 return false;
39 } 43 }
40 } 44 }
45
46 private static class Singleton<K, V> implements Cursor<K, V> {
47 private State state = State.INITIAL;
48 private final K key;
49 private final V value;
50
51 public Singleton(K key, V value) {
52 this.key = key;
53 this.value = value;
54 }
55
56 @Override
57 public K getKey() {
58 if (state == State.STARTED) {
59 return key;
60 }
61 return null;
62 }
63
64 @Override
65 public V getValue() {
66 if (state == State.STARTED) {
67 return value;
68 }
69 return null;
70 }
71
72 @Override
73 public boolean isTerminated() {
74 return state == State.TERMINATED;
75 }
76
77 @Override
78 public boolean move() {
79 if (state == State.INITIAL) {
80 state = State.STARTED;
81 return true;
82 }
83 state = State.TERMINATED;
84 return false;
85 }
86
87
88 private enum State {
89 INITIAL,
90 STARTED,
91 TERMINATED
92 }
93 }
41} 94}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/AnyInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/AnyInterpretation.java
index f906b48a..d650bd06 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/AnyInterpretation.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/AnyInterpretation.java
@@ -13,4 +13,6 @@ public sealed interface AnyInterpretation permits Interpretation {
13 AnySymbol getSymbol(); 13 AnySymbol getSymbol();
14 14
15 long getSize(); 15 long getSize();
16
17 int getAdjacentSize(int slot, int node);
16} 18}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java
index 26ad9a69..ea670cbc 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java
@@ -18,6 +18,8 @@ public non-sealed interface Interpretation<T> extends AnyInterpretation {
18 18
19 Cursor<Tuple, T> getAll(); 19 Cursor<Tuple, T> getAll();
20 20
21 Cursor<Tuple, T> getAdjacent(int slot, int node);
22
21 T put(Tuple key, T value); 23 T put(Tuple key, T value);
22 24
23 void putAll(Cursor<Tuple, T> cursor); 25 void putAll(Cursor<Tuple, T> cursor);
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/BaseIndexer.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/BaseIndexer.java
new file mode 100644
index 00000000..d9245c1d
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/BaseIndexer.java
@@ -0,0 +1,135 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.model.internal;
7
8import org.eclipse.collections.api.factory.Maps;
9import org.eclipse.collections.api.factory.primitive.IntObjectMaps;
10import org.eclipse.collections.api.map.MutableMap;
11import org.eclipse.collections.api.map.primitive.MutableIntObjectMap;
12import tools.refinery.store.map.AnyVersionedMap;
13import tools.refinery.store.map.Cursor;
14import tools.refinery.store.map.Cursors;
15import tools.refinery.store.map.VersionedMap;
16import tools.refinery.store.tuple.Tuple;
17
18import java.util.Iterator;
19import java.util.Map;
20import java.util.Set;
21
22class BaseIndexer<T> {
23 private final MutableIntObjectMap<MutableMap<Tuple, T>>[] maps;
24 private final VersionedMap<Tuple, T> versionedMap;
25
26 public BaseIndexer(int arity, VersionedMap<Tuple, T> map) {
27 if (arity < 2) {
28 throw new IllegalArgumentException("Only arity >= 2 symbols need to be indexed");
29 }
30 // There is no way in Java to create a generic array in a checked way.
31 @SuppressWarnings({"unchecked", "squid:S1905"})
32 var uncheckedMaps = (MutableIntObjectMap<MutableMap<Tuple, T>>[]) new MutableIntObjectMap[arity];
33 maps = uncheckedMaps;
34 for (int i = 0; i < arity; i++) {
35 maps[i] = IntObjectMaps.mutable.empty();
36 }
37 this.versionedMap = map;
38 if (map != null) {
39 var cursor = map.getAll();
40 while (cursor.move()) {
41 put(cursor.getKey(), cursor.getValue());
42 }
43 }
44 }
45
46 public void put(Tuple key, T value) {
47 for (int i = 0; i < maps.length; i++) {
48 var map = maps[i];
49 int element = key.get(i);
50 var adjacentTuples = map.getIfAbsentPut(element, Maps.mutable::empty);
51 adjacentTuples.put(key, value);
52 }
53 }
54
55 public void remove(Tuple key) {
56 for (int i = 0; i < maps.length; i++) {
57 var map = maps[i];
58 int element = key.get(i);
59 var adjacentTuples = map.get(element);
60 if (adjacentTuples == null) {
61 continue;
62 }
63 adjacentTuples.remove(key);
64 if (adjacentTuples.isEmpty()) {
65 map.remove(element);
66 }
67 }
68 }
69
70 private MutableMap<Tuple, T> getAdjacentMap(int slot, int node) {
71 if (slot < 0 || slot >= maps.length) {
72 throw new IllegalArgumentException("Invalid index: " + slot);
73 }
74 var map = maps[slot];
75 return map.get(node);
76 }
77
78 public int getAdjacentSize(int slot, int node) {
79 var adjacentTuples = getAdjacentMap(slot, node);
80 if (adjacentTuples == null) {
81 return 0;
82 }
83 return adjacentTuples.size();
84 }
85
86 public Cursor<Tuple, T> getAdjacent(int slot, int node) {
87 var adjacentTuples = getAdjacentMap(slot, node);
88 if (adjacentTuples == null) {
89 return Cursors.empty();
90 }
91 return new IndexCursor<>(adjacentTuples, versionedMap);
92 }
93
94 private static class IndexCursor<T> implements Cursor<Tuple, T> {
95 private final Set<AnyVersionedMap> dependingMaps;
96 private final Iterator<Map.Entry<Tuple, T>> iterator;
97 private Map.Entry<Tuple, T> entry;
98 private boolean terminated;
99
100 public IndexCursor(MutableMap<Tuple, T> adjacentTuples, VersionedMap<Tuple, T> versionedMap) {
101 dependingMaps = versionedMap == null ? Set.of() : Set.of(versionedMap);
102 iterator = adjacentTuples.entrySet().iterator();
103 }
104
105 @Override
106 public Tuple getKey() {
107 return entry.getKey();
108 }
109
110 @Override
111 public T getValue() {
112 return entry.getValue();
113 }
114
115 @Override
116 public boolean isTerminated() {
117 return terminated;
118 }
119
120 @Override
121 public boolean move() {
122 if (!terminated && iterator.hasNext()) {
123 entry = iterator.next();
124 return true;
125 }
126 terminated = true;
127 return false;
128 }
129
130 @Override
131 public Set<AnyVersionedMap> getDependingMaps() {
132 return dependingMaps;
133 }
134 }
135}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/IndexedVersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/IndexedVersionedInterpretation.java
new file mode 100644
index 00000000..e0a371de
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/IndexedVersionedInterpretation.java
@@ -0,0 +1,50 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.model.internal;
7
8import tools.refinery.store.map.Cursor;
9import tools.refinery.store.map.VersionedMap;
10import tools.refinery.store.map.VersionedMapStore;
11import tools.refinery.store.representation.Symbol;
12import tools.refinery.store.tuple.Tuple;
13
14import java.util.Objects;
15
16class IndexedVersionedInterpretation<T> extends VersionedInterpretation<T> {
17 private final BaseIndexer<T> indexer;
18
19 public IndexedVersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMapStore<Tuple, T> store,
20 VersionedMap<Tuple, T> map) {
21 super(model, symbol, store, map);
22 indexer = new BaseIndexer<>(symbol.arity(), map);
23 }
24
25 @Override
26 public Cursor<Tuple, T> getAdjacent(int slot, int node) {
27 return indexer.getAdjacent(slot, node);
28 }
29
30 @Override
31 public int getAdjacentSize(int slot, int node) {
32 return indexer.getAdjacentSize(slot, node);
33 }
34
35 @Override
36 protected boolean shouldNotifyRestoreListeners() {
37 // Always call the {@code valueChanged} method to update the index.
38 return true;
39 }
40
41 @Override
42 protected void valueChanged(Tuple key, T fromValue, T toValue, boolean restoring) {
43 if (Objects.equals(toValue, getSymbol().defaultValue())) {
44 indexer.remove(key);
45 } else {
46 indexer.put(key, toValue);
47 }
48 super.valueChanged(key, fromValue, toValue, restoring);
49 }
50}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/NullaryVersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/NullaryVersionedInterpretation.java
new file mode 100644
index 00000000..96639a8e
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/NullaryVersionedInterpretation.java
@@ -0,0 +1,29 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.model.internal;
7
8import tools.refinery.store.map.Cursor;
9import tools.refinery.store.map.VersionedMap;
10import tools.refinery.store.map.VersionedMapStore;
11import tools.refinery.store.representation.Symbol;
12import tools.refinery.store.tuple.Tuple;
13
14class NullaryVersionedInterpretation<T> extends VersionedInterpretation<T> {
15 public NullaryVersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMapStore<Tuple, T> store,
16 VersionedMap<Tuple, T> map) {
17 super(model, symbol, store, map);
18 }
19
20 @Override
21 public Cursor<Tuple, T> getAdjacent(int slot, int node) {
22 throw new IllegalArgumentException("Invalid index: " + slot);
23 }
24
25 @Override
26 public int getAdjacentSize(int slot, int node) {
27 throw new IllegalArgumentException("Invalid index: " + slot);
28 }
29}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/UnaryVersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/UnaryVersionedInterpretation.java
new file mode 100644
index 00000000..4ec04358
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/UnaryVersionedInterpretation.java
@@ -0,0 +1,50 @@
1/*
2 * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/>
3 *
4 * SPDX-License-Identifier: EPL-2.0
5 */
6package tools.refinery.store.model.internal;
7
8import tools.refinery.store.map.Cursor;
9import tools.refinery.store.map.Cursors;
10import tools.refinery.store.map.VersionedMap;
11import tools.refinery.store.map.VersionedMapStore;
12import tools.refinery.store.representation.Symbol;
13import tools.refinery.store.tuple.Tuple;
14
15import java.util.Objects;
16
17class UnaryVersionedInterpretation<T> extends VersionedInterpretation<T> {
18 public UnaryVersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMapStore<Tuple, T> store,
19 VersionedMap<Tuple, T> map) {
20 super(model, symbol, store, map);
21 }
22
23 private void validateSlot(int slot) {
24 if (slot != 0) {
25 throw new IllegalArgumentException("Invalid index: " + slot);
26 }
27 }
28
29 @Override
30 public Cursor<Tuple, T> getAdjacent(int slot, int node) {
31 validateSlot(slot);
32 var key = Tuple.of(node);
33 var value = get(key);
34 if (Objects.equals(value, getSymbol().defaultValue())) {
35 return Cursors.empty();
36 }
37 return Cursors.singleton(key, value);
38 }
39
40 @Override
41 public int getAdjacentSize(int slot, int node) {
42 validateSlot(slot);
43 var key = Tuple.of(node);
44 var value = get(key);
45 if (Objects.equals(value, getSymbol().defaultValue())) {
46 return 0;
47 }
48 return 1;
49 }
50}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java
index 86101ce3..877028cc 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java
@@ -21,7 +21,7 @@ import tools.refinery.store.tuple.Tuple;
21import java.util.ArrayList; 21import java.util.ArrayList;
22import java.util.List; 22import java.util.List;
23 23
24public class VersionedInterpretation<T> implements Interpretation<T> { 24public abstract class VersionedInterpretation<T> implements Interpretation<T> {
25 private final ModelImpl model; 25 private final ModelImpl model;
26 private final Symbol<T> symbol; 26 private final Symbol<T> symbol;
27 private final VersionedMapStore<Tuple, T> store; 27 private final VersionedMapStore<Tuple, T> store;
@@ -29,8 +29,8 @@ public class VersionedInterpretation<T> implements Interpretation<T> {
29 private final List<InterpretationListener<T>> listeners = new ArrayList<>(); 29 private final List<InterpretationListener<T>> listeners = new ArrayList<>();
30 private final List<InterpretationListener<T>> restoreListeners = new ArrayList<>(); 30 private final List<InterpretationListener<T>> restoreListeners = new ArrayList<>();
31 31
32 private VersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMapStore<Tuple, T> store, 32 protected VersionedInterpretation(ModelImpl model, Symbol<T> symbol, VersionedMapStore<Tuple, T> store,
33 VersionedMap<Tuple, T> map) { 33 VersionedMap<Tuple, T> map) {
34 this.model = model; 34 this.model = model;
35 this.symbol = symbol; 35 this.symbol = symbol;
36 this.store = store; 36 this.store = store;
@@ -58,6 +58,7 @@ public class VersionedInterpretation<T> implements Interpretation<T> {
58 .formatted(symbol, symbol.arity())); 58 .formatted(symbol, symbol.arity()));
59 } 59 }
60 } 60 }
61
61 @Override 62 @Override
62 public T get(Tuple key) { 63 public T get(Tuple key) {
63 checkKey(key); 64 checkKey(key);
@@ -69,7 +70,7 @@ public class VersionedInterpretation<T> implements Interpretation<T> {
69 return map.getAll(); 70 return map.getAll();
70 } 71 }
71 72
72 private void notifyListeners(Tuple key, T fromValue, T toValue, boolean restoring) { 73 protected void valueChanged(Tuple key, T fromValue, T toValue, boolean restoring) {
73 var listenerList = restoring ? restoreListeners : listeners; 74 var listenerList = restoring ? restoreListeners : listeners;
74 int listenerCount = listenerList.size(); 75 int listenerCount = listenerList.size();
75 // Use a for loop instead of a for-each loop to avoid <code>Iterator</code> allocation overhead. 76 // Use a for loop instead of a for-each loop to avoid <code>Iterator</code> allocation overhead.
@@ -84,7 +85,7 @@ public class VersionedInterpretation<T> implements Interpretation<T> {
84 checkKey(key); 85 checkKey(key);
85 model.markAsChanged(); 86 model.markAsChanged();
86 var oldValue = map.put(key, value); 87 var oldValue = map.put(key, value);
87 notifyListeners(key, oldValue, value, false); 88 valueChanged(key, oldValue, value, false);
88 return oldValue; 89 return oldValue;
89 } 90 }
90 91
@@ -125,11 +126,15 @@ public class VersionedInterpretation<T> implements Interpretation<T> {
125 return map.commit(); 126 return map.commit();
126 } 127 }
127 128
129 protected boolean shouldNotifyRestoreListeners() {
130 return !restoreListeners.isEmpty();
131 }
132
128 public void restore(long state) { 133 public void restore(long state) {
129 if (!restoreListeners.isEmpty()) { 134 if (shouldNotifyRestoreListeners()) {
130 var diffCursor = getDiffCursor(state); 135 var diffCursor = getDiffCursor(state);
131 while (diffCursor.move()) { 136 while (diffCursor.move()) {
132 notifyListeners(diffCursor.getKey(), diffCursor.getFromValue(), diffCursor.getToValue(), true); 137 valueChanged(diffCursor.getKey(), diffCursor.getFromValue(), diffCursor.getToValue(), true);
133 } 138 }
134 } 139 }
135 map.restore(state); 140 map.restore(state);
@@ -153,7 +158,7 @@ public class VersionedInterpretation<T> implements Interpretation<T> {
153 @SuppressWarnings("unchecked") 158 @SuppressWarnings("unchecked")
154 var typedSymbol = (Symbol<T>) symbol; 159 var typedSymbol = (Symbol<T>) symbol;
155 var map = store.createMap(); 160 var map = store.createMap();
156 return new VersionedInterpretation<>(model, typedSymbol, store, map); 161 return of(model, typedSymbol, store, map);
157 } 162 }
158 163
159 static <T> VersionedInterpretation<T> of(ModelImpl model, AnySymbol symbol, VersionedMapStore<Tuple, T> store, 164 static <T> VersionedInterpretation<T> of(ModelImpl model, AnySymbol symbol, VersionedMapStore<Tuple, T> store,
@@ -161,6 +166,15 @@ public class VersionedInterpretation<T> implements Interpretation<T> {
161 @SuppressWarnings("unchecked") 166 @SuppressWarnings("unchecked")
162 var typedSymbol = (Symbol<T>) symbol; 167 var typedSymbol = (Symbol<T>) symbol;
163 var map = store.createMap(state); 168 var map = store.createMap(state);
164 return new VersionedInterpretation<>(model, typedSymbol, store, map); 169 return of(model, typedSymbol, store, map);
170 }
171
172 private static <T> VersionedInterpretation<T> of(ModelImpl model, Symbol<T> typedSymbol,
173 VersionedMapStore<Tuple, T> store, VersionedMap<Tuple, T> map) {
174 return switch (typedSymbol.arity()) {
175 case 0 -> new NullaryVersionedInterpretation<>(model, typedSymbol, store, map);
176 case 1 -> new UnaryVersionedInterpretation<>(model, typedSymbol, store, map);
177 default -> new IndexedVersionedInterpretation<>(model, typedSymbol, store, map);
178 };
165 } 179 }
166} 180}