Hashtable patch, more serialization patches

Jose Orlando Pereira jop at di.uminho.pt
Thu Feb 4 09:44:57 PST 1999


Hi,

There seems to be a bug in java.lang.Hashtable that is triggered when,
after being used for a while, all free slots are marked as "removed".
The first patch below corrects this problem.

The second patch is a rather large patch to object streams which:
 - corrects the bug I described yesterday (demonstrated by
   Alias.java), by introducing kaffe.util.IdentityHashtable (is
   kaffe.util the right place for this stuff?) and using it instead
   of a regular Hashtable in ObjectOutputStream;
 - substitutes Vector for Hashtable in ObjectInputStream, which
   increases performance of deserialization;
 - corrects another bug related to serializing deep class hierarchies
   which use alternating readObject/writeObject and default serialization
   strategies.

The current snapshot with these patches (and the multicast related one 
I sent earlier) enables Kaffe to run my app, making me a happy man. :-)

-- 
Jose Orlando Pereira
* mailto:jop at di.uminho.pt * http://gsd.di.uminho.pt/~jop *
-------------- next part --------------
diff -Nru kaffe-snap/libraries/javalib/java/util/Hashtable.java kaffe-snap-jop/libraries/javalib/java/util/Hashtable.java
--- kaffe-snap/libraries/javalib/java/util/Hashtable.java	Sun Dec 13 22:11:53 1998
+++ kaffe-snap-jop/libraries/javalib/java/util/Hashtable.java	Thu Feb  4 15:43:37 1999
@@ -206,6 +206,12 @@
 	return (null);
       }
     }
+	if (space != -1) {
+	keys[space] = key;
+	elements[space] = value;
+	numberOfKeys++;
+	return (null);
+	}
     // We shouldn't get here.
     throw new Error("Inconsistent Hashtable");
   }
-------------- next part --------------
diff -Nru kaffe-snap/libraries/javalib/java/io/ObjectInputStream.java kaffe-snap-jop/libraries/javalib/java/io/ObjectInputStream.java
--- kaffe-snap/libraries/javalib/java/io/ObjectInputStream.java	Wed Feb  3 21:02:09 1999
+++ kaffe-snap-jop/libraries/javalib/java/io/ObjectInputStream.java	Thu Feb  4 16:52:15 1999
@@ -12,7 +12,7 @@
 
 import java.lang.String;
 import java.io.Serializable;
-import java.util.Hashtable;
+import java.util.Vector;
 import kaffe.util.NotImplemented;
 
 public class ObjectInputStream
@@ -25,8 +25,8 @@
 private int pos = 0;
 private int len = 0;
 private boolean doBlocking = false;
-private Hashtable objectsDone = new Hashtable();
-private int nextKey = 0x007e0000;
+private Vector objectsDone = new Vector();
+private static final int firstKey = 0x007e0001;
 private Object currObject;
 private ObjectStreamClass currStreamClass;
 private boolean enableResolve = false;
@@ -190,8 +190,7 @@
 
 		int tok = readUnsignedByte();
 		if (tok == ObjectStreamConstants.TC_REFERENCE) {
-			Integer key = new Integer(readInt());
-			currObject = objectsDone.get(key);
+			currObject = objectsDone.elementAt(readInt()-firstKey);
 			if (currObject == null) {
 				throw new StreamCorruptedException("reference to unknown object");
 			}
@@ -209,23 +208,16 @@
 					throw new StreamCorruptedException("expected class desc");
 				}
 				currObject = allocateNewObject(currStreamClass.clazz, null);
