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