/* * Copyright (c) 2014, the Dart project authors. * * Licensed under the Eclipse Public License v1.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.eclipse.org/legal/epl-v10.html * * 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 com.google.dart.engine.internal.index.file; import java.util.Arrays; /** * A hash map with {@code int[]} keys and {@code int} values. * * @coverage dart.engine.index */ public class IntArrayToIntMap { private static class Entry { private final int[] key; private int value; Entry next; public Entry(int[] key, int value, Entry next) { this.key = key; this.value = value; this.next = next; } } private final float loadFactor; private int capacity; private int threshold; private int size; private int[][] keys; private int[] values; private Entry[] entries; public IntArrayToIntMap(int initialCapacity, float loadFactor) { this.loadFactor = loadFactor; capacity = initialCapacity; threshold = (int) (capacity * loadFactor); size = 0; keys = new int[capacity][]; values = new int[capacity]; entries = new Entry[capacity]; } /** * Returns the value to which the specified key is mapped, or the given default value if this map * contains no mapping for the key. * * @param key the key whose associated value is to be returned * @param defaultValue the default value to to returned if there is no value associated with the * given key * @return the value associated with {@code key}, or {@code defaultValue} if there is no mapping * for {@code key} */ public int get(int[] key, int defaultValue) { int hash = hash(key); int index = hash % capacity; // try "intKeys" { int[] intKey = keys[index]; if (intKey != null && Arrays.equals(intKey, key)) { return values[index]; } } // try "entries" { Entry entry = entries[index]; while (entry != null) { if (Arrays.equals(entry.key, key)) { return entry.value; } entry = entry.next; } } // not found return defaultValue; } /** * Associates the specified value with the specified key in this map. */ public void put(int[] key, int value) { if (size >= threshold) { rehash(); } // prepare hash int hash = hash(key); int index = hash % capacity; // try "keys" Entry entry = entries[index]; if (entry == null) { int[] existingKey = keys[index]; if (existingKey == null) { keys[index] = key; values[index] = value; size++; return; } if (Arrays.equals(existingKey, key)) { values[index] = value; return; } // collision, create new Entry Entry existingEntry = new Entry(existingKey, values[index], null); keys[index] = null; entries[index] = new Entry(key, value, existingEntry); size++; return; } // check existing entries while (entry != null) { if (Arrays.equals(entry.key, key)) { entry.value = value; return; } entry = entry.next; } // add new Entry entries[index] = new Entry(key, value, entries[index]); size++; } /** * Removes the mapping for a key from this map if it is present. * * @param key the key whose mapping is to be removed from the map * @param defaultValue the default value to to returned if there is no value associated with the * given key * @return the previous value associated with {@code key}, or {@code defaultValue} if there was no * mapping for {@code key} */ public int remove(int[] key, int defaultValue) { int hash = hash(key); int index = hash % capacity; // try "keys" { int[] existingKey = keys[index]; if (existingKey != null && Arrays.equals(existingKey, key)) { size--; keys[index] = null; return values[index]; } } // try "entries" { Entry entry = entries[index]; Entry prev = null; while (entry != null) { if (Arrays.equals(entry.key, key)) { size--; int value = entry.value; if (entries[index] == entry) { entries[index] = entry.next; } else { prev.next = entry.next; } return value; } prev = entry; entry = entry.next; } } // not found return defaultValue; } /** * Returns the number of key-value mappings in this map. */ public int size() { return size; } private int hash(int[] key) { int result = 1; for (int element : key) { result = 31 * result + element; } return result & 0x7FFFFFFF; } private void rehash() { IntArrayToIntMap newMap = new IntArrayToIntMap(capacity * 2 + 1, loadFactor); // put values for (int i = 0; i < keys.length; i++) { int[] key = keys[i]; if (key != null) { newMap.put(key, values[i]); } } for (int i = 0; i < entries.length; i++) { Entry entry = entries[i]; while (entry != null) { newMap.put(entry.key, entry.value); entry = entry.next; } } // copy data capacity = newMap.capacity; threshold = newMap.threshold; keys = newMap.keys; values = newMap.values; entries = newMap.entries; } }