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