diff options
Diffstat (limited to 'infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjToIntMap.java')
-rw-r--r-- | infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjToIntMap.java | 697 |
1 files changed, 697 insertions, 0 deletions
diff --git a/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjToIntMap.java b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjToIntMap.java new file mode 100644 index 0000000..4aa7d23 --- /dev/null +++ b/infrastructure/rhino1_7R1/src/org/mozilla/javascript/ObjToIntMap.java @@ -0,0 +1,697 @@ +/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Rhino code, released + * May 6, 1999. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1997-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Igor Bukanov + * + * Alternatively, the contents of this file may be used under the terms of + * the GNU General Public License Version 2 or later (the "GPL"), in which + * case the provisions of the GPL are applicable instead of those above. If + * you wish to allow use of your version of this file only under the terms of + * the GPL and not to allow others to use your version of this file under the + * MPL, indicate your decision by deleting the provisions above and replacing + * them with the notice and other provisions required by the GPL. If you do + * not delete the provisions above, a recipient may use your version of this + * file under either the MPL or the GPL. + * + * ***** END LICENSE BLOCK ***** */ + +package org.mozilla.javascript; + +import java.io.Serializable; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** + * Map to associate objects to integers. + * The map does not synchronize any of its operation, so either use + * it from a single thread or do own synchronization or perform all mutation + * operations on one thread before passing the map to others + * + * @author Igor Bukanov + * + */ + +public class ObjToIntMap implements Serializable +{ + static final long serialVersionUID = -1542220580748809402L; + +// Map implementation via hashtable, +// follows "The Art of Computer Programming" by Donald E. Knuth + +// ObjToIntMap is a copy cat of ObjToIntMap with API adjusted to object keys + + public static class Iterator { + + Iterator(ObjToIntMap master) { + this.master = master; + } + + final void init(Object[] keys, int[] values, int keyCount) { + this.keys = keys; + this.values = values; + this.cursor = -1; + this.remaining = keyCount; + } + + public void start() { + master.initIterator(this); + next(); + } + + public boolean done() { + return remaining < 0; + } + + public void next() { + if (remaining == -1) Kit.codeBug(); + if (remaining == 0) { + remaining = -1; + cursor = -1; + }else { + for (++cursor; ; ++cursor) { + Object key = keys[cursor]; + if (key != null && key != DELETED) { + --remaining; + break; + } + } + } + } + + public Object getKey() { + Object key = keys[cursor]; + if (key == UniqueTag.NULL_VALUE) { key = null; } + return key; + } + + public int getValue() { + return values[cursor]; + } + + public void setValue(int value) { + values[cursor] = value; + } + + ObjToIntMap master; + private int cursor; + private int remaining; + private Object[] keys; + private int[] values; + } + + public ObjToIntMap() { + this(4); + } + + public ObjToIntMap(int keyCountHint) { + if (keyCountHint < 0) Kit.codeBug(); + // Table grow when number of stored keys >= 3/4 of max capacity + int minimalCapacity = keyCountHint * 4 / 3; + int i; + for (i = 2; (1 << i) < minimalCapacity; ++i) { } + power = i; + if (check && power < 2) Kit.codeBug(); + } + + public boolean isEmpty() { + return keyCount == 0; + } + + public int size() { + return keyCount; + } + + public boolean has(Object key) { + if (key == null) { key = UniqueTag.NULL_VALUE; } + return 0 <= findIndex(key); + } + + /** + * Get integer value assigned with key. + * @return key integer value or defaultValue if key is absent + */ + public int get(Object key, int defaultValue) { + if (key == null) { key = UniqueTag.NULL_VALUE; } + int index = findIndex(key); + if (0 <= index) { + return values[index]; + } + return defaultValue; + } + + /** + * Get integer value assigned with key. + * @return key integer value + * @throws RuntimeException if key does not exist + */ + public int getExisting(Object key) { + if (key == null) { key = UniqueTag.NULL_VALUE; } + int index = findIndex(key); + if (0 <= index) { + return values[index]; + } + // Key must exist + Kit.codeBug(); + return 0; + } + + public void put(Object key, int value) { + if (key == null) { key = UniqueTag.NULL_VALUE; } + int index = ensureIndex(key); + values[index] = value; + } + + /** + * If table already contains a key that equals to keyArg, return that key + * while setting its value to zero, otherwise add keyArg with 0 value to + * the table and return it. + */ + public Object intern(Object keyArg) { + boolean nullKey = false; + if (keyArg == null) { + nullKey = true; + keyArg = UniqueTag.NULL_VALUE; + } + int index = ensureIndex(keyArg); + values[index] = 0; + return (nullKey) ? null : keys[index]; + } + + public void remove(Object key) { + if (key == null) { key = UniqueTag.NULL_VALUE; } + int index = findIndex(key); + if (0 <= index) { + keys[index] = DELETED; + --keyCount; + } + } + + public void clear() { + int i = keys.length; + while (i != 0) { + keys[--i] = null; + } + keyCount = 0; + occupiedCount = 0; + } + + public Iterator newIterator() { + return new Iterator(this); + } + + // The sole purpose of the method is to avoid accessing private fields + // from the Iterator inner class to workaround JDK 1.1 compiler bug which + // generates code triggering VerifierError on recent JVMs + final void initIterator(Iterator i) { + i.init(keys, values, keyCount); + } + + /** Return array of present keys */ + public Object[] getKeys() { + Object[] array = new Object[keyCount]; + getKeys(array, 0); + return array; + } + + public void getKeys(Object[] array, int offset) { + int count = keyCount; + for (int i = 0; count != 0; ++i) { + Object key = keys[i]; + if (key != null && key != DELETED) { + if (key == UniqueTag.NULL_VALUE) { key = null; } + array[offset] = key; + ++offset; + --count; + } + } + } + + private static int tableLookupStep(int fraction, int mask, int power) { + int shift = 32 - 2 * power; + if (shift >= 0) { + return ((fraction >>> shift) & mask) | 1; + } + else { + return (fraction & (mask >>> -shift)) | 1; + } + } + + private int findIndex(Object key) { + if (keys != null) { + int hash = key.hashCode(); + int fraction = hash * A; + int index = fraction >>> (32 - power); + Object test = keys[index]; + if (test != null) { + int N = 1 << power; + if (test == key + || (values[N + index] == hash && test.equals(key))) + { + return index; + } + // Search in table after first failed attempt + int mask = N - 1; + int step = tableLookupStep(fraction, mask, power); + int n = 0; + for (;;) { + if (check) { + if (n >= occupiedCount) Kit.codeBug(); + ++n; + } + index = (index + step) & mask; + test = keys[index]; + if (test == null) { + break; + } + if (test == key + || (values[N + index] == hash && test.equals(key))) + { + return index; + } + } + } + } + return -1; + } + +// Insert key that is not present to table without deleted entries +// and enough free space + private int insertNewKey(Object key, int hash) { + if (check && occupiedCount != keyCount) Kit.codeBug(); + if (check && keyCount == 1 << power) Kit.codeBug(); + int fraction = hash * A; + int index = fraction >>> (32 - power); + int N = 1 << power; + if (keys[index] != null) { + int mask = N - 1; + int step = tableLookupStep(fraction, mask, power); + int firstIndex = index; + do { + if (check && keys[index] == DELETED) Kit.codeBug(); + index = (index + step) & mask; + if (check && firstIndex == index) Kit.codeBug(); + } while (keys[index] != null); + } + keys[index] = key; + values[N + index] = hash; + ++occupiedCount; + ++keyCount; + + return index; + } + + private void rehashTable() { + if (keys == null) { + if (check && keyCount != 0) Kit.codeBug(); + if (check && occupiedCount != 0) Kit.codeBug(); + int N = 1 << power; + keys = new Object[N]; + values = new int[2 * N]; + } + else { + // Check if removing deleted entries would free enough space + if (keyCount * 2 >= occupiedCount) { + // Need to grow: less then half of deleted entries + ++power; + } + int N = 1 << power; + Object[] oldKeys = keys; + int[] oldValues = values; + int oldN = oldKeys.length; + keys = new Object[N]; + values = new int[2 * N]; + + int remaining = keyCount; + occupiedCount = keyCount = 0; + for (int i = 0; remaining != 0; ++i) { + Object key = oldKeys[i]; + if (key != null && key != DELETED) { + int keyHash = oldValues[oldN + i]; + int index = insertNewKey(key, keyHash); + values[index] = oldValues[i]; + --remaining; + } + } + } + } + +// Ensure key index creating one if necessary + private int ensureIndex(Object key) { + int hash = key.hashCode(); + int index = -1; + int firstDeleted = -1; + if (keys != null) { + int fraction = hash * A; + index = fraction >>> (32 - power); + Object test = keys[index]; + if (test != null) { + int N = 1 << power; + if (test == key + || (values[N + index] == hash && test.equals(key))) + { + return index; + } + if (test == DELETED) { + firstDeleted = index; + } + + // Search in table after first failed attempt + int mask = N - 1; + int step = tableLookupStep(fraction, mask, power); + int n = 0; + for (;;) { + if (check) { + if (n >= occupiedCount) Kit.codeBug(); + ++n; + } + index = (index + step) & mask; + test = keys[index]; + if (test == null) { + break; + } + if (test == key + || (values[N + index] == hash && test.equals(key))) + { + return index; + } + if (test == DELETED && firstDeleted < 0) { + firstDeleted = index; + } + } + } + } + // Inserting of new key + if (check && keys != null && keys[index] != null) + Kit.codeBug(); + if (firstDeleted >= 0) { + index = firstDeleted; + } + else { + // Need to consume empty entry: check occupation level + if (keys == null || occupiedCount * 4 >= (1 << power) * 3) { + // Too litle unused entries: rehash + rehashTable(); + return insertNewKey(key, hash); + } + ++occupiedCount; + } + keys[index] = key; + values[(1 << power) + index] = hash; + ++keyCount; + return index; + } + + private void writeObject(ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + int count = keyCount; + for (int i = 0; count != 0; ++i) { + Object key = keys[i]; + if (key != null && key != DELETED) { + --count; + out.writeObject(key); + out.writeInt(values[i]); + } + } + } + + private void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + int writtenKeyCount = keyCount; + if (writtenKeyCount != 0) { + keyCount = 0; + int N = 1 << power; + keys = new Object[N]; + values = new int[2 * N]; + for (int i = 0; i != writtenKeyCount; ++i) { + Object key = in.readObject(); + int hash = key.hashCode(); + int index = insertNewKey(key, hash); + values[index] = in.readInt(); + } + } + } + +// A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32) +// See Knuth etc. + private static final int A = 0x9e3779b9; + + private static final Object DELETED = new Object(); + +// Structure of kyes and values arrays (N == 1 << power): +// keys[0 <= i < N]: key value or null or DELETED mark +// values[0 <= i < N]: value of key at keys[i] +// values[N <= i < 2*N]: hash code of key at keys[i-N] + + private transient Object[] keys; + private transient int[] values; + + private int power; + private int keyCount; + private transient int occupiedCount; // == keyCount + deleted_count + +// If true, enables consitency checks + private static final boolean check = false; + +/* TEST START + + public static void main(String[] args) { + if (!check) { + System.err.println("Set check to true and re-run"); + throw new RuntimeException("Set check to true and re-run"); + } + + ObjToIntMap map; + map = new ObjToIntMap(0); + testHash(map, 3); + map = new ObjToIntMap(0); + testHash(map, 10 * 1000); + map = new ObjToIntMap(); + testHash(map, 10 * 1000); + map = new ObjToIntMap(30 * 1000); + testHash(map, 10 * 100); + map.clear(); + testHash(map, 4); + map = new ObjToIntMap(0); + testHash(map, 10 * 100); + } + + private static void testHash(ObjToIntMap map, int N) { + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + Object key = testKey(i); + check(-1 == map.get(key, -1)); + map.put(key, i); + check(i == map.get(key, -1)); + } + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + Object key = testKey(i); + map.put(key, i); + check(i == map.get(key, -1)); + } + + check(map.size() == N); + + System.out.print("."); System.out.flush(); + Object[] keys = map.getKeys(); + check(keys.length == N); + for (int i = 0; i != N; ++i) { + Object key = keys[i]; + check(map.has(key)); + } + + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + Object key = testKey(i); + check(i == map.get(key, -1)); + } + + int Nsqrt = -1; + for (int i = 0; ; ++i) { + if (i * i >= N) { + Nsqrt = i; + break; + } + } + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + Object key = testKey(i * i); + map.put(key, i); + check(i == map.get(key, -1)); + } + + check(map.size() == 2 * N - Nsqrt); + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + Object key = testKey(i * i); + check(i == map.get(key, -1)); + } + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + Object key = testKey(-1 - i * i); + map.put(key, i); + check(i == map.get(key, -1)); + } + + check(map.size() == 3 * N - Nsqrt); + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + Object key = testKey(-1 - i * i); + map.remove(key); + check(!map.has(key)); + } + + check(map.size() == 2 * N - Nsqrt); + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + Object key = testKey(i * i); + check(i == map.get(key, -1)); + } + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + Object key = testKey(i); + int j = intSqrt(i); + if (j * j == i) { + check(j == map.get(key, -1)); + }else { + check(i == map.get(key, -1)); + } + } + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + Object key = testKey(i * i); + map.remove(key); + check(-2 == map.get(key, -2)); + } + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + Object key = testKey(i); + map.put(key, i); + check(i == map.get(key, -2)); + } + + check(map.size() == N); + + System.out.print("."); System.out.flush(); + for (int i = 0; i != N; ++i) { + Object key = testKey(i); + check(i == map.get(key, -1)); + } + + System.out.print("."); System.out.flush(); + ObjToIntMap copy = (ObjToIntMap)writeAndRead(map); + check(copy.size() == N); + + for (int i = 0; i != N; ++i) { + Object key = testKey(i); + check(i == copy.get(key, -1)); + } + + System.out.print("."); System.out.flush(); + checkSameMaps(copy, map); + + System.out.println(); System.out.flush(); + } + + private static void checkSameMaps(ObjToIntMap map1, ObjToIntMap map2) { + check(map1.size() == map2.size()); + Object[] keys = map1.getKeys(); + check(keys.length == map1.size()); + for (int i = 0; i != keys.length; ++i) { + check(map1.get(keys[i], -1) == map2.get(keys[i], -1)); + } + } + + private static void check(boolean condition) { + if (!condition) Kit.codeBug(); + } + + private static Object[] testPool; + + private static Object testKey(int i) { + int MAX_POOL = 100; + if (0 <= i && i < MAX_POOL) { + if (testPool != null && testPool[i] != null) { + return testPool[i]; + } + } + Object x = new Double(i + 0.5); + if (0 <= i && i < MAX_POOL) { + if (testPool == null) { + testPool = new Object[MAX_POOL]; + } + testPool[i] = x; + } + return x; + } + + private static int intSqrt(int i) { + int approx = (int)Math.sqrt(i) + 1; + while (approx * approx > i) { + --approx; + } + return approx; + } + + private static Object writeAndRead(Object obj) { + try { + java.io.ByteArrayOutputStream + bos = new java.io.ByteArrayOutputStream(); + java.io.ObjectOutputStream + out = new java.io.ObjectOutputStream(bos); + out.writeObject(obj); + out.close(); + byte[] data = bos.toByteArray(); + java.io.ByteArrayInputStream + bis = new java.io.ByteArrayInputStream(data); + java.io.ObjectInputStream + in = new java.io.ObjectInputStream(bis); + Object result = in.readObject(); + in.close(); + return result; + }catch (Exception ex) { + throw new RuntimeException("Unexpected"); + } + } + +// TEST END */ + +} |