aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects
diff options
context:
space:
mode:
authorLibravatar OszkarSemerath <semerath@mit.bme.hu>2023-02-05 21:35:28 +0100
committerLibravatar OszkarSemerath <semerath@mit.bme.hu>2023-02-05 21:35:28 +0100
commita36a8eb9ad499eb0f50204e462e9bc1c45544294 (patch)
treea6408aa5d8919eb13ef3d80d22293b7975428a5f /subprojects
parentFixing warning caused by an "unused parameter" which is used by an annotation (diff)
downloadrefinery-a36a8eb9ad499eb0f50204e462e9bc1c45544294.tar.gz
refinery-a36a8eb9ad499eb0f50204e462e9bc1c45544294.tar.zst
refinery-a36a8eb9ad499eb0f50204e462e9bc1c45544294.zip
Delta store commit
Diffstat (limited to 'subprojects')
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java90
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java127
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java62
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java15
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java34
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java31
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java47
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java10
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java156
9 files changed, 572 insertions, 0 deletions
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java
new file mode 100644
index 00000000..2bd758e2
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java
@@ -0,0 +1,90 @@
1package tools.refinery.store.map;
2
3import java.util.*;
4
5import tools.refinery.store.map.internal.DeltaDiffCursor;
6import tools.refinery.store.map.internal.MapDelta;
7import tools.refinery.store.map.internal.MapTransaction;
8import tools.refinery.store.map.internal.VersionedMapDeltaImpl;
9
10public class VersionedMapStoreDeltaImpl<K, V> implements VersionedMapStore<K, V>{
11 // Static data
12 protected final V defaultValue;
13
14 // Dynamic data
15 protected final Map<Long,MapTransaction<K, V>> states = new HashMap<>();
16 protected long nextID = 0;
17
18 public VersionedMapStoreDeltaImpl(V defaultValue) {
19 super();
20 this.defaultValue = defaultValue;
21 }
22
23 @Override
24 public VersionedMap<K, V> createMap() {
25 return new VersionedMapDeltaImpl<>(this, defaultValue);
26 }
27
28 @Override
29 public VersionedMap<K, V> createMap(long state) {
30 VersionedMapDeltaImpl<K, V> result = new VersionedMapDeltaImpl<>(this, defaultValue);
31 result.restore(state);
32 return result;
33 }
34
35 public synchronized MapTransaction<K, V> appendTransaction(MapDelta<K, V>[] deltas, MapTransaction<K, V> previous, long[] versionContainer) {
36 long version = nextID++;
37 versionContainer[0] = version;
38 if(deltas == null) {
39 states.put(version, previous);
40 return previous;
41 } else {
42 MapTransaction<K, V> transaction = new MapTransaction<>(deltas, version, previous);
43 states.put(version, transaction);
44 return transaction;
45 }
46 }
47
48 private synchronized MapTransaction<K,V> getState(long state) {
49 return states.get(state);
50 }
51
52 public void getPath(long to, List<MapDelta<K, V>[]> forwardTransactions) {
53 MapTransaction<K,V> toTransaction = getState(to);
54 while(toTransaction != null) {
55 forwardTransactions.add(toTransaction.deltas());
56 toTransaction = toTransaction.parent();
57 }
58 }
59
60 public void getPath(long from, long to,
61 List<MapDelta<K, V>[]> backwardTransactions,
62 List<MapDelta<K, V>[]> forwardTransactions)
63 {
64 MapTransaction<K,V> fromTransaction = getState(from);
65 MapTransaction<K,V> toTransaction = getState(to);
66 while(fromTransaction != toTransaction) {
67 if(fromTransaction == null || fromTransaction.version() < toTransaction.version()) {
68 forwardTransactions.add(toTransaction.deltas());
69 toTransaction = toTransaction.parent();
70 } else {
71 backwardTransactions.add(fromTransaction.deltas());
72 fromTransaction = fromTransaction.parent();
73 }
74 }
75 }
76
77
78 @Override
79 public synchronized Set<Long> getStates() {
80 return states.keySet();
81 }
82
83 @Override
84 public DiffCursor<K, V> getDiffCursor(long fromState, long toState) {
85 List<MapDelta<K, V>[]> backwardTransactions = new ArrayList<>();
86 List<MapDelta<K, V>[]> forwardTransactions = new ArrayList<>();
87 getPath(fromState, toState, backwardTransactions, forwardTransactions);
88 return new DeltaDiffCursor<>(backwardTransactions, forwardTransactions);
89 }
90}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java
new file mode 100644
index 00000000..75180bf9
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java
@@ -0,0 +1,127 @@
1package tools.refinery.store.map.internal;
2
3import tools.refinery.store.map.AnyVersionedMap;
4import tools.refinery.store.map.DiffCursor;
5
6import java.util.Collections;
7import java.util.List;
8import java.util.Set;
9
10public class DeltaDiffCursor<K, V> implements DiffCursor<K, V> {
11 final List<MapDelta<K, V>[]> backwardTransactions;
12 final List<MapDelta<K, V>[]> forwardTransactions;
13
14 /**
15 * Denotes the direction of traversal. False means backwards, true means
16 * forward.
17 */
18 boolean direction;
19 int listIndex;
20 int arrayIndex;
21
22 public DeltaDiffCursor(List<MapDelta<K, V>[]> backwardTransactions, List<MapDelta<K, V>[]> forwardTransactions) {
23 this.backwardTransactions = backwardTransactions;
24 this.forwardTransactions = forwardTransactions;
25
26 if (!backwardTransactions.isEmpty()) {
27 direction = false;
28 listIndex = 0;
29 arrayIndex = backwardTransactions.get(listIndex).length - 1;
30 } else if (!forwardTransactions.isEmpty()) {
31 direction = true;
32 listIndex = forwardTransactions.size() - 1;
33 arrayIndex = 0;
34 } else {
35 direction = true;
36 listIndex = -1;
37 }
38 }
39
40 protected MapDelta<K, V> getCurrentDelta() {
41 final List<MapDelta<K, V>[]> list;
42 if (!direction) {
43 list = this.backwardTransactions;
44 } else {
45 list = this.forwardTransactions;
46 }
47 return list.get(listIndex)[arrayIndex];
48 }
49
50 @Override
51 public K getKey() {
52 return getCurrentDelta().getKey();
53 }
54
55 @Override
56 public V getValue() {
57 return getToValue();
58 }
59
60 @Override
61 public boolean isTerminated() {
62 return this.direction && listIndex == -1;
63 }
64
65 @Override
66 public boolean move() {
67 if (isTerminated()) {
68 return false;
69 } else {
70 if (this.direction) {
71 if (arrayIndex+1 < forwardTransactions.get(listIndex).length) {
72 arrayIndex++;
73 return true;
74 } else {
75 if (listIndex-1 >= 0) {
76 listIndex--;
77 return true;
78 } else {
79 listIndex = -1;
80 return false;
81 }
82 }
83 } else {
84 if (arrayIndex > 0) {
85 arrayIndex--;
86 return true;
87 } else {
88 if (listIndex+1 < backwardTransactions.size()) {
89 listIndex++;
90 this.arrayIndex = backwardTransactions.get(listIndex).length - 1;
91 return true;
92 } else {
93 this.direction = true;
94 if (!this.forwardTransactions.isEmpty()) {
95 listIndex = forwardTransactions.size() - 1;
96 arrayIndex = 0;
97 return true;
98 } else {
99 listIndex = -1;
100 return false;
101 }
102 }
103 }
104 }
105 }
106 }
107
108 @Override
109 public boolean isDirty() {
110 return false;
111 }
112
113 @Override
114 public Set<AnyVersionedMap> getDependingMaps() {
115 return Collections.emptySet();
116 }
117
118 @Override
119 public V getFromValue() {
120 return getCurrentDelta().getOldValue();
121 }
122
123 @Override
124 public V getToValue() {
125 return getCurrentDelta().getNewValue();
126 }
127}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java
new file mode 100644
index 00000000..4a8e9709
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java
@@ -0,0 +1,62 @@
1package tools.refinery.store.map.internal;
2
3import java.util.*;
4import java.util.Map.Entry;
5
6import tools.refinery.store.map.AnyVersionedMap;
7import tools.refinery.store.map.Cursor;
8import tools.refinery.store.map.VersionedMap;
9
10public class IteratorAsCursor<K, V> implements Cursor<K, V> {
11 final Iterator<Entry<K, V>> iterator;
12 final VersionedMap<K, V> source;
13
14 private boolean terminated;
15 private K key;
16 private V value;
17
18 public IteratorAsCursor(VersionedMap<K, V> source, Map<K, V> current) {
19 this.iterator = current.entrySet().iterator();
20 this.source = source;
21 move();
22 }
23
24 @Override
25 public K getKey() {
26 return key;
27 }
28
29 @Override
30 public V getValue() {
31 return value;
32 }
33
34 @Override
35 public boolean isTerminated() {
36 return terminated;
37 }
38
39 @Override
40 public boolean move() {
41 terminated = iterator.hasNext();
42 if (terminated) {
43 this.key = null;
44 this.value = null;
45 } else {
46 Entry<K, V> next = iterator.next();
47 this.key = next.getKey();
48 this.value = next.getValue();
49 }
50 return !terminated;
51 }
52
53 @Override
54 public boolean isDirty() {
55 return false;
56 }
57
58 @Override
59 public Set<AnyVersionedMap> getDependingMaps() {
60 return Set.of(this.source);
61 }
62}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java
new file mode 100644
index 00000000..86e9fe62
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java
@@ -0,0 +1,15 @@
1package tools.refinery.store.map.internal;
2
3public record MapDelta<K, V>(K key, V oldValue, V newValue) {
4 public K getKey() {
5 return key;
6 }
7
8 public V getOldValue() {
9 return oldValue;
10 }
11
12 public V getNewValue() {
13 return newValue;
14 }
15}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java
new file mode 100644
index 00000000..5996048e
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java
@@ -0,0 +1,34 @@
1package tools.refinery.store.map.internal;
2
3import java.util.Arrays;
4import java.util.Objects;
5
6public record MapTransaction<K, V>(MapDelta<K, V>[] deltas, long version, MapTransaction<K, V> parent) {
7
8 @Override
9 public int hashCode() {
10 final int prime = 31;
11 int result = 1;
12 result = prime * result + Arrays.hashCode(deltas);
13 result = prime * result + Objects.hash(parent, version);
14 return result;
15 }
16
17 @Override
18 public boolean equals(Object obj) {
19 if (this == obj)
20 return true;
21 if (obj == null)
22 return false;
23 if (getClass() != obj.getClass())
24 return false;
25 @SuppressWarnings("unchecked")
26 MapTransaction<K, V> other = (MapTransaction<K, V>) obj;
27 return Arrays.equals(deltas, other.deltas) && Objects.equals(parent, other.parent) && version == other.version;
28 }
29
30 @Override
31 public String toString() {
32 return "MapTransaction " + version + " " + Arrays.toString(deltas);
33 }
34}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java
new file mode 100644
index 00000000..3b3f94ae
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java
@@ -0,0 +1,31 @@
1package tools.refinery.store.map.internal;
2
3import java.util.ArrayList;
4import java.util.List;
5
6public class UncommittedDeltaArrayStore<K, V> implements UncommittedDeltaStore<K, V> {
7 final List<MapDelta<K, V>> uncommittedOldValues = new ArrayList<>();
8
9 @Override
10 public void processChange(K key, V oldValue, V newValue) {
11 uncommittedOldValues.add(new MapDelta<>(key, oldValue, newValue));
12 }
13
14 @Override
15 public MapDelta<K, V>[] extractDeltas() {
16 if (uncommittedOldValues.isEmpty()) {
17 return null;
18 } else {
19 @SuppressWarnings("unchecked")
20 MapDelta<K, V>[] result = uncommittedOldValues.toArray(new MapDelta[0]);
21 return result;
22 }
23 }
24
25 @Override
26 public MapDelta<K, V>[] extractAndDeleteDeltas() {
27 MapDelta<K, V>[] res = extractDeltas();
28 this.uncommittedOldValues.clear();
29 return res;
30 }
31}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java
new file mode 100644
index 00000000..73df5080
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java
@@ -0,0 +1,47 @@
1package tools.refinery.store.map.internal;
2
3import java.util.HashMap;
4import java.util.Map;
5import java.util.Map.Entry;
6
7import tools.refinery.store.map.VersionedMap;
8
9public class UncommittedDeltaMapStore<K, V> implements UncommittedDeltaStore<K, V> {
10 final VersionedMap<K, V> source;
11 final Map<K, V> uncommittedOldValues = new HashMap<>();
12
13 public UncommittedDeltaMapStore(VersionedMap<K, V> source) {
14 this.source = source;
15 }
16
17 @Override
18 public void processChange(K key, V oldValue, V newValue) {
19 this.uncommittedOldValues.putIfAbsent(key, oldValue);
20 }
21
22 @Override
23 public MapDelta<K, V>[] extractDeltas() {
24 if (uncommittedOldValues.isEmpty()) {
25 return null;
26 } else {
27 @SuppressWarnings("unchecked")
28 MapDelta<K, V>[] deltas = new MapDelta[uncommittedOldValues.size()];
29 int i = 0;
30 for (Entry<K, V> entry : uncommittedOldValues.entrySet()) {
31 final K key = entry.getKey();
32 final V oldValue = entry.getValue();
33 final V newValue = source.get(key);
34 deltas[i] = new MapDelta<>(key, oldValue, newValue);
35 }
36
37 return deltas;
38 }
39 }
40
41 @Override
42 public MapDelta<K, V>[] extractAndDeleteDeltas() {
43 MapDelta<K, V>[] res = extractDeltas();
44 this.uncommittedOldValues.clear();
45 return res;
46 }
47}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java
new file mode 100644
index 00000000..37e5817c
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java
@@ -0,0 +1,10 @@
1package tools.refinery.store.map.internal;
2
3public interface UncommittedDeltaStore<K, V> {
4 void processChange(K key, V oldValue, V newValue);
5
6 MapDelta<K, V>[] extractDeltas();
7
8 MapDelta<K, V>[] extractAndDeleteDeltas();
9
10}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java
new file mode 100644
index 00000000..deedf134
--- /dev/null
+++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java
@@ -0,0 +1,156 @@
1package tools.refinery.store.map.internal;
2
3import java.util.*;
4
5import tools.refinery.store.map.*;
6
7public class VersionedMapDeltaImpl<K, V> implements VersionedMap<K, V> {
8 protected final VersionedMapStoreDeltaImpl<K, V> store;
9
10 final Map<K, V> current;
11
12 final UncommittedDeltaStore<K, V> uncommittedStore;
13 MapTransaction<K, V> previous;
14
15 protected final V defaultValue;
16
17 public VersionedMapDeltaImpl(VersionedMapStoreDeltaImpl<K, V> store, V defaultValue) {
18 this.store = store;
19 this.defaultValue = defaultValue;
20
21 current = new HashMap<>();
22 uncommittedStore = new UncommittedDeltaArrayStore<>();
23 }
24
25 @Override
26 public long commit() {
27 MapDelta<K, V>[] deltas = uncommittedStore.extractAndDeleteDeltas();
28 long[] versionContainer = new long[1];
29 this.previous = this.store.appendTransaction(deltas, previous, versionContainer);
30 return versionContainer[0];
31 }
32
33 @Override
34 public void restore(long state) {
35 // 1. restore uncommitted states
36 MapDelta<K, V>[] uncommitted = this.uncommittedStore.extractAndDeleteDeltas();
37 if (uncommitted != null) {
38 backward(uncommitted);
39 }
40
41 // 2. get common ancestor
42 List<MapDelta<K, V>[]> forward = new ArrayList<>();
43 if (this.previous == null) {
44 this.store.getPath(state, forward);
45 this.forward(forward);
46 } else {
47 List<MapDelta<K, V>[]> backward = new ArrayList<>();
48 this.store.getPath(this.previous.version(), state, backward, forward);
49 this.backward(backward);
50 this.forward(forward);
51 }
52 }
53
54 protected void forward(List<MapDelta<K, V>[]> changes) {
55 for (int i = changes.size() - 1; i >= 0; i--) {
56 forward(changes.get(i));
57 }
58 }
59
60 protected void backward(List<MapDelta<K, V>[]> changes) {
61 for (int i = 0; i < changes.size(); i++) {
62 backward(changes.get(i));
63 }
64 }
65
66 protected void forward(MapDelta<K, V>[] changes) {
67 for (int i = 0; i < changes.length; i++) {
68 final MapDelta<K, V> change = changes[i];
69 current.put(change.getKey(), change.getNewValue());
70 }
71 }
72
73 protected void backward(MapDelta<K, V>[] changes) {
74 for (int i = changes.length - 1; i >= 0; i--) {
75 final MapDelta<K, V> change = changes[i];
76 current.put(change.getKey(), change.getOldValue());
77 }
78 }
79
80 @Override
81 public V get(K key) {
82 return current.getOrDefault(key, defaultValue);
83 }
84
85 @Override
86 public Cursor<K, V> getAll() {
87 return new IteratorAsCursor<>(this, current);
88 }
89
90 @Override
91 public V put(K key, V value) {
92 if (value == defaultValue) {
93 V res = current.remove(key);
94 if (res == null) {
95 // no changes
96 return defaultValue;
97 } else {
98 uncommittedStore.processChange(key, res, value);
99 return res;
100 }
101 } else {
102 V oldValue = current.put(key, value);
103 uncommittedStore.processChange(key, oldValue, value);
104 return oldValue;
105 }
106 }
107
108 @Override
109 public void putAll(Cursor<K, V> cursor) {
110 throw new UnsupportedOperationException();
111
112 }
113
114 @Override
115 public long getSize() {
116 return current.size();
117 }
118
119 @Override
120 public DiffCursor<K, V> getDiffCursor(long state) {
121 MapDelta<K, V>[] backward = this.uncommittedStore.extractDeltas();
122 List<MapDelta<K, V>[]> backwardTransactions = new ArrayList<>();
123 List<MapDelta<K, V>[]> forwardTransactions = new ArrayList<>();
124
125 if (backward != null) {
126 backwardTransactions.add(backward);
127 }
128
129 if (this.previous != null) {
130 store.getPath(this.previous.version(), state, backwardTransactions, forwardTransactions);
131 } else {
132 store.getPath(state, forwardTransactions);
133 }
134
135 return new DeltaDiffCursor<>(backwardTransactions, forwardTransactions);
136 }
137
138 @Override
139 public int contentHashCode(ContentHashCode mode) {
140 return this.current.hashCode();
141 }
142
143 @Override
144 public boolean contentEquals(AnyVersionedMap other) {
145 if (other instanceof VersionedMapDeltaImpl<?, ?> versioned) {
146 if (versioned == this) {
147 return true;
148 } else {
149 return Objects.equals(this.defaultValue, versioned.defaultValue) &&
150 Objects.equals(this.current, versioned.current);
151 }
152 } else {
153 throw new UnsupportedOperationException("Comparing different map implementations is ineffective.");
154 }
155 }
156}