ObjToIntMap was added to map Objects to int in a memory wise way and VariableTable was modified to use ObjToIntMap for itsVariableNames

This commit is contained in:
igor%mir2.org 2002-03-14 20:37:15 +00:00
Родитель 73da3c3144
Коммит e6d6980a29
2 изменённых файлов: 590 добавлений и 31 удалений

Просмотреть файл

@ -0,0 +1,567 @@
/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
* 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 Netscape are
* Copyright (C) 1997-2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (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 NPL, indicate your decision by
* deleting the provisions above and replace 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 NPL or the GPL.
*/
package org.mozilla.javascript;
import java.io.Serializable;
/**
* 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 = -55740507849272970L;
// 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;
cursor = -1;
}
void init(Object[] keys, int[] values) {
this.keys = keys;
this.values = values;
this.cursor = (keys == null) ? 0 : keys.length;
}
public void start() {
master.initIterator(this);
next();
}
public boolean done() {
return cursor < 0;
}
public void next() {
if (cursor < 0) Context.codeBug();
--cursor;
while (cursor >= 0) {
Object key = keys[cursor];
if (key != null && key != DELETED) {
break;
}
--cursor;
}
}
public Object getKey() {
return keys[cursor];
}
public int getValue() {
return values[cursor];
}
public void setValue(int value) {
values[cursor] = value;
}
ObjToIntMap master;
private int cursor;
private Object[] keys;
private int[] values;
}
public ObjToIntMap() {
this(4);
}
public ObjToIntMap(int keyCountHint) {
if (keyCountHint < 0) Context.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) Context.codeBug();
}
public boolean isEmpty() {
return keyCount == 0;
}
public int size() {
return keyCount;
}
public boolean has(Object key) {
return 0 <= findIndex(key);
}
/**
* Get integer value assigned with key.
* @return key integer value or defaultValue if key is absent or does
* not have int value
*/
public int get(Object key, int defaultValue) {
if (key == null) Context.codeBug();
int index = findIndex(key);
if (0 <= index) {
return values[index];
}
return defaultValue;
}
/**
* Get integer value assigned with key.
* @return key integer value or defaultValue if key does not exist or does
* not have int value
* @throws RuntimeException if key does not exist or does
* not have int value
*/
public int getExisting(Object key) {
if (key == null) Context.codeBug();
int index = findIndex(key);
if (0 <= index) {
return values[index];
}
// Key must exist
Context.codeBug();
return 0;
}
public void put(Object key, int value) {
if (key == null) Context.codeBug();
int index = ensureIndex(key);
values[index] = value;
}
public void remove(Object key) {
if (key == null) Context.codeBug();
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);
}
void initIterator(Iterator i) {
i.init(keys, values);
}
/** Return array of present keys */
public Object[] getKeys() {
Object[] keys = this.keys;
int count = keyCount;
Object[] result = new Object[count];
for (int i = 0; count != 0; ++i) {
Object key = keys[i];
if (key != null && key != DELETED) {
result[--count] = key;
}
}
return result;
}
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) Context.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;
}
private int getFreeIndex(Object key, int hash) {
int fraction = hash * A;
int index = fraction >>> (32 - power);
if (keys[index] != null) {
int mask = (1 << power) - 1;
int step = tableLookupStep(fraction, mask, power);
int firstIndex = index;
do {
if (check && keys[index] == DELETED) Context.codeBug();
index = (index + step) & mask;
if (check && firstIndex == index) Context.codeBug();
} while (keys[index] != null);
}
return index;
}
private void rehashTable() {
if (keys == null) {
if (check && keyCount != 0) Context.codeBug();
if (check && occupiedCount != 0) Context.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];
for (int i = 0, remaining = keyCount; remaining != 0; ++i) {
Object key = oldKeys[i];
if (key != null && key != DELETED) {
int keyHash = oldValues[oldN + i];
int index = getFreeIndex(key, keyHash);
keys[index] = key;
values[index] = oldValues[i];
values[N + index] = keyHash;
--remaining;
}
}
occupiedCount = keyCount;
}
}
// 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) Context.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)
Context.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();
index = getFreeIndex(key, hash);
}
++occupiedCount;
}
keys[index] = key;
values[(1 << power) + index] = hash;
++keyCount;
return index;
}
// 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 Object[] keys;
private int[] values;
private int power;
private int keyCount;
private int occupiedCount; // == keyCount + deleted_count
// If true, enables consitency checks
private static final boolean check = false;
/*
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.println(); System.out.flush();
}
private static void check(boolean condition) {
if (!condition) Context.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;
}
//*/
}

