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