diff -r 5652acd48509 -r 42bc1e89134d emul/compact/src/main/java/java/io/ObjectOutputStream.java --- a/emul/compact/src/main/java/java/io/ObjectOutputStream.java Mon Feb 25 19:00:08 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2369 +0,0 @@ -/* - * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.io; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import org.apidesign.bck2brwsr.emul.lang.System; - -/** - * An ObjectOutputStream writes primitive data types and graphs of Java objects - * to an OutputStream. The objects can be read (reconstituted) using an - * ObjectInputStream. Persistent storage of objects can be accomplished by - * using a file for the stream. If the stream is a network socket stream, the - * objects can be reconstituted on another host or in another process. - * - *

Only objects that support the java.io.Serializable interface can be - * written to streams. The class of each serializable object is encoded - * including the class name and signature of the class, the values of the - * object's fields and arrays, and the closure of any other objects referenced - * from the initial objects. - * - *

The method writeObject is used to write an object to the stream. Any - * object, including Strings and arrays, is written with writeObject. Multiple - * objects or primitives can be written to the stream. The objects must be - * read back from the corresponding ObjectInputstream with the same types and - * in the same order as they were written. - * - *

Primitive data types can also be written to the stream using the - * appropriate methods from DataOutput. Strings can also be written using the - * writeUTF method. - * - *

The default serialization mechanism for an object writes the class of the - * object, the class signature, and the values of all non-transient and - * non-static fields. References to other objects (except in transient or - * static fields) cause those objects to be written also. Multiple references - * to a single object are encoded using a reference sharing mechanism so that - * graphs of objects can be restored to the same shape as when the original was - * written. - * - *

For example to write an object that can be read by the example in - * ObjectInputStream: - *
- *

- *      FileOutputStream fos = new FileOutputStream("t.tmp");
- *      ObjectOutputStream oos = new ObjectOutputStream(fos);
- *
- *      oos.writeInt(12345);
- *      oos.writeObject("Today");
- *      oos.writeObject(new Date());
- *
- *      oos.close();
- * 
- * - *

Classes that require special handling during the serialization and - * deserialization process must implement special methods with these exact - * signatures: - *
- *

- * private void readObject(java.io.ObjectInputStream stream)
- *     throws IOException, ClassNotFoundException;
- * private void writeObject(java.io.ObjectOutputStream stream)
- *     throws IOException
- * private void readObjectNoData()
- *     throws ObjectStreamException;
- * 
- * - *

The writeObject method is responsible for writing the state of the object - * for its particular class so that the corresponding readObject method can - * restore it. The method does not need to concern itself with the state - * belonging to the object's superclasses or subclasses. State is saved by - * writing the individual fields to the ObjectOutputStream using the - * writeObject method or by using the methods for primitive data types - * supported by DataOutput. - * - *

Serialization does not write out the fields of any object that does not - * implement the java.io.Serializable interface. Subclasses of Objects that - * are not serializable can be serializable. In this case the non-serializable - * class must have a no-arg constructor to allow its fields to be initialized. - * In this case it is the responsibility of the subclass to save and restore - * the state of the non-serializable class. It is frequently the case that the - * fields of that class are accessible (public, package, or protected) or that - * there are get and set methods that can be used to restore the state. - * - *

Serialization of an object can be prevented by implementing writeObject - * and readObject methods that throw the NotSerializableException. The - * exception will be caught by the ObjectOutputStream and abort the - * serialization process. - * - *

Implementing the Externalizable interface allows the object to assume - * complete control over the contents and format of the object's serialized - * form. The methods of the Externalizable interface, writeExternal and - * readExternal, are called to save and restore the objects state. When - * implemented by a class they can write and read their own state using all of - * the methods of ObjectOutput and ObjectInput. It is the responsibility of - * the objects to handle any versioning that occurs. - * - *

Enum constants are serialized differently than ordinary serializable or - * externalizable objects. The serialized form of an enum constant consists - * solely of its name; field values of the constant are not transmitted. To - * serialize an enum constant, ObjectOutputStream writes the string returned by - * the constant's name method. Like other serializable or externalizable - * objects, enum constants can function as the targets of back references - * appearing subsequently in the serialization stream. The process by which - * enum constants are serialized cannot be customized; any class-specific - * writeObject and writeReplace methods defined by enum types are ignored - * during serialization. Similarly, any serialPersistentFields or - * serialVersionUID field declarations are also ignored--all enum types have a - * fixed serialVersionUID of 0L. - * - *

Primitive data, excluding serializable fields and externalizable data, is - * written to the ObjectOutputStream in block-data records. A block data record - * is composed of a header and data. The block data header consists of a marker - * and the number of bytes to follow the header. Consecutive primitive data - * writes are merged into one block-data record. The blocking factor used for - * a block-data record will be 1024 bytes. Each block-data record will be - * filled up to 1024 bytes, or be written whenever there is a termination of - * block-data mode. Calls to the ObjectOutputStream methods writeObject, - * defaultWriteObject and writeFields initially terminate any existing - * block-data record. - * - * @author Mike Warres - * @author Roger Riggs - * @see java.io.DataOutput - * @see java.io.ObjectInputStream - * @see java.io.Serializable - * @see java.io.Externalizable - * @see Object Serialization Specification, Section 2, Object Output Classes - * @since JDK1.1 - */ -public class ObjectOutputStream - extends OutputStream implements ObjectOutput, ObjectStreamConstants -{ - /** filter stream for handling block data conversion */ - private final BlockDataOutputStream bout; - /** obj -> wire handle map */ - private final HandleTable handles; - /** obj -> replacement obj map */ - private final ReplaceTable subs; - /** stream protocol version */ - private int protocol = PROTOCOL_VERSION_2; - /** recursion depth */ - private int depth; - - /** buffer for writing primitive field values */ - private byte[] primVals; - - /** if true, invoke writeObjectOverride() instead of writeObject() */ - private final boolean enableOverride; - /** if true, invoke replaceObject() */ - private boolean enableReplace; - - // values below valid only during upcalls to writeObject()/writeExternal() - /** - * Context during upcalls to class-defined writeObject methods; holds - * object currently being serialized and descriptor for current class. - * Null when not during writeObject upcall. - */ - private Object curContext; - /** current PutField object */ - private PutFieldImpl curPut; - - /** custom storage for debug trace info */ - private final DebugTraceInfoStack debugInfoStack; - - /** - * value of "sun.io.serialization.extendedDebugInfo" property, - * as true or false for extended information about exception's place - */ - private static final boolean extendedDebugInfo = false; - - /** - * Creates an ObjectOutputStream that writes to the specified OutputStream. - * This constructor writes the serialization stream header to the - * underlying stream; callers may wish to flush the stream immediately to - * ensure that constructors for receiving ObjectInputStreams will not block - * when reading the header. - * - *

If a security manager is installed, this constructor will check for - * the "enableSubclassImplementation" SerializablePermission when invoked - * directly or indirectly by the constructor of a subclass which overrides - * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared - * methods. - * - * @param out output stream to write to - * @throws IOException if an I/O error occurs while writing stream header - * @throws SecurityException if untrusted subclass illegally overrides - * security-sensitive methods - * @throws NullPointerException if out is null - * @since 1.4 - * @see ObjectOutputStream#ObjectOutputStream() - * @see ObjectOutputStream#putFields() - * @see ObjectInputStream#ObjectInputStream(InputStream) - */ - public ObjectOutputStream(OutputStream out) throws IOException { - verifySubclass(); - bout = new BlockDataOutputStream(out); - handles = new HandleTable(10, (float) 3.00); - subs = new ReplaceTable(10, (float) 3.00); - enableOverride = false; - writeStreamHeader(); - bout.setBlockDataMode(true); - if (extendedDebugInfo) { - debugInfoStack = new DebugTraceInfoStack(); - } else { - debugInfoStack = null; - } - } - - /** - * Provide a way for subclasses that are completely reimplementing - * ObjectOutputStream to not have to allocate private data just used by - * this implementation of ObjectOutputStream. - * - *

If there is a security manager installed, this method first calls the - * security manager's checkPermission method with a - * SerializablePermission("enableSubclassImplementation") - * permission to ensure it's ok to enable subclassing. - * - * @throws SecurityException if a security manager exists and its - * checkPermission method denies enabling - * subclassing. - * @see SecurityManager#checkPermission - * @see java.io.SerializablePermission - */ - protected ObjectOutputStream() throws IOException, SecurityException { - throw new SecurityException(); - } - - /** - * Specify stream protocol version to use when writing the stream. - * - *

This routine provides a hook to enable the current version of - * Serialization to write in a format that is backwards compatible to a - * previous version of the stream format. - * - *

Every effort will be made to avoid introducing additional - * backwards incompatibilities; however, sometimes there is no - * other alternative. - * - * @param version use ProtocolVersion from java.io.ObjectStreamConstants. - * @throws IllegalStateException if called after any objects - * have been serialized. - * @throws IllegalArgumentException if invalid version is passed in. - * @throws IOException if I/O errors occur - * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1 - * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2 - * @since 1.2 - */ - public void useProtocolVersion(int version) throws IOException { - if (handles.size() != 0) { - // REMIND: implement better check for pristine stream? - throw new IllegalStateException("stream non-empty"); - } - switch (version) { - case PROTOCOL_VERSION_1: - case PROTOCOL_VERSION_2: - protocol = version; - break; - - default: - throw new IllegalArgumentException( - "unknown version: " + version); - } - } - - /** - * Write the specified object to the ObjectOutputStream. The class of the - * object, the signature of the class, and the values of the non-transient - * and non-static fields of the class and all of its supertypes are - * written. Default serialization for a class can be overridden using the - * writeObject and the readObject methods. Objects referenced by this - * object are written transitively so that a complete equivalent graph of - * objects can be reconstructed by an ObjectInputStream. - * - *

Exceptions are thrown for problems with the OutputStream and for - * classes that should not be serialized. All exceptions are fatal to the - * OutputStream, which is left in an indeterminate state, and it is up to - * the caller to ignore or recover the stream state. - * - * @throws InvalidClassException Something is wrong with a class used by - * serialization. - * @throws NotSerializableException Some object to be serialized does not - * implement the java.io.Serializable interface. - * @throws IOException Any exception thrown by the underlying - * OutputStream. - */ - public final void writeObject(Object obj) throws IOException { - if (enableOverride) { - writeObjectOverride(obj); - return; - } - try { - writeObject0(obj, false); - } catch (IOException ex) { - if (depth == 0) { - writeFatalException(ex); - } - throw ex; - } - } - - /** - * Method used by subclasses to override the default writeObject method. - * This method is called by trusted subclasses of ObjectInputStream that - * constructed ObjectInputStream using the protected no-arg constructor. - * The subclass is expected to provide an override method with the modifier - * "final". - * - * @param obj object to be written to the underlying stream - * @throws IOException if there are I/O errors while writing to the - * underlying stream - * @see #ObjectOutputStream() - * @see #writeObject(Object) - * @since 1.2 - */ - protected void writeObjectOverride(Object obj) throws IOException { - } - - /** - * Writes an "unshared" object to the ObjectOutputStream. This method is - * identical to writeObject, except that it always writes the given object - * as a new, unique object in the stream (as opposed to a back-reference - * pointing to a previously serialized instance). Specifically: - *

- * While writing an object via writeUnshared does not in itself guarantee a - * unique reference to the object when it is deserialized, it allows a - * single object to be defined multiple times in a stream, so that multiple - * calls to readUnshared by the receiver will not conflict. Note that the - * rules described above only apply to the base-level object written with - * writeUnshared, and not to any transitively referenced sub-objects in the - * object graph to be serialized. - * - *

ObjectOutputStream subclasses which override this method can only be - * constructed in security contexts possessing the - * "enableSubclassImplementation" SerializablePermission; any attempt to - * instantiate such a subclass without this permission will cause a - * SecurityException to be thrown. - * - * @param obj object to write to stream - * @throws NotSerializableException if an object in the graph to be - * serialized does not implement the Serializable interface - * @throws InvalidClassException if a problem exists with the class of an - * object to be serialized - * @throws IOException if an I/O error occurs during serialization - * @since 1.4 - */ - public void writeUnshared(Object obj) throws IOException { - try { - writeObject0(obj, true); - } catch (IOException ex) { - if (depth == 0) { - writeFatalException(ex); - } - throw ex; - } - } - - /** - * Write the non-static and non-transient fields of the current class to - * this stream. This may only be called from the writeObject method of the - * class being serialized. It will throw the NotActiveException if it is - * called otherwise. - * - * @throws IOException if I/O errors occur while writing to the underlying - * OutputStream - */ - public void defaultWriteObject() throws IOException { - if ( curContext == null ) { - throw new NotActiveException("not in call to writeObject"); - } - Object curObj = null; // curContext.getObj(); - ObjectStreamClass curDesc = null; // curContext.getDesc(); - bout.setBlockDataMode(false); - defaultWriteFields(curObj, curDesc); - bout.setBlockDataMode(true); - } - - /** - * Retrieve the object used to buffer persistent fields to be written to - * the stream. The fields will be written to the stream when writeFields - * method is called. - * - * @return an instance of the class Putfield that holds the serializable - * fields - * @throws IOException if I/O errors occur - * @since 1.2 - */ - public ObjectOutputStream.PutField putFields() throws IOException { - if (curPut == null) { - if (curContext == null) { - throw new NotActiveException("not in call to writeObject"); - } - Object curObj = null; // curContext.getObj(); - ObjectStreamClass curDesc = null; // curContext.getDesc(); - curPut = new PutFieldImpl(curDesc); - } - return curPut; - } - - /** - * Write the buffered fields to the stream. - * - * @throws IOException if I/O errors occur while writing to the underlying - * stream - * @throws NotActiveException Called when a classes writeObject method was - * not called to write the state of the object. - * @since 1.2 - */ - public void writeFields() throws IOException { - if (curPut == null) { - throw new NotActiveException("no current PutField object"); - } - bout.setBlockDataMode(false); - curPut.writeFields(); - bout.setBlockDataMode(true); - } - - /** - * Reset will disregard the state of any objects already written to the - * stream. The state is reset to be the same as a new ObjectOutputStream. - * The current point in the stream is marked as reset so the corresponding - * ObjectInputStream will be reset at the same point. Objects previously - * written to the stream will not be refered to as already being in the - * stream. They will be written to the stream again. - * - * @throws IOException if reset() is invoked while serializing an object. - */ - public void reset() throws IOException { - if (depth != 0) { - throw new IOException("stream active"); - } - bout.setBlockDataMode(false); - bout.writeByte(TC_RESET); - clear(); - bout.setBlockDataMode(true); - } - - /** - * Subclasses may implement this method to allow class data to be stored in - * the stream. By default this method does nothing. The corresponding - * method in ObjectInputStream is resolveClass. This method is called - * exactly once for each unique class in the stream. The class name and - * signature will have already been written to the stream. This method may - * make free use of the ObjectOutputStream to save any representation of - * the class it deems suitable (for example, the bytes of the class file). - * The resolveClass method in the corresponding subclass of - * ObjectInputStream must read and use any data or objects written by - * annotateClass. - * - * @param cl the class to annotate custom data for - * @throws IOException Any exception thrown by the underlying - * OutputStream. - */ - protected void annotateClass(Class cl) throws IOException { - } - - /** - * Subclasses may implement this method to store custom data in the stream - * along with descriptors for dynamic proxy classes. - * - *

This method is called exactly once for each unique proxy class - * descriptor in the stream. The default implementation of this method in - * ObjectOutputStream does nothing. - * - *

The corresponding method in ObjectInputStream is - * resolveProxyClass. For a given subclass of - * ObjectOutputStream that overrides this method, the - * resolveProxyClass method in the corresponding subclass of - * ObjectInputStream must read any data or objects written by - * annotateProxyClass. - * - * @param cl the proxy class to annotate custom data for - * @throws IOException any exception thrown by the underlying - * OutputStream - * @see ObjectInputStream#resolveProxyClass(String[]) - * @since 1.3 - */ - protected void annotateProxyClass(Class cl) throws IOException { - } - - /** - * This method will allow trusted subclasses of ObjectOutputStream to - * substitute one object for another during serialization. Replacing - * objects is disabled until enableReplaceObject is called. The - * enableReplaceObject method checks that the stream requesting to do - * replacement can be trusted. The first occurrence of each object written - * into the serialization stream is passed to replaceObject. Subsequent - * references to the object are replaced by the object returned by the - * original call to replaceObject. To ensure that the private state of - * objects is not unintentionally exposed, only trusted streams may use - * replaceObject. - * - *

The ObjectOutputStream.writeObject method takes a parameter of type - * Object (as opposed to type Serializable) to allow for cases where - * non-serializable objects are replaced by serializable ones. - * - *

When a subclass is replacing objects it must insure that either a - * complementary substitution must be made during deserialization or that - * the substituted object is compatible with every field where the - * reference will be stored. Objects whose type is not a subclass of the - * type of the field or array element abort the serialization by raising an - * exception and the object is not be stored. - * - *

This method is called only once when each object is first - * encountered. All subsequent references to the object will be redirected - * to the new object. This method should return the object to be - * substituted or the original object. - * - *

Null can be returned as the object to be substituted, but may cause - * NullReferenceException in classes that contain references to the - * original object since they may be expecting an object instead of - * null. - * - * @param obj the object to be replaced - * @return the alternate object that replaced the specified one - * @throws IOException Any exception thrown by the underlying - * OutputStream. - */ - protected Object replaceObject(Object obj) throws IOException { - return obj; - } - - /** - * Enable the stream to do replacement of objects in the stream. When - * enabled, the replaceObject method is called for every object being - * serialized. - * - *

If enable is true, and there is a security manager - * installed, this method first calls the security manager's - * checkPermission method with a - * SerializablePermission("enableSubstitution") permission to - * ensure it's ok to enable the stream to do replacement of objects in the - * stream. - * - * @param enable boolean parameter to enable replacement of objects - * @return the previous setting before this method was invoked - * @throws SecurityException if a security manager exists and its - * checkPermission method denies enabling the stream - * to do replacement of objects in the stream. - * @see SecurityManager#checkPermission - * @see java.io.SerializablePermission - */ - protected boolean enableReplaceObject(boolean enable) - throws SecurityException - { - throw new SecurityException(); - } - - /** - * The writeStreamHeader method is provided so subclasses can append or - * prepend their own header to the stream. It writes the magic number and - * version to the stream. - * - * @throws IOException if I/O errors occur while writing to the underlying - * stream - */ - protected void writeStreamHeader() throws IOException { - bout.writeShort(STREAM_MAGIC); - bout.writeShort(STREAM_VERSION); - } - - /** - * Write the specified class descriptor to the ObjectOutputStream. Class - * descriptors are used to identify the classes of objects written to the - * stream. Subclasses of ObjectOutputStream may override this method to - * customize the way in which class descriptors are written to the - * serialization stream. The corresponding method in ObjectInputStream, - * readClassDescriptor, should then be overridden to - * reconstitute the class descriptor from its custom stream representation. - * By default, this method writes class descriptors according to the format - * defined in the Object Serialization specification. - * - *

Note that this method will only be called if the ObjectOutputStream - * is not using the old serialization stream format (set by calling - * ObjectOutputStream's useProtocolVersion method). If this - * serialization stream is using the old format - * (PROTOCOL_VERSION_1), the class descriptor will be written - * internally in a manner that cannot be overridden or customized. - * - * @param desc class descriptor to write to the stream - * @throws IOException If an I/O error has occurred. - * @see java.io.ObjectInputStream#readClassDescriptor() - * @see #useProtocolVersion(int) - * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1 - * @since 1.3 - */ - protected void writeClassDescriptor(ObjectStreamClass desc) - throws IOException - { - desc.writeNonProxy(this); - } - - /** - * Writes a byte. This method will block until the byte is actually - * written. - * - * @param val the byte to be written to the stream - * @throws IOException If an I/O error has occurred. - */ - public void write(int val) throws IOException { - bout.write(val); - } - - /** - * Writes an array of bytes. This method will block until the bytes are - * actually written. - * - * @param buf the data to be written - * @throws IOException If an I/O error has occurred. - */ - public void write(byte[] buf) throws IOException { - bout.write(buf, 0, buf.length, false); - } - - /** - * Writes a sub array of bytes. - * - * @param buf the data to be written - * @param off the start offset in the data - * @param len the number of bytes that are written - * @throws IOException If an I/O error has occurred. - */ - public void write(byte[] buf, int off, int len) throws IOException { - if (buf == null) { - throw new NullPointerException(); - } - int endoff = off + len; - if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) { - throw new IndexOutOfBoundsException(); - } - bout.write(buf, off, len, false); - } - - /** - * Flushes the stream. This will write any buffered output bytes and flush - * through to the underlying stream. - * - * @throws IOException If an I/O error has occurred. - */ - public void flush() throws IOException { - bout.flush(); - } - - /** - * Drain any buffered data in ObjectOutputStream. Similar to flush but - * does not propagate the flush to the underlying stream. - * - * @throws IOException if I/O errors occur while writing to the underlying - * stream - */ - protected void drain() throws IOException { - bout.drain(); - } - - /** - * Closes the stream. This method must be called to release any resources - * associated with the stream. - * - * @throws IOException If an I/O error has occurred. - */ - public void close() throws IOException { - flush(); - clear(); - bout.close(); - } - - /** - * Writes a boolean. - * - * @param val the boolean to be written - * @throws IOException if I/O errors occur while writing to the underlying - * stream - */ - public void writeBoolean(boolean val) throws IOException { - bout.writeBoolean(val); - } - - /** - * Writes an 8 bit byte. - * - * @param val the byte value to be written - * @throws IOException if I/O errors occur while writing to the underlying - * stream - */ - public void writeByte(int val) throws IOException { - bout.writeByte(val); - } - - /** - * Writes a 16 bit short. - * - * @param val the short value to be written - * @throws IOException if I/O errors occur while writing to the underlying - * stream - */ - public void writeShort(int val) throws IOException { - bout.writeShort(val); - } - - /** - * Writes a 16 bit char. - * - * @param val the char value to be written - * @throws IOException if I/O errors occur while writing to the underlying - * stream - */ - public void writeChar(int val) throws IOException { - bout.writeChar(val); - } - - /** - * Writes a 32 bit int. - * - * @param val the integer value to be written - * @throws IOException if I/O errors occur while writing to the underlying - * stream - */ - public void writeInt(int val) throws IOException { - bout.writeInt(val); - } - - /** - * Writes a 64 bit long. - * - * @param val the long value to be written - * @throws IOException if I/O errors occur while writing to the underlying - * stream - */ - public void writeLong(long val) throws IOException { - bout.writeLong(val); - } - - /** - * Writes a 32 bit float. - * - * @param val the float value to be written - * @throws IOException if I/O errors occur while writing to the underlying - * stream - */ - public void writeFloat(float val) throws IOException { - bout.writeFloat(val); - } - - /** - * Writes a 64 bit double. - * - * @param val the double value to be written - * @throws IOException if I/O errors occur while writing to the underlying - * stream - */ - public void writeDouble(double val) throws IOException { - bout.writeDouble(val); - } - - /** - * Writes a String as a sequence of bytes. - * - * @param str the String of bytes to be written - * @throws IOException if I/O errors occur while writing to the underlying - * stream - */ - public void writeBytes(String str) throws IOException { - bout.writeBytes(str); - } - - /** - * Writes a String as a sequence of chars. - * - * @param str the String of chars to be written - * @throws IOException if I/O errors occur while writing to the underlying - * stream - */ - public void writeChars(String str) throws IOException { - bout.writeChars(str); - } - - /** - * Primitive data write of this String in - * modified UTF-8 - * format. Note that there is a - * significant difference between writing a String into the stream as - * primitive data or as an Object. A String instance written by writeObject - * is written into the stream as a String initially. Future writeObject() - * calls write references to the string into the stream. - * - * @param str the String to be written - * @throws IOException if I/O errors occur while writing to the underlying - * stream - */ - public void writeUTF(String str) throws IOException { - bout.writeUTF(str); - } - - /** - * Provide programmatic access to the persistent fields to be written - * to ObjectOutput. - * - * @since 1.2 - */ - public static abstract class PutField { - - /** - * Put the value of the named boolean field into the persistent field. - * - * @param name the name of the serializable field - * @param val the value to assign to the field - * @throws IllegalArgumentException if name does not - * match the name of a serializable field for the class whose fields - * are being written, or if the type of the named field is not - * boolean - */ - public abstract void put(String name, boolean val); - - /** - * Put the value of the named byte field into the persistent field. - * - * @param name the name of the serializable field - * @param val the value to assign to the field - * @throws IllegalArgumentException if name does not - * match the name of a serializable field for the class whose fields - * are being written, or if the type of the named field is not - * byte - */ - public abstract void put(String name, byte val); - - /** - * Put the value of the named char field into the persistent field. - * - * @param name the name of the serializable field - * @param val the value to assign to the field - * @throws IllegalArgumentException if name does not - * match the name of a serializable field for the class whose fields - * are being written, or if the type of the named field is not - * char - */ - public abstract void put(String name, char val); - - /** - * Put the value of the named short field into the persistent field. - * - * @param name the name of the serializable field - * @param val the value to assign to the field - * @throws IllegalArgumentException if name does not - * match the name of a serializable field for the class whose fields - * are being written, or if the type of the named field is not - * short - */ - public abstract void put(String name, short val); - - /** - * Put the value of the named int field into the persistent field. - * - * @param name the name of the serializable field - * @param val the value to assign to the field - * @throws IllegalArgumentException if name does not - * match the name of a serializable field for the class whose fields - * are being written, or if the type of the named field is not - * int - */ - public abstract void put(String name, int val); - - /** - * Put the value of the named long field into the persistent field. - * - * @param name the name of the serializable field - * @param val the value to assign to the field - * @throws IllegalArgumentException if name does not - * match the name of a serializable field for the class whose fields - * are being written, or if the type of the named field is not - * long - */ - public abstract void put(String name, long val); - - /** - * Put the value of the named float field into the persistent field. - * - * @param name the name of the serializable field - * @param val the value to assign to the field - * @throws IllegalArgumentException if name does not - * match the name of a serializable field for the class whose fields - * are being written, or if the type of the named field is not - * float - */ - public abstract void put(String name, float val); - - /** - * Put the value of the named double field into the persistent field. - * - * @param name the name of the serializable field - * @param val the value to assign to the field - * @throws IllegalArgumentException if name does not - * match the name of a serializable field for the class whose fields - * are being written, or if the type of the named field is not - * double - */ - public abstract void put(String name, double val); - - /** - * Put the value of the named Object field into the persistent field. - * - * @param name the name of the serializable field - * @param val the value to assign to the field - * (which may be null) - * @throws IllegalArgumentException if name does not - * match the name of a serializable field for the class whose fields - * are being written, or if the type of the named field is not a - * reference type - */ - public abstract void put(String name, Object val); - - /** - * Write the data and fields to the specified ObjectOutput stream, - * which must be the same stream that produced this - * PutField object. - * - * @param out the stream to write the data and fields to - * @throws IOException if I/O errors occur while writing to the - * underlying stream - * @throws IllegalArgumentException if the specified stream is not - * the same stream that produced this PutField - * object - * @deprecated This method does not write the values contained by this - * PutField object in a proper format, and may - * result in corruption of the serialization stream. The - * correct way to write PutField data is by - * calling the {@link java.io.ObjectOutputStream#writeFields()} - * method. - */ - @Deprecated - public abstract void write(ObjectOutput out) throws IOException; - } - - - /** - * Returns protocol version in use. - */ - int getProtocolVersion() { - return protocol; - } - - /** - * Writes string without allowing it to be replaced in stream. Used by - * ObjectStreamClass to write class descriptor type strings. - */ - void writeTypeString(String str) throws IOException { - int handle; - if (str == null) { - writeNull(); - } else if ((handle = handles.lookup(str)) != -1) { - writeHandle(handle); - } else { - writeString(str, false); - } - } - - /** - * Verifies that this (possibly subclass) instance can be constructed - * without violating security constraints: the subclass must not override - * security-sensitive non-final methods, or else the - * "enableSubclassImplementation" SerializablePermission is checked. - */ - private void verifySubclass() { - Class cl = getClass(); - if (cl == ObjectOutputStream.class) { - return; - } - throw new SecurityException(); - } - - /** - * Clears internal data structures. - */ - private void clear() { - subs.clear(); - handles.clear(); - } - - /** - * Underlying writeObject/writeUnshared implementation. - */ - private void writeObject0(Object obj, boolean unshared) - throws IOException - { - boolean oldMode = bout.setBlockDataMode(false); - depth++; - try { - // handle previously written and non-replaceable objects - int h; - if ((obj = subs.lookup(obj)) == null) { - writeNull(); - return; - } else if (!unshared && (h = handles.lookup(obj)) != -1) { - writeHandle(h); - return; - } else if (obj instanceof Class) { - writeClass((Class) obj, unshared); - return; - } else if (obj instanceof ObjectStreamClass) { - writeClassDesc((ObjectStreamClass) obj, unshared); - return; - } - - // check for replacement object - Object orig = obj; - Class cl = obj.getClass(); - ObjectStreamClass desc; - for (;;) { - // REMIND: skip this check for strings/arrays? - Class repCl; - desc = ObjectStreamClass.lookup(cl, true); - if (!desc.hasWriteReplaceMethod() || - (obj = desc.invokeWriteReplace(obj)) == null || - (repCl = obj.getClass()) == cl) - { - break; - } - cl = repCl; - } - if (enableReplace) { - Object rep = replaceObject(obj); - if (rep != obj && rep != null) { - cl = rep.getClass(); - desc = ObjectStreamClass.lookup(cl, true); - } - obj = rep; - } - - // if object replaced, run through original checks a second time - if (obj != orig) { - subs.assign(orig, obj); - if (obj == null) { - writeNull(); - return; - } else if (!unshared && (h = handles.lookup(obj)) != -1) { - writeHandle(h); - return; - } else if (obj instanceof Class) { - writeClass((Class) obj, unshared); - return; - } else if (obj instanceof ObjectStreamClass) { - writeClassDesc((ObjectStreamClass) obj, unshared); - return; - } - } - - // remaining cases - if (obj instanceof String) { - writeString((String) obj, unshared); - } else if (cl.isArray()) { - writeArray(obj, desc, unshared); - } else if (obj instanceof Enum) { - writeEnum((Enum) obj, desc, unshared); - } else if (obj instanceof Serializable) { - writeOrdinaryObject(obj, desc, unshared); - } else { - if (extendedDebugInfo) { - throw new NotSerializableException( - cl.getName() + "\n" + debugInfoStack.toString()); - } else { - throw new NotSerializableException(cl.getName()); - } - } - } finally { - depth--; - bout.setBlockDataMode(oldMode); - } - } - - /** - * Writes null code to stream. - */ - private void writeNull() throws IOException { - bout.writeByte(TC_NULL); - } - - /** - * Writes given object handle to stream. - */ - private void writeHandle(int handle) throws IOException { - bout.writeByte(TC_REFERENCE); - bout.writeInt(baseWireHandle + handle); - } - - /** - * Writes representation of given class to stream. - */ - private void writeClass(Class cl, boolean unshared) throws IOException { - bout.writeByte(TC_CLASS); - writeClassDesc(ObjectStreamClass.lookup(cl, true), false); - handles.assign(unshared ? null : cl); - } - - /** - * Writes representation of given class descriptor to stream. - */ - private void writeClassDesc(ObjectStreamClass desc, boolean unshared) - throws IOException - { - int handle; - if (desc == null) { - writeNull(); - } else if (!unshared && (handle = handles.lookup(desc)) != -1) { - writeHandle(handle); - } else if (desc.isProxy()) { - writeProxyDesc(desc, unshared); - } else { - writeNonProxyDesc(desc, unshared); - } - } - - /** - * Writes class descriptor representing a dynamic proxy class to stream. - */ - private void writeProxyDesc(ObjectStreamClass desc, boolean unshared) - throws IOException - { - bout.writeByte(TC_PROXYCLASSDESC); - handles.assign(unshared ? null : desc); - - Class cl = desc.forClass(); - Class[] ifaces = cl.getInterfaces(); - bout.writeInt(ifaces.length); - for (int i = 0; i < ifaces.length; i++) { - bout.writeUTF(ifaces[i].getName()); - } - - bout.setBlockDataMode(true); - annotateProxyClass(cl); - bout.setBlockDataMode(false); - bout.writeByte(TC_ENDBLOCKDATA); - - writeClassDesc(desc.getSuperDesc(), false); - } - - /** - * Writes class descriptor representing a standard (i.e., not a dynamic - * proxy) class to stream. - */ - private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared) - throws IOException - { - bout.writeByte(TC_CLASSDESC); - handles.assign(unshared ? null : desc); - - if (protocol == PROTOCOL_VERSION_1) { - // do not invoke class descriptor write hook with old protocol - desc.writeNonProxy(this); - } else { - writeClassDescriptor(desc); - } - - Class cl = desc.forClass(); - bout.setBlockDataMode(true); - annotateClass(cl); - bout.setBlockDataMode(false); - bout.writeByte(TC_ENDBLOCKDATA); - - writeClassDesc(desc.getSuperDesc(), false); - } - - /** - * Writes given string to stream, using standard or long UTF format - * depending on string length. - */ - private void writeString(String str, boolean unshared) throws IOException { - handles.assign(unshared ? null : str); - long utflen = bout.getUTFLength(str); - if (utflen <= 0xFFFF) { - bout.writeByte(TC_STRING); - bout.writeUTF(str, utflen); - } else { - bout.writeByte(TC_LONGSTRING); - bout.writeLongUTF(str, utflen); - } - } - - /** - * Writes given array object to stream. - */ - private void writeArray(Object array, - ObjectStreamClass desc, - boolean unshared) - throws IOException - { - bout.writeByte(TC_ARRAY); - writeClassDesc(desc, false); - handles.assign(unshared ? null : array); - - Class ccl = desc.forClass().getComponentType(); - if (ccl.isPrimitive()) { - if (ccl == Integer.TYPE) { - int[] ia = (int[]) array; - bout.writeInt(ia.length); - bout.writeInts(ia, 0, ia.length); - } else if (ccl == Byte.TYPE) { - byte[] ba = (byte[]) array; - bout.writeInt(ba.length); - bout.write(ba, 0, ba.length, true); - } else if (ccl == Long.TYPE) { - long[] ja = (long[]) array; - bout.writeInt(ja.length); - bout.writeLongs(ja, 0, ja.length); - } else if (ccl == Float.TYPE) { - float[] fa = (float[]) array; - bout.writeInt(fa.length); - bout.writeFloats(fa, 0, fa.length); - } else if (ccl == Double.TYPE) { - double[] da = (double[]) array; - bout.writeInt(da.length); - bout.writeDoubles(da, 0, da.length); - } else if (ccl == Short.TYPE) { - short[] sa = (short[]) array; - bout.writeInt(sa.length); - bout.writeShorts(sa, 0, sa.length); - } else if (ccl == Character.TYPE) { - char[] ca = (char[]) array; - bout.writeInt(ca.length); - bout.writeChars(ca, 0, ca.length); - } else if (ccl == Boolean.TYPE) { - boolean[] za = (boolean[]) array; - bout.writeInt(za.length); - bout.writeBooleans(za, 0, za.length); - } else { - throw new InternalError(); - } - } else { - Object[] objs = (Object[]) array; - int len = objs.length; - bout.writeInt(len); - if (extendedDebugInfo) { - debugInfoStack.push( - "array (class \"" + array.getClass().getName() + - "\", size: " + len + ")"); - } - try { - for (int i = 0; i < len; i++) { - if (extendedDebugInfo) { - debugInfoStack.push( - "element of array (index: " + i + ")"); - } - try { - writeObject0(objs[i], false); - } finally { - if (extendedDebugInfo) { - debugInfoStack.pop(); - } - } - } - } finally { - if (extendedDebugInfo) { - debugInfoStack.pop(); - } - } - } - } - - /** - * Writes given enum constant to stream. - */ - private void writeEnum(Enum en, - ObjectStreamClass desc, - boolean unshared) - throws IOException - { - bout.writeByte(TC_ENUM); - ObjectStreamClass sdesc = desc.getSuperDesc(); - writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false); - handles.assign(unshared ? null : en); - writeString(en.name(), false); - } - - /** - * Writes representation of a "ordinary" (i.e., not a String, Class, - * ObjectStreamClass, array, or enum constant) serializable object to the - * stream. - */ - private void writeOrdinaryObject(Object obj, - ObjectStreamClass desc, - boolean unshared) - throws IOException - { - if (extendedDebugInfo) { - debugInfoStack.push( - (depth == 1 ? "root " : "") + "object (class \"" + - obj.getClass().getName() + "\", " + obj.toString() + ")"); - } - try { - desc.checkSerialize(); - - bout.writeByte(TC_OBJECT); - writeClassDesc(desc, false); - handles.assign(unshared ? null : obj); - if (desc.isExternalizable() && !desc.isProxy()) { - writeExternalData((Externalizable) obj); - } else { - writeSerialData(obj, desc); - } - } finally { - if (extendedDebugInfo) { - debugInfoStack.pop(); - } - } - } - - /** - * Writes externalizable data of given object by invoking its - * writeExternal() method. - */ - private void writeExternalData(Externalizable obj) throws IOException { - PutFieldImpl oldPut = curPut; - curPut = null; - - if (extendedDebugInfo) { - debugInfoStack.push("writeExternal data"); - } - Object oldContext = curContext; - try { - curContext = null; - if (protocol == PROTOCOL_VERSION_1) { - obj.writeExternal(this); - } else { - bout.setBlockDataMode(true); - obj.writeExternal(this); - bout.setBlockDataMode(false); - bout.writeByte(TC_ENDBLOCKDATA); - } - } finally { - curContext = oldContext; - if (extendedDebugInfo) { - debugInfoStack.pop(); - } - } - - curPut = oldPut; - } - - /** - * Writes instance data for each serializable class of given object, from - * superclass to subclass. - */ - private void writeSerialData(Object obj, ObjectStreamClass desc) - throws IOException - { - ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout(); - for (int i = 0; i < slots.length; i++) { - ObjectStreamClass slotDesc = slots[i].desc; - if (slotDesc.hasWriteObjectMethod()) { - PutFieldImpl oldPut = curPut; - curPut = null; - Object oldContext = curContext; - - if (extendedDebugInfo) { - debugInfoStack.push( - "custom writeObject data (class \"" + - slotDesc.getName() + "\")"); - } - try { - curContext = new Object(); //new SerialCallbackContext(obj, slotDesc); - bout.setBlockDataMode(true); - slotDesc.invokeWriteObject(obj, this); - bout.setBlockDataMode(false); - bout.writeByte(TC_ENDBLOCKDATA); - } finally { - //curContext.setUsed(); - curContext = oldContext; - if (extendedDebugInfo) { - debugInfoStack.pop(); - } - } - - curPut = oldPut; - } else { - defaultWriteFields(obj, slotDesc); - } - } - } - - /** - * Fetches and writes values of serializable fields of given object to - * stream. The given class descriptor specifies which field values to - * write, and in which order they should be written. - */ - private void defaultWriteFields(Object obj, ObjectStreamClass desc) - throws IOException - { - // REMIND: perform conservative isInstance check here? - desc.checkDefaultSerialize(); - - int primDataSize = desc.getPrimDataSize(); - if (primVals == null || primVals.length < primDataSize) { - primVals = new byte[primDataSize]; - } - desc.getPrimFieldValues(obj, primVals); - bout.write(primVals, 0, primDataSize, false); - - ObjectStreamField[] fields = desc.getFields(false); - Object[] objVals = new Object[desc.getNumObjFields()]; - int numPrimFields = fields.length - objVals.length; - desc.getObjFieldValues(obj, objVals); - for (int i = 0; i < objVals.length; i++) { - if (extendedDebugInfo) { - debugInfoStack.push( - "field (class \"" + desc.getName() + "\", name: \"" + - fields[numPrimFields + i].getName() + "\", type: \"" + - fields[numPrimFields + i].getType() + "\")"); - } - try { - writeObject0(objVals[i], - fields[numPrimFields + i].isUnshared()); - } finally { - if (extendedDebugInfo) { - debugInfoStack.pop(); - } - } - } - } - - /** - * Attempts to write to stream fatal IOException that has caused - * serialization to abort. - */ - private void writeFatalException(IOException ex) throws IOException { - /* - * Note: the serialization specification states that if a second - * IOException occurs while attempting to serialize the original fatal - * exception to the stream, then a StreamCorruptedException should be - * thrown (section 2.1). However, due to a bug in previous - * implementations of serialization, StreamCorruptedExceptions were - * rarely (if ever) actually thrown--the "root" exceptions from - * underlying streams were thrown instead. This historical behavior is - * followed here for consistency. - */ - clear(); - boolean oldMode = bout.setBlockDataMode(false); - try { - bout.writeByte(TC_EXCEPTION); - writeObject0(ex, false); - clear(); - } finally { - bout.setBlockDataMode(oldMode); - } - } - - /** - * Converts specified span of float values into byte values. - */ - // REMIND: remove once hotspot inlines Float.floatToIntBits - private static native void floatsToBytes(float[] src, int srcpos, - byte[] dst, int dstpos, - int nfloats); - - /** - * Converts specified span of double values into byte values. - */ - // REMIND: remove once hotspot inlines Double.doubleToLongBits - private static native void doublesToBytes(double[] src, int srcpos, - byte[] dst, int dstpos, - int ndoubles); - - /** - * Default PutField implementation. - */ - private class PutFieldImpl extends PutField { - - /** class descriptor describing serializable fields */ - private final ObjectStreamClass desc; - /** primitive field values */ - private final byte[] primVals; - /** object field values */ - private final Object[] objVals; - - /** - * Creates PutFieldImpl object for writing fields defined in given - * class descriptor. - */ - PutFieldImpl(ObjectStreamClass desc) { - this.desc = desc; - primVals = new byte[desc.getPrimDataSize()]; - objVals = new Object[desc.getNumObjFields()]; - } - - public void put(String name, boolean val) { - Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val); - } - - public void put(String name, byte val) { - primVals[getFieldOffset(name, Byte.TYPE)] = val; - } - - public void put(String name, char val) { - Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val); - } - - public void put(String name, short val) { - Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val); - } - - public void put(String name, int val) { - Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val); - } - - public void put(String name, float val) { - Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val); - } - - public void put(String name, long val) { - Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val); - } - - public void put(String name, double val) { - Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val); - } - - public void put(String name, Object val) { - objVals[getFieldOffset(name, Object.class)] = val; - } - - // deprecated in ObjectOutputStream.PutField - public void write(ObjectOutput out) throws IOException { - /* - * Applications should *not* use this method to write PutField - * data, as it will lead to stream corruption if the PutField - * object writes any primitive data (since block data mode is not - * unset/set properly, as is done in OOS.writeFields()). This - * broken implementation is being retained solely for behavioral - * compatibility, in order to support applications which use - * OOS.PutField.write() for writing only non-primitive data. - * - * Serialization of unshared objects is not implemented here since - * it is not necessary for backwards compatibility; also, unshared - * semantics may not be supported by the given ObjectOutput - * instance. Applications which write unshared objects using the - * PutField API must use OOS.writeFields(). - */ - if (ObjectOutputStream.this != out) { - throw new IllegalArgumentException("wrong stream"); - } - out.write(primVals, 0, primVals.length); - - ObjectStreamField[] fields = desc.getFields(false); - int numPrimFields = fields.length - objVals.length; - // REMIND: warn if numPrimFields > 0? - for (int i = 0; i < objVals.length; i++) { - if (fields[numPrimFields + i].isUnshared()) { - throw new IOException("cannot write unshared object"); - } - out.writeObject(objVals[i]); - } - } - - /** - * Writes buffered primitive data and object fields to stream. - */ - void writeFields() throws IOException { - bout.write(primVals, 0, primVals.length, false); - - ObjectStreamField[] fields = desc.getFields(false); - int numPrimFields = fields.length - objVals.length; - for (int i = 0; i < objVals.length; i++) { - if (extendedDebugInfo) { - debugInfoStack.push( - "field (class \"" + desc.getName() + "\", name: \"" + - fields[numPrimFields + i].getName() + "\", type: \"" + - fields[numPrimFields + i].getType() + "\")"); - } - try { - writeObject0(objVals[i], - fields[numPrimFields + i].isUnshared()); - } finally { - if (extendedDebugInfo) { - debugInfoStack.pop(); - } - } - } - } - - /** - * Returns offset of field with given name and type. A specified type - * of null matches all types, Object.class matches all non-primitive - * types, and any other non-null type matches assignable types only. - * Throws IllegalArgumentException if no matching field found. - */ - private int getFieldOffset(String name, Class type) { - ObjectStreamField field = desc.getField(name, type); - if (field == null) { - throw new IllegalArgumentException("no such field " + name + - " with type " + type); - } - return field.getOffset(); - } - } - - /** - * Buffered output stream with two modes: in default mode, outputs data in - * same format as DataOutputStream; in "block data" mode, outputs data - * bracketed by block data markers (see object serialization specification - * for details). - */ - private static class BlockDataOutputStream - extends OutputStream implements DataOutput - { - /** maximum data block length */ - private static final int MAX_BLOCK_SIZE = 1024; - /** maximum data block header length */ - private static final int MAX_HEADER_SIZE = 5; - /** (tunable) length of char buffer (for writing strings) */ - private static final int CHAR_BUF_SIZE = 256; - - /** buffer for writing general/block data */ - private final byte[] buf = new byte[MAX_BLOCK_SIZE]; - /** buffer for writing block data headers */ - private final byte[] hbuf = new byte[MAX_HEADER_SIZE]; - /** char buffer for fast string writes */ - private final char[] cbuf = new char[CHAR_BUF_SIZE]; - - /** block data mode */ - private boolean blkmode = false; - /** current offset into buf */ - private int pos = 0; - - /** underlying output stream */ - private final OutputStream out; - /** loopback stream (for data writes that span data blocks) */ - private final DataOutputStream dout; - - /** - * Creates new BlockDataOutputStream on top of given underlying stream. - * Block data mode is turned off by default. - */ - BlockDataOutputStream(OutputStream out) { - this.out = out; - dout = new DataOutputStream(this); - } - - /** - * Sets block data mode to the given mode (true == on, false == off) - * and returns the previous mode value. If the new mode is the same as - * the old mode, no action is taken. If the new mode differs from the - * old mode, any buffered data is flushed before switching to the new - * mode. - */ - boolean setBlockDataMode(boolean mode) throws IOException { - if (blkmode == mode) { - return blkmode; - } - drain(); - blkmode = mode; - return !blkmode; - } - - /** - * Returns true if the stream is currently in block data mode, false - * otherwise. - */ - boolean getBlockDataMode() { - return blkmode; - } - - /* ----------------- generic output stream methods ----------------- */ - /* - * The following methods are equivalent to their counterparts in - * OutputStream, except that they partition written data into data - * blocks when in block data mode. - */ - - public void write(int b) throws IOException { - if (pos >= MAX_BLOCK_SIZE) { - drain(); - } - buf[pos++] = (byte) b; - } - - public void write(byte[] b) throws IOException { - write(b, 0, b.length, false); - } - - public void write(byte[] b, int off, int len) throws IOException { - write(b, off, len, false); - } - - public void flush() throws IOException { - drain(); - out.flush(); - } - - public void close() throws IOException { - flush(); - out.close(); - } - - /** - * Writes specified span of byte values from given array. If copy is - * true, copies the values to an intermediate buffer before writing - * them to underlying stream (to avoid exposing a reference to the - * original byte array). - */ - void write(byte[] b, int off, int len, boolean copy) - throws IOException - { - if (!(copy || blkmode)) { // write directly - drain(); - out.write(b, off, len); - return; - } - - while (len > 0) { - if (pos >= MAX_BLOCK_SIZE) { - drain(); - } - if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) { - // avoid unnecessary copy - writeBlockHeader(MAX_BLOCK_SIZE); - out.write(b, off, MAX_BLOCK_SIZE); - off += MAX_BLOCK_SIZE; - len -= MAX_BLOCK_SIZE; - } else { - int wlen = Math.min(len, MAX_BLOCK_SIZE - pos); - System.arraycopy(b, off, buf, pos, wlen); - pos += wlen; - off += wlen; - len -= wlen; - } - } - } - - /** - * Writes all buffered data from this stream to the underlying stream, - * but does not flush underlying stream. - */ - void drain() throws IOException { - if (pos == 0) { - return; - } - if (blkmode) { - writeBlockHeader(pos); - } - out.write(buf, 0, pos); - pos = 0; - } - - /** - * Writes block data header. Data blocks shorter than 256 bytes are - * prefixed with a 2-byte header; all others start with a 5-byte - * header. - */ - private void writeBlockHeader(int len) throws IOException { - if (len <= 0xFF) { - hbuf[0] = TC_BLOCKDATA; - hbuf[1] = (byte) len; - out.write(hbuf, 0, 2); - } else { - hbuf[0] = TC_BLOCKDATALONG; - Bits.putInt(hbuf, 1, len); - out.write(hbuf, 0, 5); - } - } - - - /* ----------------- primitive data output methods ----------------- */ - /* - * The following methods are equivalent to their counterparts in - * DataOutputStream, except that they partition written data into data - * blocks when in block data mode. - */ - - public void writeBoolean(boolean v) throws IOException { - if (pos >= MAX_BLOCK_SIZE) { - drain(); - } - Bits.putBoolean(buf, pos++, v); - } - - public void writeByte(int v) throws IOException { - if (pos >= MAX_BLOCK_SIZE) { - drain(); - } - buf[pos++] = (byte) v; - } - - public void writeChar(int v) throws IOException { - if (pos + 2 <= MAX_BLOCK_SIZE) { - Bits.putChar(buf, pos, (char) v); - pos += 2; - } else { - dout.writeChar(v); - } - } - - public void writeShort(int v) throws IOException { - if (pos + 2 <= MAX_BLOCK_SIZE) { - Bits.putShort(buf, pos, (short) v); - pos += 2; - } else { - dout.writeShort(v); - } - } - - public void writeInt(int v) throws IOException { - if (pos + 4 <= MAX_BLOCK_SIZE) { - Bits.putInt(buf, pos, v); - pos += 4; - } else { - dout.writeInt(v); - } - } - - public void writeFloat(float v) throws IOException { - if (pos + 4 <= MAX_BLOCK_SIZE) { - Bits.putFloat(buf, pos, v); - pos += 4; - } else { - dout.writeFloat(v); - } - } - - public void writeLong(long v) throws IOException { - if (pos + 8 <= MAX_BLOCK_SIZE) { - Bits.putLong(buf, pos, v); - pos += 8; - } else { - dout.writeLong(v); - } - } - - public void writeDouble(double v) throws IOException { - if (pos + 8 <= MAX_BLOCK_SIZE) { - Bits.putDouble(buf, pos, v); - pos += 8; - } else { - dout.writeDouble(v); - } - } - - public void writeBytes(String s) throws IOException { - int endoff = s.length(); - int cpos = 0; - int csize = 0; - for (int off = 0; off < endoff; ) { - if (cpos >= csize) { - cpos = 0; - csize = Math.min(endoff - off, CHAR_BUF_SIZE); - s.getChars(off, off + csize, cbuf, 0); - } - if (pos >= MAX_BLOCK_SIZE) { - drain(); - } - int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos); - int stop = pos + n; - while (pos < stop) { - buf[pos++] = (byte) cbuf[cpos++]; - } - off += n; - } - } - - public void writeChars(String s) throws IOException { - int endoff = s.length(); - for (int off = 0; off < endoff; ) { - int csize = Math.min(endoff - off, CHAR_BUF_SIZE); - s.getChars(off, off + csize, cbuf, 0); - writeChars(cbuf, 0, csize); - off += csize; - } - } - - public void writeUTF(String s) throws IOException { - writeUTF(s, getUTFLength(s)); - } - - - /* -------------- primitive data array output methods -------------- */ - /* - * The following methods write out spans of primitive data values. - * Though equivalent to calling the corresponding primitive write - * methods repeatedly, these methods are optimized for writing groups - * of primitive data values more efficiently. - */ - - void writeBooleans(boolean[] v, int off, int len) throws IOException { - int endoff = off + len; - while (off < endoff) { - if (pos >= MAX_BLOCK_SIZE) { - drain(); - } - int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos)); - while (off < stop) { - Bits.putBoolean(buf, pos++, v[off++]); - } - } - } - - void writeChars(char[] v, int off, int len) throws IOException { - int limit = MAX_BLOCK_SIZE - 2; - int endoff = off + len; - while (off < endoff) { - if (pos <= limit) { - int avail = (MAX_BLOCK_SIZE - pos) >> 1; - int stop = Math.min(endoff, off + avail); - while (off < stop) { - Bits.putChar(buf, pos, v[off++]); - pos += 2; - } - } else { - dout.writeChar(v[off++]); - } - } - } - - void writeShorts(short[] v, int off, int len) throws IOException { - int limit = MAX_BLOCK_SIZE - 2; - int endoff = off + len; - while (off < endoff) { - if (pos <= limit) { - int avail = (MAX_BLOCK_SIZE - pos) >> 1; - int stop = Math.min(endoff, off + avail); - while (off < stop) { - Bits.putShort(buf, pos, v[off++]); - pos += 2; - } - } else { - dout.writeShort(v[off++]); - } - } - } - - void writeInts(int[] v, int off, int len) throws IOException { - int limit = MAX_BLOCK_SIZE - 4; - int endoff = off + len; - while (off < endoff) { - if (pos <= limit) { - int avail = (MAX_BLOCK_SIZE - pos) >> 2; - int stop = Math.min(endoff, off + avail); - while (off < stop) { - Bits.putInt(buf, pos, v[off++]); - pos += 4; - } - } else { - dout.writeInt(v[off++]); - } - } - } - - void writeFloats(float[] v, int off, int len) throws IOException { - int limit = MAX_BLOCK_SIZE - 4; - int endoff = off + len; - while (off < endoff) { - if (pos <= limit) { - int avail = (MAX_BLOCK_SIZE - pos) >> 2; - int chunklen = Math.min(endoff - off, avail); - floatsToBytes(v, off, buf, pos, chunklen); - off += chunklen; - pos += chunklen << 2; - } else { - dout.writeFloat(v[off++]); - } - } - } - - void writeLongs(long[] v, int off, int len) throws IOException { - int limit = MAX_BLOCK_SIZE - 8; - int endoff = off + len; - while (off < endoff) { - if (pos <= limit) { - int avail = (MAX_BLOCK_SIZE - pos) >> 3; - int stop = Math.min(endoff, off + avail); - while (off < stop) { - Bits.putLong(buf, pos, v[off++]); - pos += 8; - } - } else { - dout.writeLong(v[off++]); - } - } - } - - void writeDoubles(double[] v, int off, int len) throws IOException { - int limit = MAX_BLOCK_SIZE - 8; - int endoff = off + len; - while (off < endoff) { - if (pos <= limit) { - int avail = (MAX_BLOCK_SIZE - pos) >> 3; - int chunklen = Math.min(endoff - off, avail); - doublesToBytes(v, off, buf, pos, chunklen); - off += chunklen; - pos += chunklen << 3; - } else { - dout.writeDouble(v[off++]); - } - } - } - - /** - * Returns the length in bytes of the UTF encoding of the given string. - */ - long getUTFLength(String s) { - int len = s.length(); - long utflen = 0; - for (int off = 0; off < len; ) { - int csize = Math.min(len - off, CHAR_BUF_SIZE); - s.getChars(off, off + csize, cbuf, 0); - for (int cpos = 0; cpos < csize; cpos++) { - char c = cbuf[cpos]; - if (c >= 0x0001 && c <= 0x007F) { - utflen++; - } else if (c > 0x07FF) { - utflen += 3; - } else { - utflen += 2; - } - } - off += csize; - } - return utflen; - } - - /** - * Writes the given string in UTF format. This method is used in - * situations where the UTF encoding length of the string is already - * known; specifying it explicitly avoids a prescan of the string to - * determine its UTF length. - */ - void writeUTF(String s, long utflen) throws IOException { - if (utflen > 0xFFFFL) { - throw new UTFDataFormatException(); - } - writeShort((int) utflen); - if (utflen == (long) s.length()) { - writeBytes(s); - } else { - writeUTFBody(s); - } - } - - /** - * Writes given string in "long" UTF format. "Long" UTF format is - * identical to standard UTF, except that it uses an 8 byte header - * (instead of the standard 2 bytes) to convey the UTF encoding length. - */ - void writeLongUTF(String s) throws IOException { - writeLongUTF(s, getUTFLength(s)); - } - - /** - * Writes given string in "long" UTF format, where the UTF encoding - * length of the string is already known. - */ - void writeLongUTF(String s, long utflen) throws IOException { - writeLong(utflen); - if (utflen == (long) s.length()) { - writeBytes(s); - } else { - writeUTFBody(s); - } - } - - /** - * Writes the "body" (i.e., the UTF representation minus the 2-byte or - * 8-byte length header) of the UTF encoding for the given string. - */ - private void writeUTFBody(String s) throws IOException { - int limit = MAX_BLOCK_SIZE - 3; - int len = s.length(); - for (int off = 0; off < len; ) { - int csize = Math.min(len - off, CHAR_BUF_SIZE); - s.getChars(off, off + csize, cbuf, 0); - for (int cpos = 0; cpos < csize; cpos++) { - char c = cbuf[cpos]; - if (pos <= limit) { - if (c <= 0x007F && c != 0) { - buf[pos++] = (byte) c; - } else if (c > 0x07FF) { - buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F)); - buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F)); - buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F)); - pos += 3; - } else { - buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F)); - buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F)); - pos += 2; - } - } else { // write one byte at a time to normalize block - if (c <= 0x007F && c != 0) { - write(c); - } else if (c > 0x07FF) { - write(0xE0 | ((c >> 12) & 0x0F)); - write(0x80 | ((c >> 6) & 0x3F)); - write(0x80 | ((c >> 0) & 0x3F)); - } else { - write(0xC0 | ((c >> 6) & 0x1F)); - write(0x80 | ((c >> 0) & 0x3F)); - } - } - } - off += csize; - } - } - } - - /** - * Lightweight identity hash table which maps objects to integer handles, - * assigned in ascending order. - */ - private static class HandleTable { - - /* number of mappings in table/next available handle */ - private int size; - /* size threshold determining when to expand hash spine */ - private int threshold; - /* factor for computing size threshold */ - private final float loadFactor; - /* maps hash value -> candidate handle value */ - private int[] spine; - /* maps handle value -> next candidate handle value */ - private int[] next; - /* maps handle value -> associated object */ - private Object[] objs; - - /** - * Creates new HandleTable with given capacity and load factor. - */ - HandleTable(int initialCapacity, float loadFactor) { - this.loadFactor = loadFactor; - spine = new int[initialCapacity]; - next = new int[initialCapacity]; - objs = new Object[initialCapacity]; - threshold = (int) (initialCapacity * loadFactor); - clear(); - } - - /** - * Assigns next available handle to given object, and returns handle - * value. Handles are assigned in ascending order starting at 0. - */ - int assign(Object obj) { - if (size >= next.length) { - growEntries(); - } - if (size >= threshold) { - growSpine(); - } - insert(obj, size); - return size++; - } - - /** - * Looks up and returns handle associated with given object, or -1 if - * no mapping found. - */ - int lookup(Object obj) { - if (size == 0) { - return -1; - } - int index = hash(obj) % spine.length; - for (int i = spine[index]; i >= 0; i = next[i]) { - if (objs[i] == obj) { - return i; - } - } - return -1; - } - - /** - * Resets table to its initial (empty) state. - */ - void clear() { - Arrays.fill(spine, -1); - Arrays.fill(objs, 0, size, null); - size = 0; - } - - /** - * Returns the number of mappings currently in table. - */ - int size() { - return size; - } - - /** - * Inserts mapping object -> handle mapping into table. Assumes table - * is large enough to accommodate new mapping. - */ - private void insert(Object obj, int handle) { - int index = hash(obj) % spine.length; - objs[handle] = obj; - next[handle] = spine[index]; - spine[index] = handle; - } - - /** - * Expands the hash "spine" -- equivalent to increasing the number of - * buckets in a conventional hash table. - */ - private void growSpine() { - spine = new int[(spine.length << 1) + 1]; - threshold = (int) (spine.length * loadFactor); - Arrays.fill(spine, -1); - for (int i = 0; i < size; i++) { - insert(objs[i], i); - } - } - - /** - * Increases hash table capacity by lengthening entry arrays. - */ - private void growEntries() { - int newLength = (next.length << 1) + 1; - int[] newNext = new int[newLength]; - System.arraycopy(next, 0, newNext, 0, size); - next = newNext; - - Object[] newObjs = new Object[newLength]; - System.arraycopy(objs, 0, newObjs, 0, size); - objs = newObjs; - } - - /** - * Returns hash value for given object. - */ - private int hash(Object obj) { - return System.identityHashCode(obj) & 0x7FFFFFFF; - } - } - - /** - * Lightweight identity hash table which maps objects to replacement - * objects. - */ - private static class ReplaceTable { - - /* maps object -> index */ - private final HandleTable htab; - /* maps index -> replacement object */ - private Object[] reps; - - /** - * Creates new ReplaceTable with given capacity and load factor. - */ - ReplaceTable(int initialCapacity, float loadFactor) { - htab = new HandleTable(initialCapacity, loadFactor); - reps = new Object[initialCapacity]; - } - - /** - * Enters mapping from object to replacement object. - */ - void assign(Object obj, Object rep) { - int index = htab.assign(obj); - while (index >= reps.length) { - grow(); - } - reps[index] = rep; - } - - /** - * Looks up and returns replacement for given object. If no - * replacement is found, returns the lookup object itself. - */ - Object lookup(Object obj) { - int index = htab.lookup(obj); - return (index >= 0) ? reps[index] : obj; - } - - /** - * Resets table to its initial (empty) state. - */ - void clear() { - Arrays.fill(reps, 0, htab.size(), null); - htab.clear(); - } - - /** - * Returns the number of mappings currently in table. - */ - int size() { - return htab.size(); - } - - /** - * Increases table capacity. - */ - private void grow() { - Object[] newReps = new Object[(reps.length << 1) + 1]; - System.arraycopy(reps, 0, newReps, 0, reps.length); - reps = newReps; - } - } - - /** - * Stack to keep debug information about the state of the - * serialization process, for embedding in exception messages. - */ - private static class DebugTraceInfoStack { - private final List stack; - - DebugTraceInfoStack() { - stack = new ArrayList<>(); - } - - /** - * Removes all of the elements from enclosed list. - */ - void clear() { - stack.clear(); - } - - /** - * Removes the object at the top of enclosed list. - */ - void pop() { - stack.remove(stack.size()-1); - } - - /** - * Pushes a String onto the top of enclosed list. - */ - void push(String entry) { - stack.add("\t- " + entry); - } - - /** - * Returns a string representation of this object - */ - public String toString() { - StringBuilder buffer = new StringBuilder(); - if (!stack.isEmpty()) { - for(int i = stack.size(); i > 0; i-- ) { - buffer.append(stack.get(i-1) + ((i != 1) ? "\n" : "")); - } - } - return buffer.toString(); - } - } - -}