Просмотреть файл

@ -20,6 +20,7 @@
*
* Contributor(s):
* Roger Lawrence
* Igor Bukanov
*
* Alternatively, the contents of this file may be used under the
* terms of the GNU Public License (the "GPL"), in which case the
@ -58,19 +59,15 @@ public class VariableTable {
}
public LocalVariable getVariable(String name) {
Integer vIndex = (Integer)(itsVariableNames.get(name));
if (vIndex != null)
return (LocalVariable)(itsVariables.elementAt(vIndex.intValue()));
int vIndex = itsVariableNames.get(name, -1);
if (vIndex != -1)
return (LocalVariable)(itsVariables.elementAt(vIndex));
else
return null;
}
public int getOrdinal(String name) {
Integer vIndex = (Integer)(itsVariableNames.get(name));
if (vIndex != null)
return vIndex.intValue();
else
return -1;
return itsVariableNames.get(name, -1);
}
public String getName(int index) {
@ -97,53 +94,48 @@ public class VariableTable {
}
public void addParameter(String pName) {
Integer pIndex = (Integer)(itsVariableNames.get(pName));
if (pIndex != null) {
LocalVariable p = (LocalVariable)
(itsVariables.elementAt(pIndex.intValue()));
int pIndex = itsVariableNames.get(pName, -1);
if (pIndex != -1) {
LocalVariable p = (LocalVariable)(itsVariables.elementAt(pIndex));
if (p.isParameter()) {
String message = Context.getMessage1("msg.dup.parms", pName);
Context.reportWarning(message, null, 0, null, 0);
}
else { // there's a local variable with this name, blow it off
itsVariables.removeElementAt(pIndex.intValue());
itsVariables.removeElementAt(pIndex);
}
}
int curIndex = varStart++;
LocalVariable lVar = createLocalVariable(pName, true);
itsVariables.insertElementAt(lVar, curIndex);
itsVariableNames.put(pName, new Integer(curIndex));
itsVariableNames.put(pName, curIndex);
}
public void addLocal(String vName) {
Integer vIndex = (Integer)(itsVariableNames.get(vName));
if (vIndex != null) {
int vIndex = itsVariableNames.get(vName, -1);
if (vIndex != -1) {
// There's already a variable or parameter with this name.
return;
}
int index = itsVariables.size();
LocalVariable lVar = createLocalVariable(vName, false);
itsVariables.addElement(lVar);
itsVariableNames.put(vName, new Integer(index));
itsVariableNames.put(vName, index);
}
// This should only be called very early in compilation
public void removeLocal(String name) {
Integer i = (Integer) itsVariableNames.get(name);
if (i != null) {
itsVariables.removeElementAt(i.intValue());
int i = itsVariableNames.get(name, -1);
if (i != -1) {
itsVariables.removeElementAt(i);
itsVariableNames.remove(name);
Hashtable ht = new Hashtable(11);
Enumeration e = itsVariableNames.keys();
while (e.hasMoreElements()) {
Object k = e.nextElement();
Integer v = (Integer) itsVariableNames.get(k);
int v2 = v.intValue();
if (v2 > i.intValue())
v = new Integer(v2 - 1);
ht.put(k, v);
ObjToIntMap.Iterator iter = itsVariableNames.newIterator();
for (iter.start(); !iter.done(); iter.next()) {
int v = iter.getValue();
if (v > i) {
iter.setValue(v - 1);
}
}
itsVariableNames = ht;
}
}
@ -151,7 +143,7 @@ public class VariableTable {
protected Vector itsVariables = new Vector();
// mapping from name to index in list
protected Hashtable itsVariableNames = new Hashtable(11);
private ObjToIntMap itsVariableNames = new ObjToIntMap(11);
protected int varStart; // index in list of first variable