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

Save This Page
Home » openjdk-7 » java » io » [javadoc | source]