diff options
author | Richard Hendricks <richardhendricks@pobox.com> | 2018-08-26 02:24:24 -0500 |
---|---|---|
committer | Richard Hendricks <richardhendricks@pobox.com> | 2018-08-26 02:24:24 -0500 |
commit | 453becda075fa68fccacdb069544a8c95c96ecfa (patch) | |
tree | 22457c8229a1c269d39cbd32069cc84dc4a5a65c /src | |
parent | 2c989cac10dbcf516adc63e57e0557803df95da6 (diff) | |
download | GT5-Unofficial-453becda075fa68fccacdb069544a8c95c96ecfa.tar.gz GT5-Unofficial-453becda075fa68fccacdb069544a8c95c96ecfa.tar.bz2 GT5-Unofficial-453becda075fa68fccacdb069544a8c95c96ecfa.zip |
Final version of Alkalus's type filter fix. #3367
Diffstat (limited to 'src')
-rw-r--r-- | src/main/java/gregtech/api/enums/OrePrefixes.java | 2 | ||||
-rw-r--r-- | src/main/java/gregtech/api/objects/ObjMap.java | 281 |
2 files changed, 282 insertions, 1 deletions
diff --git a/src/main/java/gregtech/api/enums/OrePrefixes.java b/src/main/java/gregtech/api/enums/OrePrefixes.java index 3edb12ed25..72f04dd7c9 100644 --- a/src/main/java/gregtech/api/enums/OrePrefixes.java +++ b/src/main/java/gregtech/api/enums/OrePrefixes.java @@ -878,7 +878,7 @@ public enum OrePrefixes { }
else {
- aCurrentSet = new ObjMap<Integer, Boolean>(mPrefixedItems.size(), 0.5f);
+ aCurrentSet = new ObjMap<Integer, Boolean>((mPrefixedItems != null && mPrefixedItems.size() > 0 ? mPrefixedItems.size() : 1000), 0.5f);
mCachedResults.put(this.toString(), aCurrentSet);
}
diff --git a/src/main/java/gregtech/api/objects/ObjMap.java b/src/main/java/gregtech/api/objects/ObjMap.java new file mode 100644 index 0000000000..0db6083cfe --- /dev/null +++ b/src/main/java/gregtech/api/objects/ObjMap.java @@ -0,0 +1,281 @@ +package gregtech.api.objects; + +import java.util.Arrays; + +/** + * Object-2-object map based on IntIntMap4a + */ +public class ObjMap<K, V> +{ + private static final Object FREE_KEY = new Object(); + private static final Object REMOVED_KEY = new Object(); + + /** Keys and values */ + private Object[] m_data; + + /** Value for the null key (if inserted into a map) */ + private Object m_nullValue; + private boolean m_hasNull; + + /** Fill factor, must be between (0 and 1) */ + private final float m_fillFactor; + /** We will resize a map once it reaches this size */ + private int m_threshold; + /** Current map size */ + private int m_size; + /** Mask to calculate the original position */ + private int m_mask; + /** Mask to wrap the actual array pointer */ + private int m_mask2; + + public ObjMap( final int size, final float fillFactor ) + { + if ( fillFactor <= 0 || fillFactor >= 1 ) + throw new IllegalArgumentException( "FillFactor must be in (0, 1)" ); + if ( size <= 0 ) + throw new IllegalArgumentException( "Size must be positive!" ); + final int capacity = arraySize(size, fillFactor); + m_mask = capacity - 1; + m_mask2 = capacity * 2 - 1; + m_fillFactor = fillFactor; + + m_data = new Object[capacity * 2]; + Arrays.fill( m_data, FREE_KEY ); + + m_threshold = (int) (capacity * fillFactor); + } + + @SuppressWarnings("unchecked") + public V get( final K key ) + { + if ( key == null ) + return (V) m_nullValue; //we null it on remove, so safe not to check a flag here + + int ptr = (key.hashCode() & m_mask) << 1; + Object k = m_data[ ptr ]; + + if ( k == FREE_KEY ) + return null; //end of chain already + if ( k.equals( key ) ) //we check FREE and REMOVED prior to this call + return (V) m_data[ ptr + 1 ]; + while ( true ) + { + ptr = (ptr + 2) & m_mask2; //that's next index + k = m_data[ ptr ]; + if ( k == FREE_KEY ) + return null; + if ( k.equals( key ) ) + return (V) m_data[ ptr + 1 ]; + } + } + + @SuppressWarnings("unchecked") + public V put( final K key, final V value ) + { + if ( key == null ) + return insertNullKey(value); + + int ptr = getStartIndex(key) << 1; + Object k = m_data[ptr]; + + if ( k == FREE_KEY ) //end of chain already + { + m_data[ ptr ] = key; + m_data[ ptr + 1 ] = value; + if ( m_size >= m_threshold ) + rehash( m_data.length * 2 ); //size is set inside + else + ++m_size; + return null; + } + else if ( k.equals( key ) ) //we check FREE and REMOVED prior to this call + { + final Object ret = m_data[ ptr + 1 ]; + m_data[ ptr + 1 ] = value; + return (V) ret; + } + + int firstRemoved = -1; + if ( k == REMOVED_KEY ) + firstRemoved = ptr; //we may find a key later + + while ( true ) + { + ptr = ( ptr + 2 ) & m_mask2; //that's next index calculation + k = m_data[ ptr ]; + if ( k == FREE_KEY ) + { + if ( firstRemoved != -1 ) + ptr = firstRemoved; + m_data[ ptr ] = key; + m_data[ ptr + 1 ] = value; + if ( m_size >= m_threshold ) + rehash( m_data.length * 2 ); //size is set inside + else + ++m_size; + return null; + } + else if ( k.equals( key ) ) + { + final Object ret = m_data[ ptr + 1 ]; + m_data[ ptr + 1 ] = value; + return (V) ret; + } + else if ( k == REMOVED_KEY ) + { + if ( firstRemoved == -1 ) + firstRemoved = ptr; + } + } + } + + @SuppressWarnings("unchecked") + public V remove( final K key ) + { + if ( key == null ) + return removeNullKey(); + + int ptr = getStartIndex(key) << 1; + Object k = m_data[ ptr ]; + if ( k == FREE_KEY ) + return null; //end of chain already + else if ( k.equals( key ) ) //we check FREE and REMOVED prior to this call + { + --m_size; + if ( m_data[ ( ptr + 2 ) & m_mask2 ] == FREE_KEY ) + m_data[ ptr ] = FREE_KEY; + else + m_data[ ptr ] = REMOVED_KEY; + final V ret = (V) m_data[ ptr + 1 ]; + m_data[ ptr + 1 ] = null; + return ret; + } + while ( true ) + { + ptr = ( ptr + 2 ) & m_mask2; //that's next index calculation + k = m_data[ ptr ]; + if ( k == FREE_KEY ) + return null; + else if ( k.equals( key ) ) + { + --m_size; + if ( m_data[ ( ptr + 2 ) & m_mask2 ] == FREE_KEY ) + m_data[ ptr ] = FREE_KEY; + else + m_data[ ptr ] = REMOVED_KEY; + final V ret = (V) m_data[ ptr + 1 ]; + m_data[ ptr + 1 ] = null; + return ret; + } + } + } + + @SuppressWarnings("unchecked") + private V insertNullKey(final V value) + { + if ( m_hasNull ) + { + final Object ret = m_nullValue; + m_nullValue = value; + return (V) ret; + } + else + { + m_nullValue = value; + ++m_size; + return null; + } + } + + @SuppressWarnings("unchecked") + private V removeNullKey() + { + if ( m_hasNull ) + { + final Object ret = m_nullValue; + m_nullValue = null; + m_hasNull = false; + --m_size; + return (V) ret; + } + else + { + return null; + } + } + + public int size() + { + return m_size; + } + + @SuppressWarnings("unchecked") + private void rehash( final int newCapacity ) + { + m_threshold = (int) (newCapacity/2 * m_fillFactor); + m_mask = newCapacity/2 - 1; + m_mask2 = newCapacity - 1; + + final int oldCapacity = m_data.length; + final Object[] oldData = m_data; + + m_data = new Object[ newCapacity ]; + Arrays.fill( m_data, FREE_KEY ); + + m_size = m_hasNull ? 1 : 0; + + for ( int i = 0; i < oldCapacity; i += 2 ) { + final Object oldKey = oldData[ i ]; + if( oldKey != FREE_KEY && oldKey != REMOVED_KEY ) + put( (K)oldKey, (V)oldData[ i + 1 ]); + } + } + + public int getStartIndex( final Object key ) + { + //key is not null here + return key.hashCode() & m_mask; + } + + /** Taken from FastUtil implementation */ + + /** Return the least power of two greater than or equal to the specified value. + * + * <p>Note that this function will return 1 when the argument is 0. + * + * @param x a long integer smaller than or equal to 2<sup>62</sup>. + * @return the least power of two greater than or equal to the specified value. + */ + public static long nextPowerOfTwo( long x ) { + if ( x == 0 ) return 1; + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return ( x | x >> 32 ) + 1; + } + + /** Returns the least power of two smaller than or equal to 2<sup>30</sup> and larger than or equal to <code>Math.ceil( expected / f )</code>. + * + * @param expected the expected number of elements in a hash table. + * @param f the load factor. + * @return the minimum possible size for a backing array. + * @throws IllegalArgumentException if the necessary size is larger than 2<sup>30</sup>. + */ + public static int arraySize( final int expected, final float f ) { + final long s = Math.max( 2, nextPowerOfTwo( (long)Math.ceil( expected / f ) ) ); + if ( s > (1 << 30) ) throw new IllegalArgumentException( "Too large (" + expected + " expected elements with load factor " + f + ")" ); + return (int)s; + } + + //taken from FastUtil + private static final int INT_PHI = 0x9E3779B9; + + public static int phiMix( final int x ) { + final int h = x * INT_PHI; + return h ^ (h >> 16); +} + +} |