blob: 2874772bf3f3094fdc87636714aebfe76c1c9904 [file] [log] [blame]
/*
* Copyright 2002-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.apache.directmemory.lightning.internal.util;
import java.util.Iterator;
/*
* Taken from lwjgl project
*/
/**
* A hash map using primitive longs as keys rather than objects.
*
* @author Justin Couch
* @author Alex Chaffee (alex@apache.org)
* @author Stephen Colebourne
* @author Nathan Sweet
*/
public final class FastLongMap<V>
implements Iterable<FastLongMap.Entry<V>>
{
private Entry[] table;
private int size, mask, capacity, threshold;
/** Same as: FastLongMap(16, 0.75f); */
public FastLongMap()
{
this( 16, 0.75f );
}
/** Same as: FastLongMap(initialCapacity, 0.75f); */
public FastLongMap( int initialCapacity )
{
this( initialCapacity, 0.75f );
}
public FastLongMap( int initialCapacity, float loadFactor )
{
if ( initialCapacity > 1 << 30 )
{
throw new IllegalArgumentException( "initialCapacity is too large." );
}
if ( initialCapacity < 0 )
{
throw new IllegalArgumentException( "initialCapacity must be greater than zero." );
}
if ( loadFactor <= 0 )
{
throw new IllegalArgumentException( "initialCapacity must be greater than zero." );
}
capacity = 1;
while ( capacity < initialCapacity )
{
capacity <<= 1;
}
this.threshold = (int) ( capacity * loadFactor );
this.table = new Entry[capacity];
this.mask = capacity - 1;
}
private int index( final long key )
{
return index( key, mask );
}
private static int index( final long key, final int mask )
{
final int hash = (int) ( key ^ ( key >>> 32 ) );
return hash & mask;
}
public V put( long key, V value )
{
final Entry<V>[] table = this.table;
int index = index( key );
// Check if key already exists.
for ( Entry<V> e = table[index]; e != null; e = e.next )
{
if ( e.key != key )
{
continue;
}
V oldValue = e.value;
e.value = value;
return oldValue;
}
table[index] = new Entry<V>( key, value, table[index] );
if ( size++ >= threshold )
{
rehash( table );
}
return null;
}
private void rehash( final Entry<V>[] table )
{
final int newCapacity = 2 * capacity;
final int newMask = newCapacity - 1;
final Entry<V>[] newTable = new Entry[newCapacity];
for ( int i = 0, index; i < table.length; i++ )
{
Entry<V> e = table[i];
if ( e == null )
{
continue;
}
do
{
final Entry<V> next = e.next;
index = index( e.key, newMask );
e.next = newTable[index];
newTable[index] = e;
e = next;
}
while ( e != null );
}
this.table = newTable;
capacity = newCapacity;
mask = newMask;
threshold *= 2;
}
public V get( long key )
{
final int index = index( key );
for ( Entry<V> e = table[index]; e != null; e = e.next )
{
if ( e.key == key )
{
return e.value;
}
}
return null;
}
public boolean containsValue( Object value )
{
final Entry<V>[] table = this.table;
for ( int i = table.length - 1; i >= 0; i-- )
{
for ( Entry<V> e = table[i]; e != null; e = e.next )
{
if ( e.value.equals( value ) )
{
return true;
}
}
}
return false;
}
public boolean containsKey( long key )
{
final int index = index( key );
for ( Entry<V> e = table[index]; e != null; e = e.next )
{
if ( e.key == key )
{
return true;
}
}
return false;
}
public V remove( long key )
{
final int index = index( key );
Entry<V> prev = table[index];
Entry<V> e = prev;
while ( e != null )
{
Entry<V> next = e.next;
if ( e.key == key )
{
size--;
if ( prev == e )
{
table[index] = next;
}
else
{
prev.next = next;
}
return e.value;
}
prev = e;
e = next;
}
return null;
}
public int size()
{
return size;
}
public boolean isEmpty()
{
return size == 0;
}
public void clear()
{
final Entry<V>[] table = this.table;
for ( int index = table.length - 1; index >= 0; index-- )
{
table[index] = null;
}
size = 0;
}
@Override
public EntryIterator iterator()
{
return new EntryIterator();
}
public class EntryIterator
implements Iterator<Entry<V>>
{
private int nextIndex;
private Entry<V> current;
EntryIterator()
{
reset();
}
public void reset()
{
current = null;
// Find first bucket.
final Entry<V>[] table = FastLongMap.this.table;
int i;
for ( i = table.length - 1; i >= 0; i-- )
{
if ( table[i] != null )
{
break;
}
}
nextIndex = i;
}
@Override
public boolean hasNext()
{
if ( nextIndex >= 0 )
{
return true;
}
Entry e = current;
return e != null && e.next != null;
}
@Override
public Entry<V> next()
{
// Next entry in current bucket.
Entry<V> e = current;
if ( e != null )
{
e = e.next;
if ( e != null )
{
current = e;
return e;
}
}
// Use the bucket at nextIndex and find the next nextIndex.
final Entry<V>[] table = FastLongMap.this.table;
int i = nextIndex;
e = current = table[i];
while ( --i >= 0 )
{
if ( table[i] != null )
{
break;
}
}
nextIndex = i;
return e;
}
@Override
public void remove()
{
FastLongMap.this.remove( current.key );
}
}
static final class Entry<T>
{
final long key;
T value;
Entry<T> next;
Entry( long key, T value, Entry<T> next )
{
this.key = key;
this.value = value;
this.next = next;
}
public long getKey()
{
return key;
}
public T getValue()
{
return value;
}
}
}