Save This Page
Home » openjdk-7 » java » io » [javadoc | source]
    1   /*
    2    * Copyright 1996-2006 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   
   26   package java.io;
   27   
   28   import java.io.ObjectStreamClass.WeakClassKey;
   29   import java.lang.ref.ReferenceQueue;
   30   import java.security.AccessController;
   31   import java.security.PrivilegedAction;
   32   import java.util.ArrayList;
   33   import java.util.Arrays;
   34   import java.util.List;
   35   import java.util.concurrent.ConcurrentHashMap;
   36   import java.util.concurrent.ConcurrentMap;
   37   import static java.io.ObjectStreamClass.processQueue;
   38   
   39   /**
   40    * An ObjectOutputStream writes primitive data types and graphs of Java objects
   41    * to an OutputStream.  The objects can be read (reconstituted) using an
   42    * ObjectInputStream.  Persistent storage of objects can be accomplished by
   43    * using a file for the stream.  If the stream is a network socket stream, the
   44    * objects can be reconstituted on another host or in another process.
   45    *
   46    * <p>Only objects that support the java.io.Serializable interface can be
   47    * written to streams.  The class of each serializable object is encoded
   48    * including the class name and signature of the class, the values of the
   49    * object's fields and arrays, and the closure of any other objects referenced
   50    * from the initial objects.
   51    *
   52    * <p>The method writeObject is used to write an object to the stream.  Any
   53    * object, including Strings and arrays, is written with writeObject. Multiple
   54    * objects or primitives can be written to the stream.  The objects must be
   55    * read back from the corresponding ObjectInputstream with the same types and
   56    * in the same order as they were written.
   57    *
   58    * <p>Primitive data types can also be written to the stream using the
   59    * appropriate methods from DataOutput. Strings can also be written using the
   60    * writeUTF method.
   61    *
   62    * <p>The default serialization mechanism for an object writes the class of the
   63    * object, the class signature, and the values of all non-transient and
   64    * non-static fields.  References to other objects (except in transient or
   65    * static fields) cause those objects to be written also. Multiple references
   66    * to a single object are encoded using a reference sharing mechanism so that
   67    * graphs of objects can be restored to the same shape as when the original was
   68    * written.
   69    *
   70    * <p>For example to write an object that can be read by the example in
   71    * ObjectInputStream:
   72    * <br>
   73    * <pre>
   74    *      FileOutputStream fos = new FileOutputStream("t.tmp");
   75    *      ObjectOutputStream oos = new ObjectOutputStream(fos);
   76    *
   77    *      oos.writeInt(12345);
   78    *      oos.writeObject("Today");
   79    *      oos.writeObject(new Date());
   80    *
   81    *      oos.close();
   82    * </pre>
   83    *
   84    * <p>Classes that require special handling during the serialization and
   85    * deserialization process must implement special methods with these exact
   86    * signatures:
   87    * <br>
   88    * <pre>
   89    * private void readObject(java.io.ObjectInputStream stream)
   90    *     throws IOException, ClassNotFoundException;
   91    * private void writeObject(java.io.ObjectOutputStream stream)
   92    *     throws IOException
   93    * private void readObjectNoData()
   94    *     throws ObjectStreamException;
   95    * </pre>
   96    *
   97    * <p>The writeObject method is responsible for writing the state of the object
   98    * for its particular class so that the corresponding readObject method can
   99    * restore it.  The method does not need to concern itself with the state
  100    * belonging to the object's superclasses or subclasses.  State is saved by
  101    * writing the individual fields to the ObjectOutputStream using the
  102    * writeObject method or by using the methods for primitive data types
  103    * supported by DataOutput.
  104    *
  105    * <p>Serialization does not write out the fields of any object that does not
  106    * implement the java.io.Serializable interface.  Subclasses of Objects that
  107    * are not serializable can be serializable. In this case the non-serializable
  108    * class must have a no-arg constructor to allow its fields to be initialized.
  109    * In this case it is the responsibility of the subclass to save and restore
  110    * the state of the non-serializable class. It is frequently the case that the
  111    * fields of that class are accessible (public, package, or protected) or that
  112    * there are get and set methods that can be used to restore the state.
  113    *
  114    * <p>Serialization of an object can be prevented by implementing writeObject
  115    * and readObject methods that throw the NotSerializableException.  The
  116    * exception will be caught by the ObjectOutputStream and abort the
  117    * serialization process.
  118    *
  119    * <p>Implementing the Externalizable interface allows the object to assume
  120    * complete control over the contents and format of the object's serialized
  121    * form.  The methods of the Externalizable interface, writeExternal and
  122    * readExternal, are called to save and restore the objects state.  When
  123    * implemented by a class they can write and read their own state using all of
  124    * the methods of ObjectOutput and ObjectInput.  It is the responsibility of
  125    * the objects to handle any versioning that occurs.
  126    *
  127    * <p>Enum constants are serialized differently than ordinary serializable or
  128    * externalizable objects.  The serialized form of an enum constant consists
  129    * solely of its name; field values of the constant are not transmitted.  To
  130    * serialize an enum constant, ObjectOutputStream writes the string returned by
  131    * the constant's name method.  Like other serializable or externalizable
  132    * objects, enum constants can function as the targets of back references
  133    * appearing subsequently in the serialization stream.  The process by which
  134    * enum constants are serialized cannot be customized; any class-specific
  135    * writeObject and writeReplace methods defined by enum types are ignored
  136    * during serialization.  Similarly, any serialPersistentFields or
  137    * serialVersionUID field declarations are also ignored--all enum types have a
  138    * fixed serialVersionUID of 0L.
  139    *
  140    * <p>Primitive data, excluding serializable fields and externalizable data, is
  141    * written to the ObjectOutputStream in block-data records. A block data record
  142    * is composed of a header and data. The block data header consists of a marker
  143    * and the number of bytes to follow the header.  Consecutive primitive data
  144    * writes are merged into one block-data record.  The blocking factor used for
  145    * a block-data record will be 1024 bytes.  Each block-data record will be
  146    * filled up to 1024 bytes, or be written whenever there is a termination of
  147    * block-data mode.  Calls to the ObjectOutputStream methods writeObject,
  148    * defaultWriteObject and writeFields initially terminate any existing
  149    * block-data record.
  150    *
  151    * @author      Mike Warres
  152    * @author      Roger Riggs
  153    * @see java.io.DataOutput
  154    * @see java.io.ObjectInputStream
  155    * @see java.io.Serializable
  156    * @see java.io.Externalizable
  157    * @see <a href="../../../platform/serialization/spec/output.html">Object Serialization Specification, Section 2, Object Output Classes</a>
  158    * @since       JDK1.1
  159    */
  160   public class ObjectOutputStream
  161       extends OutputStream implements ObjectOutput, ObjectStreamConstants
  162   {
  163   
  164       private static class Caches {
  165           /** cache of subclass security audit results */
  166           static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits =
  167               new ConcurrentHashMap<WeakClassKey,Boolean>();
  168   
  169           /** queue for WeakReferences to audited subclasses */
  170           static final ReferenceQueue<Class<?>> subclassAuditsQueue =
  171               new ReferenceQueue<Class<?>>();
  172       }
  173   
  174       /** filter stream for handling block data conversion */
  175       private final BlockDataOutputStream bout;
  176       /** obj -> wire handle map */
  177       private final HandleTable handles;
  178       /** obj -> replacement obj map */
  179       private final ReplaceTable subs;
  180       /** stream protocol version */
  181       private int protocol = PROTOCOL_VERSION_2;
  182       /** recursion depth */
  183       private int depth;
  184   
  185       /** buffer for writing primitive field values */
  186       private byte[] primVals;
  187   
  188       /** if true, invoke writeObjectOverride() instead of writeObject() */
  189       private final boolean enableOverride;
  190       /** if true, invoke replaceObject() */
  191       private boolean enableReplace;
  192   
  193       // values below valid only during upcalls to writeObject()/writeExternal()
  194       /** object currently being serialized */
  195       private Object curObj;
  196       /** descriptor for current class (null if in writeExternal()) */
  197       private ObjectStreamClass curDesc;
  198       /** current PutField object */
  199       private PutFieldImpl curPut;
  200   
  201       /** custom storage for debug trace info */
  202       private final DebugTraceInfoStack debugInfoStack;
  203   
  204       /**
  205        * value of "sun.io.serialization.extendedDebugInfo" property,
  206        * as true or false for extended information about exception's place
  207        */
  208       private static final boolean extendedDebugInfo =
  209           java.security.AccessController.doPrivileged(
  210               new sun.security.action.GetBooleanAction(
  211                   "sun.io.serialization.extendedDebugInfo")).booleanValue();
  212   
  213       /**
  214        * Creates an ObjectOutputStream that writes to the specified OutputStream.
  215        * This constructor writes the serialization stream header to the
  216        * underlying stream; callers may wish to flush the stream immediately to
  217        * ensure that constructors for receiving ObjectInputStreams will not block
  218        * when reading the header.
  219        *
  220        * <p>If a security manager is installed, this constructor will check for
  221        * the "enableSubclassImplementation" SerializablePermission when invoked
  222        * directly or indirectly by the constructor of a subclass which overrides
  223        * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared
  224        * methods.
  225        *
  226        * @param   out output stream to write to
  227        * @throws  IOException if an I/O error occurs while writing stream header
  228        * @throws  SecurityException if untrusted subclass illegally overrides
  229        *          security-sensitive methods
  230        * @throws  NullPointerException if <code>out</code> is <code>null</code>
  231        * @since   1.4
  232        * @see     ObjectOutputStream#ObjectOutputStream()
  233        * @see     ObjectOutputStream#putFields()
  234        * @see     ObjectInputStream#ObjectInputStream(InputStream)
  235        */
  236       public ObjectOutputStream(OutputStream out) throws IOException {
  237           verifySubclass();
  238           bout = new BlockDataOutputStream(out);
  239           handles = new HandleTable(10, (float) 3.00);
  240           subs = new ReplaceTable(10, (float) 3.00);
  241           enableOverride = false;
  242           writeStreamHeader();
  243           bout.setBlockDataMode(true);
  244           if (extendedDebugInfo) {
  245               debugInfoStack = new DebugTraceInfoStack();
  246           } else {
  247               debugInfoStack = null;
  248           }
  249       }
  250   
  251       /**
  252        * Provide a way for subclasses that are completely reimplementing
  253        * ObjectOutputStream to not have to allocate private data just used by
  254        * this implementation of ObjectOutputStream.
  255        *
  256        * <p>If there is a security manager installed, this method first calls the
  257        * security manager's <code>checkPermission</code> method with a
  258        * <code>SerializablePermission("enableSubclassImplementation")</code>
  259        * permission to ensure it's ok to enable subclassing.
  260        *
  261        * @throws  SecurityException if a security manager exists and its
  262        *          <code>checkPermission</code> method denies enabling
  263        *          subclassing.
  264        * @see SecurityManager#checkPermission
  265        * @see java.io.SerializablePermission
  266        */
  267       protected ObjectOutputStream() throws IOException, SecurityException {
  268           SecurityManager sm = System.getSecurityManager();
  269           if (sm != null) {
  270               sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
  271           }
  272           bout = null;
  273           handles = null;
  274           subs = null;
  275           enableOverride = true;
  276           debugInfoStack = null;
  277       }
  278   
  279       /**
  280        * Specify stream protocol version to use when writing the stream.
  281        *
  282        * <p>This routine provides a hook to enable the current version of
  283        * Serialization to write in a format that is backwards compatible to a
  284        * previous version of the stream format.
  285        *
  286        * <p>Every effort will be made to avoid introducing additional
  287        * backwards incompatibilities; however, sometimes there is no
  288        * other alternative.
  289        *
  290        * @param   version use ProtocolVersion from java.io.ObjectStreamConstants.
  291        * @throws  IllegalStateException if called after any objects
  292        *          have been serialized.
  293        * @throws  IllegalArgumentException if invalid version is passed in.
  294        * @throws  IOException if I/O errors occur
  295        * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
  296        * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2
  297        * @since   1.2
  298        */
  299       public void useProtocolVersion(int version) throws IOException {
  300           if (handles.size() != 0) {
  301               // REMIND: implement better check for pristine stream?
  302               throw new IllegalStateException("stream non-empty");
  303           }
  304           switch (version) {
  305               case PROTOCOL_VERSION_1:
  306               case PROTOCOL_VERSION_2:
  307                   protocol = version;
  308                   break;
  309   
  310               default:
  311                   throw new IllegalArgumentException(
  312                       "unknown version: " + version);
  313           }
  314       }
  315   
  316       /**
  317        * Write the specified object to the ObjectOutputStream.  The class of the
  318        * object, the signature of the class, and the values of the non-transient
  319        * and non-static fields of the class and all of its supertypes are
  320        * written.  Default serialization for a class can be overridden using the
  321        * writeObject and the readObject methods.  Objects referenced by this
  322        * object are written transitively so that a complete equivalent graph of
  323        * objects can be reconstructed by an ObjectInputStream.
  324        *
  325        * <p>Exceptions are thrown for problems with the OutputStream and for
  326        * classes that should not be serialized.  All exceptions are fatal to the
  327        * OutputStream, which is left in an indeterminate state, and it is up to
  328        * the caller to ignore or recover the stream state.
  329        *
  330        * @throws  InvalidClassException Something is wrong with a class used by
  331        *          serialization.
  332        * @throws  NotSerializableException Some object to be serialized does not
  333        *          implement the java.io.Serializable interface.
  334        * @throws  IOException Any exception thrown by the underlying
  335        *          OutputStream.
  336        */
  337       public final void writeObject(Object obj) throws IOException {
  338           if (enableOverride) {
  339               writeObjectOverride(obj);
  340               return;
  341           }
  342           try {
  343               writeObject0(obj, false);
  344           } catch (IOException ex) {
  345               if (depth == 0) {
  346                   writeFatalException(ex);
  347               }
  348               throw ex;
  349           }
  350       }
  351   
  352       /**
  353        * Method used by subclasses to override the default writeObject method.
  354        * This method is called by trusted subclasses of ObjectInputStream that
  355        * constructed ObjectInputStream using the protected no-arg constructor.
  356        * The subclass is expected to provide an override method with the modifier
  357        * "final".
  358        *
  359        * @param   obj object to be written to the underlying stream
  360        * @throws  IOException if there are I/O errors while writing to the
  361        *          underlying stream
  362        * @see #ObjectOutputStream()
  363        * @see #writeObject(Object)
  364        * @since 1.2
  365        */
  366       protected void writeObjectOverride(Object obj) throws IOException {
  367       }
  368   
  369       /**
  370        * Writes an "unshared" object to the ObjectOutputStream.  This method is
  371        * identical to writeObject, except that it always writes the given object
  372        * as a new, unique object in the stream (as opposed to a back-reference
  373        * pointing to a previously serialized instance).  Specifically:
  374        * <ul>
  375        *   <li>An object written via writeUnshared is always serialized in the
  376        *       same manner as a newly appearing object (an object that has not
  377        *       been written to the stream yet), regardless of whether or not the
  378        *       object has been written previously.
  379        *
  380        *   <li>If writeObject is used to write an object that has been previously
  381        *       written with writeUnshared, the previous writeUnshared operation
  382        *       is treated as if it were a write of a separate object.  In other
  383        *       words, ObjectOutputStream will never generate back-references to
  384        *       object data written by calls to writeUnshared.
  385        * </ul>
  386        * While writing an object via writeUnshared does not in itself guarantee a
  387        * unique reference to the object when it is deserialized, it allows a
  388        * single object to be defined multiple times in a stream, so that multiple
  389        * calls to readUnshared by the receiver will not conflict.  Note that the
  390        * rules described above only apply to the base-level object written with
  391        * writeUnshared, and not to any transitively referenced sub-objects in the
  392        * object graph to be serialized.
  393        *
  394        * <p>ObjectOutputStream subclasses which override this method can only be
  395        * constructed in security contexts possessing the
  396        * "enableSubclassImplementation" SerializablePermission; any attempt to
  397        * instantiate such a subclass without this permission will cause a
  398        * SecurityException to be thrown.
  399        *
  400        * @param   obj object to write to stream
  401        * @throws  NotSerializableException if an object in the graph to be
  402        *          serialized does not implement the Serializable interface
  403        * @throws  InvalidClassException if a problem exists with the class of an
  404        *          object to be serialized
  405        * @throws  IOException if an I/O error occurs during serialization
  406        * @since 1.4
  407        */
  408       public void writeUnshared(Object obj) throws IOException {
  409           try {
  410               writeObject0(obj, true);
  411           } catch (IOException ex) {
  412               if (depth == 0) {
  413                   writeFatalException(ex);
  414               }
  415               throw ex;
  416           }
  417       }
  418   
  419       /**
  420        * Write the non-static and non-transient fields of the current class to
  421        * this stream.  This may only be called from the writeObject method of the
  422        * class being serialized. It will throw the NotActiveException if it is
  423        * called otherwise.
  424        *
  425        * @throws  IOException if I/O errors occur while writing to the underlying
  426        *          <code>OutputStream</code>
  427        */
  428       public void defaultWriteObject() throws IOException {
  429           if (curObj == null || curDesc == null) {
  430               throw new NotActiveException("not in call to writeObject");
  431           }
  432           bout.setBlockDataMode(false);
  433           defaultWriteFields(curObj, curDesc);
  434           bout.setBlockDataMode(true);
  435       }
  436   
  437       /**
  438        * Retrieve the object used to buffer persistent fields to be written to
  439        * the stream.  The fields will be written to the stream when writeFields
  440        * method is called.
  441        *
  442        * @return  an instance of the class Putfield that holds the serializable
  443        *          fields
  444        * @throws  IOException if I/O errors occur
  445        * @since 1.2
  446        */
  447       public ObjectOutputStream.PutField putFields() throws IOException {
  448           if (curPut == null) {
  449               if (curObj == null || curDesc == null) {
  450                   throw new NotActiveException("not in call to writeObject");
  451               }
  452               curPut = new PutFieldImpl(curDesc);
  453           }
  454           return curPut;
  455       }
  456   
  457       /**
  458        * Write the buffered fields to the stream.
  459        *
  460        * @throws  IOException if I/O errors occur while writing to the underlying
  461        *          stream
  462        * @throws  NotActiveException Called when a classes writeObject method was
  463        *          not called to write the state of the object.
  464        * @since 1.2
  465        */
  466       public void writeFields() throws IOException {
  467           if (curPut == null) {
  468               throw new NotActiveException("no current PutField object");
  469           }
  470           bout.setBlockDataMode(false);
  471           curPut.writeFields();
  472           bout.setBlockDataMode(true);
  473       }
  474   
  475       /**
  476        * Reset will disregard the state of any objects already written to the
  477        * stream.  The state is reset to be the same as a new ObjectOutputStream.
  478        * The current point in the stream is marked as reset so the corresponding
  479        * ObjectInputStream will be reset at the same point.  Objects previously
  480        * written to the stream will not be refered to as already being in the
  481        * stream.  They will be written to the stream again.
  482        *
  483        * @throws  IOException if reset() is invoked while serializing an object.
  484        */
  485       public void reset() throws IOException {
  486           if (depth != 0) {
  487               throw new IOException("stream active");
  488           }
  489           bout.setBlockDataMode(false);
  490           bout.writeByte(TC_RESET);
  491           clear();
  492           bout.setBlockDataMode(true);
  493       }
  494   
  495       /**
  496        * Subclasses may implement this method to allow class data to be stored in
  497        * the stream. By default this method does nothing.  The corresponding
  498        * method in ObjectInputStream is resolveClass.  This method is called
  499        * exactly once for each unique class in the stream.  The class name and
  500        * signature will have already been written to the stream.  This method may
  501        * make free use of the ObjectOutputStream to save any representation of
  502        * the class it deems suitable (for example, the bytes of the class file).
  503        * The resolveClass method in the corresponding subclass of
  504        * ObjectInputStream must read and use any data or objects written by
  505        * annotateClass.
  506        *
  507        * @param   cl the class to annotate custom data for
  508        * @throws  IOException Any exception thrown by the underlying
  509        *          OutputStream.
  510        */
  511       protected void annotateClass(Class<?> cl) throws IOException {
  512       }
  513   
  514       /**
  515        * Subclasses may implement this method to store custom data in the stream
  516        * along with descriptors for dynamic proxy classes.
  517        *
  518        * <p>This method is called exactly once for each unique proxy class
  519        * descriptor in the stream.  The default implementation of this method in
  520        * <code>ObjectOutputStream</code> does nothing.
  521        *
  522        * <p>The corresponding method in <code>ObjectInputStream</code> is
  523        * <code>resolveProxyClass</code>.  For a given subclass of
  524        * <code>ObjectOutputStream</code> that overrides this method, the
  525        * <code>resolveProxyClass</code> method in the corresponding subclass of
  526        * <code>ObjectInputStream</code> must read any data or objects written by
  527        * <code>annotateProxyClass</code>.
  528        *
  529        * @param   cl the proxy class to annotate custom data for
  530        * @throws  IOException any exception thrown by the underlying
  531        *          <code>OutputStream</code>
  532        * @see ObjectInputStream#resolveProxyClass(String[])
  533        * @since   1.3
  534        */
  535       protected void annotateProxyClass(Class<?> cl) throws IOException {
  536       }
  537   
  538       /**
  539        * This method will allow trusted subclasses of ObjectOutputStream to
  540        * substitute one object for another during serialization. Replacing
  541        * objects is disabled until enableReplaceObject is called. The
  542        * enableReplaceObject method checks that the stream requesting to do
  543        * replacement can be trusted.  The first occurrence of each object written
  544        * into the serialization stream is passed to replaceObject.  Subsequent
  545        * references to the object are replaced by the object returned by the
  546        * original call to replaceObject.  To ensure that the private state of
  547        * objects is not unintentionally exposed, only trusted streams may use
  548        * replaceObject.
  549        *
  550        * <p>The ObjectOutputStream.writeObject method takes a parameter of type
  551        * Object (as opposed to type Serializable) to allow for cases where
  552        * non-serializable objects are replaced by serializable ones.
  553        *
  554        * <p>When a subclass is replacing objects it must insure that either a
  555        * complementary substitution must be made during deserialization or that
  556        * the substituted object is compatible with every field where the
  557        * reference will be stored.  Objects whose type is not a subclass of the
  558        * type of the field or array element abort the serialization by raising an
  559        * exception and the object is not be stored.
  560        *
  561        * <p>This method is called only once when each object is first
  562        * encountered.  All subsequent references to the object will be redirected
  563        * to the new object. This method should return the object to be
  564        * substituted or the original object.
  565        *
  566        * <p>Null can be returned as the object to be substituted, but may cause
  567        * NullReferenceException in classes that contain references to the
  568        * original object since they may be expecting an object instead of
  569        * null.
  570        *
  571        * @param   obj the object to be replaced
  572        * @return  the alternate object that replaced the specified one
  573        * @throws  IOException Any exception thrown by the underlying
  574        *          OutputStream.
  575        */
  576       protected Object replaceObject(Object obj) throws IOException {
  577           return obj;
  578       }
  579   
  580       /**
  581        * Enable the stream to do replacement of objects in the stream.  When
  582        * enabled, the replaceObject method is called for every object being
  583        * serialized.
  584        *
  585        * <p>If <code>enable</code> is true, and there is a security manager
  586        * installed, this method first calls the security manager's
  587        * <code>checkPermission</code> method with a
  588        * <code>SerializablePermission("enableSubstitution")</code> permission to
  589        * ensure it's ok to enable the stream to do replacement of objects in the
  590        * stream.
  591        *
  592        * @param   enable boolean parameter to enable replacement of objects
  593        * @return  the previous setting before this method was invoked
  594        * @throws  SecurityException if a security manager exists and its
  595        *          <code>checkPermission</code> method denies enabling the stream
  596        *          to do replacement of objects in the stream.
  597        * @see SecurityManager#checkPermission
  598        * @see java.io.SerializablePermission
  599        */
  600       protected boolean enableReplaceObject(boolean enable)
  601           throws SecurityException
  602       {
  603           if (enable == enableReplace) {
  604               return enable;
  605           }
  606           if (enable) {
  607               SecurityManager sm = System.getSecurityManager();
  608               if (sm != null) {
  609                   sm.checkPermission(SUBSTITUTION_PERMISSION);
  610               }
  611           }
  612           enableReplace = enable;
  613           return !enableReplace;
  614       }
  615   
  616       /**
  617        * The writeStreamHeader method is provided so subclasses can append or
  618        * prepend their own header to the stream.  It writes the magic number and
  619        * version to the stream.
  620        *
  621        * @throws  IOException if I/O errors occur while writing to the underlying
  622        *          stream
  623        */
  624       protected void writeStreamHeader() throws IOException {
  625           bout.writeShort(STREAM_MAGIC);
  626           bout.writeShort(STREAM_VERSION);
  627       }
  628   
  629       /**
  630        * Write the specified class descriptor to the ObjectOutputStream.  Class
  631        * descriptors are used to identify the classes of objects written to the
  632        * stream.  Subclasses of ObjectOutputStream may override this method to
  633        * customize the way in which class descriptors are written to the
  634        * serialization stream.  The corresponding method in ObjectInputStream,
  635        * <code>readClassDescriptor</code>, should then be overridden to
  636        * reconstitute the class descriptor from its custom stream representation.
  637        * By default, this method writes class descriptors according to the format
  638        * defined in the Object Serialization specification.
  639        *
  640        * <p>Note that this method will only be called if the ObjectOutputStream
  641        * is not using the old serialization stream format (set by calling
  642        * ObjectOutputStream's <code>useProtocolVersion</code> method).  If this
  643        * serialization stream is using the old format
  644        * (<code>PROTOCOL_VERSION_1</code>), the class descriptor will be written
  645        * internally in a manner that cannot be overridden or customized.
  646        *
  647        * @param   desc class descriptor to write to the stream
  648        * @throws  IOException If an I/O error has occurred.
  649        * @see java.io.ObjectInputStream#readClassDescriptor()
  650        * @see #useProtocolVersion(int)
  651        * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
  652        * @since 1.3
  653        */
  654       protected void writeClassDescriptor(ObjectStreamClass desc)
  655           throws IOException
  656       {
  657           desc.writeNonProxy(this);
  658       }
  659   
  660       /**
  661        * Writes a byte. This method will block until the byte is actually
  662        * written.
  663        *
  664        * @param   val the byte to be written to the stream
  665        * @throws  IOException If an I/O error has occurred.
  666        */
  667       public void write(int val) throws IOException {
  668           bout.write(val);
  669       }
  670   
  671       /**
  672        * Writes an array of bytes. This method will block until the bytes are
  673        * actually written.
  674        *
  675        * @param   buf the data to be written
  676        * @throws  IOException If an I/O error has occurred.
  677        */
  678       public void write(byte[] buf) throws IOException {
  679           bout.write(buf, 0, buf.length, false);
  680       }
  681   
  682       /**
  683        * Writes a sub array of bytes.
  684        *
  685        * @param   buf the data to be written
  686        * @param   off the start offset in the data
  687        * @param   len the number of bytes that are written
  688        * @throws  IOException If an I/O error has occurred.
  689        */
  690       public void write(byte[] buf, int off, int len) throws IOException {
  691           if (buf == null) {
  692               throw new NullPointerException();
  693           }
  694           int endoff = off + len;
  695           if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
  696               throw new IndexOutOfBoundsException();
  697           }
  698           bout.write(buf, off, len, false);
  699       }
  700   
  701       /**
  702        * Flushes the stream. This will write any buffered output bytes and flush
  703        * through to the underlying stream.
  704        *
  705        * @throws  IOException If an I/O error has occurred.
  706        */
  707       public void flush() throws IOException {
  708           bout.flush();
  709       }
  710   
  711       /**
  712        * Drain any buffered data in ObjectOutputStream.  Similar to flush but
  713        * does not propagate the flush to the underlying stream.
  714        *
  715        * @throws  IOException if I/O errors occur while writing to the underlying
  716        *          stream
  717        */
  718       protected void drain() throws IOException {
  719           bout.drain();
  720       }
  721   
  722       /**
  723        * Closes the stream. This method must be called to release any resources
  724        * associated with the stream.
  725        *
  726        * @throws  IOException If an I/O error has occurred.
  727        */
  728       public void close() throws IOException {
  729           flush();
  730           clear();
  731           bout.close();
  732       }
  733   
  734       /**
  735        * Writes a boolean.
  736        *
  737        * @param   val the boolean to be written
  738        * @throws  IOException if I/O errors occur while writing to the underlying
  739        *          stream
  740        */
  741       public void writeBoolean(boolean val) throws IOException {
  742           bout.writeBoolean(val);
  743       }
  744   
  745       /**
  746        * Writes an 8 bit byte.
  747        *
  748        * @param   val the byte value to be written
  749        * @throws  IOException if I/O errors occur while writing to the underlying
  750        *          stream
  751        */
  752       public void writeByte(int val) throws IOException  {
  753           bout.writeByte(val);
  754       }
  755   
  756       /**
  757        * Writes a 16 bit short.
  758        *
  759        * @param   val the short value to be written
  760        * @throws  IOException if I/O errors occur while writing to the underlying
  761        *          stream
  762        */
  763       public void writeShort(int val)  throws IOException {
  764           bout.writeShort(val);
  765       }
  766   
  767       /**
  768        * Writes a 16 bit char.
  769        *
  770        * @param   val the char value to be written
  771        * @throws  IOException if I/O errors occur while writing to the underlying
  772        *          stream
  773        */
  774       public void writeChar(int val)  throws IOException {
  775           bout.writeChar(val);
  776       }
  777   
  778       /**
  779        * Writes a 32 bit int.
  780        *
  781        * @param   val the integer value to be written
  782        * @throws  IOException if I/O errors occur while writing to the underlying
  783        *          stream
  784        */
  785       public void writeInt(int val)  throws IOException {
  786           bout.writeInt(val);
  787       }
  788   
  789       /**
  790        * Writes a 64 bit long.
  791        *
  792        * @param   val the long value to be written
  793        * @throws  IOException if I/O errors occur while writing to the underlying
  794        *          stream
  795        */
  796       public void writeLong(long val)  throws IOException {
  797           bout.writeLong(val);
  798       }
  799   
  800       /**
  801        * Writes a 32 bit float.
  802        *
  803        * @param   val the float value to be written
  804        * @throws  IOException if I/O errors occur while writing to the underlying
  805        *          stream
  806        */
  807       public void writeFloat(float val) throws IOException {
  808           bout.writeFloat(val);
  809       }
  810   
  811       /**
  812        * Writes a 64 bit double.
  813        *
  814        * @param   val the double value to be written
  815        * @throws  IOException if I/O errors occur while writing to the underlying
  816        *          stream
  817        */
  818       public void writeDouble(double val) throws IOException {
  819           bout.writeDouble(val);
  820       }
  821   
  822       /**
  823        * Writes a String as a sequence of bytes.
  824        *
  825        * @param   str the String of bytes to be written
  826        * @throws  IOException if I/O errors occur while writing to the underlying
  827        *          stream
  828        */
  829       public void writeBytes(String str) throws IOException {
  830           bout.writeBytes(str);
  831       }
  832   
  833       /**
  834        * Writes a String as a sequence of chars.
  835        *
  836        * @param   str the String of chars to be written
  837        * @throws  IOException if I/O errors occur while writing to the underlying
  838        *          stream
  839        */
  840       public void writeChars(String str) throws IOException {
  841           bout.writeChars(str);
  842       }
  843   
  844       /**
  845        * Primitive data write of this String in
  846        * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
  847        * format.  Note that there is a
  848        * significant difference between writing a String into the stream as
  849        * primitive data or as an Object. A String instance written by writeObject
  850        * is written into the stream as a String initially. Future writeObject()
  851        * calls write references to the string into the stream.
  852        *
  853        * @param   str the String to be written
  854        * @throws  IOException if I/O errors occur while writing to the underlying
  855        *          stream
  856        */
  857       public void writeUTF(String str) throws IOException {
  858           bout.writeUTF(str);
  859       }
  860   
  861       /**
  862        * Provide programmatic access to the persistent fields to be written
  863        * to ObjectOutput.
  864        *
  865        * @since 1.2
  866        */
  867       public static abstract class PutField {
  868   
  869           /**
  870            * Put the value of the named boolean field into the persistent field.
  871            *
  872            * @param  name the name of the serializable field
  873            * @param  val the value to assign to the field
  874            * @throws IllegalArgumentException if <code>name</code> does not
  875            * match the name of a serializable field for the class whose fields
  876            * are being written, or if the type of the named field is not
  877            * <code>boolean</code>
  878            */
  879           public abstract void put(String name, boolean val);
  880   
  881           /**
  882            * Put the value of the named byte field into the persistent field.
  883            *
  884            * @param  name the name of the serializable field
  885            * @param  val the value to assign to the field
  886            * @throws IllegalArgumentException if <code>name</code> does not
  887            * match the name of a serializable field for the class whose fields
  888            * are being written, or if the type of the named field is not
  889            * <code>byte</code>
  890            */
  891           public abstract void put(String name, byte val);
  892   
  893           /**
  894            * Put the value of the named char field into the persistent field.
  895            *
  896            * @param  name the name of the serializable field
  897            * @param  val the value to assign to the field
  898            * @throws IllegalArgumentException if <code>name</code> does not
  899            * match the name of a serializable field for the class whose fields
  900            * are being written, or if the type of the named field is not
  901            * <code>char</code>
  902            */
  903           public abstract void put(String name, char val);
  904   
  905           /**
  906            * Put the value of the named short field into the persistent field.
  907            *
  908            * @param  name the name of the serializable field
  909            * @param  val the value to assign to the field
  910            * @throws IllegalArgumentException if <code>name</code> does not
  911            * match the name of a serializable field for the class whose fields
  912            * are being written, or if the type of the named field is not
  913            * <code>short</code>
  914            */
  915           public abstract void put(String name, short val);
  916   
  917           /**
  918            * Put the value of the named int field into the persistent field.
  919            *
  920            * @param  name the name of the serializable field
  921            * @param  val the value to assign to the field
  922            * @throws IllegalArgumentException if <code>name</code> does not
  923            * match the name of a serializable field for the class whose fields
  924            * are being written, or if the type of the named field is not
  925            * <code>int</code>
  926            */
  927           public abstract void put(String name, int val);
  928   
  929           /**
  930            * Put the value of the named long field into the persistent field.
  931            *
  932            * @param  name the name of the serializable field
  933            * @param  val the value to assign to the field
  934            * @throws IllegalArgumentException if <code>name</code> does not
  935            * match the name of a serializable field for the class whose fields
  936            * are being written, or if the type of the named field is not
  937            * <code>long</code>
  938            */
  939           public abstract void put(String name, long val);
  940   
  941           /**
  942            * Put the value of the named float field into the persistent field.
  943            *
  944            * @param  name the name of the serializable field
  945            * @param  val the value to assign to the field
  946            * @throws IllegalArgumentException if <code>name</code> does not
  947            * match the name of a serializable field for the class whose fields
  948            * are being written, or if the type of the named field is not
  949            * <code>float</code>
  950            */
  951           public abstract void put(String name, float val);
  952   
  953           /**
  954            * Put the value of the named double field into the persistent field.
  955            *
  956            * @param  name the name of the serializable field
  957            * @param  val the value to assign to the field
  958            * @throws IllegalArgumentException if <code>name</code> does not
  959            * match the name of a serializable field for the class whose fields
  960            * are being written, or if the type of the named field is not
  961            * <code>double</code>
  962            */
  963           public abstract void put(String name, double val);
  964   
  965           /**
  966            * Put the value of the named Object field into the persistent field.
  967            *
  968            * @param  name the name of the serializable field
  969            * @param  val the value to assign to the field
  970            *         (which may be <code>null</code>)
  971            * @throws IllegalArgumentException if <code>name</code> does not
  972            * match the name of a serializable field for the class whose fields
  973            * are being written, or if the type of the named field is not a
  974            * reference type
  975            */
  976           public abstract void put(String name, Object val);
  977   
  978           /**
  979            * Write the data and fields to the specified ObjectOutput stream,
  980            * which must be the same stream that produced this
  981            * <code>PutField</code> object.
  982            *
  983            * @param  out the stream to write the data and fields to
  984            * @throws IOException if I/O errors occur while writing to the
  985            *         underlying stream
  986            * @throws IllegalArgumentException if the specified stream is not
  987            *         the same stream that produced this <code>PutField</code>
  988            *         object
  989            * @deprecated This method does not write the values contained by this
  990            *         <code>PutField</code> object in a proper format, and may
  991            *         result in corruption of the serialization stream.  The
  992            *         correct way to write <code>PutField</code> data is by
  993            *         calling the {@link java.io.ObjectOutputStream#writeFields()}
  994            *         method.
  995            */
  996           @Deprecated
  997           public abstract void write(ObjectOutput out) throws IOException;
  998       }
  999   
 1000   
 1001       /**
 1002        * Returns protocol version in use.
 1003        */
 1004       int getProtocolVersion() {
 1005           return protocol;
 1006       }
 1007   
 1008       /**
 1009        * Writes string without allowing it to be replaced in stream.  Used by
 1010        * ObjectStreamClass to write class descriptor type strings.
 1011        */
 1012       void writeTypeString(String str) throws IOException {
 1013           int handle;
 1014           if (str == null) {
 1015               writeNull();
 1016           } else if ((handle = handles.lookup(str)) != -1) {
 1017               writeHandle(handle);
 1018           } else {
 1019               writeString(str, false);
 1020           }
 1021       }
 1022   
 1023       /**
 1024        * Verifies that this (possibly subclass) instance can be constructed
 1025        * without violating security constraints: the subclass must not override
 1026        * security-sensitive non-final methods, or else the
 1027        * "enableSubclassImplementation" SerializablePermission is checked.
 1028        */
 1029       private void verifySubclass() {
 1030           Class cl = getClass();
 1031           if (cl == ObjectOutputStream.class) {
 1032               return;
 1033           }
 1034           SecurityManager sm = System.getSecurityManager();
 1035           if (sm == null) {
 1036               return;
 1037           }
 1038           processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
 1039           WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
 1040           Boolean result = Caches.subclassAudits.get(key);
 1041           if (result == null) {
 1042               result = Boolean.valueOf(auditSubclass(cl));
 1043               Caches.subclassAudits.putIfAbsent(key, result);
 1044           }
 1045           if (result.booleanValue()) {
 1046               return;
 1047           }
 1048           sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
 1049       }
 1050   
 1051       /**
 1052        * Performs reflective checks on given subclass to verify that it doesn't
 1053        * override security-sensitive non-final methods.  Returns true if subclass
 1054        * is "safe", false otherwise.
 1055        */
 1056       private static boolean auditSubclass(final Class subcl) {
 1057           Boolean result = AccessController.doPrivileged(
 1058               new PrivilegedAction<Boolean>() {
 1059                   public Boolean run() {
 1060                       for (Class cl = subcl;
 1061                            cl != ObjectOutputStream.class;
 1062                            cl = cl.getSuperclass())
 1063                       {
 1064                           try {
 1065                               cl.getDeclaredMethod(
 1066                                   "writeUnshared", new Class[] { Object.class });
 1067                               return Boolean.FALSE;
 1068                           } catch (NoSuchMethodException ex) {
 1069                           }
 1070                           try {
 1071                               cl.getDeclaredMethod("putFields", (Class[]) null);
 1072                               return Boolean.FALSE;
 1073                           } catch (NoSuchMethodException ex) {
 1074                           }
 1075                       }
 1076                       return Boolean.TRUE;
 1077                   }
 1078               }
 1079           );
 1080           return result.booleanValue();
 1081       }
 1082   
 1083       /**
 1084        * Clears internal data structures.
 1085        */
 1086       private void clear() {
 1087           subs.clear();
 1088           handles.clear();
 1089       }
 1090   
 1091       /**
 1092        * Underlying writeObject/writeUnshared implementation.
 1093        */
 1094       private void writeObject0(Object obj, boolean unshared)
 1095           throws IOException
 1096       {
 1097           boolean oldMode = bout.setBlockDataMode(false);
 1098           depth++;
 1099           try {
 1100               // handle previously written and non-replaceable objects
 1101               int h;
 1102               if ((obj = subs.lookup(obj)) == null) {
 1103                   writeNull();
 1104                   return;
 1105               } else if (!unshared && (h = handles.lookup(obj)) != -1) {
 1106                   writeHandle(h);
 1107                   return;
 1108               } else if (obj instanceof Class) {
 1109                   writeClass((Class) obj, unshared);
 1110                   return;
 1111               } else if (obj instanceof ObjectStreamClass) {
 1112                   writeClassDesc((ObjectStreamClass) obj, unshared);
 1113                   return;
 1114               }
 1115   
 1116               // check for replacement object
 1117               Object orig = obj;
 1118               Class cl = obj.getClass();
 1119               ObjectStreamClass desc;
 1120               for (;;) {
 1121                   // REMIND: skip this check for strings/arrays?
 1122                   Class repCl;
 1123                   desc = ObjectStreamClass.lookup(cl, true);
 1124                   if (!desc.hasWriteReplaceMethod() ||
 1125                       (obj = desc.invokeWriteReplace(obj)) == null ||
 1126                       (repCl = obj.getClass()) == cl)
 1127                   {
 1128                       break;
 1129                   }
 1130                   cl = repCl;
 1131               }
 1132               if (enableReplace) {
 1133                   Object rep = replaceObject(obj);
 1134                   if (rep != obj && rep != null) {
 1135                       cl = rep.getClass();
 1136                       desc = ObjectStreamClass.lookup(cl, true);
 1137                   }
 1138                   obj = rep;
 1139               }
 1140   
 1141               // if object replaced, run through original checks a second time
 1142               if (obj != orig) {
 1143                   subs.assign(orig, obj);
 1144                   if (obj == null) {
 1145                       writeNull();
 1146                       return;
 1147                   } else if (!unshared && (h = handles.lookup(obj)) != -1) {
 1148                       writeHandle(h);
 1149                       return;
 1150                   } else if (obj instanceof Class) {
 1151                       writeClass((Class) obj, unshared);
 1152                       return;
 1153                   } else if (obj instanceof ObjectStreamClass) {
 1154                       writeClassDesc((ObjectStreamClass) obj, unshared);
 1155                       return;
 1156                   }
 1157               }
 1158   
 1159               // remaining cases
 1160               if (obj instanceof String) {
 1161                   writeString((String) obj, unshared);
 1162               } else if (cl.isArray()) {
 1163                   writeArray(obj, desc, unshared);
 1164               } else if (obj instanceof Enum) {
 1165                   writeEnum((Enum) obj, desc, unshared);
 1166               } else if (obj instanceof Serializable) {
 1167                   writeOrdinaryObject(obj, desc, unshared);
 1168               } else {
 1169                   if (extendedDebugInfo) {
 1170                       throw new NotSerializableException(
 1171                           cl.getName() + "\n" + debugInfoStack.toString());
 1172                   } else {
 1173                       throw new NotSerializableException(cl.getName());
 1174                   }
 1175               }
 1176           } finally {
 1177               depth--;
 1178               bout.setBlockDataMode(oldMode);
 1179           }
 1180       }
 1181   
 1182       /**
 1183        * Writes null code to stream.
 1184        */
 1185       private void writeNull() throws IOException {
 1186           bout.writeByte(TC_NULL);
 1187       }
 1188   
 1189       /**
 1190        * Writes given object handle to stream.
 1191        */
 1192       private void writeHandle(int handle) throws IOException {
 1193           bout.writeByte(TC_REFERENCE);
 1194           bout.writeInt(baseWireHandle + handle);
 1195       }
 1196   
 1197       /**
 1198        * Writes representation of given class to stream.
 1199        */
 1200       private void writeClass(Class cl, boolean unshared) throws IOException {
 1201           bout.writeByte(TC_CLASS);
 1202           writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
 1203           handles.assign(unshared ? null : cl);
 1204       }
 1205   
 1206       /**
 1207        * Writes representation of given class descriptor to stream.
 1208        */
 1209       private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
 1210           throws IOException
 1211       {
 1212           int handle;
 1213           if (desc == null) {
 1214               writeNull();
 1215           } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
 1216               writeHandle(handle);
 1217           } else if (desc.isProxy()) {
 1218               writeProxyDesc(desc, unshared);
 1219           } else {
 1220               writeNonProxyDesc(desc, unshared);
 1221           }
 1222       }
 1223   
 1224       /**
 1225        * Writes class descriptor representing a dynamic proxy class to stream.
 1226        */
 1227       private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
 1228           throws IOException
 1229       {
 1230           bout.writeByte(TC_PROXYCLASSDESC);
 1231           handles.assign(unshared ? null : desc);
 1232   
 1233           Class cl = desc.forClass();
 1234           Class[] ifaces = cl.getInterfaces();
 1235           bout.writeInt(ifaces.length);
 1236           for (int i = 0; i < ifaces.length; i++) {
 1237               bout.writeUTF(ifaces[i].getName());
 1238           }
 1239   
 1240           bout.setBlockDataMode(true);
 1241           annotateProxyClass(cl);
 1242           bout.setBlockDataMode(false);
 1243           bout.writeByte(TC_ENDBLOCKDATA);
 1244   
 1245           writeClassDesc(desc.getSuperDesc(), false);
 1246       }
 1247   
 1248       /**
 1249        * Writes class descriptor representing a standard (i.e., not a dynamic
 1250        * proxy) class to stream.
 1251        */
 1252       private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
 1253           throws IOException
 1254       {
 1255           bout.writeByte(TC_CLASSDESC);
 1256           handles.assign(unshared ? null : desc);
 1257   
 1258           if (protocol == PROTOCOL_VERSION_1) {
 1259               // do not invoke class descriptor write hook with old protocol
 1260               desc.writeNonProxy(this);
 1261           } else {
 1262               writeClassDescriptor(desc);
 1263           }
 1264   
 1265           Class cl = desc.forClass();
 1266           bout.setBlockDataMode(true);
 1267           annotateClass(cl);
 1268           bout.setBlockDataMode(false);
 1269           bout.writeByte(TC_ENDBLOCKDATA);
 1270   
 1271           writeClassDesc(desc.getSuperDesc(), false);
 1272       }
 1273   
 1274       /**
 1275        * Writes given string to stream, using standard or long UTF format
 1276        * depending on string length.
 1277        */
 1278       private void writeString(String str, boolean unshared) throws IOException {
 1279           handles.assign(unshared ? null : str);
 1280           long utflen = bout.getUTFLength(str);
 1281           if (utflen <= 0xFFFF) {
 1282               bout.writeByte(TC_STRING);
 1283               bout.writeUTF(str, utflen);
 1284           } else {
 1285               bout.writeByte(TC_LONGSTRING);
 1286               bout.writeLongUTF(str, utflen);
 1287           }
 1288       }
 1289   
 1290       /**
 1291        * Writes given array object to stream.
 1292        */
 1293       private void writeArray(Object array,
 1294                               ObjectStreamClass desc,
 1295                               boolean unshared)
 1296           throws IOException
 1297       {
 1298           bout.writeByte(TC_ARRAY);
 1299           writeClassDesc(desc, false);
 1300           handles.assign(unshared ? null : array);
 1301   
 1302           Class ccl = desc.forClass().getComponentType();
 1303           if (ccl.isPrimitive()) {
 1304               if (ccl == Integer.TYPE) {
 1305                   int[] ia = (int[]) array;
 1306                   bout.writeInt(ia.length);
 1307                   bout.writeInts(ia, 0, ia.length);
 1308               } else if (ccl == Byte.TYPE) {
 1309                   byte[] ba = (byte[]) array;
 1310                   bout.writeInt(ba.length);
 1311                   bout.write(ba, 0, ba.length, true);
 1312               } else if (ccl == Long.TYPE) {
 1313                   long[] ja = (long[]) array;
 1314                   bout.writeInt(ja.length);
 1315                   bout.writeLongs(ja, 0, ja.length);
 1316               } else if (ccl == Float.TYPE) {
 1317                   float[] fa = (float[]) array;
 1318                   bout.writeInt(fa.length);
 1319                   bout.writeFloats(fa, 0, fa.length);
 1320               } else if (ccl == Double.TYPE) {
 1321                   double[] da = (double[]) array;
 1322                   bout.writeInt(da.length);
 1323                   bout.writeDoubles(da, 0, da.length);
 1324               } else if (ccl == Short.TYPE) {
 1325                   short[] sa = (short[]) array;
 1326                   bout.writeInt(sa.length);
 1327                   bout.writeShorts(sa, 0, sa.length);
 1328               } else if (ccl == Character.TYPE) {
 1329                   char[] ca = (char[]) array;
 1330                   bout.writeInt(ca.length);
 1331                   bout.writeChars(ca, 0, ca.length);
 1332               } else if (ccl == Boolean.TYPE) {
 1333                   boolean[] za = (boolean[]) array;
 1334                   bout.writeInt(za.length);
 1335                   bout.writeBooleans(za, 0, za.length);
 1336               } else {
 1337                   throw new InternalError();
 1338               }
 1339           } else {
 1340               Object[] objs = (Object[]) array;
 1341               int len = objs.length;
 1342               bout.writeInt(len);
 1343               if (extendedDebugInfo) {
 1344                   debugInfoStack.push(
 1345                       "array (class \"" + array.getClass().getName() +
 1346                       "\", size: " + len  + ")");
 1347               }
 1348               try {
 1349                   for (int i = 0; i < len; i++) {
 1350                       if (extendedDebugInfo) {
 1351                           debugInfoStack.push(
 1352                               "element of array (index: " + i + ")");
 1353                       }
 1354                       try {
 1355                           writeObject0(objs[i], false);
 1356                       } finally {
 1357                           if (extendedDebugInfo) {
 1358                               debugInfoStack.pop();
 1359                           }
 1360                       }
 1361                   }
 1362               } finally {
 1363                   if (extendedDebugInfo) {
 1364                       debugInfoStack.pop();
 1365                   }
 1366               }
 1367           }
 1368       }
 1369   
 1370       /**
 1371        * Writes given enum constant to stream.
 1372        */
 1373       private void writeEnum(Enum en,
 1374                              ObjectStreamClass desc,
 1375                              boolean unshared)
 1376           throws IOException
 1377       {
 1378           bout.writeByte(TC_ENUM);
 1379           ObjectStreamClass sdesc = desc.getSuperDesc();
 1380           writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
 1381           handles.assign(unshared ? null : en);
 1382           writeString(en.name(), false);
 1383       }
 1384   
 1385       /**
 1386        * Writes representation of a "ordinary" (i.e., not a String, Class,
 1387        * ObjectStreamClass, array, or enum constant) serializable object to the
 1388        * stream.
 1389        */
 1390       private void writeOrdinaryObject(Object obj,
 1391                                        ObjectStreamClass desc,
 1392                                        boolean unshared)
 1393           throws IOException
 1394       {
 1395           if (extendedDebugInfo) {
 1396               debugInfoStack.push(
 1397                   (depth == 1 ? "root " : "") + "object (class \"" +
 1398                   obj.getClass().getName() + "\", " + obj.toString() + ")");
 1399           }
 1400           try {
 1401               desc.checkSerialize();
 1402   
 1403               bout.writeByte(TC_OBJECT);
 1404               writeClassDesc(desc, false);
 1405               handles.assign(unshared ? null : obj);
 1406               if (desc.isExternalizable() && !desc.isProxy()) {
 1407                   writeExternalData((Externalizable) obj);
 1408               } else {
 1409                   writeSerialData(obj, desc);
 1410               }
 1411           } finally {
 1412               if (extendedDebugInfo) {
 1413                   debugInfoStack.pop();
 1414               }
 1415           }
 1416       }
 1417   
 1418       /**
 1419        * Writes externalizable data of given object by invoking its
 1420        * writeExternal() method.
 1421        */
 1422       private void writeExternalData(Externalizable obj) throws IOException {
 1423           Object oldObj = curObj;
 1424           ObjectStreamClass oldDesc = curDesc;
 1425           PutFieldImpl oldPut = curPut;
 1426           curObj = obj;
 1427           curDesc = null;
 1428           curPut = null;
 1429   
 1430           if (extendedDebugInfo) {
 1431               debugInfoStack.push("writeExternal data");
 1432           }
 1433           try {
 1434               if (protocol == PROTOCOL_VERSION_1) {
 1435                   obj.writeExternal(this);
 1436               } else {
 1437                   bout.setBlockDataMode(true);
 1438                   obj.writeExternal(this);
 1439                   bout.setBlockDataMode(false);
 1440                   bout.writeByte(TC_ENDBLOCKDATA);
 1441               }
 1442           } finally {
 1443               if (extendedDebugInfo) {
 1444                   debugInfoStack.pop();
 1445               }
 1446           }
 1447   
 1448           curObj = oldObj;
 1449           curDesc = oldDesc;
 1450           curPut = oldPut;
 1451       }
 1452   
 1453       /**
 1454        * Writes instance data for each serializable class of given object, from
 1455        * superclass to subclass.
 1456        */
 1457       private void writeSerialData(Object obj, ObjectStreamClass desc)
 1458           throws IOException
 1459       {
 1460           ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
 1461           for (int i = 0; i < slots.length; i++) {
 1462               ObjectStreamClass slotDesc = slots[i].desc;
 1463               if (slotDesc.hasWriteObjectMethod()) {
 1464                   Object oldObj = curObj;
 1465                   ObjectStreamClass oldDesc = curDesc;
 1466                   PutFieldImpl oldPut = curPut;
 1467                   curObj = obj;
 1468                   curDesc = slotDesc;
 1469                   curPut = null;
 1470   
 1471                   if (extendedDebugInfo) {
 1472                       debugInfoStack.push(
 1473                           "custom writeObject data (class \"" +
 1474                           slotDesc.getName() + "\")");
 1475                   }
 1476                   try {
 1477                       bout.setBlockDataMode(true);
 1478                       slotDesc.invokeWriteObject(obj, this);
 1479                       bout.setBlockDataMode(false);
 1480                       bout.writeByte(TC_ENDBLOCKDATA);
 1481                   } finally {
 1482                       if (extendedDebugInfo) {
 1483                           debugInfoStack.pop();
 1484                       }
 1485                   }
 1486   
 1487                   curObj = oldObj;
 1488                   curDesc = oldDesc;
 1489                   curPut = oldPut;
 1490               } else {
 1491                   defaultWriteFields(obj, slotDesc);
 1492               }
 1493           }
 1494       }
 1495   
 1496       /**
 1497        * Fetches and writes values of serializable fields of given object to
 1498        * stream.  The given class descriptor specifies which field values to
 1499        * write, and in which order they should be written.
 1500        */
 1501       private void defaultWriteFields(Object obj, ObjectStreamClass desc)
 1502           throws IOException
 1503       {
 1504           // REMIND: perform conservative isInstance check here?
 1505           desc.checkDefaultSerialize();
 1506   
 1507           int primDataSize = desc.getPrimDataSize();
 1508           if (primVals == null || primVals.length < primDataSize) {
 1509               primVals = new byte[primDataSize];
 1510           }
 1511           desc.getPrimFieldValues(obj, primVals);
 1512           bout.write(primVals, 0, primDataSize, false);
 1513   
 1514           ObjectStreamField[] fields = desc.getFields(false);
 1515           Object[] objVals = new Object[desc.getNumObjFields()];
 1516           int numPrimFields = fields.length - objVals.length;
 1517           desc.getObjFieldValues(obj, objVals);
 1518           for (int i = 0; i < objVals.length; i++) {
 1519               if (extendedDebugInfo) {
 1520                   debugInfoStack.push(
 1521                       "field (class \"" + desc.getName() + "\", name: \"" +
 1522                       fields[numPrimFields + i].getName() + "\", type: \"" +
 1523                       fields[numPrimFields + i].getType() + "\")");
 1524               }
 1525               try {
 1526                   writeObject0(objVals[i],
 1527                                fields[numPrimFields + i].isUnshared());
 1528               } finally {
 1529                   if (extendedDebugInfo) {
 1530                       debugInfoStack.pop();
 1531                   }
 1532               }
 1533           }
 1534       }
 1535   
 1536       /**
 1537        * Attempts to write to stream fatal IOException that has caused
 1538        * serialization to abort.
 1539        */
 1540       private void writeFatalException(IOException ex) throws IOException {
 1541           /*
 1542            * Note: the serialization specification states that if a second
 1543            * IOException occurs while attempting to serialize the original fatal
 1544            * exception to the stream, then a StreamCorruptedException should be
 1545            * thrown (section 2.1).  However, due to a bug in previous
 1546            * implementations of serialization, StreamCorruptedExceptions were
 1547            * rarely (if ever) actually thrown--the "root" exceptions from
 1548            * underlying streams were thrown instead.  This historical behavior is
 1549            * followed here for consistency.
 1550            */
 1551           clear();
 1552           boolean oldMode = bout.setBlockDataMode(false);
 1553           try {
 1554               bout.writeByte(TC_EXCEPTION);
 1555               writeObject0(ex, false);
 1556               clear();
 1557           } finally {
 1558               bout.setBlockDataMode(oldMode);
 1559           }
 1560       }
 1561   
 1562       /**
 1563        * Converts specified span of float values into byte values.
 1564        */
 1565       // REMIND: remove once hotspot inlines Float.floatToIntBits
 1566       private static native void floatsToBytes(float[] src, int srcpos,
 1567                                                byte[] dst, int dstpos,
 1568                                                int nfloats);
 1569   
 1570       /**
 1571        * Converts specified span of double values into byte values.
 1572        */
 1573       // REMIND: remove once hotspot inlines Double.doubleToLongBits
 1574       private static native void doublesToBytes(double[] src, int srcpos,
 1575                                                 byte[] dst, int dstpos,
 1576                                                 int ndoubles);
 1577   
 1578       /**
 1579        * Default PutField implementation.
 1580        */
 1581       private class PutFieldImpl extends PutField {
 1582   
 1583           /** class descriptor describing serializable fields */
 1584           private final ObjectStreamClass desc;
 1585           /** primitive field values */
 1586           private final byte[] primVals;
 1587           /** object field values */
 1588           private final Object[] objVals;
 1589   
 1590           /**
 1591            * Creates PutFieldImpl object for writing fields defined in given
 1592            * class descriptor.
 1593            */
 1594           PutFieldImpl(ObjectStreamClass desc) {
 1595               this.desc = desc;
 1596               primVals = new byte[desc.getPrimDataSize()];
 1597               objVals = new Object[desc.getNumObjFields()];
 1598           }
 1599   
 1600           public void put(String name, boolean val) {
 1601               Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val);
 1602           }
 1603   
 1604           public void put(String name, byte val) {
 1605               primVals[getFieldOffset(name, Byte.TYPE)] = val;
 1606           }
 1607   
 1608           public void put(String name, char val) {
 1609               Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val);
 1610           }
 1611   
 1612           public void put(String name, short val) {
 1613               Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val);
 1614           }
 1615   
 1616           public void put(String name, int val) {
 1617               Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val);
 1618           }
 1619   
 1620           public void put(String name, float val) {
 1621               Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val);
 1622           }
 1623   
 1624           public void put(String name, long val) {
 1625               Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
 1626           }
 1627   
 1628           public void put(String name, double val) {
 1629               Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val);
 1630           }
 1631   
 1632           public void put(String name, Object val) {
 1633               objVals[getFieldOffset(name, Object.class)] = val;
 1634           }
 1635   
 1636           // deprecated in ObjectOutputStream.PutField
 1637           public void write(ObjectOutput out) throws IOException {
 1638               /*
 1639                * Applications should *not* use this method to write PutField
 1640                * data, as it will lead to stream corruption if the PutField
 1641                * object writes any primitive data (since block data mode is not
 1642                * unset/set properly, as is done in OOS.writeFields()).  This
 1643                * broken implementation is being retained solely for behavioral
 1644                * compatibility, in order to support applications which use
 1645                * OOS.PutField.write() for writing only non-primitive data.
 1646                *
 1647                * Serialization of unshared objects is not implemented here since
 1648                * it is not necessary for backwards compatibility; also, unshared
 1649                * semantics may not be supported by the given ObjectOutput
 1650                * instance.  Applications which write unshared objects using the
 1651                * PutField API must use OOS.writeFields().
 1652                */
 1653               if (ObjectOutputStream.this != out) {
 1654                   throw new IllegalArgumentException("wrong stream");
 1655               }
 1656               out.write(primVals, 0, primVals.length);
 1657   
 1658               ObjectStreamField[] fields = desc.getFields(false);
 1659               int numPrimFields = fields.length - objVals.length;
 1660               // REMIND: warn if numPrimFields > 0?
 1661               for (int i = 0; i < objVals.length; i++) {
 1662                   if (fields[numPrimFields + i].isUnshared()) {
 1663                       throw new IOException("cannot write unshared object");
 1664                   }
 1665                   out.writeObject(objVals[i]);
 1666               }
 1667           }
 1668   
 1669           /**
 1670            * Writes buffered primitive data and object fields to stream.
 1671            */
 1672           void writeFields() throws IOException {
 1673               bout.write(primVals, 0, primVals.length, false);
 1674   
 1675               ObjectStreamField[] fields = desc.getFields(false);
 1676               int numPrimFields = fields.length - objVals.length;
 1677               for (int i = 0; i < objVals.length; i++) {
 1678                   if (extendedDebugInfo) {
 1679                       debugInfoStack.push(
 1680                           "field (class \"" + desc.getName() + "\", name: \"" +
 1681                           fields[numPrimFields + i].getName() + "\", type: \"" +
 1682                           fields[numPrimFields + i].getType() + "\")");
 1683                   }
 1684                   try {
 1685                       writeObject0(objVals[i],
 1686                                    fields[numPrimFields + i].isUnshared());
 1687                   } finally {
 1688                       if (extendedDebugInfo) {
 1689                           debugInfoStack.pop();
 1690                       }
 1691                   }
 1692               }
 1693           }
 1694   
 1695           /**
 1696            * Returns offset of field with given name and type.  A specified type
 1697            * of null matches all types, Object.class matches all non-primitive
 1698            * types, and any other non-null type matches assignable types only.
 1699            * Throws IllegalArgumentException if no matching field found.
 1700            */
 1701           private int getFieldOffset(String name, Class type) {
 1702               ObjectStreamField field = desc.getField(name, type);
 1703               if (field == null) {
 1704                   throw new IllegalArgumentException("no such field " + name +
 1705                                                      " with type " + type);
 1706               }
 1707               return field.getOffset();
 1708           }
 1709       }
 1710   
 1711       /**
 1712        * Buffered output stream with two modes: in default mode, outputs data in
 1713        * same format as DataOutputStream; in "block data" mode, outputs data
 1714        * bracketed by block data markers (see object serialization specification
 1715        * for details).
 1716        */
 1717       private static class BlockDataOutputStream
 1718           extends OutputStream implements DataOutput
 1719       {
 1720           /** maximum data block length */
 1721           private static final int MAX_BLOCK_SIZE = 1024;
 1722           /** maximum data block header length */
 1723           private static final int MAX_HEADER_SIZE = 5;
 1724           /** (tunable) length of char buffer (for writing strings) */
 1725           private static final int CHAR_BUF_SIZE = 256;
 1726   
 1727           /** buffer for writing general/block data */
 1728           private final byte[] buf = new byte[MAX_BLOCK_SIZE];
 1729           /** buffer for writing block data headers */
 1730           private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
 1731           /** char buffer for fast string writes */
 1732           private final char[] cbuf = new char[CHAR_BUF_SIZE];
 1733   
 1734           /** block data mode */
 1735           private boolean blkmode = false;
 1736           /** current offset into buf */
 1737           private int pos = 0;
 1738   
 1739           /** underlying output stream */
 1740           private final OutputStream out;
 1741           /** loopback stream (for data writes that span data blocks) */
 1742           private final DataOutputStream dout;
 1743   
 1744           /**
 1745            * Creates new BlockDataOutputStream on top of given underlying stream.
 1746            * Block data mode is turned off by default.
 1747            */
 1748           BlockDataOutputStream(OutputStream out) {
 1749               this.out = out;
 1750               dout = new DataOutputStream(this);
 1751           }
 1752   
 1753           /**
 1754            * Sets block data mode to the given mode (true == on, false == off)
 1755            * and returns the previous mode value.  If the new mode is the same as
 1756            * the old mode, no action is taken.  If the new mode differs from the
 1757            * old mode, any buffered data is flushed before switching to the new
 1758            * mode.
 1759            */
 1760           boolean setBlockDataMode(boolean mode) throws IOException {
 1761               if (blkmode == mode) {
 1762                   return blkmode;
 1763               }
 1764               drain();
 1765               blkmode = mode;
 1766               return !blkmode;
 1767           }
 1768   
 1769           /**
 1770            * Returns true if the stream is currently in block data mode, false
 1771            * otherwise.
 1772            */
 1773           boolean getBlockDataMode() {
 1774               return blkmode;
 1775           }
 1776   
 1777           /* ----------------- generic output stream methods ----------------- */
 1778           /*
 1779            * The following methods are equivalent to their counterparts in
 1780            * OutputStream, except that they partition written data into data
 1781            * blocks when in block data mode.
 1782            */
 1783   
 1784           public void write(int b) throws IOException {
 1785               if (pos >= MAX_BLOCK_SIZE) {
 1786                   drain();
 1787               }
 1788               buf[pos++] = (byte) b;
 1789           }
 1790   
 1791           public void write(byte[] b) throws IOException {
 1792               write(b, 0, b.length, false);
 1793           }
 1794   
 1795           public void write(byte[] b, int off, int len) throws IOException {
 1796               write(b, off, len, false);
 1797           }
 1798   
 1799           public void flush() throws IOException {
 1800               drain();
 1801               out.flush();
 1802           }
 1803   
 1804           public void close() throws IOException {
 1805               flush();
 1806               out.close();
 1807           }
 1808   
 1809           /**
 1810            * Writes specified span of byte values from given array.  If copy is
 1811            * true, copies the values to an intermediate buffer before writing
 1812            * them to underlying stream (to avoid exposing a reference to the
 1813            * original byte array).
 1814            */
 1815           void write(byte[] b, int off, int len, boolean copy)
 1816               throws IOException
 1817           {
 1818               if (!(copy || blkmode)) {           // write directly
 1819                   drain();
 1820                   out.write(b, off, len);
 1821                   return;
 1822               }
 1823   
 1824               while (len > 0) {
 1825                   if (pos >= MAX_BLOCK_SIZE) {
 1826                       drain();
 1827                   }
 1828                   if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) {
 1829                       // avoid unnecessary copy
 1830                       writeBlockHeader(MAX_BLOCK_SIZE);
 1831                       out.write(b, off, MAX_BLOCK_SIZE);
 1832                       off += MAX_BLOCK_SIZE;
 1833                       len -= MAX_BLOCK_SIZE;
 1834                   } else {
 1835                       int wlen = Math.min(len, MAX_BLOCK_SIZE - pos);
 1836                       System.arraycopy(b, off, buf, pos, wlen);
 1837                       pos += wlen;
 1838                       off += wlen;
 1839                       len -= wlen;
 1840                   }
 1841               }
 1842           }
 1843   
 1844           /**
 1845            * Writes all buffered data from this stream to the underlying stream,
 1846            * but does not flush underlying stream.
 1847            */
 1848           void drain() throws IOException {
 1849               if (pos == 0) {
 1850                   return;
 1851               }
 1852               if (blkmode) {
 1853                   writeBlockHeader(pos);
 1854               }
 1855               out.write(buf, 0, pos);
 1856               pos = 0;
 1857           }
 1858   
 1859           /**
 1860            * Writes block data header.  Data blocks shorter than 256 bytes are
 1861            * prefixed with a 2-byte header; all others start with a 5-byte
 1862            * header.
 1863            */
 1864           private void writeBlockHeader(int len) throws IOException {
 1865               if (len <= 0xFF) {
 1866                   hbuf[0] = TC_BLOCKDATA;
 1867                   hbuf[1] = (byte) len;
 1868                   out.write(hbuf, 0, 2);
 1869               } else {
 1870                   hbuf[0] = TC_BLOCKDATALONG;
 1871                   Bits.putInt(hbuf, 1, len);
 1872                   out.write(hbuf, 0, 5);
 1873               }
 1874           }
 1875   
 1876   
 1877           /* ----------------- primitive data output methods ----------------- */
 1878           /*
 1879            * The following methods are equivalent to their counterparts in
 1880            * DataOutputStream, except that they partition written data into data
 1881            * blocks when in block data mode.
 1882            */
 1883   
 1884           public void writeBoolean(boolean v) throws IOException {
 1885               if (pos >= MAX_BLOCK_SIZE) {
 1886                   drain();
 1887               }
 1888               Bits.putBoolean(buf, pos++, v);
 1889           }
 1890   
 1891           public void writeByte(int v) throws IOException {
 1892               if (pos >= MAX_BLOCK_SIZE) {
 1893                   drain();
 1894               }
 1895               buf[pos++] = (byte) v;
 1896           }
 1897   
 1898           public void writeChar(int v) throws IOException {
 1899               if (pos + 2 <= MAX_BLOCK_SIZE) {
 1900                   Bits.putChar(buf, pos, (char) v);
 1901                   pos += 2;
 1902               } else {
 1903                   dout.writeChar(v);
 1904               }
 1905           }
 1906   
 1907           public void writeShort(int v) throws IOException {
 1908               if (pos + 2 <= MAX_BLOCK_SIZE) {
 1909                   Bits.putShort(buf, pos, (short) v);
 1910                   pos += 2;
 1911               } else {
 1912                   dout.writeShort(v);
 1913               }
 1914           }
 1915   
 1916           public void writeInt(int v) throws IOException {
 1917               if (pos + 4 <= MAX_BLOCK_SIZE) {
 1918                   Bits.putInt(buf, pos, v);
 1919                   pos += 4;
 1920               } else {
 1921                   dout.writeInt(v);
 1922               }
 1923           }
 1924   
 1925           public void writeFloat(float v) throws IOException {
 1926               if (pos + 4 <= MAX_BLOCK_SIZE) {
 1927                   Bits.putFloat(buf, pos, v);
 1928                   pos += 4;
 1929               } else {
 1930                   dout.writeFloat(v);
 1931               }
 1932           }
 1933   
 1934           public void writeLong(long v) throws IOException {
 1935               if (pos + 8 <= MAX_BLOCK_SIZE) {
 1936                   Bits.putLong(buf, pos, v);
 1937                   pos += 8;
 1938               } else {
 1939                   dout.writeLong(v);
 1940               }
 1941           }
 1942   
 1943           public void writeDouble(double v) throws IOException {
 1944               if (pos + 8 <= MAX_BLOCK_SIZE) {
 1945                   Bits.putDouble(buf, pos, v);
 1946                   pos += 8;
 1947               } else {
 1948                   dout.writeDouble(v);
 1949               }
 1950           }
 1951   
 1952           public void writeBytes(String s) throws IOException {
 1953               int endoff = s.length();
 1954               int cpos = 0;
 1955               int csize = 0;
 1956               for (int off = 0; off < endoff; ) {
 1957                   if (cpos >= csize) {
 1958                       cpos = 0;
 1959                       csize = Math.min(endoff - off, CHAR_BUF_SIZE);
 1960                       s.getChars(off, off + csize, cbuf, 0);
 1961                   }
 1962                   if (pos >= MAX_BLOCK_SIZE) {
 1963                       drain();
 1964                   }
 1965                   int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
 1966                   int stop = pos + n;
 1967                   while (pos < stop) {
 1968                       buf[pos++] = (byte) cbuf[cpos++];
 1969                   }
 1970                   off += n;
 1971               }
 1972           }
 1973   
 1974           public void writeChars(String s) throws IOException {
 1975               int endoff = s.length();
 1976               for (int off = 0; off < endoff; ) {
 1977                   int csize = Math.min(endoff - off, CHAR_BUF_SIZE);
 1978                   s.getChars(off, off + csize, cbuf, 0);
 1979                   writeChars(cbuf, 0, csize);
 1980                   off += csize;
 1981               }
 1982           }
 1983   
 1984           public void writeUTF(String s) throws IOException {
 1985               writeUTF(s, getUTFLength(s));
 1986           }
 1987   
 1988   
 1989           /* -------------- primitive data array output methods -------------- */
 1990           /*
 1991            * The following methods write out spans of primitive data values.
 1992            * Though equivalent to calling the corresponding primitive write
 1993            * methods repeatedly, these methods are optimized for writing groups
 1994            * of primitive data values more efficiently.
 1995            */
 1996   
 1997           void writeBooleans(boolean[] v, int off, int len) throws IOException {
 1998               int endoff = off + len;
 1999               while (off < endoff) {
 2000                   if (pos >= MAX_BLOCK_SIZE) {
 2001                       drain();
 2002                   }
 2003                   int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos));
 2004                   while (off < stop) {
 2005                       Bits.putBoolean(buf, pos++, v[off++]);
 2006                   }
 2007               }
 2008           }
 2009   
 2010           void writeChars(char[] v, int off, int len) throws IOException {
 2011               int limit = MAX_BLOCK_SIZE - 2;
 2012               int endoff = off + len;
 2013               while (off < endoff) {
 2014                   if (pos <= limit) {
 2015                       int avail = (MAX_BLOCK_SIZE - pos) >> 1;
 2016                       int stop = Math.min(endoff, off + avail);
 2017                       while (off < stop) {
 2018                           Bits.putChar(buf, pos, v[off++]);
 2019                           pos += 2;
 2020                       }
 2021                   } else {
 2022                       dout.writeChar(v[off++]);
 2023                   }
 2024               }
 2025           }
 2026   
 2027           void writeShorts(short[] v, int off, int len) throws IOException {
 2028               int limit = MAX_BLOCK_SIZE - 2;
 2029               int endoff = off + len;
 2030               while (off < endoff) {
 2031                   if (pos <= limit) {
 2032                       int avail = (MAX_BLOCK_SIZE - pos) >> 1;
 2033                       int stop = Math.min(endoff, off + avail);
 2034                       while (off < stop) {
 2035                           Bits.putShort(buf, pos, v[off++]);
 2036                           pos += 2;
 2037                       }
 2038                   } else {
 2039                       dout.writeShort(v[off++]);
 2040                   }
 2041               }
 2042           }
 2043   
 2044           void writeInts(int[] v, int off, int len) throws IOException {
 2045               int limit = MAX_BLOCK_SIZE - 4;
 2046               int endoff = off + len;
 2047               while (off < endoff) {
 2048                   if (pos <= limit) {
 2049                       int avail = (MAX_BLOCK_SIZE - pos) >> 2;
 2050                       int stop = Math.min(endoff, off + avail);
 2051                       while (off < stop) {
 2052                           Bits.putInt(buf, pos, v[off++]);
 2053                           pos += 4;
 2054                       }
 2055                   } else {
 2056                       dout.writeInt(v[off++]);
 2057                   }
 2058               }
 2059           }
 2060   
 2061           void writeFloats(float[] v, int off, int len) throws IOException {
 2062               int limit = MAX_BLOCK_SIZE - 4;
 2063               int endoff = off + len;
 2064               while (off < endoff) {
 2065                   if (pos <= limit) {
 2066                       int avail = (MAX_BLOCK_SIZE - pos) >> 2;
 2067                       int chunklen = Math.min(endoff - off, avail);
 2068                       floatsToBytes(v, off, buf, pos, chunklen);
 2069                       off += chunklen;
 2070                       pos += chunklen << 2;
 2071                   } else {
 2072                       dout.writeFloat(v[off++]);
 2073                   }
 2074               }
 2075           }
 2076   
 2077           void writeLongs(long[] v, int off, int len) throws IOException {
 2078               int limit = MAX_BLOCK_SIZE - 8;
 2079               int endoff = off + len;
 2080               while (off < endoff) {
 2081                   if (pos <= limit) {
 2082                       int avail = (MAX_BLOCK_SIZE - pos) >> 3;
 2083                       int stop = Math.min(endoff, off + avail);
 2084                       while (off < stop) {
 2085                           Bits.putLong(buf, pos, v[off++]);
 2086                           pos += 8;
 2087                       }
 2088                   } else {
 2089                       dout.writeLong(v[off++]);
 2090                   }
 2091               }
 2092           }
 2093   
 2094           void writeDoubles(double[] v, int off, int len) throws IOException {
 2095               int limit = MAX_BLOCK_SIZE - 8;
 2096               int endoff = off + len;
 2097               while (off < endoff) {
 2098                   if (pos <= limit) {
 2099                       int avail = (MAX_BLOCK_SIZE - pos) >> 3;
 2100                       int chunklen = Math.min(endoff - off, avail);
 2101                       doublesToBytes(v, off, buf, pos, chunklen);
 2102                       off += chunklen;
 2103                       pos += chunklen << 3;
 2104                   } else {
 2105                       dout.writeDouble(v[off++]);
 2106                   }
 2107               }
 2108           }
 2109   
 2110           /**
 2111            * Returns the length in bytes of the UTF encoding of the given string.
 2112            */
 2113           long getUTFLength(String s) {
 2114               int len = s.length();
 2115               long utflen = 0;
 2116               for (int off = 0; off < len; ) {
 2117                   int csize = Math.min(len - off, CHAR_BUF_SIZE);
 2118                   s.getChars(off, off + csize, cbuf, 0);
 2119                   for (int cpos = 0; cpos < csize; cpos++) {
 2120                       char c = cbuf[cpos];
 2121                       if (c >= 0x0001 && c <= 0x007F) {
 2122                           utflen++;
 2123                       } else if (c > 0x07FF) {
 2124                           utflen += 3;
 2125                       } else {
 2126                           utflen += 2;
 2127                       }
 2128                   }
 2129                   off += csize;
 2130               }
 2131               return utflen;
 2132           }
 2133   
 2134           /**
 2135            * Writes the given string in UTF format.  This method is used in
 2136            * situations where the UTF encoding length of the string is already
 2137            * known; specifying it explicitly avoids a prescan of the string to
 2138            * determine its UTF length.
 2139            */
 2140           void writeUTF(String s, long utflen) throws IOException {
 2141               if (utflen > 0xFFFFL) {
 2142                   throw new UTFDataFormatException();
 2143               }
 2144               writeShort((int) utflen);
 2145               if (utflen == (long) s.length()) {
 2146                   writeBytes(s);
 2147               } else {
 2148                   writeUTFBody(s);
 2149               }
 2150           }
 2151   
 2152           /**
 2153            * Writes given string in "long" UTF format.  "Long" UTF format is
 2154            * identical to standard UTF, except that it uses an 8 byte header
 2155            * (instead of the standard 2 bytes) to convey the UTF encoding length.
 2156            */
 2157           void writeLongUTF(String s) throws IOException {
 2158               writeLongUTF(s, getUTFLength(s));
 2159           }
 2160   
 2161           /**
 2162            * Writes given string in "long" UTF format, where the UTF encoding
 2163            * length of the string is already known.
 2164            */
 2165           void writeLongUTF(String s, long utflen) throws IOException {
 2166               writeLong(utflen);
 2167               if (utflen == (long) s.length()) {
 2168                   writeBytes(s);
 2169               } else {
 2170                   writeUTFBody(s);
 2171               }
 2172           }
 2173   
 2174           /**
 2175            * Writes the "body" (i.e., the UTF representation minus the 2-byte or
 2176            * 8-byte length header) of the UTF encoding for the given string.
 2177            */
 2178           private void writeUTFBody(String s) throws IOException {
 2179               int limit = MAX_BLOCK_SIZE - 3;
 2180               int len = s.length();
 2181               for (int off = 0; off < len; ) {
 2182                   int csize = Math.min(len - off, CHAR_BUF_SIZE);
 2183                   s.getChars(off, off + csize, cbuf, 0);
 2184                   for (int cpos = 0; cpos < csize; cpos++) {
 2185                       char c = cbuf[cpos];
 2186                       if (pos <= limit) {
 2187                           if (c <= 0x007F && c != 0) {
 2188                               buf[pos++] = (byte) c;
 2189                           } else if (c > 0x07FF) {
 2190                               buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));
 2191                               buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
 2192                               buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));
 2193                               pos += 3;
 2194                           } else {
 2195                               buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));
 2196                               buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));
 2197                               pos += 2;
 2198                           }
 2199                       } else {    // write one byte at a time to normalize block
 2200                           if (c <= 0x007F && c != 0) {
 2201                               write(c);
 2202                           } else if (c > 0x07FF) {
 2203                               write(0xE0 | ((c >> 12) & 0x0F));
 2204                               write(0x80 | ((c >> 6) & 0x3F));
 2205                               write(0x80 | ((c >> 0) & 0x3F));
 2206                           } else {
 2207                               write(0xC0 | ((c >> 6) & 0x1F));
 2208                               write(0x80 | ((c >> 0) & 0x3F));
 2209                           }
 2210                       }
 2211                   }
 2212                   off += csize;
 2213               }
 2214           }
 2215       }
 2216   
 2217       /**
 2218        * Lightweight identity hash table which maps objects to integer handles,
 2219        * assigned in ascending order.
 2220        */
 2221       private static class HandleTable {
 2222   
 2223           /* number of mappings in table/next available handle */
 2224           private int size;
 2225           /* size threshold determining when to expand hash spine */
 2226           private int threshold;
 2227           /* factor for computing size threshold */
 2228           private final float loadFactor;
 2229           /* maps hash value -> candidate handle value */
 2230           private int[] spine;
 2231           /* maps handle value -> next candidate handle value */
 2232           private int[] next;
 2233           /* maps handle value -> associated object */
 2234           private Object[] objs;
 2235   
 2236           /**
 2237            * Creates new HandleTable with given capacity and load factor.
 2238            */
 2239           HandleTable(int initialCapacity, float loadFactor) {
 2240               this.loadFactor = loadFactor;
 2241               spine = new int[initialCapacity];
 2242               next = new int[initialCapacity];
 2243               objs = new Object[initialCapacity];
 2244               threshold = (int) (initialCapacity * loadFactor);
 2245               clear();
 2246           }
 2247   
 2248           /**
 2249            * Assigns next available handle to given object, and returns handle
 2250            * value.  Handles are assigned in ascending order starting at 0.
 2251            */
 2252           int assign(Object obj) {
 2253               if (size >= next.length) {
 2254                   growEntries();
 2255               }
 2256               if (size >= threshold) {
 2257                   growSpine();
 2258               }
 2259               insert(obj, size);
 2260               return size++;
 2261           }
 2262   
 2263           /**
 2264            * Looks up and returns handle associated with given object, or -1 if
 2265            * no mapping found.
 2266            */
 2267           int lookup(Object obj) {
 2268               if (size == 0) {
 2269                   return -1;
 2270               }
 2271               int index = hash(obj) % spine.length;
 2272               for (int i = spine[index]; i >= 0; i = next[i]) {
 2273                   if (objs[i] == obj) {
 2274                       return i;
 2275                   }
 2276               }
 2277               return -1;
 2278           }
 2279   
 2280           /**
 2281            * Resets table to its initial (empty) state.
 2282            */
 2283           void clear() {
 2284               Arrays.fill(spine, -1);
 2285               Arrays.fill(objs, 0, size, null);
 2286               size = 0;
 2287           }
 2288   
 2289           /**
 2290            * Returns the number of mappings currently in table.
 2291            */
 2292           int size() {
 2293               return size;
 2294           }
 2295   
 2296           /**
 2297            * Inserts mapping object -> handle mapping into table.  Assumes table
 2298            * is large enough to accommodate new mapping.
 2299            */
 2300           private void insert(Object obj, int handle) {
 2301               int index = hash(obj) % spine.length;
 2302               objs[handle] = obj;
 2303               next[handle] = spine[index];
 2304               spine[index] = handle;
 2305           }
 2306   
 2307           /**
 2308            * Expands the hash "spine" -- equivalent to increasing the number of
 2309            * buckets in a conventional hash table.
 2310            */
 2311           private void growSpine() {
 2312               spine = new int[(spine.length << 1) + 1];
 2313               threshold = (int) (spine.length * loadFactor);
 2314               Arrays.fill(spine, -1);
 2315               for (int i = 0; i < size; i++) {
 2316                   insert(objs[i], i);
 2317               }
 2318           }
 2319   
 2320           /**
 2321            * Increases hash table capacity by lengthening entry arrays.
 2322            */
 2323           private void growEntries() {
 2324               int newLength = (next.length << 1) + 1;
 2325               int[] newNext = new int[newLength];
 2326               System.arraycopy(next, 0, newNext, 0, size);
 2327               next = newNext;
 2328   
 2329               Object[] newObjs = new Object[newLength];
 2330               System.arraycopy(objs, 0, newObjs, 0, size);
 2331               objs = newObjs;
 2332           }
 2333   
 2334           /**
 2335            * Returns hash value for given object.
 2336            */
 2337           private int hash(Object obj) {
 2338               return System.identityHashCode(obj) & 0x7FFFFFFF;
 2339           }
 2340       }
 2341   
 2342       /**
 2343        * Lightweight identity hash table which maps objects to replacement
 2344        * objects.
 2345        */
 2346       private static class ReplaceTable {
 2347   
 2348           /* maps object -> index */
 2349           private final HandleTable htab;
 2350           /* maps index -> replacement object */
 2351           private Object[] reps;
 2352   
 2353           /**
 2354            * Creates new ReplaceTable with given capacity and load factor.
 2355            */
 2356           ReplaceTable(int initialCapacity, float loadFactor) {
 2357               htab = new HandleTable(initialCapacity, loadFactor);
 2358               reps = new Object[initialCapacity];
 2359           }
 2360   
 2361           /**
 2362            * Enters mapping from object to replacement object.
 2363            */
 2364           void assign(Object obj, Object rep) {
 2365               int index = htab.assign(obj);
 2366               while (index >= reps.length) {
 2367                   grow();
 2368               }
 2369               reps[index] = rep;
 2370           }
 2371   
 2372           /**
 2373            * Looks up and returns replacement for given object.  If no
 2374            * replacement is found, returns the lookup object itself.
 2375            */
 2376           Object lookup(Object obj) {
 2377               int index = htab.lookup(obj);
 2378               return (index >= 0) ? reps[index] : obj;
 2379           }
 2380   
 2381           /**
 2382            * Resets table to its initial (empty) state.
 2383            */
 2384           void clear() {
 2385               Arrays.fill(reps, 0, htab.size(), null);
 2386               htab.clear();
 2387           }
 2388   
 2389           /**
 2390            * Returns the number of mappings currently in table.
 2391            */
 2392           int size() {
 2393               return htab.size();
 2394           }
 2395   
 2396           /**
 2397            * Increases table capacity.
 2398            */
 2399           private void grow() {
 2400               Object[] newReps = new Object[(reps.length << 1) + 1]