-				Integer key = new Integer(++nextKey);
-				objectsDone.put(key, currObject);
+				objectsDone.addElement(currObject);
 				if ((currStreamClass.method & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0) {
-                                        try {
-                                                ((Externalizable)currObject).readExternal(this);
-                                        }
-                                        catch (ClassCastException _) {
-                                        }
-				}
-				else if ((currStreamClass.method & ObjectStreamConstants.SC_WRRD_METHODS) != 0) {
-					invokeObjectReader(currObject, currObject.getClass());
-				}
-				else if ((currStreamClass.method & ObjectStreamConstants.SC_SERIALIZABLE) != 0) {
-					defaultReadObject();
+                	try {
+                		((Externalizable)currObject).readExternal(this);
+                	}
+                	catch (ClassCastException _) {
+                	}
 				}
 				else {
-					throw new StreamCorruptedException("unknown method type: " + currStreamClass.method);
+					readDataFields(currStreamClass, currObject);
 				}
 				break;
 
@@ -242,8 +234,7 @@
 			case ObjectStreamConstants.TC_CLASSDESC:
 				ObjectStreamClass cls = new ObjectStreamClass();
 				currObject = (Object)cls;
-				Integer key = new Integer(++nextKey);
-				objectsDone.put(key, currObject);
+				objectsDone.addElement(currObject);
 				invokeObjectReader(currObject, ObjectStreamClass.class);
 				cls.clazz = resolveClass(cls);
 				cls.buildFieldsAndOffset();
@@ -251,14 +242,13 @@
 
 			case ObjectStreamConstants.TC_STRING:
 				currObject = readUTF();
-				Integer key = new Integer(++nextKey);
-				objectsDone.put(key, currObject);
+				objectsDone.addElement(currObject);
 				break;
 
 			case ObjectStreamConstants.TC_RESET:
 				pos = 0;
 				len = 0;
-				objectsDone = new Hashtable();
+				objectsDone = new Vector();
 				enableBuffering(true);
 				break;
 
@@ -283,14 +273,23 @@
 
 public final void defaultReadObject() throws IOException, ClassNotFoundException, NotActiveException
 {
-	readDataFields(currStreamClass, currObject);
+	inputClassFields(currObject, currStreamClass.clazz, currStreamClass.fieldRdWr);
 }
 
-private void readDataFields(ObjectStreamClass strm, Object obj)
+private void readDataFields(ObjectStreamClass strm, Object obj) throws IOException, ClassNotFoundException 
 {
 	if (strm != null) {
 		readDataFields(strm.superclazzStream, obj);
-		inputClassFields(obj, strm.clazz, strm.fieldRdWr);
+		ObjectStreamClass oldStreamClass = currStreamClass;
+		currStreamClass = strm;
+		if ((strm.method & ObjectStreamConstants.SC_WRRD_METHODS) != 0) {
+			invokeObjectReader(obj, strm.clazz);
+		}
+		else if ((strm.method & ObjectStreamConstants.SC_SERIALIZABLE) != 0) {
+			defaultReadObject();
+		} else
+			throw new StreamCorruptedException("unknown method type: " + strm.method);
+		currStreamClass = oldStreamClass;
 	}
 }
 
@@ -314,8 +313,7 @@
 {
 	int len = readInt();
 	Object obj = allocateNewArray(currStreamClass.clazz, len);
-	Integer key = new Integer(++nextKey);
-	objectsDone.put(key, obj);
+	objectsDone.addElement(obj);
 	Class elem = currStreamClass.clazz.getComponentType();
 
 	if (elem == Object.class) {
diff -Nru kaffe-snap/libraries/javalib/java/io/ObjectOutputStream.java kaffe-snap-jop/libraries/javalib/java/io/ObjectOutputStream.java
--- kaffe-snap/libraries/javalib/java/io/ObjectOutputStream.java	Wed Dec  9 23:20:12 1998
+++ kaffe-snap-jop/libraries/javalib/java/io/ObjectOutputStream.java	Thu Feb  4 16:52:22 1999
@@ -12,7 +12,7 @@
 
 import java.lang.String;
 import java.io.Serializable;
-import java.util.Hashtable;
+import kaffe.util.IdentityHashtable;
 import kaffe.util.NotImplemented;
 
 public class ObjectOutputStream
@@ -26,7 +26,7 @@
 private OutputStream out;
 private byte[] outBuf = new byte[255];
 private int pos = 0;
-private Hashtable objectsDone = new Hashtable();
+private IdentityHashtable objectsDone = new IdentityHashtable();
 private int nextKey = 0x007e0000;
 private boolean doBlocking = false;
 private boolean enableReplace = false;
@@ -88,7 +88,7 @@
 		throw new IOException("currently serializing object");
 	}
 	enableBuffering(false);
-	objectsDone = new Hashtable();
+	objectsDone = new IdentityHashtable();
 	pos = 0;
 	writeByte(ObjectStreamConstants.TC_RESET);
 	enableBuffering(false);
@@ -265,24 +265,21 @@
 				key = new Integer(++nextKey);
 				objectsDone.put(obj, key);
 
-				if ((currStreamClass.method & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0) {
+				if (currStreamClass.clazz.isArray()) {
+					writeArray(currStreamClass, currObject);
+				}
+				else if ((currStreamClass.method & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0) {
 					try {
 						((Externalizable)obj).writeExternal(this);
 					}
 					catch (ClassCastException _) {
 					}
 				}
-				else if ((currStreamClass.method & ObjectStreamConstants.SC_WRRD_METHODS) != 0) {
-					invokeObjectWriter(obj, cls);
-				}
-				else if ((currStreamClass.method & ObjectStreamConstants.SC_SERIALIZABLE) != 0) {
-					defaultWriteObject();
-				}
 				else if ((currStreamClass.method & ObjectStreamConstants.SC_STRING) != 0) {
 					writeUTF((String)obj);
 				}
 				else {
-					throw new StreamCorruptedException("unknown method type");
+					writeDataFields(currStreamClass, currObject);
 				}
 				// Write any annotations.
 				annotateClass(currStreamClass.clazz);
@@ -303,22 +300,25 @@
 		if (currObject == null) {
 			throw new NotActiveException();
 		}
-		if (currStreamClass.clazz.isArray()) {
-			writeArray(currStreamClass, currObject);
-		}
-		else {
-			writeDataFields(currStreamClass, currObject);
-		}
+		outputClassFields(currObject, currStreamClass.clazz, currStreamClass.fieldRdWr);
 	}
 }
 
-private void writeDataFields(ObjectStreamClass strm, Object obj)
+private void writeDataFields(ObjectStreamClass strm, Object obj) throws IOException
 {
 	if (strm == null) {
 		return;
 	}
 	writeDataFields(strm.superclazzStream, obj);
-	outputClassFields(obj, strm.clazz, strm.fieldRdWr);
+	ObjectStreamClass oldStreamClass = currStreamClass;
+	currStreamClass = strm;
+	if ((strm.method & ObjectStreamConstants.SC_WRRD_METHODS) != 0) {
+		invokeObjectWriter(obj, strm.clazz);
+	}
+	else if ((strm.method & ObjectStreamConstants.SC_SERIALIZABLE) != 0) {
+		defaultWriteObject();
+	}
+	currStreamClass = oldStreamClass;
 }
 
 private void writeArray(ObjectStreamClass strm, Object obj) throws IOException
diff -Nru kaffe-snap/libraries/javalib/kaffe/util/IdentityHastable.java kaffe-snap-jop/libraries/javalib/kaffe/util/IdentityHastable.java
--- kaffe-snap/libraries/javalib/kaffe/util/IdentityHastable.java	Thu Jan  1 01:00:00 1970
+++ kaffe-snap-jop/libraries/javalib/kaffe/util/IdentityHastable.java	Thu Feb  4 15:43:26 1999
@@ -0,0 +1,227 @@
+/*
+ * Java core library component.
+ *
+ * Copyright (c) 1997, 1998
+ *      Transvirtual Technologies, Inc.  All rights reserved.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file.
+ */
+
+package kaffe.util;
+
+import java.lang.String;
+import java.util.NoSuchElementException;
+
+/* IdentityHashtable is a simplified java.util.Hashtable for use by
+   java.io.ObjectOutputStream. It uses System.identityHashCode and
+   == instead of hashCode() and equals(). */
+
+public class IdentityHashtable {
+  transient private Object keys[];
+  transient private Object elements[];
+  transient private float loadFactor;
+  private int numberOfKeys;
+  transient private int rehashLimit;
+
+  private static final int DEFAULTCAPACITY = 101;
+  private static final float DEFAULTLOADFACTOR = (float)0.75;
+  private static final Object removed = new Object();
+  private static final Object free = null;
+
+  public IdentityHashtable() {
+    this(DEFAULTCAPACITY, DEFAULTLOADFACTOR);
+  }
+  
+  public IdentityHashtable(int initialCapacity) {
+    this(initialCapacity, DEFAULTLOADFACTOR);
+  }
+  
+  public IdentityHashtable(int initialCapacity, float loadFactor)
+  {
+    if (initialCapacity <= 0) {
+      throw new Error("Initial capacity is <= 0");
+    }
+    if (loadFactor <= 0.0) {
+      throw new Error("Load Factor is <= 0");
+    }
+    this.loadFactor = loadFactor;
+    this.keys = new Object[initialCapacity];
+    this.elements = new Object[initialCapacity];
+    this.numberOfKeys = 0;
+    this.rehashLimit = (int)(loadFactor * (float)initialCapacity);
+  }
+  
+  public int size() {
+    return (numberOfKeys);
+  }
+  
+  public boolean isEmpty() {
+    return (numberOfKeys == 0);
+  }
+  
+  public synchronized boolean contains(Object value) {
+    for (int pos = elements.length-1; pos >= 0; pos--) {
+      if (value.equals(elements[pos])) {
+	  return (true);
+      }
+    }
+    return false;
+  }
+  
+  public synchronized boolean containsKey(Object key) {
+    return (get(key) != null);
+  }
+  
+  private int calculateBucket(Object key) {
+    return ((System.identityHashCode(key) & Integer.MAX_VALUE) % keys.length);
+  }
+
+  public synchronized Object get(Object key)
+  {
+    int posn = calculateBucket(key);
+    int limit = keys.length;
+    for (int i = posn; i < limit; i++) {
+      Object mkey = keys[i];
+      if (key==mkey) {
+	return (elements[i]);
+      }
+      if (mkey == free) {
+	return (null);
+      }
+    }
+    for (int i = 0; i < posn; i++) {
+      Object mkey = keys[i];
+      if (key==mkey) {
+	return (elements[i]);
+      }
+      if (mkey == free) {
+	return (null);
+      }
+    }
+    return (null);
+  }
+  
+  protected synchronized void rehash()
+  {
+    int newCapacity = keys.length * 2;
+    Object oldKeys[] = keys;
+    Object oldElements[] = elements;
+
+    keys = new Object[newCapacity];
+    elements = new Object[newCapacity];
+    rehashLimit = (int)(loadFactor * (float)newCapacity);
+    numberOfKeys = 0;
+
+    /* Go through adding all the data to the new data */
+    for (int pos = oldKeys.length-1; pos >= 0; pos--) {
+      if (oldKeys[pos] != free && oldKeys[pos] != removed) {
+	put(oldKeys[pos], oldElements[pos]);
+      }
+    }
+  }
+  
+  public synchronized Object put(Object key, Object value) {
+    if (numberOfKeys >= rehashLimit) {
+      rehash();
+    }
+
+    int posn = calculateBucket(key);
+    int limit = keys.length;
+    int space = -1;
+    for (int i = posn; i < limit; i++) {
+      Object mkey = keys[i];
+      if (key==mkey) {
+	Object oldElement = elements[i];
+	elements[i] = value;
+	return (oldElement);
+      }
+      if (mkey == removed) {
+	if (space == -1) {
+	  space = i;
+	}
+      }
+      else if (mkey == free) {
+	if (space == -1) {
+	  space = i;
+	}
+	keys[space] = key;
+	elements[space] = value;
+	numberOfKeys++;
+	return (null);
+      }
+    }
+    for (int i = 0; i < posn; i++) {
+      Object mkey = keys[i];
+      if (key==mkey) {
+	Object oldElement = elements[i];
+	elements[i] = value;
+	return (oldElement);
+      }
+      if (mkey == removed) {
+	if (space == -1) {
+	  space = i;
+	}
+      }
+      else if (mkey == free) {
+	if (space == -1) {
+	  space = i;
+	}
+	keys[space] = key;
+	elements[space] = value;
+	numberOfKeys++;
+	return (null);
+      }
+    }
+	if (space != -1) {
+	keys[space] = key;
+	elements[space] = value;
+	numberOfKeys++;
+	return (null);
+      }
+    // We shouldn't get here.
+    throw new Error("Inconsistent IdentityHashtable");
+  }
+  
+  public synchronized Object remove(Object key) {
+
+    int posn = calculateBucket(key);
+    int limit = keys.length;
+    for (int i = posn; i < limit; i++) {
+      Object mkey = keys[i];
+      if (key==mkey) {
+	Object oldElement = elements[i];
+	elements[i] = removed;
+	keys[i] = removed;
+	numberOfKeys--;
+	return (oldElement);
+      }
+      if (mkey == free) {
+	return (null);
+      }
+    }
+    for (int i = 0; i < posn; i++) {
+      Object mkey = keys[i];
+      if (key==mkey) {
+	Object oldElement = elements[i];
+	elements[i] = removed;
+	keys[i] = removed;
+	numberOfKeys--;
+	return (oldElement);
+      }
+      if (mkey == free) {
+	return (null);
+      }
+    }
+    return (null);
+  }
+  
+  public synchronized void clear() {
+    for (int pos = keys.length - 1; pos >= 0; pos--) {
+      keys[pos] = free;
+      elements[pos] = free;
+    }
+    numberOfKeys = 0;
+  }
+}
+


More information about the kaffe mailing list