mirror of
https://github.com/NoCheatPlus/NoCheatPlus.git
synced 2025-01-27 18:01:33 +01:00
[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.
This commit is contained in:
parent
01671a04fd
commit
9efcf01766
@ -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.<br>
|
||||
* This implementation is not thread safe, though changing values and
|
||||
* get/contains should work if the map stays unchanged.
|
||||
*
|
||||
* @author mc_dev
|
||||
*
|
||||
* @param <V>
|
||||
*/
|
||||
public class CoordMap<V> {
|
||||
|
||||
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<V> {
|
||||
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 <V>
|
||||
*/
|
||||
public static final class CoordMapIterator<V> implements Iterator<Entry<V>> {
|
||||
private final CoordMap<V> map;
|
||||
private final List<Entry<V>>[] 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<V> 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<Entry<V>> 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<V> next() {
|
||||
while (slot < entries.length) {
|
||||
final List<Entry<V>> bucket = entries[slot];
|
||||
if (bucket == null){
|
||||
slot ++;
|
||||
index = 0;
|
||||
}
|
||||
else {
|
||||
final int size = bucket.size();
|
||||
if (index < size) {
|
||||
final Entry<V> 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<Entry<V>> 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<Entry<V>>[] 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. <br>
|
||||
* 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.<br>
|
||||
* 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<Entry<V>> bucket = entries[slot];
|
||||
if (bucket == null) return null;
|
||||
;
|
||||
for (final Entry<V> 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<Entry<V>> bucket = entries[slot];
|
||||
if (bucket != null) {
|
||||
for (final Entry<V> 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<Entry<V>>();
|
||||
entries[slot] = bucket;
|
||||
}
|
||||
bucket.add(new Entry<V>(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<Entry<V>> bucket = entries[slot];
|
||||
if (bucket == null) return null;
|
||||
else {
|
||||
for (int i = 0; i < bucket.size(); i++) {
|
||||
final Entry<V> 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<Entry<V>>[] 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<Entry<V>> oldBucket = entries[oldSlot];
|
||||
if (oldBucket == null) continue;
|
||||
for (final Entry<V> entry : oldBucket) {
|
||||
final int newSlot = Math.abs(entry.hash) % newCapacity;
|
||||
List<Entry<V>> newBucket = newEntries[newSlot];
|
||||
if (newBucket == null) {
|
||||
if (used < 0) newBucket = new LinkedList<Entry<V>>();
|
||||
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<Entry<V>> iterator(){
|
||||
return new CoordMapIterator<V>(this);
|
||||
}
|
||||
}
|
@ -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 <K>
|
||||
* @param <V>
|
||||
*/
|
||||
public class ManagedMap<K, V>{
|
||||
|
||||
protected class ValueWrap{
|
||||
public long ts;
|
||||
public V value;
|
||||
public ValueWrap(final V value){
|
||||
ts = System.currentTimeMillis();
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected final LinkedHashMap<K, ValueWrap> map;
|
||||
|
||||
public ManagedMap(int defaultCapacity, float loadFactor){
|
||||
map = new LinkedHashMap<K, ValueWrap>(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<K> expire(final long ts){
|
||||
final List<K> rem = new LinkedList<K>();
|
||||
for (final Entry<K, ValueWrap> 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;
|
||||
}
|
||||
|
||||
}
|
@ -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.<br>
|
||||
* This implementation is not thread safe, though changing values and
|
||||
* get/contains should work if the map stays unchanged.
|
||||
*
|
||||
* <br>
|
||||
* Abstract base implementation for a hash map version.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractCoordHashMap<V, E extends fr.neatmonster.nocheatplus.utilities.ds.map.AbstractCoordHashMap.HashEntry<V>> implements CoordMap<V> {
|
||||
|
||||
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<V> implements Entry<V>{
|
||||
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<E>[] 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. <br>
|
||||
* 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<E> 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<E> 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<E>();
|
||||
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<E> 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<E>[] 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<E> oldBucket = entries[oldSlot];
|
||||
if (oldBucket == null) {
|
||||
continue;
|
||||
}
|
||||
for (final E entry : oldBucket) {
|
||||
final int newSlot = Math.abs(entry.hash) % newCapacity;
|
||||
List<E> newBucket = newEntries[newSlot];
|
||||
if (newBucket == null) {
|
||||
if (used < 0) {
|
||||
newBucket = new LinkedList<E>();
|
||||
}
|
||||
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);
|
||||
|
||||
}
|
@ -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.<br>
|
||||
* This implementation is not thread safe, though changing values and
|
||||
* get/contains should work if the map stays unchanged.
|
||||
* <hr>
|
||||
* Simple hash map implementation of CoordMap<V>.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
* @param <V>
|
||||
*/
|
||||
public class CoordHashMap<V> extends AbstractCoordHashMap<V, fr.neatmonster.nocheatplus.utilities.ds.map.AbstractCoordHashMap.HashEntry<V>> {
|
||||
|
||||
// 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 <V>
|
||||
*/
|
||||
public static class HashIterator<V> implements Iterator<Entry<V>> {
|
||||
private final CoordHashMap<V> map;
|
||||
private final List<HashEntry<V>>[] 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<V> 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<HashEntry<V>> 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<V> next() {
|
||||
while (slot < entries.length) {
|
||||
final List<HashEntry<V>> bucket = entries[slot];
|
||||
if (bucket == null){
|
||||
slot ++;
|
||||
index = 0;
|
||||
}
|
||||
else {
|
||||
final int size = bucket.size();
|
||||
if (index < size) {
|
||||
final Entry<V> 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<HashEntry<V>> 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<Entry<V>> iterator(){
|
||||
return new HashIterator<V>(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected fr.neatmonster.nocheatplus.utilities.ds.map.AbstractCoordHashMap.HashEntry<V> newEntry(int x, int y, int z, V value, int hash) {
|
||||
return new HashEntry<V>(x, y, z, value, hash);
|
||||
}
|
||||
|
||||
}
|
@ -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 <V>
|
||||
* Type of the values to contain for a triple of coordinates.
|
||||
*/
|
||||
public interface CoordMap<V> {
|
||||
|
||||
/**
|
||||
* Entry for iteration.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
* @param <V>
|
||||
*/
|
||||
public static interface Entry<V> {
|
||||
public int getX();
|
||||
public int getY();
|
||||
public int getZ();
|
||||
public V getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the map contains a value for the given coordinates.<br>
|
||||
* 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).
|
||||
* <hr>
|
||||
* There is no guarantee that any checks for concurrent modification are
|
||||
* performed.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Iterator<Entry<V>> iterator();
|
||||
|
||||
}
|
@ -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.<br>
|
||||
* This implementation is not thread safe, though changing values and
|
||||
* get/contains should work if the map stays unchanged.
|
||||
* <hr>
|
||||
* Linked hash map implementation of CoordMap<V>, allowing for insertion/access
|
||||
* order. Default order is the order of insertion.
|
||||
*
|
||||
* @author asofold
|
||||
*
|
||||
* @param <V>
|
||||
*/
|
||||
public class LinkedCoordHashMap<V> extends AbstractCoordHashMap<V, fr.neatmonster.nocheatplus.utilities.ds.map.LinkedCoordHashMap.LinkedHashEntry<V>> {
|
||||
|
||||
// TODO: Implement linked structure + iterator (+reversed iteration).
|
||||
|
||||
public static class LinkedHashEntry<V> extends fr.neatmonster.nocheatplus.utilities.ds.map.AbstractCoordHashMap.HashEntry<V> {
|
||||
|
||||
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<Entry<V>> 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<Entry<V>> iterator(boolean reversed) {
|
||||
throw new IllegalStateException("Not yet implemented."); // TODO: Implement
|
||||
}
|
||||
|
||||
@Override
|
||||
protected LinkedHashEntry<V> newEntry(int x, int y, int z, V value, int hash) {
|
||||
throw new IllegalStateException("Not yet implemented."); // TODO: Implement
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -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 <K>
|
||||
* @param <V>
|
||||
*/
|
||||
public class ManagedMap<K, V>{
|
||||
|
||||
protected class ValueWrap{
|
||||
public long ts;
|
||||
public V value;
|
||||
public ValueWrap(final V value){
|
||||
ts = System.currentTimeMillis();
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
protected final LinkedHashMap<K, ValueWrap> map;
|
||||
|
||||
public ManagedMap(int defaultCapacity, float loadFactor){
|
||||
map = new LinkedHashMap<K, ValueWrap>(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<K> expire(final long ts){
|
||||
final List<K> rem = new LinkedList<K>();
|
||||
for (final Entry<K, ValueWrap> 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;
|
||||
}
|
||||
|
||||
}
|
@ -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<Integer> map;
|
||||
|
||||
// Fill and check
|
||||
map = new CoordMap<Integer>(initialSize, loadFactor);
|
||||
map = new CoordHashMap<Integer>(initialSize, loadFactor);
|
||||
fillMap(map, coords);
|
||||
assertSize(map, indexMap.size());
|
||||
matchAll(map, coords);
|
||||
|
||||
// Fill and check with iterator.
|
||||
map = new CoordMap<Integer>(initialSize, loadFactor);
|
||||
map = new CoordHashMap<Integer>(initialSize, loadFactor);
|
||||
fillMap(map, coords);
|
||||
assertSize(map, indexMap.size());
|
||||
matchAllIterator(map, indexMap);
|
||||
|
||||
// Normal removing
|
||||
map = new CoordMap<Integer>(initialSize, loadFactor);
|
||||
map = new CoordHashMap<Integer>(initialSize, loadFactor);
|
||||
fillMap(map, coords);
|
||||
assertSize(map, indexMap.size());
|
||||
removeAll(map, coords);
|
||||
assertSize(map, 0);
|
||||
|
||||
// Removing with iterator.
|
||||
map = new CoordMap<Integer>(initialSize, loadFactor);
|
||||
map = new CoordHashMap<Integer>(initialSize, loadFactor);
|
||||
fillMap(map, coords);
|
||||
assertSize(map, indexMap.size());
|
||||
removeAllIterator(map, indexMap);
|
||||
assertSize(map, 0);
|
||||
|
||||
// Fill twice.
|
||||
map = new CoordMap<Integer>(initialSize, loadFactor);
|
||||
map = new CoordHashMap<Integer>(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<Integer>(initialSize, loadFactor);
|
||||
map = new CoordHashMap<Integer>(initialSize, loadFactor);
|
||||
fillMap(map, coords);
|
||||
assertSize(map, indexMap.size());
|
||||
fillMap(map, coords);
|
||||
|
@ -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).
|
||||
|
@ -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<Integer> idMap = new CoordMap<Integer>(23);
|
||||
private final CoordMap<Integer> idMap = new CoordHashMap<Integer>(23);
|
||||
|
||||
/** Cached data values. */
|
||||
private final CoordMap<Integer> dataMap = new CoordMap<Integer>(23);
|
||||
private final CoordMap<Integer> dataMap = new CoordHashMap<Integer>(23);
|
||||
|
||||
/** Cached shape values. */
|
||||
private final CoordMap<double[]> boundsMap = new CoordMap<double[]>(23);
|
||||
private final CoordMap<double[]> boundsMap = new CoordHashMap<double[]>(23);
|
||||
|
||||
protected int maxBlockY = 255;
|
||||
|
||||
|
@ -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<Integer> idMapStored = new CoordMap<Integer>(23);
|
||||
private final CoordMap<Integer> idMapStored = new CoordHashMap<Integer>(23);
|
||||
|
||||
/** Cached data values. */
|
||||
private final CoordMap<Integer> dataMapStored = new CoordMap<Integer>(23);
|
||||
private final CoordMap<Integer> dataMapStored = new CoordHashMap<Integer>(23);
|
||||
|
||||
/** Cached shape values. */
|
||||
private final CoordMap<double[]> boundsMapStored = new CoordMap<double[]>(23);
|
||||
private final CoordMap<double[]> boundsMapStored = new CoordHashMap<double[]>(23);
|
||||
|
||||
/**
|
||||
* Convenience method to copy a cuboid region given by two endpoints without any order specified.
|
||||
|
Loading…
Reference in New Issue
Block a user