diff --git a/classpath/java/io/ObjectInputStream.java b/classpath/java/io/ObjectInputStream.java index d0adaf0ad5..d12fcd127a 100644 --- a/classpath/java/io/ObjectInputStream.java +++ b/classpath/java/io/ObjectInputStream.java @@ -2,9 +2,11 @@ package java.io; public class ObjectInputStream extends InputStream { private final InputStream in; + private final Reader r; public ObjectInputStream(InputStream in) { this.in = in; + this.r = new PushbackReader(new InputStreamReader(in)); } public int read() throws IOException { @@ -18,4 +20,257 @@ public class ObjectInputStream extends InputStream { public void close() throws IOException { in.close(); } + + public Object readObject() throws IOException { + return readObject(new HashMap()); + } + + public boolean readBoolean() throws IOException { + read('z'); + return readLongToken() != 0; + } + + public byte readByte() throws IOException { + read('b'); + return (byte) readLongToken(); + } + + public char readChar() throws IOException { + read('c'); + return (char) readLongToken(); + } + + public short readShort() throws IOException { + read('s'); + return (short) readLongToken(); + } + + public int readInt() throws IOException { + read('i'); + return (int) readLongToken(); + } + + public long readLong() throws IOException { + read('j'); + return readLongToken(); + } + + public float readFloat() throws IOException { + read('f'); + return (float) readDoubleToken(); + } + + public double readDouble() throws IOException { + read('d'); + return readDoubleToken(); + } + + private void skipSpace() throws IOException { + int c; + while ((c = r.read()) != -1 && Character.isSpace((char) c)); + if (c != -1) { + r.unread(c); + } + } + + private void read(char v) throws IOException { + skipSpace(); + + int c = r.read(); + if (c != v) { + if (c == -1) { + throw new EOFException(); + } else { + throw new StreamCorruptedException(); + } + } + } + + private String readStringToken() throws IOException { + skipSpace(); + + StringBuilder sb = new StringBuilder(); + int c; + while ((c = r.read()) != -1 && ! Character.isSpace((char) c)) { + sb.append((char) c); + } + if (c != -1) { + r.unread(c); + } + return sb.toString(); + } + + private long readLongToken() throws IOException { + return Long.parseLong(readStringToken()); + } + + private double readDoubleToken() throws IOException { + return Double.parseDouble(readStringToken()); + } + + private Object readObject(HashMap map) throws IOException { + skipSpace(); + switch (r.read()) { + case 'a': + return deserializeArray(map); + case 'l': + return deserialize(map); + case 'n': + return null; + case -1: + throw new EOFException(); + default: + throw new StreamCorruptedException(); + } + } + + private Object deserializeArray(HashMap map) + throws IOException + { + read('('); + int id = (int) readLongToken(); + Class c = Class.forName(readStringToken()); + int length = (int) readLongToken(); + Object o = Array.newInstance(c.getComponentType(), length); + + map.put(id, o); + + for (int i = 0; i < length; ++i) { + skipSpace(); + + switch (r.read()) { + case 'a': + Array.set(o, i, deserializeArray(map)); + break; + + case 'l': + Array.set(o, i, deserialize(map)); + break; + + case 'r': + Array.set(o, i, map.get((int) readLongToken())); + break; + + case 'n': + Array.set(o, i, null); + break; + + case 'z': + f.setBoolean(o, readLongToken() != 0); + break; + + case 'b': + f.setByte(o, (byte) readLongToken()); + break; + + case 'c': + f.setChar(o, (char) readLongToken()); + break; + + case 's': + f.setShort(o, (short) readLongToken()); + break; + + case 'i': + f.setInt(o, (int) readLongToken()); + break; + + case 'j': + f.setLong(o, readLongToken()); + break; + + case 'f': + f.setFloat(o, (float) readDoubleToken()); + break; + + case 'd': + f.setDouble(o, readDoubleToken()); + break; + + case -1: + throw new EOFException(); + + default: + throw new StreamCorruptedException(); + } + } + + read(')'); + } + + private Object deserialize(HashMap map) + throws IOException + { + read('('); + int id = (int) readLongToken(); + Class c = Class.forName(readStringToken()); + Object o = c.newInstance(); + + map.put(id, o); + + for (Field f: c.getFields()) { + int modifiers = f.getModifiers(); + if ((modifiers & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) { + skipSpace(); + + switch (r.read()) { + case 'a': + Array.set(o, i, deserializeArray(map)); + break; + + case 'l': + Array.set(o, i, deserialize(map)); + break; + + case 'r': + Array.set(o, i, map.get((int) readLongToken())); + break; + + case 'n': + Array.set(o, i, null); + break; + + case 'z': + Array.setBoolean(o, i, readLongToken() != 0); + break; + + case 'b': + Array.setByte(o, i, (byte) readLongToken()); + break; + + case 'c': + Array.setChar(o, i, (char) readLongToken()); + break; + + case 's': + Array.setShort(o, i, (short) readLongToken()); + break; + + case 'i': + Array.setInt(o, i, (int) readLongToken()); + break; + + case 'j': + Array.setLong(o, i, readLongToken()); + break; + + case 'f': + Array.setFloat(o, i, (float) readDoubleToken()); + break; + + case 'd': + Array.setDouble(o, i, readDoubleToken()); + break; + + case -1: + throw new EOFException(); + + default: + throw new StreamCorruptedException(); + } + } + } + + read(')'); + } } diff --git a/classpath/java/io/ObjectOutputStream.java b/classpath/java/io/ObjectOutputStream.java index 9a57d10bcd..a771b8e000 100644 --- a/classpath/java/io/ObjectOutputStream.java +++ b/classpath/java/io/ObjectOutputStream.java @@ -1,10 +1,12 @@ package java.io; +import java.util.IdentityHashMap; + public class ObjectOutputStream extends OutputStream { - private final OutputStream out; + private final PrintStream out; public ObjectOutputStream(OutputStream out) { - this.out = out; + this.out = new PrintStream(out); } public void write(int c) throws IOException { @@ -22,5 +24,157 @@ public class ObjectOutputStream extends OutputStream { public void close() throws IOException { out.close(); } + + public void writeObject(Object o) throws IOException { + writeObject(o, new IdentityHashMap(), 0); + } + + public void writeBoolean(boolean v) { + out.print("z"); + out.print((v ? 1 : 0)); + } + + public void writeByte(byte v) { + out.print("b"); + out.print((int) v); + } + + public void writeChar(char v) { + out.print("c"); + out.print((int) v); + } + + public void writeShort(short v) { + out.print("s"); + out.print((int) v); + } + + public void writeInt(int v) { + out.print("i"); + out.print(v); + } + + public void writeLong(long v) { + out.print("j"); + out.print(v); + } + + public void writeFloat(float v) { + out.print("f"); + out.print(v); + } + + public void writeDouble(double v) { + out.print("d"); + out.print(v); + } + + private void writeObject(Object o, IdentityHashMap map, + int nextId) + throws IOException + { + if (o == null) { + out.print("n"); + } else { + Integer id = map.get(new Identity(o)); + if (id == null) { + map.put(new Identity(o), nextId); + + Class c = o.getClass(); + if (c.isArray()) { + serializeArray(o, map, nextId); + } else if (Serializable.class.isAssignableFrom(c)) { + serialize(o, map, nextId); + } else { + throw new NotSerializableException(c.getName()); + } + } else { + out.print("r"); + out.print(id.intValue()); + } + } + } + + private void serializeArray(Object o, IdentityHashMap map, + int nextId) + throws IOException + { + Class c = o.getClass(); + Class t = c.getComponentType(); + int length = Array.getLength(o); + + out.print("a("); + out.print(nextId++); + out.print(" "); + out.print(c.getName()); + out.print(" "); + out.print(length); + + for (int i = 0; i < length; ++i) { + out.print(" "); + if (t.equals(boolean.class)) { + writeBoolean(Array.getBoolean(o)); + } else if (t.equals(byte.class)) { + writeByte(Array.getByte(o)); + } else if (t.equals(char.class)) { + writeChar(Array.getChar(o)); + } else if (t.equals(short.class)) { + writeShort(Array.getShort(o)); + } else if (t.equals(int.class)) { + writeInt(Array.getInt(o)); + } else if (t.equals(long.class)) { + writeLong(Array.getLong(o)); + } else if (t.equals(float.class)) { + writeFloat(Array.getFloat(o)); + } else if (t.equals(double.class)) { + writeDouble(Array.getDouble(o)); + } else { + writeObject(Array.get(o), map, nextId); + } + } + + out.print(")"); + } + + private void serialize(Object o, IdentityHashMap map, + int nextId) + throws IOException + { + Class c = o.getClass(); + + out.print("l("); + out.print(nextId++); + out.print(" "); + out.print(c.getName()); + + for (Field f: c.getFields()) { + int modifiers = f.getModifiers(); + if ((modifiers & (Modifier.TRANSIENT | Modifier.STATIC)) == 0) { + out.print(" "); + Class t = f.getType(); + if (t.equals(boolean.class)) { + writeBoolean(f.getBoolean(o)); + } else if (t.equals(byte.class)) { + writeByte(f.getByte(o)); + } else if (t.equals(char.class)) { + writeChar(f.getChar(o)); + } else if (t.equals(short.class)) { + writeShort(f.getShort(o)); + } else if (t.equals(int.class)) { + writeInt(f.getInt(o)); + } else if (t.equals(long.class)) { + writeLong(f.getLong(o)); + } else if (t.equals(float.class)) { + writeFloat(f.getFloat(o)); + } else if (t.equals(double.class)) { + writeDouble(f.getDouble(o)); + } else { + writeObject(f.get(o), map, nextId); + } + } + } + + out.print(")"); + } } diff --git a/classpath/java/util/HashMap.java b/classpath/java/util/HashMap.java index c7f129d786..8c859da55e 100644 --- a/classpath/java/util/HashMap.java +++ b/classpath/java/util/HashMap.java @@ -3,17 +3,17 @@ package java.util; public class HashMap implements Map { private int size; private Cell[] array; - private final CellFactory factory; + private final Helper helper; - HashMap(int capacity, CellFactory factory) { + HashMap(int capacity, Helper helper) { if (capacity > 0) { array = new Cell[nextPowerOfTwo(capacity)]; } - this.factory = factory; + this.helper = helper; } public HashMap(int capacity) { - this(capacity, new MyCellFactory()); + this(capacity, new MyHelper()); } public HashMap() { @@ -30,14 +30,6 @@ public class HashMap implements Map { return size; } - private static int hash(Object a) { - return (a == null ? 0 : a.hashCode()); - } - - private static boolean equal(Object a, Object b) { - return (a == null && b == null) || (a != null && a.equals(b)); - } - private void resize() { if (array == null || size >= array.length * 2) { resize(array == null ? 16 : array.length * 2); @@ -115,7 +107,7 @@ public class HashMap implements Map { private Cell putCell(K key, V value) { Cell c = find(key); if (c == null) { - insert(factory.make(key, value, null)); + insert(helper.make(key, value, null)); } else { V old = c.getValue(); c.setValue(value); @@ -196,11 +188,15 @@ public class HashMap implements Map { public void setNext(HashMap.Cell next); } - interface CellFactory { + interface Helper { public Cell make(K key, V value, Cell next); + + public int hash(K key); + + public boolean equal(K a, K b); } - private static class MyCell implements Cell { + private class MyCell implements Cell { public final K key; public V value; public Cell next; @@ -232,14 +228,22 @@ public class HashMap implements Map { } public int hashCode() { - return (key == null ? 0 : key.hashCode()); + return helper.hash(key); } } - private static class MyCellFactory implements CellFactory { + static class MyHelper implements Helper { public Cell make(K key, V value, Cell next) { return new MyCell(key, value, next); } + + public int hash(K a) { + return (a == null ? 0 : a.hashCode()); + } + + public boolean equal(K a, K b) { + return (a == null && b == null) || (a != null && a.equals(b)); + } } private class EntrySet implements Set> { diff --git a/classpath/java/util/IdentityHashMap.java b/classpath/java/util/IdentityHashMap.java new file mode 100644 index 0000000000..be58d2eee9 --- /dev/null +++ b/classpath/java/util/IdentityHashMap.java @@ -0,0 +1,65 @@ +package java.util; + +public class IdentityHashMap implements Map { + private final HashMap map; + + public IdentityHashMap(int capacity) { + map = new HashMap(capacity, new MyHelper()); + } + + public IdentityHashMap() { + this(0); + } + + public int size() { + return map.size(); + } + + public boolean containsKey(K key) { + return map.containsKey(key); + } + + public boolean containsValue(V value) { + return map.containsValue(value); + } + + public V get(K key) { + return map.get(key); + } + + public V put(K key, V value) { + return map.put(key, value); + } + + public V remove(K key) { + return map.remove(key); + } + + public void clear() { + map.clear(); + } + + public Set> entrySet() { + return map.entrySet(); + } + + public Set keySet() { + return map.keySet(); + } + + public Collection values() { + return map.values(); + } + + private static class MyHelper + extends HashMap.MyHelper + { + public int hash(K a) { + return (a == null ? 0 : System.identityHashCode(a)); + } + + public boolean equal(K a, K b) { + return a == b; + } + } +} diff --git a/classpath/java/util/WeakHashMap.java b/classpath/java/util/WeakHashMap.java index 340acb6d90..5fb18bf615 100644 --- a/classpath/java/util/WeakHashMap.java +++ b/classpath/java/util/WeakHashMap.java @@ -9,7 +9,7 @@ public class WeakHashMap implements Map { private final ReferenceQueue queue; public WeakHashMap(int capacity) { - map = new HashMap(capacity, new MyCellFactory()); + map = new HashMap(capacity, new MyHelper()); queue = new ReferenceQueue(); } @@ -111,8 +111,8 @@ public class WeakHashMap implements Map { } } - private static class MyCellFactory - implements HashMap.CellFactory + private static class MyHelper + extends HashMap.MyHelper { public HashMap.Cell make(K key, V value, HashMap.Cell next) { return new MyCell(key, value, next);