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