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