diff --git a/core/src/main/java/com/boydti/fawe/object/collection/BlockVectorSet.java b/core/src/main/java/com/boydti/fawe/object/collection/BlockVectorSet.java new file mode 100644 index 00000000..057bcbd5 --- /dev/null +++ b/core/src/main/java/com/boydti/fawe/object/collection/BlockVectorSet.java @@ -0,0 +1,184 @@ +package com.boydti.fawe.object.collection; + +import com.boydti.fawe.util.MathMan; +import com.sk89q.worldedit.MutableBlockVector; +import com.sk89q.worldedit.Vector; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.objects.ObjectIterator; +import java.util.AbstractCollection; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Set; + +/** + * The BlockVectorSet is a Memory optimized Set for storing BlockVectors + * - Internally it uses a map of Index->LocalBlockVectorSet + * - All BlockVectors must be a valid world coordinate: y=[0,255],x=[-30000000,30000000],z=[-30000000,30000000] + * - This will use ~8 bytes for every 64 BlockVectors (about 800x less than a HashSet) + */ +public class BlockVectorSet extends AbstractCollection implements Set { + private Int2ObjectMap localSets = new Int2ObjectOpenHashMap<>(); + + @Override + public int size() { + int size = 0; + for (Int2ObjectMap.Entry entry : localSets.int2ObjectEntrySet()) { + size += entry.getValue().size(); + } + return size; + } + + @Override + public boolean isEmpty() { + for (Int2ObjectMap.Entry entry : localSets.int2ObjectEntrySet()) { + if (!entry.getValue().isEmpty()) { + return false; + } + } + return true; + } + + public boolean contains(int x, int y, int z) { + int pair = MathMan.pair((short) (x >> 11), (short) (z >> 11)); + LocalBlockVectorSet localMap = localSets.get(pair); + return localMap.contains(x & 2047, y, z & 2047); + } + + @Override + public boolean contains(Object o) { + if (o instanceof Vector) { + Vector v = (Vector) o; + return contains(v.getBlockX(), v.getBlockY(), v.getBlockZ()); + } + return false; + } + + @Override + public Iterator iterator() { + final ObjectIterator> entries = localSets.int2ObjectEntrySet().iterator(); + if (!entries.hasNext()) { + return new ArrayList().iterator(); + } + return new Iterator() { + Int2ObjectMap.Entry entry = entries.next(); + Iterator entryIter = entry.getValue().iterator(); + MutableBlockVector mutable = new MutableBlockVector(); + + @Override + public void remove() { + entryIter.remove(); + } + + @Override + public boolean hasNext() { + return entryIter.hasNext() || entries.hasNext(); + } + + @Override + public Vector next() { + while (!entryIter.hasNext()) { + if (!entries.hasNext()) { + throw new NoSuchElementException("End of iterator"); + } + entry = entries.next(); + entryIter = entry.getValue().iterator(); + } + Vector localPos = entryIter.next(); + int pair = entry.getIntKey(); + int cx = MathMan.unpairX(pair); + int cz = MathMan.unpairY(pair); + return mutable.setComponents((cx << 11) + localPos.getBlockX(), localPos.getBlockY(), (cz << 11) + localPos.getBlockZ()); + } + }; + } + + @Override + public boolean add(Vector vector) { + return add(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ()); + } + + public boolean add(int x, int y, int z) { + int pair = MathMan.pair((short) (x >> 11), (short) (z >> 11)); + LocalBlockVectorSet localMap = localSets.get(pair); + if (localMap == null) { + localMap = new LocalBlockVectorSet(); + localMap.setOffset(1024, 1024); + localSets.put(pair, localMap); + } + return localMap.add(x & 2047, y, z & 2047); + } + + public boolean remove(int x, int y, int z) { + int pair = MathMan.pair((short) (x >> 11), (short) (z >> 11)); + LocalBlockVectorSet localMap = localSets.get(pair); + if (localMap != null) { + if (localMap.remove(x & 2047, y, z & 2047)) { + if (localMap.isEmpty()) { + localSets.remove(pair); + } + return true; + } + } + return false; + } + + @Override + public boolean remove(Object o) { + if (o instanceof Vector) { + Vector v = (Vector) o; + return remove(v.getBlockX(), v.getBlockY(), v.getBlockZ()); + } + return false; + } + + @Override + public boolean containsAll(Collection c) { + for (Object o : c) { + if (!contains(o)) { + return false; + } + } + return true; + } + + @Override + public boolean addAll(Collection c) { + boolean result = false; + for (Vector v : c) { + result |= add(v); + } + return result; + } + + @Override + public boolean retainAll(Collection c) { + Objects.requireNonNull(c); + boolean modified = false; + Iterator it = iterator(); + while (it.hasNext()) { + if (!c.contains(it.next())) { + it.remove(); + modified = true; + } + } + return modified; + } + + @Override + public boolean removeAll(Collection c) { + boolean result = false; + for (Object o : c) { + result |= remove(o); + } + return result; + } + + @Override + public void clear() { + localSets.clear(); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/boydti/fawe/object/collection/LocalBlockVectorSet.java b/core/src/main/java/com/boydti/fawe/object/collection/LocalBlockVectorSet.java index e60d4b78..c4021171 100644 --- a/core/src/main/java/com/boydti/fawe/object/collection/LocalBlockVectorSet.java +++ b/core/src/main/java/com/boydti/fawe/object/collection/LocalBlockVectorSet.java @@ -9,7 +9,7 @@ import java.util.Iterator; import java.util.Set; /** - * The LocalBlockVectorSet is a Memory and CPU optimized set for storing BlockVectors which are all in a local region + * The LocalBlockVectorSet is a Memory and CPU optimized Set for storing BlockVectors which are all in a local region * - All vectors must be in a 2048 * 2048 area centered around the first entry * - This will use 8 bytes for every 64 BlockVectors (about 800x less than a HashSet) */ @@ -33,18 +33,24 @@ public class LocalBlockVectorSet implements Set { return set.isEmpty(); } + public boolean contains(int x, int y, int z) { + return set.get(MathMan.tripleSearchCoords(x - offsetX, y, z - offsetZ)); + } + @Override public boolean contains(Object o) { if (o instanceof Vector) { Vector v = (Vector) o; - int x = v.getBlockX(); - int y = v.getBlockY(); - int z = v.getBlockZ(); - return set.get(MathMan.tripleSearchCoords(x - offsetX, y, z - offsetZ)); + return contains(v.getBlockX(), v.getBlockY(), v.getBlockZ()); } return false; } + public void setOffset(int x, int z) { + this.offsetX = x; + this.offsetZ = z; + } + @Override public Iterator iterator() { return new Iterator() { @@ -105,40 +111,55 @@ public class LocalBlockVectorSet implements Set { return array; } - @Override - public boolean add(Vector vector) { + public boolean add(int x, int y, int z) { if (offsetX == Integer.MAX_VALUE) { - offsetX = vector.getBlockX(); - offsetZ = vector.getBlockZ(); + offsetX = x; + offsetZ = z; } - int relX = vector.getBlockX() - offsetX; - int relZ = vector.getBlockZ() - offsetZ; + int relX = x - offsetX; + int relZ = z - offsetZ; if (relX > 1023 || relX < -1024 || relZ > 1023 || relZ < -1024) { throw new UnsupportedOperationException("LocalVectorSet can only contain vectors within 1024 blocks (cuboid) of the first entry. "); } - int index = getIndex(vector); + if (y < 0 || y > 256) { + throw new UnsupportedOperationException("LocalVectorSet can only contain vectors from y elem:[0,255]"); + } + int index = getIndex(x, y, z); boolean value = set.get(index); set.set(index); return !value; } + @Override + public boolean add(Vector vector) { + return add(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ()); + } + private int getIndex(Vector vector) { return MathMan.tripleSearchCoords(vector.getBlockX() - offsetX, vector.getBlockY(), vector.getBlockZ() - offsetZ); } + private int getIndex(int x, int y, int z) { + return MathMan.tripleSearchCoords(x - offsetX, y, z - offsetZ); + } + + public boolean remove(int x, int y, int z) { + int relX = x - offsetX; + int relZ = z - offsetZ; + if (relX > 1023 || relX < -1024 || relZ > 1023 || relZ < -1024) { + return false; + } + int index = MathMan.tripleSearchCoords(relX, y, relZ); + boolean value = set.get(index); + set.clear(index); + return value; + } + @Override public boolean remove(Object o) { if (o instanceof Vector) { Vector v = (Vector) o; - int relX = v.getBlockX() - offsetX; - int relZ = v.getBlockZ() - offsetZ; - if (relX > 1023 || relX < -1024 || relZ > 1023 || relZ < -1024) { - return false; - } - int index = MathMan.tripleSearchCoords(relX, v.getBlockY(), relZ); - boolean value = set.get(index); - set.clear(index); - return value; + return remove(v.getBlockX(), v.getBlockY(), v.getBlockZ()); } return false; }