1. Implementing Externalizable interface in ObjToIntMap and UintMap to allow for efficient storage of internal hash table data. For ObjToIntMap it allows to restore correctly cached values of object's hash codes and do not store internal DELETED mark.

2. ObjToIntMap.clear and UintMap.clear now do not discard internal buffers, but clears references to external objects to match behavior of Java Vector.clear and Hashtable.clear.
This commit is contained in:
igor%mir2.org 2002-03-15 07:13:33 +00:00
Родитель afcecf88fe
Коммит af8cfb85ac
2 изменённых файлов: 338 добавлений и 59 удалений

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

@ -35,7 +35,10 @@
package org.mozilla.javascript;
import java.io.Serializable;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* Map to associate objects to integers.
@ -47,9 +50,9 @@ import java.io.Serializable;
*
*/
public class ObjToIntMap implements Serializable {
public class ObjToIntMap implements Externalizable {
// static final long serialVersionUID = -55740507849272970L;
static final long serialVersionUID = -6999544351027769835L;
// Map implementation via hashtable,
// follows "The Art of Computer Programming" by Donald E. Knuth
@ -200,7 +203,6 @@ public class ObjToIntMap implements Serializable {
/** 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) {
@ -260,11 +262,16 @@ public class ObjToIntMap implements Serializable {
return -1;
}
private int getFreeIndex(Object key, int hash) {
// 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) Context.codeBug();
if (check && keyCount == 1 << power) Context.codeBug();
int fraction = hash * A;
int index = fraction >>> (32 - power);
int N = 1 << power;
if (keys[index] != null) {
int mask = (1 << power) - 1;
int mask = N - 1;
int step = tableLookupStep(fraction, mask, power);
int firstIndex = index;
do {
@ -273,6 +280,11 @@ public class ObjToIntMap implements Serializable {
if (check && firstIndex == index) Context.codeBug();
} while (keys[index] != null);
}
keys[index] = key;
values[N + index] = hash;
++occupiedCount;
++keyCount;
return index;
}
@ -297,18 +309,17 @@ public class ObjToIntMap implements Serializable {
keys = new Object[N];
values = new int[2 * N];
for (int i = 0, remaining = keyCount; remaining != 0; ++i) {
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 = getFreeIndex(key, keyHash);
keys[index] = key;
int index = insertNewKey(key, keyHash);
values[index] = oldValues[i];
values[N + index] = keyHash;
--remaining;
}
}
occupiedCount = keyCount;
}
}
@ -368,7 +379,7 @@ public class ObjToIntMap implements Serializable {
if (keys == null || occupiedCount * 4 >= (1 << power) * 3) {
// Too litle unused entries: rehash
rehashTable();
index = getFreeIndex(key, hash);
return insertNewKey(key, hash);
}
++occupiedCount;
}
@ -378,6 +389,41 @@ public class ObjToIntMap implements Serializable {
return index;
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(power);
out.writeInt(keyCount);
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]);
}
}
}
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException
{
power = in.readInt();
int writtenKeyCount = in.readInt();
if (writtenKeyCount != 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;
@ -399,7 +445,8 @@ public class ObjToIntMap implements Serializable {
// 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");
@ -528,9 +575,36 @@ public class ObjToIntMap implements Serializable {
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) Context.codeBug();
}
@ -562,6 +636,27 @@ public class ObjToIntMap implements Serializable {
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 */
//*/
}

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

@ -35,7 +35,10 @@
package org.mozilla.javascript;
import java.io.Serializable;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
/**
* Map to associate non-negative integers to objects or integers.
@ -47,9 +50,9 @@ import java.io.Serializable;
*
*/
class UintMap implements Serializable {
class UintMap implements Externalizable {
static final long serialVersionUID = -55740507849272970L;
static final long serialVersionUID = -4108482308923954951L;
// Map implementation via hashtable,
// follows "The Art of Computer Programming" by Donald E. Knuth
@ -59,13 +62,13 @@ class UintMap implements Serializable {
}
public UintMap(int initialCapacity) {
if (Context.check && initialCapacity < 0) Context.codeBug();
if (initialCapacity < 0) Context.codeBug();
// Table grow when number of stored keys >= 3/4 of max capacity
int minimalCapacity = initialCapacity * 4 / 3;
int i;
for (i = 2; (1 << i) < minimalCapacity; ++i) { }
minimalPower = i;
if (checkSelf && minimalPower < 2) Context.codeBug();
power = i;
if (check && power < 2) Context.codeBug();
}
public boolean isEmpty() {
@ -77,18 +80,18 @@ class UintMap implements Serializable {
}
public boolean has(int key) {
if (Context.check && key < 0) Context.codeBug();
if (key < 0) Context.codeBug();
return 0 <= findIndex(key);
}
public boolean isObjectType(int key) {
if (Context.check && key < 0) Context.codeBug();
if (key < 0) Context.codeBug();
int index = findIndex(key);
return index >= 0 && isObjectTypeValue(index);
}
public boolean isIntType(int key) {
if (Context.check && key < 0) Context.codeBug();
if (key < 0) Context.codeBug();
int index = findIndex(key);
return index >= 0 && !isObjectTypeValue(index);
}
@ -99,7 +102,7 @@ class UintMap implements Serializable {
* not have object value
*/
public Object getObject(int key) {
if (Context.check && key < 0) Context.codeBug();
if (key < 0) Context.codeBug();
if (values != null) {
int index = findIndex(key);
if (0 <= index) {
@ -115,7 +118,7 @@ class UintMap implements Serializable {
* not have int value
*/
public int getInt(int key, int defaultValue) {
if (Context.check && key < 0) Context.codeBug();
if (key < 0) Context.codeBug();
if (ivaluesShift != 0) {
int index = findIndex(key);
if (0 <= index) {
@ -135,7 +138,7 @@ class UintMap implements Serializable {
* not have int value
*/
public int getExistingInt(int key) {
if (Context.check && key < 0) Context.codeBug();
if (key < 0) Context.codeBug();
if (ivaluesShift != 0) {
int index = findIndex(key);
if (0 <= index) {
@ -145,12 +148,12 @@ class UintMap implements Serializable {
}
}
// Key must exist
if (Context.check) Context.codeBug();
Context.codeBug();
return 0;
}
public void put(int key, Object value) {
if (Context.check && !(key >= 0 && value != null)) Context.codeBug();
if (!(key >= 0 && value != null)) Context.codeBug();
int index = ensureIndex(key, false);
if (values == null) {
values = new Object[1 << power];
@ -159,13 +162,16 @@ class UintMap implements Serializable {
}
public void put(int key, int value) {
if (Context.check && key < 0) Context.codeBug();
if (key < 0) Context.codeBug();
int index = ensureIndex(key, true);
if (ivaluesShift == 0) {
int N = 1 << power;
int[] tmp = new int[N * 2];
System.arraycopy(keys, 0, tmp, 0, N);
keys = tmp;
// keys.length cam be N * 2 after clear which set ivaluesShift to 0
if (keys.length != N * 2) {
int[] tmp = new int[N * 2];
System.arraycopy(keys, 0, tmp, 0, N);
keys = tmp;
}
ivaluesShift = N;
}
keys[ivaluesShift + index] = value;
@ -173,7 +179,7 @@ class UintMap implements Serializable {
}
public void remove(int key) {
if (Context.check && key < 0) Context.codeBug();
if (key < 0) Context.codeBug();
int index = findIndex(key);
if (0 <= index) {
keys[index] = DELETED;
@ -183,9 +189,17 @@ class UintMap implements Serializable {
}
public void clear() {
power = 0;
keys = null;
values = null;
int N = 1 << power;
if (keys != null) {
for (int i = 0; i != N; ++i) {
keys[i] = EMPTY;
}
if (values != null) {
for (int i = 0; i != N; ++i) {
values[i] = null;
}
}
}
ivaluesShift = 0;
keyCount = 0;
occupiedCount = 0;
@ -228,7 +242,7 @@ class UintMap implements Serializable {
int step = tableLookupStep(fraction, mask, power);
int n = 0;
do {
if (checkSelf) {
if (check) {
if (n >= occupiedCount) Context.codeBug();
++n;
}
@ -241,7 +255,11 @@ class UintMap implements Serializable {
return -1;
}
private int getFreeIndex(int key) {
// Insert key that is not present to table without deleted entries
// and enough free space
private int insertNewKey(int key) {
if (check && occupiedCount != keyCount) Context.codeBug();
if (check && keyCount == 1 << power) Context.codeBug();
int[] keys = this.keys;
int fraction = key * A;
int index = fraction >>> (32 - power);
@ -250,17 +268,19 @@ class UintMap implements Serializable {
int step = tableLookupStep(fraction, mask, power);
int firstIndex = index;
do {
if (checkSelf && keys[index] == DELETED) Context.codeBug();
if (check && keys[index] == DELETED) Context.codeBug();
index = (index + step) & mask;
if (checkSelf && firstIndex == index) Context.codeBug();
if (check && firstIndex == index) Context.codeBug();
} while (keys[index] != EMPTY);
}
keys[index] = key;
++occupiedCount;
++keyCount;
return index;
}
private void rehashTable(boolean ensureIntSpace) {
if (keys == null) { power = minimalPower; }
else {
if (keys != null) {
// Check if removing deleted entries would free enough space
if (keyCount * 2 >= occupiedCount) {
// Need to grow: less then half of deleted entries
@ -281,12 +301,14 @@ class UintMap implements Serializable {
Object[] oldValues = values;
if (oldValues != null) { values = new Object[N]; }
if (old != null) {
for (int i = 0, remaining = keyCount; remaining != 0; ++i) {
int entry = old[i];
if (entry != EMPTY && entry != DELETED) {
int index = getFreeIndex(entry);
keys[index] = entry;
int oldCount = keyCount;
occupiedCount = 0;
if (oldCount != 0) {
keyCount = 0;
for (int i = 0, remaining = oldCount; remaining != 0; ++i) {
int key = old[i];
if (key != EMPTY && key != DELETED) {
int index = insertNewKey(key);
if (oldValues != null) {
values[index] = oldValues[i];
}
@ -297,7 +319,6 @@ class UintMap implements Serializable {
}
}
}
occupiedCount = keyCount;
}
// Ensure key index creating one if necessary
@ -317,7 +338,7 @@ class UintMap implements Serializable {
int step = tableLookupStep(fraction, mask, power);
int n = 0;
do {
if (checkSelf) {
if (check) {
if (n >= occupiedCount) Context.codeBug();
++n;
}
@ -331,7 +352,7 @@ class UintMap implements Serializable {
}
}
// Inserting of new key
if (checkSelf && keys != null && keys[index] != EMPTY)
if (check && keys != null && keys[index] != EMPTY)
Context.codeBug();
if (firstDeleted >= 0) {
index = firstDeleted;
@ -342,7 +363,7 @@ class UintMap implements Serializable {
// Too litle unused entries: rehash
rehashTable(intType);
keys = this.keys;
index = getFreeIndex(key);
return insertNewKey(key);
}
++occupiedCount;
}
@ -352,11 +373,72 @@ class UintMap implements Serializable {
}
private boolean isObjectTypeValue(int index) {
if (checkSelf && !(index >= 0 && index < (1 << power)))
if (check && !(index >= 0 && index < (1 << power)))
Context.codeBug();
return values != null && values[index] != null;
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(power);
out.writeInt(keyCount);
boolean hasIntValues = (ivaluesShift != 0);
boolean hasObjectValues = (values != null);
out.writeBoolean(hasIntValues);
out.writeBoolean(hasObjectValues);
int count = keyCount;
for (int i = 0; count != 0; ++i) {
int key = keys[i];
if (key != EMPTY && key != DELETED) {
--count;
out.writeInt(key);
if (hasIntValues) {
out.writeInt(keys[ivaluesShift + i]);
}
if (hasObjectValues) {
out.writeObject(values[i]);
}
}
}
}
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException
{
power = in.readInt();
int writtenKeyCount = in.readInt();
boolean hasIntValues = in.readBoolean();
boolean hasObjectValues = in.readBoolean();
if (writtenKeyCount != 0) {
int N = 1 << power;
if (hasIntValues) {
keys = new int[2 * N];
ivaluesShift = N;
}else {
keys = new int[N];
}
for (int i = 0; i != N; ++i) {
keys[i] = EMPTY;
}
if (hasObjectValues) {
values = new Object[N];
}
for (int i = 0; i != writtenKeyCount; ++i) {
int key = in.readInt();
int index = insertNewKey(key);
if (hasIntValues) {
int ivalue = in.readInt();
keys[ivaluesShift + index] = ivalue;
}
if (hasObjectValues) {
values[index] = in.readObject();
}
}
}
}
// A == golden_ratio * (1 << 32) = ((sqrt(5) - 1) / 2) * (1 << 32)
// See Knuth etc.
private static final int A = 0x9e3779b9;
@ -372,7 +454,6 @@ class UintMap implements Serializable {
private int[] keys;
private Object[] values;
private int minimalPower;
private int power;
private int keyCount;
private int occupiedCount; // == keyCount + deleted_count
@ -381,13 +462,21 @@ class UintMap implements Serializable {
// values associated with keys
private int ivaluesShift;
// Rudimentary support for Design-by-Contract
private static final boolean checkSelf = Context.check && false;
// 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");
}
UintMap map;
map = new UintMap();
testHash(map, 2);
map = new UintMap();
testHash(map, 10 * 1000);
map = new UintMap(30 * 1000);
testHash(map, 10 * 100);
@ -469,12 +558,107 @@ class UintMap implements Serializable {
check(map.size() == old_size);
}
System.out.print("."); System.out.flush();
map.clear();
check(map.size() == 0);
for (int i = 0; i != N; ++i) {
map.put(i * i, i);
map.put(i * i + 1, new Double(i+0.5));
}
checkSameMaps(map, (UintMap)writeAndRead(map));
System.out.print("."); System.out.flush();
map = new UintMap(0);
checkSameMaps(map, (UintMap)writeAndRead(map));
map = new UintMap(1);
checkSameMaps(map, (UintMap)writeAndRead(map));
map = new UintMap(1000);
checkSameMaps(map, (UintMap)writeAndRead(map));
System.out.print("."); System.out.flush();
map = new UintMap(N / 10);
for (int i = 0; i != N; ++i) {
map.put(2*i+1, i);
}
checkSameMaps(map, (UintMap)writeAndRead(map));
System.out.print("."); System.out.flush();
map = new UintMap(N / 10);
for (int i = 0; i != N; ++i) {
map.put(2*i+1, i);
}
for (int i = 0; i != N / 2; ++i) {
map.remove(2*i+1);
}
checkSameMaps(map, (UintMap)writeAndRead(map));
System.out.print("."); System.out.flush();
map = new UintMap();
for (int i = 0; i != N; ++i) {
map.put(2*i+1, new Double(i + 10));
}
for (int i = 0; i != N / 2; ++i) {
map.remove(2*i+1);
}
checkSameMaps(map, (UintMap)writeAndRead(map));
System.out.println(); System.out.flush();
}
static void check(boolean condition) {
private static void checkSameMaps(UintMap map1, UintMap map2) {
check(map1.size() == map2.size());
int[] keys = map1.getKeys();
check(keys.length == map1.size());
for (int i = 0; i != keys.length; ++i) {
int key = keys[i];
check(map2.has(key));
check(map1.isObjectType(key) == map2.isObjectType(key));
check(map1.isIntType(key) == map2.isIntType(key));
Object o1 = map1.getObject(key);
Object o2 = map2.getObject(key);
if (map1.isObjectType(key)) {
check(o1.equals(o2));
}else {
check(map1.getObject(key) == null);
check(map2.getObject(key) == null);
}
if (map1.isIntType(key)) {
check(map1.getExistingInt(key) == map2.getExistingInt(key));
}else {
check(map1.getInt(key, -10) == -10);
check(map1.getInt(key, -11) == -11);
check(map2.getInt(key, -10) == -10);
check(map2.getInt(key, -11) == -11);
}
}
}
private static void check(boolean condition) {
if (!condition) Context.codeBug();
}
//*/
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) {
ex.printStackTrace();
throw new RuntimeException("Unexpected");
}
}
// TEST END */
}