From e5f3e2e7b0cebce420f7f554d3fa1c8090981e09 Mon Sep 17 00:00:00 2001 From: TomTheFurry Date: Sat, 13 Aug 2022 14:35:44 +0800 Subject: [PATCH] Start working on a new concurrent container... --- .../lod/core/a7/util/LazySectionPosTree.java | 464 ++++++++++++++++++ 1 file changed, 464 insertions(+) create mode 100644 src/main/java/com/seibel/lod/core/a7/util/LazySectionPosTree.java diff --git a/src/main/java/com/seibel/lod/core/a7/util/LazySectionPosTree.java b/src/main/java/com/seibel/lod/core/a7/util/LazySectionPosTree.java new file mode 100644 index 000000000..258ec9a49 --- /dev/null +++ b/src/main/java/com/seibel/lod/core/a7/util/LazySectionPosTree.java @@ -0,0 +1,464 @@ +package com.seibel.lod.core.a7.util; + +import com.seibel.lod.core.a7.pos.DhLodPos; +import com.seibel.lod.core.util.LodUtil; +import org.apache.commons.lang3.NotImplementedException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; + +public class LazySectionPosTree implements ConcurrentMap { + class Node implements Entry { + private Node parent; + private int child0to3; + private final DhLodPos pos; + private final AtomicInteger sizeCounter = size; + private T value = null; + private Node child0 = null; + private Node child1 = null; + private Node child2 = null; + private Node child3 = null; + private Node(Node parent, int child0to3, DhLodPos pos) { + this.parent = parent; + this.child0to3 = child0to3; + this.pos = pos; + } + private Node(Node parent, int child0to3, DhLodPos pos, T value) { + this.parent = parent; + this.child0to3 = child0to3; + this.pos = pos; + this.value = value; + } + @Override + public DhLodPos getKey() { + return pos; + } + @Override + public T getValue() { + return value; + } + @Override + public T setValue(T value) { + T old = this.value; + this.value = value; + if (old == null && value != null) { + sizeCounter.incrementAndGet(); + } else if (old != null && value == null) { + sizeCounter.decrementAndGet(); + } + return old; + } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Node node = (Node) o; + return pos.equals(node.pos); + } + @Override + public int hashCode() { + return pos.hashCode(); + } + private T setIfAbsent(T value) { + T old = this.value; + if (old == null) { + this.value = value; + sizeCounter.incrementAndGet(); + } + return old; + } + private T computeIfAbsent(@NotNull Function mappingFunction) { + if (value == null) { + value = mappingFunction.apply(pos); + sizeCounter.incrementAndGet(); + } + return value; + } + private T computeIfPresent(@NotNull BiFunction remappingFunction) { + if (value != null) { + T newValue = remappingFunction.apply(pos, value); + if (newValue != null) { + value = newValue; + } else { + sizeCounter.decrementAndGet(); + value = null; + } + } + return value; + } + + + private boolean noChildren() { + return child0 == null && child1 == null && child2 == null && child3 == null; + } + + private Node makeOrGetChild(int child0to3) { + LodUtil.assertTrue(child0to3 >= 0 && child0to3 <= 3); + switch (child0to3) { + case 0: + return child0 == null ? child0 = new Node(this, 0, pos.getChild(0)) : child0; + case 1: + return child1 == null ? child1 = new Node(this, 1, pos.getChild(1)) : child1; + case 2: + return child2 == null ? child2 = new Node(this, 2, pos.getChild(2)) : child2; + case 3: + return child3 == null ? child3 = new Node(this, 3, pos.getChild(3)) : child3; + } + LodUtil.assertNotReach(); + return new Node(null, 0, pos.getChild(0)); // unreachable. Just hack to make contract happy. + } + private Node getChild(int child0to3) { + LodUtil.assertTrue(child0to3 >= 0 && child0to3 <= 3); + switch (child0to3) { + case 0: + return child0; + case 1: + return child1; + case 2: + return child2; + case 3: + return child3; + } + LodUtil.assertNotReach(); + return null; + } + private void removeChild(int child0to3) { + LodUtil.assertTrue(child0to3 >= 0 && child0to3 <= 3); + switch (child0to3) { + case 0: + child0 = null; + case 1: + child1 = null; + case 2: + child2 = null; + case 3: + child3 = null; + } + LodUtil.assertNotReach(); + } + } + private final ConcurrentSkipListMap nodes = new ConcurrentSkipListMap<>(); + private byte topLevel = 0; + private AtomicInteger size = new AtomicInteger(0); + public LazySectionPosTree() {} + @Override + public int hashCode() { + throw new NotImplementedException(); + } + @Override + public boolean equals(Object obj) { + throw new NotImplementedException(); + } + @Override + protected Object clone() throws CloneNotSupportedException { + throw new NotImplementedException(); + } + @Override + public String toString() { + throw new NotImplementedException(); + } + + @Override + public int size() { + return size.get(); + } + @Override + public boolean isEmpty() { + return size.get() == 0; + } + + private Node travel(Node from, DhLodPos pos) { + if (from == null) return null; + LodUtil.assertTrue(pos != null); + LodUtil.assertTrue(from.pos.detail > pos.detail); + LodUtil.assertTrue(from.pos.overlaps(pos)); + byte iterDetail = from.pos.detail; + while (iterDetail > pos.detail) { + from = from.getChild(pos.convertUpwardsTo(--iterDetail).getChildIndexOfParent()); + if (from == null) return null; + } + LodUtil.assertTrue(from.pos.equals(pos)); + return from; + } + private Node initTravel(Node from, DhLodPos pos) { + LodUtil.assertTrue(from != null); + LodUtil.assertTrue(pos != null); + LodUtil.assertTrue(from.pos.detail > pos.detail); + LodUtil.assertTrue(from.pos.overlaps(pos)); + byte iterDetail = from.pos.detail; + while (iterDetail > pos.detail) + from = from.makeOrGetChild(pos.convertUpwardsTo(--iterDetail).getChildIndexOfParent()); + LodUtil.assertTrue(from.pos.equals(pos)); + return from; + } + + private void upcastTreeBase(byte level) { + //TODO + } + + + @Override + public boolean containsKey(Object key) { + DhLodPos pos = (DhLodPos) key; + if (pos.detail > topLevel) return false; + if (pos.detail == topLevel) return nodes.containsKey(pos); + Node node = travel(nodes.get(pos.convertUpwardsTo(topLevel)), pos); + return node != null; + } + + @Override + public boolean containsValue(Object value) { + throw new UnsupportedOperationException("Such operation is not supported in LazySectionPosTree"); + } + + @Override + public T get(Object key) { + DhLodPos pos = (DhLodPos) key; + if (pos.detail > topLevel) return null; + if (pos.detail == topLevel) return nodes.get(pos).value; + Node node = travel(nodes.get(pos.convertUpwardsTo(topLevel)), pos); + return node == null ? null : node.value; + } + + @Override + public T getOrDefault(Object key, T defaultValue) { + T value = get(key); + return value == null ? defaultValue : value; + } + + @Nullable + @Override + public T put(DhLodPos key, T value) { + if (key.detail == topLevel) { + return nodes.computeIfAbsent(key, k -> new Node(null, 0, key, value)).setValue(value); + } + if (key.detail < topLevel) { + Node node = initTravel(nodes.get(key.convertUpwardsTo(topLevel)), key); + return node.setValue(value); + } + // key.detail > topLevel: + // Rebase the tree + upcastTreeBase(key.detail); + return nodes.computeIfAbsent(key, k -> new Node(null, 0, key, value)).setValue(value); + } + + private void removeNode(Node node) { + if (node.parent != null) { + node.parent.removeChild(node.child0to3); + if (node.parent.noChildren()) { + removeNode(node.parent); + } + } + else nodes.remove(node.pos); + } + + @Override + public T remove(Object key) { + DhLodPos pos = (DhLodPos) key; + if (pos.detail > topLevel) return null; + Node node; + if (pos.detail == topLevel) { + node = nodes.remove(pos); + } else { + node = travel(nodes.get(pos.convertUpwardsTo(topLevel)), pos); + } + if (node == null) return null; + // Pop the value + T value = node.setValue(null); + // Delete the node if there are no children + if (node.noChildren()) { + removeNode(node); + } + return value; + } + + @Override + public boolean remove(@NotNull Object key, Object value) { + DhLodPos pos = (DhLodPos) key; + if (pos.detail > topLevel) return false; + Node node; + if (pos.detail == topLevel) { + node = nodes.get(pos); + } else { + node = travel(nodes.get(pos.convertUpwardsTo(topLevel)), pos); + } + if (node == null) return false; + //TODO: Make this atomic + if (node.value.equals(value)) { + removeNode(node); + return true; + } + return false; + } + + @Override + public boolean replace(@NotNull DhLodPos key, @NotNull T oldValue, @NotNull T newValue) { + if (key.detail > topLevel) return false; + Node node; + if (key.detail == topLevel) { + node = nodes.get(key); + } else { + node = travel(nodes.get(key.convertUpwardsTo(topLevel)), key); + } + if (node == null) return false; + //TODO: Make this atomic + if (node.value.equals(oldValue)) { + node.setValue(newValue); + return true; + } + return false; + } + + @Override + public T replace(@NotNull DhLodPos key, @NotNull T value) { + if (key.detail == topLevel) { + Node n = nodes.get(key); + //TODO: Make this atomic + if (n == null || n.value==null) return null; + return n.setValue(value); + } + if (key.detail < topLevel) { + Node node = travel(nodes.get(key.convertUpwardsTo(topLevel)), key); + //TODO: Make this atomic + if (node == null || node.value==null) return null; + return node.setValue(value); + } + // key.detail > topLevel: Does not exist + return null; + } + + @Nullable + @Override + public T putIfAbsent(@NotNull DhLodPos key, T value) { + if (key.detail == topLevel) { + return nodes.computeIfAbsent(key, k -> new Node(null, 0, key, null)).setIfAbsent(value); + } + if (key.detail < topLevel) { + Node node = initTravel(nodes.get(key.convertUpwardsTo(topLevel)), key); + return node.setIfAbsent(value); + } + // key.detail > topLevel: + // Rebase the tree + upcastTreeBase(key.detail); + return nodes.computeIfAbsent(key, k -> new Node(null, 0, key, null)).setIfAbsent(value); + + } + + @Override + public T computeIfAbsent(DhLodPos key, @NotNull Function mappingFunction) { + if (key.detail == topLevel) { + return nodes.computeIfAbsent(key, k -> new Node(null, 0, key, null)).computeIfAbsent(mappingFunction); + } + if (key.detail < topLevel) { + Node node = initTravel(nodes.get(key.convertUpwardsTo(topLevel)), key); + return node.computeIfAbsent(mappingFunction); + } + // key.detail > topLevel: + // Rebase the tree + upcastTreeBase(key.detail); + return nodes.computeIfAbsent(key, k -> new Node(null, 0, key, null)).computeIfAbsent(mappingFunction); + + } + + @Override + public T computeIfPresent(DhLodPos key, @NotNull BiFunction remappingFunction) { + if (key.detail == topLevel) { + Node n = nodes.get(key); + if (n == null) return null; + T r = n.computeIfPresent(remappingFunction); + if (r == null && n.noChildren()) { + nodes.remove(key); + } + return r; + } + if (key.detail < topLevel) { + Node node = travel(nodes.get(key.convertUpwardsTo(topLevel)), key); + if (node == null) return null; + T r = node.computeIfPresent(remappingFunction); + if (r == null && node.noChildren()) { + removeNode(node); + } + } + // key.detail > topLevel: Does not exist + return null; + } + + // TODO: Improve this naive implementation of compute + @Override + public T compute(DhLodPos key, @NotNull BiFunction remappingFunction) { + T r = get(key); + if (r == null) { + r = remappingFunction.apply(key, null); + if (r != null) { + put(key, r); + } + } else { + r = remappingFunction.apply(key, r); + if (r != null) { + put(key, r); + } else { + remove(key); + } + } + return r; + } + + // TODO: Optimize putAll + @Override + public void putAll(@NotNull Map m) { + for (Map.Entry entry : m.entrySet()) { + put(entry.getKey(), entry.getValue()); + } + } + + @Override + public void clear() { + nodes.clear(); + size = new AtomicInteger(0); // Do this to swap the counter obj so old nodes won't mess up the counter + } + + @NotNull + @Override + public Set keySet() { + //TODO + throw new NotImplementedException(); + } + + @NotNull + @Override + public Collection values() { + //TODO + throw new NotImplementedException(); + } + + @NotNull + @Override + public Set> entrySet() { + //TODO + throw new NotImplementedException(); + } + + @Override + public void forEach(BiConsumer action) { + //TODO + throw new NotImplementedException(); + } + + @Override + public void replaceAll(BiFunction function) { + //TODO + throw new NotImplementedException(); + } + + // merge: Use default implementation + //public T merge(DhLodPos key, @NotNull T value, @NotNull BiFunction remappingFunction); +}