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