From 9efcf01766ed52821e321ad061d34a7b4cd9e952 Mon Sep 17 00:00:00 2001 From: asofold Date: Mon, 28 Sep 2015 12:54:00 +0200 Subject: [PATCH] [BREAKING] Move maps into a maps package. Prepare a linked CoordMap. Splits CoordMap into interface, abstract hash map, implementations. Sketch Linked version, hinting at access order, e.g. with tracking piston effects with timeouts. Missing: * Implementation of a linked version. --- .../nocheatplus/utilities/ds/CoordMap.java | 320 ------------------ .../nocheatplus/utilities/ds/ManagedMap.java | 90 ----- .../ds/map/AbstractCoordHashMap.java | 221 ++++++++++++ .../utilities/ds/map/CoordHashMap.java | 147 ++++++++ .../utilities/ds/map/CoordMap.java | 89 +++++ .../utilities/ds/map/LinkedCoordHashMap.java | 60 ++++ .../utilities/ds/map/ManagedMap.java | 92 +++++ .../nocheatplus/test/TestCoordMap.java | 17 +- .../analysis/engine/EnginePlayerDataMap.java | 2 +- .../nocheatplus/utilities/BlockCache.java | 9 +- .../nocheatplus/utilities/FakeBlockCache.java | 11 +- 11 files changed, 630 insertions(+), 428 deletions(-) delete mode 100644 NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/CoordMap.java delete mode 100644 NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/ManagedMap.java create mode 100644 NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/AbstractCoordHashMap.java create mode 100644 NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/CoordHashMap.java create mode 100644 NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/CoordMap.java create mode 100644 NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/LinkedCoordHashMap.java create mode 100644 NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/ManagedMap.java diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/CoordMap.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/CoordMap.java deleted file mode 100644 index f44ff24b..00000000 --- a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/CoordMap.java +++ /dev/null @@ -1,320 +0,0 @@ -package fr.neatmonster.nocheatplus.utilities.ds; - -import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.NoSuchElementException; - -/** - * Map int coordinates to values (just for fun). Intended for Minecraft - * coordinates, probably not for too high values.
- * This implementation is not thread safe, though changing values and - * get/contains should work if the map stays unchanged. - * - * @author mc_dev - * - * @param - */ -public class CoordMap { - - private static final int p1 = 73856093; - private static final int p2 = 19349663; - private static final int p3 = 83492791; - - private static final int getHash(final int x, final int y, final int z) { - return p1 * x ^ p2 * y ^ p3 * z; - } - - public static final class Entry { - protected final int x; - protected final int y; - protected final int z; - protected V value; - protected final int hash; - - public Entry(final int x, final int y, final int z, final V value, final int hash) { - this.x = x; - this.y = y; - this.z = z; - this.value = value; - this.hash = hash; - } - - public final int getX(){ - return x; - } - - public final int getY(){ - return y; - } - - public final int getZ(){ - return z; - } - - public final V getValue(){ - return value; - } - } - - /** - * Does NOT throw anything, not the least thread safe. - * - * @author mc_dev - * - * @param - */ - public static final class CoordMapIterator implements Iterator> { - private final CoordMap map; - private final List>[] entries; - - /** Current search position. */ - private int slot = 0; - /** Current search position. */ - private int index = 0; - - /** Last found position. */ - private int slotLast = -1; - /** Last found position. */ - private int indexLast = -1; - - protected CoordMapIterator(final CoordMap map) { - this.map = map; - entries = map.entries; - } - - @Override - public final boolean hasNext() { - // Also set index and slot to next found element. - while (slot < entries.length) { - final List> bucket = entries[slot]; - if (bucket == null){ - slot ++; - index = 0; - } - else { - if (index < bucket.size()) return true; - else { - // Not found, reset. - slot ++; - index = 0; - } - } - } - return false; - } - - @Override - public final Entry next() { - while (slot < entries.length) { - final List> bucket = entries[slot]; - if (bucket == null){ - slot ++; - index = 0; - } - else { - final int size = bucket.size(); - if (index < size) { - final Entry res = bucket.get(index); - slotLast = slot; - indexLast = index; - index++; - if (index == size) { - index = 0; - slot++; - } - return res; - } - else{ - // TODO: inconsistent, could be empty though. - slot++; - index = 0; - } - } - } - throw new NoSuchElementException(); - } - - @Override - public final void remove() { - if (slotLast == -1){ - // Next had not been called at all, - // or remove has been called several times. - return; - } - final List> bucket = entries[slotLast]; - bucket.remove(indexLast); - if (bucket.isEmpty()) entries[slotLast] = null; - else if (slotLast == slot) index --; - map.size--; - slotLast = indexLast = -1; - } - } - - // Core data. - private final float loadFactor; - private List>[] entries; - /** Current size. */ - private int size = 0; - - public CoordMap() { - this(10, 0.75f); - } - - public CoordMap(final int initialCapacity) { - this(initialCapacity, 0.75f); - } - - /** - * - * @param initialCapacity - * Initial internal array size.
- * TODO: change to expected number of elements (len = cap/load). - * @param loadFactor - */ - @SuppressWarnings("unchecked") - public CoordMap(final int initialCapacity, float loadFactor) { - this.loadFactor = loadFactor; - entries = new List[initialCapacity]; - } - - /** - * Check if the map contains a value for the given coordinates.
- * NOTE: Delegates to get, use get for fastest checks. - * - * @param x - * @param y - * @param z - * @return - */ - public final boolean contains(final int x, final int y, final int z) { - return get(x, y, z) != null; - } - - /** - * Get the value if there is a mapping for the given coordinates. - * - * @param x - * @param y - * @param z - * @return - */ - public final V get(final int x, final int y, final int z) { - final int hash = getHash(x, y, z); - final int slot = Math.abs(hash) % entries.length; - final List> bucket = entries[slot]; - if (bucket == null) return null; - ; - for (final Entry entry : bucket) { - if (hash == entry.hash && x == entry.x && z == entry.z && y == entry.y) return entry.value; - } - return null; - } - - /** - * Add value with the coordinates + hash from the last contains call. - * - * @param value - * @return If a value was replaced. - */ - public final boolean put(final int x, final int y, final int z, final V value) - { - final int hash = getHash(x, y, z); - final int absHash = Math.abs(hash); - int slot = absHash % entries.length; - List> bucket = entries[slot]; - if (bucket != null) { - for (final Entry entry : bucket) { - if (hash == entry.hash && x == entry.x && z == entry.z && y == entry.y) { - entry.value = value; - return true; - } - } - } else if (size + 1 > entries.length * loadFactor) { - resize(size + 1); - slot = absHash % entries.length; - bucket = entries[slot]; - } - if (bucket == null) { - // TODO: use array list ? - bucket = new LinkedList>(); - entries[slot] = bucket; - } - bucket.add(new Entry(x, y, z, value, hash)); - size++; - return false; - } - - /** - * Remove an entry. - * - * @param x - * @param y - * @param z - * @return - */ - public final V remove(final int x, final int y, final int z) { - final int hash = getHash(x, y, z); - final int absHash = Math.abs(hash); - int slot = absHash % entries.length; - final List> bucket = entries[slot]; - if (bucket == null) return null; - else { - for (int i = 0; i < bucket.size(); i++) { - final Entry entry = bucket.get(i); - if (entry.hash == hash && x == entry.x && z == entry.z && y == entry.y) { - bucket.remove(entry); - if (bucket.isEmpty()) entries[slot] = null; - size--; - return entry.value; - } - } - return null; - } - } - - private final void resize(final int size) { - // TODO: other capacity / allow to set strategy [also for reducing for long time use] - final int newCapacity = Math.min(Math.max((int) ((size + 4) / loadFactor), entries.length + entries.length / 4), 4); - @SuppressWarnings("unchecked") - final List>[] newEntries = new List[newCapacity]; - int used = -1; // Fill old buckets to fornt of old array. - for (int oldSlot = 0; oldSlot < entries.length; oldSlot++) { - final List> oldBucket = entries[oldSlot]; - if (oldBucket == null) continue; - for (final Entry entry : oldBucket) { - final int newSlot = Math.abs(entry.hash) % newCapacity; - List> newBucket = newEntries[newSlot]; - if (newBucket == null) { - if (used < 0) newBucket = new LinkedList>(); - else{ - newBucket = entries[used]; - entries[used] = null; - used--; - } - newEntries[newSlot] = newBucket; - } - newBucket.add(entry); - } - oldBucket.clear(); - entries[oldSlot] = null; - entries[++used] = oldBucket; - } - entries = newEntries; - } - - public final int size() { - return size; - } - - public void clear() { - size = 0; - Arrays.fill(entries, null); - // TODO: resize ? - } - - public final Iterator> iterator(){ - return new CoordMapIterator(this); - } -} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/ManagedMap.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/ManagedMap.java deleted file mode 100644 index d4d7f2cc..00000000 --- a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/ManagedMap.java +++ /dev/null @@ -1,90 +0,0 @@ -package fr.neatmonster.nocheatplus.utilities.ds; - -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map.Entry; - -/** - * Map that provides time stamp based expiration of entries. Time stamps get set with each access. - * @author mc_dev - * - * @param - * @param - */ -public class ManagedMap{ - - protected class ValueWrap{ - public long ts; - public V value; - public ValueWrap(final V value){ - ts = System.currentTimeMillis(); - this.value = value; - } - } - - protected final LinkedHashMap map; - - public ManagedMap(int defaultCapacity, float loadFactor){ - map = new LinkedHashMap(defaultCapacity, loadFactor, true); - } - - /** - * - * @param key - * @param value Previously contained value or null. - * @return - */ - public V put(final K key, final V value){ - final ValueWrap wrap = map.get(key); - if (wrap == null){ - map.put(key, new ValueWrap(value)); - return null; - } - else{ - final V res = wrap.value; - wrap.value = value; - wrap.ts = System.currentTimeMillis(); - return res; - } - } - - public V get(final K key){ - final ValueWrap wrap = map.get(key); - if (wrap == null) return null; - else{ - wrap.ts = System.currentTimeMillis(); - return wrap.value; - } - } - - public V remove(final K key){ - final ValueWrap wrap = map.remove(key); - if (wrap == null) return null; - else return wrap.value; - } - - public void clear(){ - map.clear(); - } - - /** - * Remove entries that are older than ts. - * @param ts - * @return - */ - public Collection expire(final long ts){ - final List rem = new LinkedList(); - for (final Entry entry : map.entrySet()){ - final ValueWrap wrap = entry.getValue(); - if (wrap.ts < ts) rem.add(entry.getKey()); - else break; - } - for (final K key : rem){ - map.remove(key); - } - return rem; - } - -} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/AbstractCoordHashMap.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/AbstractCoordHashMap.java new file mode 100644 index 00000000..f9ca00ba --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/AbstractCoordHashMap.java @@ -0,0 +1,221 @@ +package fr.neatmonster.nocheatplus.utilities.ds.map; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * Intended for Minecraft coordinates, probably not for too high values.
+ * This implementation is not thread safe, though changing values and + * get/contains should work if the map stays unchanged. + * + *
+ * Abstract base implementation for a hash map version. + * + * @author asofold + * + */ +public abstract class AbstractCoordHashMap> implements CoordMap { + + private static final int p1 = 73856093; + private static final int p2 = 19349663; + private static final int p3 = 83492791; + + protected static final int getHash(final int x, final int y, final int z) { + return p1 * x ^ p2 * y ^ p3 * z; + } + + public static class HashEntry implements Entry{ + protected final int x; + protected final int y; + protected final int z; + protected V value; + protected final int hash; + + public HashEntry(final int x, final int y, final int z, final V value, final int hash) { + this.x = x; + this.y = y; + this.z = z; + this.value = value; + this.hash = hash; + } + @Override + public final int getX(){ + return x; + } + @Override + public final int getY(){ + return y; + } + @Override + public final int getZ(){ + return z; + } + @Override + public final V getValue(){ + return value; + } + } + + // Core data. + private final float loadFactor; + protected List[] entries; + /** Current size. */ + protected int size = 0; + + public AbstractCoordHashMap() { + this(10, 0.75f); + } + + public AbstractCoordHashMap(final int initialCapacity) { + this(initialCapacity, 0.75f); + } + + /** + * + * @param initialCapacity + * Initial internal array size.
+ * TODO: change to expected number of elements (len = cap/load). + * @param loadFactor + */ + @SuppressWarnings("unchecked") + public AbstractCoordHashMap(final int initialCapacity, float loadFactor) { + this.loadFactor = loadFactor; + entries = new List[initialCapacity]; + } + + @Override + public boolean contains(final int x, final int y, final int z) { + return get(x, y, z) != null; + } + + @Override + public V get(final int x, final int y, final int z) { + final int hash = getHash(x, y, z); + final int slot = Math.abs(hash) % entries.length; + final List bucket = entries[slot]; + if (bucket == null) { + return null; + } + for (final E entry : bucket) { + if (hash == entry.hash && x == entry.x && z == entry.z && y == entry.y) { + return entry.value; + } + } + return null; + } + + @Override + public boolean put(final int x, final int y, final int z, final V value) { + final int hash = getHash(x, y, z); + final int absHash = Math.abs(hash); + int slot = absHash % entries.length; + List bucket = entries[slot]; + if (bucket != null) { + for (final E entry : bucket) { + if (hash == entry.hash && x == entry.x && z == entry.z && y == entry.y) { + entry.value = value; + return true; + } + } + } else if (size + 1 > entries.length * loadFactor) { + resize(size + 1); + slot = absHash % entries.length; + bucket = entries[slot]; + } + if (bucket == null) { + // TODO: use array list ? + bucket = new LinkedList(); + entries[slot] = bucket; + } + bucket.add(newEntry(x, y, z, value, hash)); + size++; + return false; + } + + @Override + public V remove(final int x, final int y, final int z) { + final int hash = getHash(x, y, z); + final int absHash = Math.abs(hash); + int slot = absHash % entries.length; + final List bucket = entries[slot]; + if (bucket == null) { + return null; + } + else { + for (int i = 0; i < bucket.size(); i++) { + final E entry = bucket.get(i); + if (entry.hash == hash && x == entry.x && z == entry.z && y == entry.y) { + bucket.remove(entry); + if (bucket.isEmpty()) { + entries[slot] = null; + } + size--; + return entry.value; + } + } + return null; + } + } + + private void resize(final int size) { + // TODO: other capacity / allow to set strategy [also for reducing for long time use] + final int newCapacity = Math.min(Math.max((int) ((size + 4) / loadFactor), entries.length + entries.length / 4), 4); + @SuppressWarnings("unchecked") + final List[] newEntries = new List[newCapacity]; + int used = -1; // Fill old buckets to front of old array. + for (int oldSlot = 0; oldSlot < entries.length; oldSlot++) { + final List oldBucket = entries[oldSlot]; + if (oldBucket == null) { + continue; + } + for (final E entry : oldBucket) { + final int newSlot = Math.abs(entry.hash) % newCapacity; + List newBucket = newEntries[newSlot]; + if (newBucket == null) { + if (used < 0) { + newBucket = new LinkedList(); + } + else{ + newBucket = entries[used]; + entries[used] = null; + used--; + } + newEntries[newSlot] = newBucket; + } + newBucket.add(entry); + } + oldBucket.clear(); + entries[oldSlot] = null; + entries[++used] = oldBucket; + } + entries = newEntries; + } + + @Override + public int size() { + return size; + } + + @Override + public void clear() { + size = 0; + Arrays.fill(entries, null); + // TODO: resize ? + } + + /** + * Get a new entry. This method can have side effects (linked structures + * etc.), it exists solely for the purpose of adding new entries within + * put(...). + * + * @param x + * @param y + * @param z + * @param value + * @param hash + * @return + */ + protected abstract E newEntry(int x, int y, int z, V value, int hash); + +} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/CoordHashMap.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/CoordHashMap.java new file mode 100644 index 00000000..d3e0201c --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/CoordHashMap.java @@ -0,0 +1,147 @@ +package fr.neatmonster.nocheatplus.utilities.ds.map; + +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * Intended for Minecraft coordinates, probably not for too high values.
+ * This implementation is not thread safe, though changing values and + * get/contains should work if the map stays unchanged. + *
+ * Simple hash map implementation of CoordMap. + * + * @author asofold + * + * @param + */ +public class CoordHashMap extends AbstractCoordHashMap> { + + // TODO: Move parts of abstract map here. + + /** + * + * Simple version of an iterator just using the internal Buckets. + * + * Does NOT throw anything, not the least thread safe. + * + * @author mc_dev + * + * @param + */ + public static class HashIterator implements Iterator> { + private final CoordHashMap map; + private final List>[] entries; + + /** Current search position. */ + private int slot = 0; + /** Current search position. */ + private int index = 0; + + /** Last found position. */ + private int slotLast = -1; + /** Last found position. */ + private int indexLast = -1; + + protected HashIterator(final CoordHashMap map) { + this.map = map; + entries = map.entries; + } + + @Override + public final boolean hasNext() { + // Also set index and slot to next found element. + while (slot < entries.length) { + final List> bucket = entries[slot]; + if (bucket == null){ + slot ++; + index = 0; + } + else { + if (index < bucket.size()) { + return true; + } + else { + // Not found, reset. + slot ++; + index = 0; + } + } + } + return false; + } + + @Override + public final Entry next() { + while (slot < entries.length) { + final List> bucket = entries[slot]; + if (bucket == null){ + slot ++; + index = 0; + } + else { + final int size = bucket.size(); + if (index < size) { + final Entry res = bucket.get(index); + slotLast = slot; + indexLast = index; + index++; + if (index == size) { + index = 0; + slot++; + } + return res; + } + else{ + // TODO: inconsistent, could be empty though. + slot++; + index = 0; + } + } + } + throw new NoSuchElementException(); + } + + @Override + public final void remove() { + if (slotLast == -1){ + // Next had not been called at all, + // or remove has been called several times. + return; + } + final List> bucket = entries[slotLast]; + bucket.remove(indexLast); + if (bucket.isEmpty()) { + entries[slotLast] = null; + } + else if (slotLast == slot) { + index --; + } + map.size--; + slotLast = indexLast = -1; + } + } + + public CoordHashMap() { + super(); + } + + public CoordHashMap(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); + } + + public CoordHashMap(int initialCapacity) { + super(initialCapacity); + } + + @Override + public Iterator> iterator(){ + return new HashIterator(this); + } + + @Override + protected fr.neatmonster.nocheatplus.utilities.ds.map.AbstractCoordHashMap.HashEntry newEntry(int x, int y, int z, V value, int hash) { + return new HashEntry(x, y, z, value, hash); + } + +} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/CoordMap.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/CoordMap.java new file mode 100644 index 00000000..054bf12f --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/CoordMap.java @@ -0,0 +1,89 @@ +package fr.neatmonster.nocheatplus.utilities.ds.map; + +import java.util.Iterator; + +/** + * Map integer coordinates to values (just for fun). + * + * @author asofold + * + * @param + * Type of the values to contain for a triple of coordinates. + */ +public interface CoordMap { + + /** + * Entry for iteration. + * + * @author asofold + * + * @param + */ + public static interface Entry { + public int getX(); + public int getY(); + public int getZ(); + public V getValue(); + } + + /** + * Check if the map contains a value for the given coordinates.
+ * NOTE: Delegates to get, use get for fastest checks. + * + * @param x + * @param y + * @param z + * @return + */ + public boolean contains(final int x, final int y, final int z); + + /** + * Get the value if there is a mapping for the given coordinates. + * + * @param x + * @param y + * @param z + * @return + */ + public V get(final int x, final int y, final int z); + + /** + * Add value with the coordinates + hash from the last contains call. + * + * @param value + * @return If a value was replaced. + */ + public boolean put(final int x, final int y, final int z, final V value); + + /** + * Remove an entry. + * + * @param x + * @param y + * @param z + * @return + */ + public V remove(final int x, final int y, final int z); + + /** + * Get the number of stored elements. + * @return + */ + public int size(); + + /** + * Remove all entries from the map. + */ + public void clear(); + + /** + * Iterator over all elements (default order to be specified). + *
+ * There is no guarantee that any checks for concurrent modification are + * performed. + * + * @return + */ + public Iterator> iterator(); + +} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/LinkedCoordHashMap.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/LinkedCoordHashMap.java new file mode 100644 index 00000000..877f6a9b --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/LinkedCoordHashMap.java @@ -0,0 +1,60 @@ +package fr.neatmonster.nocheatplus.utilities.ds.map; + +import java.util.Iterator; + +/** + * Intended for Minecraft coordinates, probably not for too high values.
+ * This implementation is not thread safe, though changing values and + * get/contains should work if the map stays unchanged. + *
+ * Linked hash map implementation of CoordMap, allowing for insertion/access + * order. Default order is the order of insertion. + * + * @author asofold + * + * @param + */ +public class LinkedCoordHashMap extends AbstractCoordHashMap> { + + // TODO: Implement linked structure + iterator (+reversed iteration). + + public static class LinkedHashEntry extends fr.neatmonster.nocheatplus.utilities.ds.map.AbstractCoordHashMap.HashEntry { + + public LinkedHashEntry(int x, int y, int z, V value, int hash) { + // TODO: linked ... + super(x, y, z, value, hash); + throw new IllegalStateException("Not yet implemented."); // TODO: Implement. + } + + } + + public boolean isAccessOrder() { + throw new IllegalStateException("Not yet implemented."); // TODO: Implement. + } + + public boolean isInsertionOrder() { + throw new IllegalStateException("Not yet implemented."); // TODO: Implement. + } + + @Override + public Iterator> iterator() { + throw new IllegalStateException("Not yet implemented."); // TODO: Implement. + } + + /** + * Control order of iteration. Actual order depends on the accessOrder flag. + * @param reversed + * @return + */ + public Iterator> iterator(boolean reversed) { + throw new IllegalStateException("Not yet implemented."); // TODO: Implement + } + + @Override + protected LinkedHashEntry newEntry(int x, int y, int z, V value, int hash) { + throw new IllegalStateException("Not yet implemented."); // TODO: Implement + } + + + +} diff --git a/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/ManagedMap.java b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/ManagedMap.java new file mode 100644 index 00000000..af161f6a --- /dev/null +++ b/NCPCommons/src/main/java/fr/neatmonster/nocheatplus/utilities/ds/map/ManagedMap.java @@ -0,0 +1,92 @@ +package fr.neatmonster.nocheatplus.utilities.ds.map; + +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; + +/** + * Map that provides time stamp based expiration of entries. Time stamps get set + * with each access. + * + * @author mc_dev + * + * @param + * @param + */ +public class ManagedMap{ + + protected class ValueWrap{ + public long ts; + public V value; + public ValueWrap(final V value){ + ts = System.currentTimeMillis(); + this.value = value; + } + } + + protected final LinkedHashMap map; + + public ManagedMap(int defaultCapacity, float loadFactor){ + map = new LinkedHashMap(defaultCapacity, loadFactor, true); + } + + /** + * + * @param key + * @param value Previously contained value or null. + * @return + */ + public V put(final K key, final V value){ + final ValueWrap wrap = map.get(key); + if (wrap == null){ + map.put(key, new ValueWrap(value)); + return null; + } + else{ + final V res = wrap.value; + wrap.value = value; + wrap.ts = System.currentTimeMillis(); + return res; + } + } + + public V get(final K key){ + final ValueWrap wrap = map.get(key); + if (wrap == null) return null; + else{ + wrap.ts = System.currentTimeMillis(); + return wrap.value; + } + } + + public V remove(final K key){ + final ValueWrap wrap = map.remove(key); + if (wrap == null) return null; + else return wrap.value; + } + + public void clear(){ + map.clear(); + } + + /** + * Remove entries that are older than ts. + * @param ts + * @return + */ + public Collection expire(final long ts){ + final List rem = new LinkedList(); + for (final Entry entry : map.entrySet()){ + final ValueWrap wrap = entry.getValue(); + if (wrap.ts < ts) rem.add(entry.getKey()); + else break; + } + for (final K key : rem){ + map.remove(key); + } + return rem; + } + +} diff --git a/NCPCommons/src/test/java/fr/neatmonster/nocheatplus/test/TestCoordMap.java b/NCPCommons/src/test/java/fr/neatmonster/nocheatplus/test/TestCoordMap.java index 5cd76a1f..1a4bc03a 100644 --- a/NCPCommons/src/test/java/fr/neatmonster/nocheatplus/test/TestCoordMap.java +++ b/NCPCommons/src/test/java/fr/neatmonster/nocheatplus/test/TestCoordMap.java @@ -12,8 +12,9 @@ import java.util.Set; import org.junit.Test; import fr.neatmonster.nocheatplus.utilities.build.BuildParameters; -import fr.neatmonster.nocheatplus.utilities.ds.CoordMap; -import fr.neatmonster.nocheatplus.utilities.ds.CoordMap.Entry; +import fr.neatmonster.nocheatplus.utilities.ds.map.CoordHashMap; +import fr.neatmonster.nocheatplus.utilities.ds.map.CoordMap; +import fr.neatmonster.nocheatplus.utilities.ds.map.CoordMap.Entry; public class TestCoordMap { @@ -208,33 +209,33 @@ public class TestCoordMap { CoordMap map; // Fill and check - map = new CoordMap(initialSize, loadFactor); + map = new CoordHashMap(initialSize, loadFactor); fillMap(map, coords); assertSize(map, indexMap.size()); matchAll(map, coords); // Fill and check with iterator. - map = new CoordMap(initialSize, loadFactor); + map = new CoordHashMap(initialSize, loadFactor); fillMap(map, coords); assertSize(map, indexMap.size()); matchAllIterator(map, indexMap); // Normal removing - map = new CoordMap(initialSize, loadFactor); + map = new CoordHashMap(initialSize, loadFactor); fillMap(map, coords); assertSize(map, indexMap.size()); removeAll(map, coords); assertSize(map, 0); // Removing with iterator. - map = new CoordMap(initialSize, loadFactor); + map = new CoordHashMap(initialSize, loadFactor); fillMap(map, coords); assertSize(map, indexMap.size()); removeAllIterator(map, indexMap); assertSize(map, 0); // Fill twice. - map = new CoordMap(initialSize, loadFactor); + map = new CoordHashMap(initialSize, loadFactor); fillMap(map, coords); assertSize(map, indexMap.size()); fillMap(map, coords); @@ -244,7 +245,7 @@ public class TestCoordMap { assertSize(map, 0); // Fill twice iterator. - map = new CoordMap(initialSize, loadFactor); + map = new CoordHashMap(initialSize, loadFactor); fillMap(map, coords); assertSize(map, indexMap.size()); fillMap(map, coords); diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/analysis/engine/EnginePlayerDataMap.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/analysis/engine/EnginePlayerDataMap.java index 5609e3f1..db6559e4 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/analysis/engine/EnginePlayerDataMap.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/checks/chat/analysis/engine/EnginePlayerDataMap.java @@ -4,7 +4,7 @@ import java.util.Collection; import fr.neatmonster.nocheatplus.checks.chat.ChatConfig; import fr.neatmonster.nocheatplus.checks.chat.analysis.engine.processors.WordProcessor; -import fr.neatmonster.nocheatplus.utilities.ds.ManagedMap; +import fr.neatmonster.nocheatplus.utilities.ds.map.ManagedMap; /** * Store EnginePlayerData. Expire data on get(String, Chatonfig). diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockCache.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockCache.java index 02f4ea1a..c69bd07f 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockCache.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/BlockCache.java @@ -7,7 +7,8 @@ import org.bukkit.entity.Entity; import fr.neatmonster.nocheatplus.NCPAPIProvider; import fr.neatmonster.nocheatplus.logging.Streams; -import fr.neatmonster.nocheatplus.utilities.ds.CoordMap; +import fr.neatmonster.nocheatplus.utilities.ds.map.CoordHashMap; +import fr.neatmonster.nocheatplus.utilities.ds.map.CoordMap; /** * Access to type-ids and data using caching techniques. @@ -67,13 +68,13 @@ public abstract class BlockCache { } /** Cached type-ids. */ - private final CoordMap idMap = new CoordMap(23); + private final CoordMap idMap = new CoordHashMap(23); /** Cached data values. */ - private final CoordMap dataMap = new CoordMap(23); + private final CoordMap dataMap = new CoordHashMap(23); /** Cached shape values. */ - private final CoordMap boundsMap = new CoordMap(23); + private final CoordMap boundsMap = new CoordHashMap(23); protected int maxBlockY = 255; diff --git a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/FakeBlockCache.java b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/FakeBlockCache.java index b43fb7ee..16dfd0f2 100644 --- a/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/FakeBlockCache.java +++ b/NCPCore/src/main/java/fr/neatmonster/nocheatplus/utilities/FakeBlockCache.java @@ -8,8 +8,9 @@ import org.bukkit.World; import org.bukkit.entity.Entity; import fr.neatmonster.nocheatplus.logging.debug.DebugUtil; -import fr.neatmonster.nocheatplus.utilities.ds.CoordMap; -import fr.neatmonster.nocheatplus.utilities.ds.CoordMap.Entry; +import fr.neatmonster.nocheatplus.utilities.ds.map.CoordHashMap; +import fr.neatmonster.nocheatplus.utilities.ds.map.CoordMap; +import fr.neatmonster.nocheatplus.utilities.ds.map.CoordMap.Entry; /** * Stand-alone BlockCache for setting data by access methods, for testing purposes. @@ -19,13 +20,13 @@ import fr.neatmonster.nocheatplus.utilities.ds.CoordMap.Entry; public class FakeBlockCache extends BlockCache { /** Cached type-ids. */ - private final CoordMap idMapStored = new CoordMap(23); + private final CoordMap idMapStored = new CoordHashMap(23); /** Cached data values. */ - private final CoordMap dataMapStored = new CoordMap(23); + private final CoordMap dataMapStored = new CoordHashMap(23); /** Cached shape values. */ - private final CoordMap boundsMapStored = new CoordMap(23); + private final CoordMap boundsMapStored = new CoordHashMap(23); /** * Convenience method to copy a cuboid region given by two endpoints without any order specified.