| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * 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 java.io; |
| |
| /** |
| * A specialization of IdentityHashMap<Object, int> for use when serializing objects. |
| * We need to assign each object we write an int 'handle' (densely packed but not starting |
| * at zero), and use the same handle any time we write the same object again. |
| */ |
| final class SerializationHandleMap { |
| /* Default load factor of 0.75; */ |
| private static final int LOAD_FACTOR = 7500; |
| |
| private Object[] keys; |
| private int[] values; |
| |
| /* Actual number of key-value pairs. */ |
| private int size; |
| |
| /* Maximum number of elements that can be put in this map before having to rehash. */ |
| private int threshold; |
| |
| public SerializationHandleMap() { |
| this.size = 0; |
| this.threshold = 21; // Copied from IdentityHashMap. |
| int arraySize = (int) (((long) threshold * 10000) / LOAD_FACTOR); |
| resizeArrays(arraySize); |
| } |
| |
| private void resizeArrays(int newSize) { |
| Object[] oldKeys = keys; |
| int[] oldValues = values; |
| |
| this.keys = new Object[newSize]; |
| this.values = new int[newSize]; |
| |
| if (oldKeys != null) { |
| for (int i = 0; i < oldKeys.length; ++i) { |
| Object key = oldKeys[i]; |
| int value = oldValues[i]; |
| int index = findIndex(key, keys); |
| keys[index] = key; |
| values[index] = value; |
| } |
| } |
| } |
| |
| public int get(Object key) { |
| int index = findIndex(key, keys); |
| if (keys[index] == key) { |
| return values[index]; |
| } |
| return -1; |
| } |
| |
| /** |
| * Returns the index where the key is found at, or the index of the next |
| * empty spot if the key is not found in this table. |
| */ |
| private int findIndex(Object key, Object[] array) { |
| int length = array.length; |
| int index = getModuloHash(key, length); |
| int last = (index + length - 1) % length; |
| while (index != last) { |
| if (array[index] == key || array[index] == null) { |
| /* |
| * Found the key, or the next empty spot (which means key is not |
| * in the table) |
| */ |
| break; |
| } |
| index = (index + 1) % length; |
| } |
| return index; |
| } |
| |
| private int getModuloHash(Object key, int length) { |
| return (System.identityHashCode(key) & 0x7FFFFFFF) % length; |
| } |
| |
| public int put(Object key, int value) { |
| Object _key = key; |
| int _value = value; |
| |
| int index = findIndex(_key, keys); |
| |
| // if the key doesn't exist in the table |
| if (keys[index] != _key) { |
| if (++size > threshold) { |
| rehash(); |
| index = findIndex(_key, keys); |
| } |
| // insert the key and assign the value to -1 initially |
| keys[index] = _key; |
| values[index] = -1; |
| } |
| |
| // insert value to where it needs to go, return the old value |
| int result = values[index]; |
| values[index] = _value; |
| return result; |
| } |
| |
| private void rehash() { |
| int newSize = keys.length * 2; |
| resizeArrays(newSize); |
| threshold = (int) ((long) (keys.length) * LOAD_FACTOR / 10000); |
| } |
| |
| public int remove(Object key) { |
| boolean hashedOk; |
| int index, next, hash; |
| int result; |
| Object object; |
| index = next = findIndex(key, keys); |
| |
| if (keys[index] != key) { |
| return -1; |
| } |
| |
| // store the value for this key |
| result = values[index]; |
| |
| // shift the following elements up if needed |
| // until we reach an empty spot |
| int length = keys.length; |
| while (true) { |
| next = (next + 2) % length; |
| object = keys[next]; |
| if (object == null) { |
| break; |
| } |
| |
| hash = getModuloHash(object, length); |
| hashedOk = hash > index; |
| if (next < index) { |
| hashedOk = hashedOk || (hash <= next); |
| } else { |
| hashedOk = hashedOk && (hash <= next); |
| } |
| if (!hashedOk) { |
| keys[index] = object; |
| values[index] = values[next]; |
| index = next; |
| } |
| } |
| size--; |
| |
| // clear both the key and the value |
| keys[index] = null; |
| values[index] = -1; |
| |
| return result; |
| } |
| |
| public boolean isEmpty() { |
| return size == 0; |
| } |
| } |