Cache.java
package neureka.common.utility;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Function;
/**
* This is a simple, fixed size cache for immutable objects which are
* shared throughout the library runtime...
* This is an internal class which should not be used outside Neurekas internals.
*
* @param <O> The type that should be cached, this may be an {@link neureka.ndim.config.NDConfiguration} or {@code int[]} array.
*/
public final class Cache<O> {
private final Object[] _buffer;
private int _size = 0;
public Cache( int size ) {
_buffer = new Object[ size ];
}
/**
* @param newObject The object which may or may not be cached.
* @return Either the provided object or the object found inside the cache...
*/
public <T extends O> T process( T newObject ) {
int index = _indexFor(newObject);
O found = _getAt(index);
if ( _equalsFor( found, newObject ) ) return (T) found;
else _setAt( index, newObject );
return newObject;
}
public boolean has( O o ) {
O found = (O) _buffer[ _indexFor( o ) ];
return found != null && found.equals( o );
}
public int size() { return _size; }
private O _getAt( int index ) {
return (O) _buffer[ index ];
}
private void _setAt( int index, O o ) {
if ( _buffer[ index ] == null && o != null ) _size++;
_buffer[ index ] = o;
}
private boolean _equalsFor( Object a, Object b ) {
if ( a != null && b != null ) {
if (a instanceof int[] && b instanceof int[])
return Arrays.equals((int[]) a, (int[]) b);
else
return Objects.equals(a, b);
}
else return false;
}
private int _indexFor( Object o ) {
return o instanceof int[] ? _index((int[]) o) : _index( o.hashCode() );
}
private int _index( int key ) {
return Math.abs(key)% _buffer.length;
}
private int _index( int[] data )
{
long key = 0;
for ( int e : data ) {
if ( e <= 10 ) key *= 10;
else if ( e <= 100 ) key *= 100;
else if ( e <= 1_000 ) key *= 1_000;
else if ( e <= 10_000 ) key *= 10_000;
else if ( e <= 100_000 ) key *= 100_000;
else if ( e <= 1_000_000 ) key *= 1_000_000;
else if ( e <= 10_000_000 ) key *= 10_000_000;
else if ( e <= 100_000_000 ) key *= 100_000_000;
else if ( e <= 1_000_000_000 ) key *= 1_000_000_000;
key += Math.abs( e ) + 1;
}
int rank = data.length;
while ( rank != 0 ) {
rank /= 10;
key *= 10;
}
key += data.length;
return _index(Long.valueOf(key).hashCode());
}
/**
* Lazy cache entries are entries whose values will be calculated
* only when the entry is being stored in the cache.
*
* @param <K> The key type parameter.
* @param <V> The value type parameter.
*/
public static class LazyEntry<K,V> {
private final K _key;
private final Function<K,V> _valueSupplier;
private V _value = null;
public LazyEntry( K directory, Function<K,V> valueSupplier ) {
_key = directory;
_valueSupplier = valueSupplier;
}
public V getValue() {
if ( _value == null ) _value = _valueSupplier.apply(_key);
return _value;
}
@Override
public boolean equals(Object o) {
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
LazyEntry that = (LazyEntry) o;
return _key.equals(that._key);
}
@Override
public int hashCode() {
return Objects.hash(_key);
}
}
}