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