Add memory/CPU optimized BlockVectorSet

Which internally it uses a map of LocalBlockVectorSet
This commit is contained in:
Jesse Boyd 2017-02-06 13:13:18 +11:00
parent 0427771b7e
commit 8fd908232e
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
2 changed files with 226 additions and 21 deletions

View File

@ -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<Vector> implements Set<Vector> {
private Int2ObjectMap<LocalBlockVectorSet> localSets = new Int2ObjectOpenHashMap<>();
@Override
public int size() {
int size = 0;
for (Int2ObjectMap.Entry<LocalBlockVectorSet> entry : localSets.int2ObjectEntrySet()) {
size += entry.getValue().size();
}
return size;
}
@Override
public boolean isEmpty() {
for (Int2ObjectMap.Entry<LocalBlockVectorSet> 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<Vector> iterator() {
final ObjectIterator<Int2ObjectMap.Entry<LocalBlockVectorSet>> entries = localSets.int2ObjectEntrySet().iterator();
if (!entries.hasNext()) {
return new ArrayList<Vector>().iterator();
}
return new Iterator<Vector>() {
Int2ObjectMap.Entry<LocalBlockVectorSet> entry = entries.next();
Iterator<Vector> 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<? extends Vector> 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();
}
}

View File

@ -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<Vector> {
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<Vector> iterator() {
return new Iterator<Vector>() {
@ -105,40 +111,55 @@ public class LocalBlockVectorSet implements Set<Vector> {
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;
}