From 7384b6973498084f4082d7e1a805b84b13a3ba44 Mon Sep 17 00:00:00 2001 From: Octavia Togami Date: Thu, 30 Jan 2020 01:05:31 -0800 Subject: [PATCH] Generify BlockMap --- .../extent/reorder/ChunkBatchingExtent.java | 2 +- .../extent/reorder/MultiStageReorder.java | 6 +- .../function/operation/SetBlockMap.java | 4 +- .../worldedit/util/collection/BlockMap.java | 129 ++++++++++-------- ...SubBlockMap.java => Int2BaseBlockMap.java} | 12 +- .../util/collection/LocatedBlockList.java | 2 +- .../util/collection/BlockMapTest.java | 2 +- 7 files changed, 86 insertions(+), 71 deletions(-) rename worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/{SubBlockMap.java => Int2BaseBlockMap.java} (93%) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java index 029c78953f..f7fe5c363b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/ChunkBatchingExtent.java @@ -43,7 +43,7 @@ */ public class ChunkBatchingExtent extends AbstractBufferingExtent { - private final BlockMap blockMap = BlockMap.create(); + private final BlockMap blockMap = BlockMap.createForBaseBlock(); private boolean enabled; public ChunkBatchingExtent(Extent extent) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java index 733d6c7de5..c55cb694da 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/extent/reorder/MultiStageReorder.java @@ -142,7 +142,7 @@ public class MultiStageReorder extends AbstractBufferingExtent implements Reorde priorityMap.put(BlockTypes.MOVING_PISTON, PlacementPriority.FINAL); } - private Map stages = new HashMap<>(); + private Map> stages = new HashMap<>(); private boolean enabled; @@ -176,7 +176,7 @@ public MultiStageReorder(Extent extent, boolean enabled) { this.enabled = enabled; for (PlacementPriority priority : PlacementPriority.values()) { - stages.put(priority, BlockMap.create()); + stages.put(priority, BlockMap.createForBaseBlock()); } } @@ -261,7 +261,7 @@ public Operation commitBefore() { } List operations = new ArrayList<>(); for (PlacementPriority priority : PlacementPriority.values()) { - BlockMap blocks = stages.get(priority); + BlockMap blocks = stages.get(priority); operations.add(new SetBlockMap(getExtent(), blocks) { @Override public Operation resume(RunContext run) throws WorldEditException { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/SetBlockMap.java b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/SetBlockMap.java index 863aadd033..99607ae165 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/SetBlockMap.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/function/operation/SetBlockMap.java @@ -34,9 +34,9 @@ public class SetBlockMap implements Operation { private final Extent extent; - private final BlockMap blocks; + private final BlockMap blocks; - public SetBlockMap(Extent extent, BlockMap blocks) { + public SetBlockMap(Extent extent, BlockMap blocks) { this.extent = checkNotNull(extent); this.blocks = checkNotNull(blocks); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/BlockMap.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/BlockMap.java index 2620a906e8..563c13de62 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/BlockMap.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/BlockMap.java @@ -24,6 +24,7 @@ import com.sk89q.worldedit.world.block.BaseBlock; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; @@ -40,6 +41,7 @@ import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; +import java.util.function.Supplier; import static com.sk89q.worldedit.math.BitMath.fixSign; import static com.sk89q.worldedit.math.BitMath.mask; @@ -47,7 +49,7 @@ /** * A space-efficient map implementation for block locations. */ -public class BlockMap extends AbstractMap { +public class BlockMap extends AbstractMap { /* ========================= IF YOU MAKE CHANGES TO THIS CLASS @@ -55,12 +57,20 @@ public class BlockMap extends AbstractMap { Or just temporarily remove the annotation disabling the related tests. ========================= */ - public static BlockMap create() { - return new BlockMap(); + public static BlockMap create() { + return create(() -> new Int2ObjectOpenHashMap<>(64, 1f)); } - public static BlockMap copyOf(Map source) { - return new BlockMap(source); + public static BlockMap createForBaseBlock() { + return create(Int2BaseBlockMap::new); + } + + private static BlockMap create(Supplier> subMapSupplier) { + return new BlockMap<>(subMapSupplier); + } + + public static BlockMap copyOf(Map source) { + return new BlockMap<>(Int2ObjectOpenHashMap::new, source); } /* @@ -108,31 +118,34 @@ private static BlockVector3 reconstructLocation(long group, int inner) { return BlockVector3.at(x, y, z); } - private final Long2ObjectMap maps = new Long2ObjectOpenHashMap<>(4, 1f); - private Set> entrySet; - private Collection values; + private final Long2ObjectMap> maps = new Long2ObjectOpenHashMap<>(4, 1f); + private final Supplier> subMapSupplier; + private Set> entrySet; + private Collection values; - private BlockMap() { + private BlockMap(Supplier> subMapSupplier) { + this.subMapSupplier = subMapSupplier; } - private BlockMap(Map source) { + private BlockMap(Supplier> subMapSupplier, Map source) { + this.subMapSupplier = subMapSupplier; putAll(source); } - private SubBlockMap getOrCreateMap(long groupKey) { - return maps.computeIfAbsent(groupKey, k -> new SubBlockMap()); + private Int2ObjectMap getOrCreateMap(long groupKey) { + return maps.computeIfAbsent(groupKey, k -> subMapSupplier.get()); } - private SubBlockMap getOrEmptyMap(long groupKey) { - return maps.getOrDefault(groupKey, SubBlockMap.EMPTY); + private Int2ObjectMap getOrEmptyMap(long groupKey) { + return maps.getOrDefault(groupKey, Int2ObjectMaps.emptyMap()); } /** * Apply the function the the map at {@code groupKey}, and if the function empties the map, * delete it from {@code maps}. */ - private R cleanlyModifyMap(long groupKey, Function, R> func) { - SubBlockMap map = maps.get(groupKey); + private R cleanlyModifyMap(long groupKey, Function, R> func) { + Int2ObjectMap map = maps.get(groupKey); if (map != null) { R result = func.apply(map); if (map.isEmpty()) { @@ -140,7 +153,7 @@ private R cleanlyModifyMap(long groupKey, Function, } return result; } - map = new SubBlockMap(); + map = subMapSupplier.get(); R result = func.apply(map); if (!map.isEmpty()) { maps.put(groupKey, map); @@ -149,19 +162,19 @@ private R cleanlyModifyMap(long groupKey, Function, } @Override - public BaseBlock put(BlockVector3 key, BaseBlock value) { + public V put(BlockVector3 key, V value) { return getOrCreateMap(toGroupKey(key)).put(toInnerKey(key), value); } @Override - public BaseBlock getOrDefault(Object key, BaseBlock defaultValue) { + public V getOrDefault(Object key, V defaultValue) { BlockVector3 vec = (BlockVector3) key; return getOrEmptyMap(toGroupKey(vec)) .getOrDefault(toInnerKey(vec), defaultValue); } @Override - public void forEach(BiConsumer action) { + public void forEach(BiConsumer action) { maps.forEach((groupKey, m) -> m.forEach((innerKey, block) -> action.accept(reconstructLocation(groupKey, innerKey), block) @@ -170,7 +183,7 @@ public void forEach(BiConsumer action) } @Override - public void replaceAll(BiFunction function) { + public void replaceAll(BiFunction function) { maps.forEach((groupKey, m) -> m.replaceAll((innerKey, block) -> function.apply(reconstructLocation(groupKey, innerKey), block) @@ -179,7 +192,7 @@ public void replaceAll(BiFunction map.replace(toInnerKey(key), oldValue, newValue)); } @Override - public BaseBlock replace(BlockVector3 key, BaseBlock value) { + public V replace(BlockVector3 key, V value) { return getOrCreateMap(toGroupKey(key)).replace(toInnerKey(key), value); } @Override - public BaseBlock computeIfAbsent(BlockVector3 key, Function mappingFunction) { + public V computeIfAbsent(BlockVector3 key, Function mappingFunction) { return cleanlyModifyMap(toGroupKey(key), map -> map.computeIfAbsent(toInnerKey(key), ik -> mappingFunction.apply(key))); } @Override - public BaseBlock computeIfPresent(BlockVector3 key, BiFunction remappingFunction) { + public V computeIfPresent(BlockVector3 key, BiFunction remappingFunction) { return cleanlyModifyMap(toGroupKey(key), map -> map.computeIfPresent(toInnerKey(key), (ik, block) -> remappingFunction.apply(key, block))); } @Override - public BaseBlock compute(BlockVector3 key, BiFunction remappingFunction) { + public V compute(BlockVector3 key, BiFunction remappingFunction) { return cleanlyModifyMap(toGroupKey(key), map -> map.compute(toInnerKey(key), (ik, block) -> remappingFunction.apply(key, block))); } @Override - public BaseBlock merge(BlockVector3 key, BaseBlock value, BiFunction remappingFunction) { + public V merge(BlockVector3 key, V value, BiFunction remappingFunction) { return cleanlyModifyMap(toGroupKey(key), map -> map.merge(toInnerKey(key), value, remappingFunction)); } @Override - public Set> entrySet() { - Set> es = entrySet; + public Set> entrySet() { + Set> es = entrySet; if (es == null) { - entrySet = es = new AbstractSet>() { + entrySet = es = new AbstractSet>() { @Override - public Iterator> iterator() { - return new AbstractIterator>() { + public Iterator> iterator() { + return new AbstractIterator>() { - private final ObjectIterator> primaryIterator + private final ObjectIterator>> primaryIterator = Long2ObjectMaps.fastIterator(maps); private long currentGroupKey; - private ObjectIterator> secondaryIterator; + private ObjectIterator> secondaryIterator; @Override - protected Entry computeNext() { + protected Entry computeNext() { if (secondaryIterator == null || !secondaryIterator.hasNext()) { if (!primaryIterator.hasNext()) { return endOfData(); } - Long2ObjectMap.Entry next = primaryIterator.next(); + Long2ObjectMap.Entry> next = primaryIterator.next(); currentGroupKey = next.getLongKey(); secondaryIterator = Int2ObjectMaps.fastIterator(next.getValue()); } - Int2ObjectMap.Entry next = secondaryIterator.next(); + Int2ObjectMap.Entry next = secondaryIterator.next(); return new LazyEntry(currentGroupKey, next.getIntKey(), next.getValue()); } }; @@ -265,14 +278,14 @@ public int size() { return es; } - private final class LazyEntry implements Map.Entry { + private final class LazyEntry implements Entry { private final long groupKey; private final int innerKey; private BlockVector3 lazyKey; - private BaseBlock value; + private V value; - private LazyEntry(long groupKey, int innerKey, BaseBlock value) { + private LazyEntry(long groupKey, int innerKey, V value) { this.groupKey = groupKey; this.innerKey = innerKey; this.value = value; @@ -288,12 +301,12 @@ public BlockVector3 getKey() { } @Override - public BaseBlock getValue() { + public V getValue() { return value; } @Override - public BaseBlock setValue(BaseBlock value) { + public V setValue(V value) { this.value = value; return getOrCreateMap(groupKey).put(innerKey, value); } @@ -301,8 +314,9 @@ public BaseBlock setValue(BaseBlock value) { public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; - Map.Entry e = (Map.Entry) o; - if (o instanceof LazyEntry) { + Entry e = (Entry) o; + if (o instanceof BlockMap.LazyEntry) { + @SuppressWarnings("unchecked") LazyEntry otherE = (LazyEntry) o; return otherE.groupKey == groupKey && otherE.innerKey == innerKey @@ -330,7 +344,7 @@ public boolean containsValue(Object value) { @Override public boolean containsKey(Object key) { BlockVector3 vec = (BlockVector3) key; - Map activeMap = maps.get(toGroupKey(vec)); + Int2ObjectMap activeMap = maps.get(toGroupKey(vec)); if (activeMap == null) { return false; } @@ -338,9 +352,9 @@ public boolean containsKey(Object key) { } @Override - public BaseBlock get(Object key) { + public V get(Object key) { BlockVector3 vec = (BlockVector3) key; - Map activeMap = maps.get(toGroupKey(vec)); + Int2ObjectMap activeMap = maps.get(toGroupKey(vec)); if (activeMap == null) { return null; } @@ -348,24 +362,25 @@ public BaseBlock get(Object key) { } @Override - public BaseBlock remove(Object key) { + public V remove(Object key) { BlockVector3 vec = (BlockVector3) key; - Map activeMap = maps.get(toGroupKey(vec)); + Int2ObjectMap activeMap = maps.get(toGroupKey(vec)); if (activeMap == null) { return null; } - BaseBlock removed = activeMap.remove(toInnerKey(vec)); + V removed = activeMap.remove(toInnerKey(vec)); if (activeMap.isEmpty()) { maps.remove(toGroupKey(vec)); } return removed; } + @SuppressWarnings("unchecked") @Override - public void putAll(Map m) { + public void putAll(Map m) { if (m instanceof BlockMap) { // optimize insertions: - ((BlockMap) m).maps.forEach((groupKey, map) -> + ((BlockMap) m).maps.forEach((groupKey, map) -> getOrCreateMap(groupKey).putAll(map) ); } else { @@ -387,12 +402,12 @@ public int size() { // we can optimize values access though, by skipping BV construction. @Override - public Collection values() { - Collection vs = values; + public Collection values() { + Collection vs = values; if (vs == null) { - values = vs = new AbstractCollection() { + values = vs = new AbstractCollection() { @Override - public Iterator iterator() { + public Iterator iterator() { return maps.values().stream() .flatMap(m -> m.values().stream()) .iterator(); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/SubBlockMap.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/Int2BaseBlockMap.java similarity index 93% rename from worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/SubBlockMap.java rename to worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/Int2BaseBlockMap.java index 440f52eeed..36d459c12c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/SubBlockMap.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/Int2BaseBlockMap.java @@ -39,7 +39,7 @@ /** * Int-to-BaseBlock map, but with optimizations for common cases. */ -class SubBlockMap extends AbstractInt2ObjectMap { +class Int2BaseBlockMap extends AbstractInt2ObjectMap { private static boolean hasInt(BlockState b) { return BlockStateIdAccess.getBlockStateId(b).isPresent(); @@ -65,7 +65,7 @@ private static BaseBlock assumeAsBlock(int id) { return state.toBaseBlock(); } - static final SubBlockMap EMPTY = new SubBlockMap(); + static final Int2BaseBlockMap EMPTY = new Int2BaseBlockMap(); private final Int2IntMap commonMap = new Int2IntOpenHashMap(64, 1f); private final Int2ObjectMap uncommonMap = new Int2ObjectOpenHashMap<>(1, 1f); @@ -88,7 +88,7 @@ public ObjectIterator> iterator() { private final ObjectIterator commonIter = Int2IntMaps.fastIterator(commonMap); - private final ObjectIterator> uncommonIter + private final ObjectIterator> uncommonIter = Int2ObjectMaps.fastIterator(uncommonMap); @Override @@ -114,7 +114,7 @@ public Entry next() { @Override public int size() { - return SubBlockMap.this.size(); + return Int2BaseBlockMap.this.size(); } }; } @@ -179,9 +179,9 @@ public void replaceAll(BiFunction> iter = Int2ObjectMaps.fastIterator(uncommonMap); + for (ObjectIterator> iter = Int2ObjectMaps.fastIterator(uncommonMap); iter.hasNext(); ) { - Int2ObjectMap.Entry next = iter.next(); + Entry next = iter.next(); BaseBlock value = function.apply(next.getIntKey(), next.getValue()); if (isUncommon(value)) { next.setValue(value); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/LocatedBlockList.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/LocatedBlockList.java index e8d11d2e2b..55c7f1a216 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/LocatedBlockList.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/collection/LocatedBlockList.java @@ -37,7 +37,7 @@ */ public class LocatedBlockList implements Iterable { - private final BlockMap blocks = BlockMap.create(); + private final BlockMap blocks = BlockMap.createForBaseBlock(); private final PositionList order = PositionList.create( WorldEdit.getInstance().getConfiguration().extendedYLimit ); diff --git a/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java index 64b18c84eb..a54eb02b67 100644 --- a/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java +++ b/worldedit-core/src/test/java/com/sk89q/worldedit/util/collection/BlockMapTest.java @@ -110,7 +110,7 @@ private static void registerBlock(String id) { private final BaseBlock air = checkNotNull(BlockTypes.AIR).getDefaultState().toBaseBlock(); private final BaseBlock oakWood = checkNotNull(BlockTypes.OAK_WOOD).getDefaultState().toBaseBlock(); - private BlockMap map = BlockMap.create(); + private BlockMap map = BlockMap.createForBaseBlock(); @BeforeEach void setUp() {