blob: b27198c4d60d76bfa12bd1afac077fea66745621 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.felix.resolver.util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
/**
* Based on fastutil Object2ObjectLinkedOpenHashMap
*/
public class OpenHashMap<K, V> implements Serializable, Cloneable, SortedMap<K, V> {
private static final long serialVersionUID = 0L;
protected transient Object[] key;
protected transient Object[] value;
protected transient int mask;
protected transient boolean containsNullKey;
protected transient int first;
protected transient int last;
protected transient long[] link;
protected transient int n;
protected transient int maxFill;
protected int size;
protected final float f;
protected V defRetValue;
protected transient Iterable<Map.Entry<K, V>> fast;
protected transient SortedSet<Map.Entry<K, V>> entries;
protected transient SortedSet<K> keys;
protected transient Collection<V> values;
public OpenHashMap(int expected, float f) {
this.first = -1;
this.last = -1;
if (f > 0.0F && f <= 1.0F) {
if (expected < 0) {
throw new IllegalArgumentException("The expected number of elements must be nonnegative");
} else {
this.f = f;
this.n = arraySize(expected, f);
this.mask = this.n - 1;
this.maxFill = maxFill(this.n, f);
this.key = new Object[this.n + 1];
this.value = new Object[this.n + 1];
this.link = new long[this.n + 1];
}
} else {
throw new IllegalArgumentException("Load factor must be greater than 0 and smaller than or equal to 1");
}
}
public OpenHashMap(int expected) {
this(expected, 0.75F);
}
public OpenHashMap() {
this(16, 0.75F);
}
public OpenHashMap(Map<? extends K, ? extends V> m, float f) {
this(m.size(), f);
this.putAll(m);
}
public OpenHashMap(Map<? extends K, ? extends V> m) {
this(m, 0.75F);
}
public OpenHashMap(K[] k, V[] v, float f) {
this(k.length, f);
if (k.length != v.length) {
throw new IllegalArgumentException("The key array and the value array have different lengths (" + k.length + " and " + v.length + ")");
} else {
for (int i = 0; i < k.length; ++i) {
this.put(k[i], v[i]);
}
}
}
public OpenHashMap(K[] k, V[] v) {
this(k, v, 0.75F);
}
public void defaultReturnValue(V rv) {
this.defRetValue = rv;
}
public V defaultReturnValue() {
return this.defRetValue;
}
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Map)) {
return false;
} else {
Map<?, ?> m = (Map<?, ?>) o;
int n = m.size();
if (this.size() != n) {
return false;
}
Iterator<? extends Entry<?, ?>> i = this.fast().iterator();
while (n-- > 0) {
Entry<?, ?> e = i.next();
Object k = e.getKey();
Object v = e.getValue();
Object v2 = m.get(k);
if (v == null) {
if (v2 != null) {
return false;
}
} else if (!v.equals(v2)) {
return false;
}
}
return true;
}
}
public String toString() {
StringBuilder s = new StringBuilder();
Iterator<Map.Entry<K, V>> i = this.fast().iterator();
int n = this.size();
boolean first = true;
s.append("{");
while (n-- != 0) {
if (first) {
first = false;
} else {
s.append(", ");
}
Map.Entry<K, V> e = i.next();
if (this == e.getKey()) {
s.append("(this map)");
} else {
s.append(String.valueOf(e.getKey()));
}
s.append("=>");
if (this == e.getValue()) {
s.append("(this map)");
} else {
s.append(String.valueOf(e.getValue()));
}
}
s.append("}");
return s.toString();
}
private int realSize() {
return this.containsNullKey ? this.size - 1 : this.size;
}
private void ensureCapacity(int capacity) {
int needed = arraySize(capacity, this.f);
if (needed > this.n) {
this.rehash(needed);
}
}
private void tryCapacity(long capacity) {
int needed = (int) Math.min(1073741824L, Math.max(2L, nextPowerOfTwo((long) Math.ceil((double) ((float) capacity / this.f)))));
if (needed > this.n) {
this.rehash(needed);
}
}
@SuppressWarnings("unchecked")
private V removeEntry(int pos) {
Object oldValue = this.value[pos];
this.value[pos] = null;
--this.size;
this.fixPointers(pos);
this.shiftKeys(pos);
if (this.size < this.maxFill / 4 && this.n > 16) {
this.rehash(this.n / 2);
}
return (V) oldValue;
}
@SuppressWarnings("unchecked")
private V removeNullEntry() {
this.containsNullKey = false;
Object oldValue = this.value[this.n];
this.value[this.n] = null;
--this.size;
this.fixPointers(this.n);
if (this.size < this.maxFill / 4 && this.n > 16) {
this.rehash(this.n / 2);
}
return (V) oldValue;
}
public void putAll(Map<? extends K, ? extends V> m) {
if ((double) this.f <= 0.5D) {
this.ensureCapacity(m.size());
} else {
this.tryCapacity((long) (this.size() + m.size()));
}
int n = m.size();
if (m instanceof OpenHashMap) {
Iterator<? extends Map.Entry<? extends K, ? extends V>> i = ((OpenHashMap<? extends K, ? extends V>) m).fast().iterator();
while (n-- != 0) {
Map.Entry<? extends K, ? extends V> e = i.next();
this.put(e.getKey(), e.getValue());
}
} else {
Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator();
while (n-- != 0) {
Map.Entry<? extends K, ? extends V> e = i.next();
this.put(e.getKey(), e.getValue());
}
}
}
private int insert(K k, V v) {
int pos;
if (k == null) {
if (this.containsNullKey) {
return this.n;
}
this.containsNullKey = true;
pos = this.n;
} else {
Object[] key = this.key;
Object curr;
if ((curr = key[pos = mix(k.hashCode()) & this.mask]) != null) {
if (curr.equals(k)) {
return pos;
}
while ((curr = key[pos = pos + 1 & this.mask]) != null) {
if (curr.equals(k)) {
return pos;
}
}
}
key[pos] = k;
}
this.value[pos] = v;
if (this.size == 0) {
this.first = this.last = pos;
this.link[pos] = -1L;
} else {
this.link[this.last] ^= (this.link[this.last] ^ (long) pos & 0xFFFFFFFFL) & 0xFFFFFFFFL;
this.link[pos] = ((long) this.last & 0xFFFFFFFFL) << 32 | 0xFFFFFFFFL;
this.last = pos;
}
if (this.size++ >= this.maxFill) {
this.rehash(arraySize(this.size + 1, this.f));
}
return -1;
}
@SuppressWarnings("unchecked")
public V put(K k, V v) {
int pos = this.insert(k, v);
if (pos < 0) {
return this.defRetValue;
} else {
Object oldValue = this.value[pos];
this.value[pos] = v;
return (V) oldValue;
}
}
@SuppressWarnings("unchecked")
public V getOrCompute(K k) {
int pos;
if (k == null) {
if (this.containsNullKey) {
return (V) this.value[this.n];
}
this.containsNullKey = true;
pos = this.n;
} else {
Object[] key = this.key;
Object curr;
if ((curr = key[pos = mix(k.hashCode()) & this.mask]) != null) {
if (curr.equals(k)) {
return (V) this.value[pos];
}
while ((curr = key[pos = pos + 1 & this.mask]) != null) {
if (curr.equals(k)) {
return (V) this.value[pos];
}
}
}
key[pos] = k;
}
Object v;
this.value[pos] = (v = compute(k));
if (this.size == 0) {
this.first = this.last = pos;
this.link[pos] = -1L;
} else {
this.link[this.last] ^= (this.link[this.last] ^ (long) pos & 0xFFFFFFFFL) & 0xFFFFFFFFL;
this.link[pos] = ((long) this.last & 0xFFFFFFFFL) << 32 | 0xFFFFFFFFL;
this.last = pos;
}
if (this.size++ >= this.maxFill) {
this.rehash(arraySize(this.size + 1, this.f));
}
return (V) v;
}
protected V compute(K k) {
throw new UnsupportedOperationException();
}
protected final void shiftKeys(int pos) {
Object[] key = this.key;
label32:
while (true) {
int last = pos;
Object curr;
for (pos = pos + 1 & this.mask; (curr = key[pos]) != null; pos = pos + 1 & this.mask) {
int slot = mix(curr.hashCode()) & this.mask;
if (last <= pos) {
if (last < slot && slot <= pos) {
continue;
}
} else if (last < slot || slot <= pos) {
continue;
}
key[last] = curr;
this.value[last] = this.value[pos];
this.fixPointers(pos, last);
continue label32;
}
key[last] = null;
this.value[last] = null;
return;
}
}
public V remove(Object k) {
if (k == null) {
return this.containsNullKey ? this.removeNullEntry() : this.defRetValue;
} else {
Object[] key = this.key;
Object curr;
int pos;
if ((curr = key[pos = mix(k.hashCode()) & this.mask]) == null) {
return this.defRetValue;
} else if (k.equals(curr)) {
return this.removeEntry(pos);
} else {
while ((curr = key[pos = pos + 1 & this.mask]) != null) {
if (k.equals(curr)) {
return this.removeEntry(pos);
}
}
return this.defRetValue;
}
}
}
@SuppressWarnings("unchecked")
public V removeFirst() {
if (this.size == 0) {
throw new NoSuchElementException();
} else {
int pos = this.first;
this.first = (int) this.link[pos];
if (0 <= this.first) {
this.link[this.first] |= 0xFFFFFFFF00000000L;
}
--this.size;
Object v = this.value[pos];
if (pos == this.n) {
this.containsNullKey = false;
this.value[this.n] = null;
} else {
this.shiftKeys(pos);
}
if (this.size < this.maxFill / 4 && this.n > 16) {
this.rehash(this.n / 2);
}
return (V) v;
}
}
@SuppressWarnings("unchecked")
public V removeLast() {
if (this.size == 0) {
throw new NoSuchElementException();
} else {
int pos = this.last;
this.last = (int) (this.link[pos] >>> 32);
if (0 <= this.last) {
this.link[this.last] |= 0xFFFFFFFFL;
}
--this.size;
Object v = this.value[pos];
if (pos == this.n) {
this.containsNullKey = false;
this.value[this.n] = null;
} else {
this.shiftKeys(pos);
}
if (this.size < this.maxFill / 4 && this.n > 16) {
this.rehash(this.n / 2);
}
return (V) v;
}
}
@SuppressWarnings("unchecked")
public V get(Object k) {
if (k == null) {
return containsNullKey ? (V) value[n] : defRetValue;
}
final Object[] key = this.key;
Object curr;
int pos;
// The starting point
if ((curr = key[pos = mix(k.hashCode()) & mask]) == null) {
return defRetValue;
}
if (k.equals(curr)) {
return (V) value[pos];
}
// There's always an usused entry
while (true) {
if ((curr = key[pos = (pos + 1) & mask]) == null) {
return defRetValue;
}
if (k.equals(curr)) {
return (V) value[pos];
}
}
}
public boolean containsKey(Object k) {
if (k == null) {
return this.containsNullKey;
} else {
Object[] key = this.key;
Object curr;
int pos;
if ((curr = key[pos = mix(k.hashCode()) & this.mask]) == null) {
return false;
} else if (k.equals(curr)) {
return true;
} else {
while ((curr = key[pos = pos + 1 & this.mask]) != null) {
if (k.equals(curr)) {
return true;
}
}
return false;
}
}
}
public boolean containsValue(Object v) {
Object[] value = this.value;
Object[] key = this.key;
if (containsNullKey && (value[n] == null && v == null) || value[n].equals(v)) {
return true;
}
for (int i = n; i-- != 0;) {
if (!(key[i] == null) && (value[i] == null && v == null) || value[i].equals(v)) {
return true;
}
}
return false;
}
public void clear() {
if (size != 0) {
size = 0;
containsNullKey = false;
Arrays.fill(key, (Object) null);
Arrays.fill(value, (Object) null);
first = last = -1;
}
}
public int size() {
return this.size;
}
public boolean isEmpty() {
return this.size == 0;
}
protected void fixPointers(int i) {
if (size == 0) {
first = last = -1;
} else if (first == i) {
first = (int) link[i];
if (0 <= first) {
link[first] |= 0xFFFFFFFF00000000L;
}
} else if (last == i) {
last = (int) (link[i] >>> 32);
if (0 <= last) {
link[last] |= 0xFFFFFFFFL;
}
} else {
long linki = link[i];
int prev = (int) (linki >>> 32);
int next = (int) linki;
link[prev] ^= (link[prev] ^ linki & 0xFFFFFFFFL) & 0xFFFFFFFFL;
link[next] ^= (link[next] ^ linki & 0xFFFFFFFF00000000L) & 0xFFFFFFFF00000000L;
}
}
protected void fixPointers(int s, int d) {
if (size == 1) {
first = last = d;
link[d] = -1L;
} else if (first == s) {
first = d;
link[(int) link[s]] ^= (link[(int) link[s]] ^ ((long) d & 0xFFFFFFFFL) << 32) & 0xFFFFFFFF00000000L;
link[d] = link[s];
} else if (last == s) {
last = d;
link[(int) (link[s] >>> 32)] ^= (link[(int) (link[s] >>> 32)] ^ (long) d & 0xFFFFFFFFL) & 0xFFFFFFFFL;
link[d] = link[s];
} else {
long links = link[s];
int prev = (int) (links >>> 32);
int next = (int) links;
link[prev] ^= (link[prev] ^ (long) d & 0xFFFFFFFFL) & 0xFFFFFFFFL;
link[next] ^= (link[next] ^ ((long) d & 0xFFFFFFFFL) << 32) & 0xFFFFFFFF00000000L;
link[d] = links;
}
}
@SuppressWarnings("unchecked")
public K firstKey() {
if (size == 0) {
throw new NoSuchElementException();
} else {
return (K) key[first];
}
}
@SuppressWarnings("unchecked")
public K lastKey() {
if (size == 0) {
throw new NoSuchElementException();
} else {
return (K) key[last];
}
}
public Comparator<? super K> comparator() {
return null;
}
public SortedMap<K, V> tailMap(K from) {
throw new UnsupportedOperationException();
}
public SortedMap<K, V> headMap(K to) {
throw new UnsupportedOperationException();
}
public SortedMap<K, V> subMap(K from, K to) {
throw new UnsupportedOperationException();
}
public Iterable<Map.Entry<K, V>> fast() {
if (fast == null) {
fast = new Iterable<Entry<K, V>>() {
public Iterator<Entry<K, V>> iterator() {
return new FastEntryIterator();
}
};
}
return fast;
}
public SortedSet<Map.Entry<K, V>> entrySet() {
if (entries == null) {
entries = new MapEntrySet();
}
return this.entries;
}
public SortedSet<K> keySet() {
if (keys == null) {
keys = new KeySet();
}
return keys;
}
public Collection<V> values() {
if (values == null) {
values = new AbstractObjectCollection<V>() {
public Iterator<V> iterator() {
return new ValueIterator();
}
public int size() {
return size;
}
public boolean contains(Object v) {
return containsValue(v);
}
public void clear() {
OpenHashMap.this.clear();
}
};
}
return values;
}
/** Rehashes the map, making the table as small as possible.
*
* <P>This method rehashes the table to the smallest size satisfying the
* load factor. It can be used when the set will not be changed anymore, so
* to optimize access speed and size.
*
* <P>If the table size is already the minimum possible, this method
* does nothing.
*
* @return true if there was enough memory to trim the map.
* @see #trim(int)
*/
public boolean trim() {
int l = arraySize(size, f);
if (l >= n) {
return true;
} else {
try {
rehash(l);
return true;
} catch (OutOfMemoryError cantDoIt) {
return false;
}
}
}
/** Rehashes this map if the table is too large.
*
* <P>Let <var>N</var> be the smallest table size that can hold
* <code>max(n,{@link #size()})</code> entries, still satisfying the load factor. If the current
* table size is smaller than or equal to <var>N</var>, this method does
* nothing. Otherwise, it rehashes this map in a table of size
* <var>N</var>.
*
* <P>This method is useful when reusing maps. {@linkplain #clear() Clearing a
* map} leaves the table size untouched. If you are reusing a map
* many times, you can call this method with a typical
* size to avoid keeping around a very large table just
* because of a few large transient maps.
*
* @param n the threshold for the trimming.
* @return true if there was enough memory to trim the map.
* @see #trim()
*/
public boolean trim(int n) {
int l = nextPowerOfTwo((int) Math.ceil((double) ((float) n / f)));
if (n <= l) {
return true;
} else {
try {
rehash(l);
return true;
} catch (OutOfMemoryError cantDoIt) {
return false;
}
}
}
/** Rehashes the map.
*
* <P>This method implements the basic rehashing strategy, and may be
* overriden by subclasses implementing different rehashing strategies (e.g.,
* disk-based rehashing). However, you should not override this method
* unless you understand the internal workings of this class.
*
* @param newN the new size
*/
protected void rehash(int newN) {
Object[] key = this.key;
Object[] value = this.value;
int mask = newN - 1;
Object[] newKey = new Object[newN + 1];
Object[] newValue = new Object[newN + 1];
int i = first, prev = -1, newPrev = -1, t, pos;
final long[] link = this.link;
final long[] newLink = new long[newN + 1];
first = -1;
for (int j = size; j-- != 0;) {
if (key[i] == null) {
pos = newN;
} else {
pos = mix(key[i].hashCode()) & mask;
while (newKey[pos] != null) {
pos = ( pos + 1 ) & mask;
}
newKey[pos] = key[i];
}
newValue[pos] = value[i];
if (prev != -1) {
newLink[newPrev] ^= (newLink[newPrev] ^ (long) pos & 0xFFFFFFFFL) & 0xFFFFFFFFL;
newLink[pos] ^= (newLink[pos] ^ ((long) newPrev & 0xFFFFFFFFL) << 32) & 0xFFFFFFFF00000000L;
newPrev = pos;
} else {
newPrev = first = pos;
newLink[pos] = -1L;
}
t = i;
i = (int) link[i];
prev = t;
}
this.link = newLink;
this.last = newPrev;
if (newPrev != -1) {
newLink[newPrev] |= -1 & 0xFFFFFFFFL;
}
n = newN;
this.mask = mask;
maxFill = maxFill(n, f);
this.key = newKey;
this.value = newValue;
}
@SuppressWarnings("unchecked")
public OpenHashMap<K, V> clone() {
OpenHashMap<K, V> c;
try {
c = (OpenHashMap<K, V>) super.clone();
} catch (CloneNotSupportedException cantHappen) {
throw new InternalError();
}
c.fast = null;
c.keys = null;
c.values = null;
c.entries = null;
c.containsNullKey = containsNullKey;
c.key = key.clone();
c.value = value.clone();
c.link = link.clone();
return c;
}
public int hashCode() {
int h = 0;
for( int j = realSize(), i = 0, t = 0; j-- != 0; ) {
while (key[i] == null) {
++i;
}
if (this != key[i]) {
t = key[i].hashCode();
}
if (this != value[i]) {
t ^= value[i] == null ? 0 : value[i].hashCode();
}
h += t;
i++;
}
if (containsNullKey) {
h += value[n] == null ? 0 : value[n].hashCode();
}
return h;
}
private void writeObject(ObjectOutputStream s) throws IOException {
Object[] key = this.key;
Object[] value = this.value;
OpenHashMap<K, V>.MapIterator i = new MapIterator();
s.defaultWriteObject();
int j = this.size;
while (j-- != 0) {
int e = i.nextEntry();
s.writeObject(key[e]);
s.writeObject(value[e]);
}
}
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
this.n = arraySize(this.size, this.f);
this.maxFill = maxFill(this.n, this.f);
this.mask = this.n - 1;
Object[] key = this.key = new Object[this.n + 1];
Object[] value = this.value = new Object[this.n + 1];
long[] link = this.link = new long[this.n + 1];
int prev = -1;
this.first = this.last = -1;
int i = this.size;
while (i-- != 0) {
Object k = s.readObject();
Object v = s.readObject();
int pos;
if (k == null) {
pos = this.n;
this.containsNullKey = true;
} else {
for (pos = mix(k.hashCode()) & this.mask; key[pos] != null; pos = pos + 1 & this.mask) {
;
}
key[pos] = k;
}
value[pos] = v;
if (this.first != -1) {
link[prev] ^= (link[prev] ^ (long) pos & 0xFFFFFFFFL) & 0xFFFFFFFFL;
link[pos] ^= (link[pos] ^ ((long) prev & 0xFFFFFFFFL) << 32) & 0xFFFFFFFF00000000L;
prev = pos;
} else {
prev = this.first = pos;
link[pos] |= 0xFFFFFFFF00000000L;
}
}
this.last = prev;
if (prev != -1) {
link[prev] |= 0xFFFFFFFFL;
}
}
private final class ValueIterator extends MapIterator implements Iterator<V> {
public ValueIterator() {
super();
}
@SuppressWarnings("unchecked")
public V next() {
return (V) value[this.nextEntry()];
}
}
private final class KeySet extends AbstractObjectSet<K> implements SortedSet<K> {
private KeySet() {
}
public Iterator<K> iterator() {
return new KeyIterator();
}
public int size() {
return size;
}
public boolean contains(Object k) {
return containsKey(k);
}
public boolean remove(Object k) {
int oldSize = size;
OpenHashMap.this.remove(k);
return size != oldSize;
}
public void clear() {
OpenHashMap.this.clear();
}
@SuppressWarnings("unchecked")
public K first() {
if (size == 0) {
throw new NoSuchElementException();
} else {
return (K) key[first];
}
}
@SuppressWarnings("unchecked")
public K last() {
if (size == 0) {
throw new NoSuchElementException();
} else {
return (K) key[last];
}
}
public Comparator<? super K> comparator() {
return null;
}
public final SortedSet<K> tailSet(K from) {
throw new UnsupportedOperationException();
}
public final SortedSet<K> headSet(K to) {
throw new UnsupportedOperationException();
}
public final SortedSet<K> subSet(K from, K to) {
throw new UnsupportedOperationException();
}
}
private final class KeyIterator extends MapIterator implements Iterator<K> {
public KeyIterator() {
super();
}
@SuppressWarnings("unchecked")
public K next() {
return (K) key[this.nextEntry()];
}
}
private final class MapEntrySet extends AbstractObjectSet<Entry<K, V>> implements SortedSet<Entry<K, V>> {
private MapEntrySet() {
}
public EntryIterator iterator() {
return new EntryIterator();
}
public Comparator<? super Entry<K, V>> comparator() {
return null;
}
public SortedSet<Entry<K, V>> subSet(Entry<K, V> fromElement, Entry<K, V> toElement) {
throw new UnsupportedOperationException();
}
public SortedSet<Entry<K, V>> headSet(Entry<K, V> toElement) {
throw new UnsupportedOperationException();
}
public SortedSet<Entry<K, V>> tailSet(Entry<K, V> fromElement) {
throw new UnsupportedOperationException();
}
public Entry<K, V> first() {
if (size == 0) {
throw new NoSuchElementException();
} else {
return new MapEntry(first);
}
}
public Entry<K, V> last() {
if (size == 0) {
throw new NoSuchElementException();
} else {
return new MapEntry(last);
}
}
public boolean contains(Object o) {
if (!(o instanceof java.util.Map.Entry)) {
return false;
} else {
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
Object k = e.getKey();
if (k == null) {
if (containsNullKey) {
if (value[n] == null) {
if (e.getValue() != null) {
return false;
}
} else if (!value[n].equals(e.getValue())) {
return false;
}
return true;
}
return false;
} else {
Object[] key = OpenHashMap.this.key;
Object curr;
int pos;
if ((curr = key[pos = mix(k.hashCode()) & mask]) == null) {
return false;
} else if (k.equals(curr)) {
return value[pos] == null ? e.getValue() == null : value[pos].equals(e.getValue());
} else {
while ((curr = key[pos = pos + 1 & mask]) != null) {
if (k.equals(curr)) {
return value[pos] == null ? e.getValue() == null : value[pos].equals(e.getValue());
}
}
return false;
}
}
}
}
public boolean remove(Object o) {
if (!(o instanceof java.util.Map.Entry)) {
return false;
} else {
Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
Object k = e.getKey();
Object v = e.getValue();
if (k == null) {
if (containsNullKey) {
if (value[n] == null) {
if (v != null) {
return false;
}
} else if (!value[n].equals(v)) {
return false;
}
removeNullEntry();
return true;
} else {
return false;
}
} else {
Object[] key = OpenHashMap.this.key;
Object curr;
int pos;
if ((curr = key[pos = mix(k.hashCode()) & mask]) == null) {
return false;
} else if (curr.equals(k)) {
if (value[pos] == null) {
if (v != null) {
return false;
}
} else if (!value[pos].equals(v)) {
return false;
}
removeEntry(pos);
return true;
} else {
while (true) {
do {
if ((curr = key[pos = pos + 1 & mask]) == null) {
return false;
}
} while (!curr.equals(k));
if (value[pos] == null) {
if (v == null) {
break;
}
} else if (value[pos].equals(v)) {
break;
}
}
removeEntry(pos);
return true;
}
}
}
}
public int size() {
return size;
}
public void clear() {
OpenHashMap.this.clear();
}
}
private class FastEntryIterator extends MapIterator implements Iterator<Entry<K, V>> {
final MapEntry entry;
public FastEntryIterator() {
super();
this.entry = new MapEntry();
}
public MapEntry next() {
this.entry.index = this.nextEntry();
return this.entry;
}
}
private class EntryIterator extends MapIterator implements Iterator<Entry<K, V>> {
private MapEntry entry;
public EntryIterator() {
super();
}
public MapEntry next() {
return this.entry = new MapEntry(this.nextEntry());
}
public void remove() {
super.remove();
this.entry.index = -1;
}
}
public static abstract class AbstractObjectSet<K> extends AbstractObjectCollection<K> implements Cloneable {
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Set)) {
return false;
} else {
Set<?> s = (Set<?>) o;
return s.size() == this.size() && this.containsAll(s);
}
}
public int hashCode() {
int h = 0;
int n = this.size();
Object k;
for (Iterator<K> i = this.iterator(); n-- != 0; h += k == null ? 0 : k.hashCode()) {
k = i.next();
}
return h;
}
}
private class MapIterator {
/**
* The entry that will be returned by the next call to {@link java.util.ListIterator#previous()} (or <code>null</code> if no previous entry exists).
*/
int prev = -1;
/**
* The entry that will be returned by the next call to {@link java.util.ListIterator#next()} (or <code>null</code> if no next entry exists).
*/
int next = -1;
/**
* The last entry that was returned (or -1 if we did not iterate or used {@link java.util.Iterator#remove()}).
*/
int curr = -1;
/**
* The current index (in the sense of a {@link java.util.ListIterator}). Note that this value is not meaningful when this iterator has been created using the nonempty constructor.
*/
int index = -1;
private MapIterator() {
this.next = first;
this.index = 0;
}
public boolean hasNext() {
return this.next != -1;
}
private void ensureIndexKnown() {
if (index < 0) {
if (prev == -1) {
index = 0;
} else if (next == -1) {
index = size;
} else {
int pos = first;
for (index = 1; pos != prev; ++index) {
pos = (int) link[pos];
}
}
}
}
public int nextEntry() {
if (!hasNext()) {
throw new NoSuchElementException();
} else {
curr = next;
next = (int) link[curr];
prev = curr;
if (index >= 0) {
++index;
}
return curr;
}
}
public void remove() {
this.ensureIndexKnown();
if (curr == -1) throw new IllegalStateException();
if (curr == prev) {
/* If the last operation was a next(), we are removing an entry that preceeds
* the current index, and thus we must decrement it. */
index--;
prev = (int) (link[curr] >>> 32);
} else {
next = (int) link[curr];
}
size--;
/* Now we manually fix the pointers. Because of our knowledge of next
* and prev, this is going to be faster than calling fixPointers(). */
if (prev == -1) {
first = next;
} else {
link[prev] ^= (link[prev] ^ (long) next & 0xFFFFFFFFL) & 0xFFFFFFFFL;
}
if (next == -1) {
last = prev;
} else {
link[next] ^= (link[next] ^ ((long) prev & 0xFFFFFFFFL) << 32) & 0xFFFFFFFF00000000L;
}
int last, slot, pos = curr;
curr = -1;
if (pos == n) {
containsNullKey = false;
value[n] = null;
} else {
Object curr;
Object[] key = OpenHashMap.this.key;
// We have to horribly duplicate the shiftKeys() code because we need to update next/prev.
for (; ; ) {
pos = ((last = pos) + 1) & mask;
for (; ; ) {
if ((curr = key[pos]) == null) {
key[last] = null;
value[last] = null;
return;
}
slot = mix(curr.hashCode()) & mask;
if (last <= pos ? last >= slot || slot > pos : last >= slot && slot > pos) break;
pos = (pos + 1) & mask;
}
key[last] = curr;
value[last] = value[pos];
if (next == pos) next = last;
if (prev == pos) prev = last;
fixPointers(pos, last);
}
}
}
}
final class MapEntry implements Entry<K, V> {
int index;
MapEntry(int index) {
this.index = index;
}
MapEntry() {
}
@SuppressWarnings("unchecked")
public K getKey() {
return (K) key[this.index];
}
@SuppressWarnings("unchecked")
public V getValue() {
return (V) value[this.index];
}
@SuppressWarnings("unchecked")
public V setValue(V v) {
Object oldValue = value[this.index];
value[this.index] = v;
return (V) oldValue;
}
public boolean equals(Object o) {
if (!(o instanceof Entry)) {
return false;
} else {
Entry<?, ?> e = (Entry<?, ?>) o;
if (key[this.index] == null) {
if (e.getKey() != null) {
return false;
}
} else if (!key[this.index].equals(e.getKey())) {
return false;
}
if (value[this.index] == null) {
if (e.getValue() != null) {
return false;
}
} else if (!value[this.index].equals(e.getValue())) {
return false;
}
return true;
}
}
public int hashCode() {
return (key[this.index] == null ? 0 :
key[this.index].hashCode()) ^ (value[this.index] == null ? 0 :
value[this.index].hashCode());
}
public String toString() {
return key[this.index] + "=>" + value[this.index];
}
}
public static abstract class AbstractObjectCollection<K> extends AbstractCollection<K> {
protected AbstractObjectCollection() {
}
public Object[] toArray() {
Object[] a = new Object[this.size()];
unwrap(this.iterator(), a);
return a;
}
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < this.size()) {
a = (T[]) Array.newInstance(a.getClass().getComponentType(), this.size());
}
unwrap(this.iterator(), a);
return a;
}
public boolean addAll(Collection<? extends K> c) {
boolean retVal = false;
Iterator<? extends K> i = c.iterator();
int n = c.size();
while (n-- != 0) {
if (this.add(i.next())) {
retVal = true;
}
}
return retVal;
}
public boolean add(K k) {
throw new UnsupportedOperationException();
}
public boolean containsAll(Collection<?> c) {
int n = c.size();
Iterator<?> i = c.iterator();
do {
if (n-- == 0) {
return true;
}
} while (this.contains(i.next()));
return false;
}
public boolean retainAll(Collection<?> c) {
boolean retVal = false;
int n = this.size();
Iterator<K> i = this.iterator();
while (n-- != 0) {
if (!c.contains(i.next())) {
i.remove();
retVal = true;
}
}
return retVal;
}
public boolean removeAll(Collection<?> c) {
boolean retVal = false;
int n = c.size();
Iterator<?> i = c.iterator();
while (n-- != 0) {
if (this.remove(i.next())) {
retVal = true;
}
}
return retVal;
}
public boolean isEmpty() {
return this.size() == 0;
}
public String toString() {
StringBuilder s = new StringBuilder();
Iterator<K> i = this.iterator();
int n = this.size();
boolean first = true;
s.append("{");
while (n-- != 0) {
if (first) {
first = false;
} else {
s.append(", ");
}
Object k = i.next();
if (this == k) {
s.append("(this collection)");
} else {
s.append(String.valueOf(k));
}
}
s.append("}");
return s.toString();
}
}
private static int arraySize(int expected, float f) {
long s = Math.max(2L, nextPowerOfTwo((long) Math.ceil((double) ((float) expected / f))));
if (s > 0x40000000L) {
throw new IllegalArgumentException("Too large (" + expected + " expected elements with load factor " + f + ")");
} else {
return (int) s;
}
}
private static int maxFill(int n, float f) {
return Math.min((int) Math.ceil((double) ((float) n * f)), n - 1);
}
private static int nextPowerOfTwo(int x) {
if (x == 0) {
return 1;
} else {
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
return (x | x >> 16) + 1;
}
}
private static long nextPowerOfTwo(long x) {
if (x == 0L) {
return 1L;
} else {
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
return (x | x >> 32) + 1L;
}
}
private static int mix(int x) {
int h = x * -1640531527;
return h ^ h >>> 16;
}
private static <K> int unwrap(Iterator<? extends K> i, K[] array, int offset, int max) {
if (max < 0) {
throw new IllegalArgumentException("The maximum number of elements (" + max + ") is negative");
} else if (offset >= 0 && offset + max <= array.length) {
int j;
for (j = max; j-- != 0 && i.hasNext(); array[offset++] = i.next()) {
;
}
return max - j - 1;
} else {
throw new IllegalArgumentException();
}
}
private static <K> int unwrap(Iterator<? extends K> i, K[] array) {
return unwrap(i, array, 0, array.length);
}
}