From 4f447bb7efd453eb6aa17fb29b8a0d7d65c03fcd Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Mon, 13 Feb 2023 01:29:28 +0100 Subject: Multiple small updates and fixes to support all upcoming tests. - AnyVersionedMap.checkIntegrity added to the superclass - Default value of the map is gettable. - Errors fixed: - Delta store failed to update reference to the previous transaction in some cases. Fixed in VersionedMapStoreDeltaImpl.java import java.util.HashMap; - Null values caused issues in UncommittedDeltaMapStore.java as putIfAbsent does not work with null. - Small fixes in DeltaDiffCursor.java and IteratorAsCursor.java --- .../tools/refinery/store/map/AnyVersionedMap.java | 5 ++ .../tools/refinery/store/map/VersionedMap.java | 2 + .../store/map/VersionedMapStoreDeltaImpl.java | 8 +- .../store/map/internal/DeltaDiffCursor.java | 2 + .../store/map/internal/IteratorAsCursor.java | 3 +- .../map/internal/UncommittedDeltaMapStore.java | 11 +-- .../store/map/internal/UncommittedDeltaStore.java | 14 ++++ .../store/map/internal/VersionedMapDeltaImpl.java | 85 ++++++++++++++++++---- .../store/map/internal/VersionedMapImpl.java | 2 + 9 files changed, 107 insertions(+), 25 deletions(-) (limited to 'subprojects/store') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/AnyVersionedMap.java b/subprojects/store/src/main/java/tools/refinery/store/map/AnyVersionedMap.java index f82a8bb1..ead79878 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/AnyVersionedMap.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/AnyVersionedMap.java @@ -37,4 +37,9 @@ public sealed interface AnyVersionedMap extends Versioned permits VersionedMap { @SuppressWarnings("squid:S1133") @Deprecated(since = "0.0.0") boolean equals(Object obj); + + /** + * Checks the integrity of the map, and throws an exception if an inconsistency is detected. + */ + void checkIntegrity(); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java index 31985e94..08ce1dbd 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java @@ -1,6 +1,8 @@ package tools.refinery.store.map; public non-sealed interface VersionedMap extends AnyVersionedMap { + V getDefaultValue(); + V get(K key); Cursor getAll(); 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 index 98dec2bb..e556a8bb 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java @@ -49,21 +49,22 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore return states.get(state); } - public void getPath(long to, List[]> forwardTransactions) { + public MapTransaction getPath(long to, List[]> forwardTransactions) { MapTransaction toTransaction = getState(to); while (toTransaction != null) { forwardTransactions.add(toTransaction.deltas()); toTransaction = toTransaction.parent(); } + return toTransaction; } - public void getPath(long from, long to, + public MapTransaction getPath(long from, long to, List[]> backwardTransactions, List[]> forwardTransactions) { MapTransaction fromTransaction = getState(from); MapTransaction toTransaction = getState(to); while (fromTransaction != toTransaction) { - if (fromTransaction == null || fromTransaction.version() < toTransaction.version()) { + if (fromTransaction == null || (toTransaction != null && fromTransaction.version() < toTransaction.version())) { forwardTransactions.add(toTransaction.deltas()); toTransaction = toTransaction.parent(); } else { @@ -71,6 +72,7 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore fromTransaction = fromTransaction.parent(); } } + return toTransaction; } 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 index 75180bf9..49ea1f67 100644 --- 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 @@ -62,6 +62,7 @@ public class DeltaDiffCursor implements DiffCursor { return this.direction && listIndex == -1; } + @Override public boolean move() { if (isTerminated()) { @@ -74,6 +75,7 @@ public class DeltaDiffCursor implements DiffCursor { } else { if (listIndex-1 >= 0) { listIndex--; + arrayIndex = 0; return true; } else { listIndex = -1; 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 index 4a8e9709..c1a0aec4 100644 --- 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 @@ -18,7 +18,6 @@ public class IteratorAsCursor implements Cursor { public IteratorAsCursor(VersionedMap source, Map current) { this.iterator = current.entrySet().iterator(); this.source = source; - move(); } @Override @@ -38,7 +37,7 @@ public class IteratorAsCursor implements Cursor { @Override public boolean move() { - terminated = iterator.hasNext(); + terminated = !iterator.hasNext(); if (terminated) { this.key = null; this.value = null; 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 index 73df5080..31423b1c 100644 --- 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 @@ -1,7 +1,6 @@ package tools.refinery.store.map.internal; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; import tools.refinery.store.map.VersionedMap; @@ -16,7 +15,9 @@ public class UncommittedDeltaMapStore implements UncommittedDeltaStore implements UncommittedDeltaStore[] deltas = new MapDelta[uncommittedOldValues.size()]; + MapDelta[] deltas = new MapDelta[uncommittedOldValues.size()]; int i = 0; for (Entry entry : uncommittedOldValues.entrySet()) { final K key = entry.getKey(); final V oldValue = entry.getValue(); final V newValue = source.get(key); - deltas[i] = new MapDelta<>(key, oldValue, newValue); + deltas[i++] = new MapDelta<>(key, oldValue, newValue); } return deltas; 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 index 37e5817c..7b017c8e 100644 --- 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 @@ -7,4 +7,18 @@ public interface UncommittedDeltaStore { MapDelta[] extractAndDeleteDeltas(); + default void checkIntegrity() { + MapDelta[] extractedDeltas = extractDeltas(); + if(extractedDeltas != null) { + for(var uncommittedOldValue : extractedDeltas) { + if(uncommittedOldValue == null) { + throw new IllegalArgumentException("Null entry in deltas!"); + } + if(uncommittedOldValue.getKey() == null) { + throw new IllegalStateException("Null key in deltas!"); + } + } + } + } + } 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 index 6f2996e1..d09e54ba 100644 --- 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 @@ -19,13 +19,18 @@ public class VersionedMapDeltaImpl implements VersionedMap { this.defaultValue = defaultValue; current = new HashMap<>(); - if(summarizeChanges) { + if (summarizeChanges) { this.uncommittedStore = new UncommittedDeltaMapStore<>(this); } else { this.uncommittedStore = new UncommittedDeltaArrayStore<>(); } } + @Override + public V getDefaultValue() { + return defaultValue; + } + @Override public long commit() { MapDelta[] deltas = uncommittedStore.extractAndDeleteDeltas(); @@ -43,16 +48,18 @@ public class VersionedMapDeltaImpl implements VersionedMap { } // 2. get common ancestor + final MapTransaction parent; List[]> forward = new ArrayList<>(); if (this.previous == null) { - this.store.getPath(state, forward); + parent = this.store.getPath(state, forward); this.forward(forward); } else { List[]> backward = new ArrayList<>(); - this.store.getPath(this.previous.version(), state, backward, forward); + parent = this.store.getPath(this.previous.version(), state, backward, forward); this.backward(backward); this.forward(forward); } + this.previous = parent; } protected void forward(List[]> changes) { @@ -70,14 +77,28 @@ public class VersionedMapDeltaImpl implements VersionedMap { protected void forward(MapDelta[] changes) { for (int i = 0; i < changes.length; i++) { final MapDelta change = changes[i]; - current.put(change.getKey(), change.getNewValue()); + K key = change.getKey(); + V newValue = change.getNewValue(); + + if(newValue == defaultValue) { + current.remove(key); + } else { + current.put(key,newValue); + } } } protected void backward(MapDelta[] changes) { for (int i = changes.length - 1; i >= 0; i--) { final MapDelta change = changes[i]; - current.put(change.getKey(), change.getOldValue()); + K key = change.getKey(); + V oldValue = change.oldValue(); + + if(oldValue == defaultValue) { + current.remove(key); + } else { + current.put(key,oldValue); + } } } @@ -93,26 +114,46 @@ public class VersionedMapDeltaImpl implements VersionedMap { @Override public V put(K key, V value) { - if (value == defaultValue) { - V res = current.remove(key); + final V oldValue; + if (Objects.equals(value, defaultValue)) { + final V res = current.remove(key); if (res == null) { - // no changes - return defaultValue; + // no changes: default > default + oldValue = defaultValue; } else { - uncommittedStore.processChange(key, res, value); - return res; + oldValue = res; } } else { - V oldValue = current.put(key, value); + final var mapValue = current.put(key, value); + if (mapValue == null) { + oldValue = defaultValue; + } else { + oldValue = mapValue; + } + } + if(!Objects.equals(oldValue,value)) { uncommittedStore.processChange(key, oldValue, value); - return oldValue; } + return oldValue; } @Override public void putAll(Cursor cursor) { - throw new UnsupportedOperationException(); - + if (cursor.getDependingMaps().contains(this)) { + List keys = new ArrayList<>(); + List values = new ArrayList<>(); + while (cursor.move()) { + keys.add(cursor.getKey()); + values.add(cursor.getValue()); + } + for (int i = 0; i < keys.size(); i++) { + this.put(keys.get(i), values.get(i)); + } + } else { + while (cursor.move()) { + this.put(cursor.getKey(), cursor.getValue()); + } + } } @Override @@ -156,4 +197,18 @@ public class VersionedMapDeltaImpl implements VersionedMap { throw new UnsupportedOperationException("Comparing different map implementations is ineffective."); } } + + @Override + public void checkIntegrity() { + this.uncommittedStore.checkIntegrity(); + + for (var entry : this.current.entrySet()) { + var value = entry.getValue(); + if (value == this.defaultValue) { + throw new IllegalStateException("Default value stored in map!"); + } else if (value == null) { + throw new IllegalStateException("null value stored in map!"); + } + } + } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java index 674ffc47..fb359431 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java @@ -43,6 +43,7 @@ public class VersionedMapImpl implements VersionedMap { this.root = data; } + @Override public V getDefaultValue() { return defaultValue; } @@ -141,6 +142,7 @@ public class VersionedMapImpl implements VersionedMap { } } + @Override public void checkIntegrity() { if (this.root != null) { this.root.checkIntegrity(hashProvider, defaultValue, 0); -- cgit v1.2.3-70-g09d2