rt/emul/compact/src/main/java/java/io/ObjectOutputStream.java
changeset 772 d382dacfd73f
parent 604 3fcc279c921b
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/rt/emul/compact/src/main/java/java/io/ObjectOutputStream.java	Tue Feb 26 16:54:16 2013 +0100
     1.3 @@ -0,0 +1,2369 @@
     1.4 +/*
     1.5 + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
     1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     1.7 + *
     1.8 + * This code is free software; you can redistribute it and/or modify it
     1.9 + * under the terms of the GNU General Public License version 2 only, as
    1.10 + * published by the Free Software Foundation.  Oracle designates this
    1.11 + * particular file as subject to the "Classpath" exception as provided
    1.12 + * by Oracle in the LICENSE file that accompanied this code.
    1.13 + *
    1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
    1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    1.16 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    1.17 + * version 2 for more details (a copy is included in the LICENSE file that
    1.18 + * accompanied this code).
    1.19 + *
    1.20 + * You should have received a copy of the GNU General Public License version
    1.21 + * 2 along with this work; if not, write to the Free Software Foundation,
    1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    1.23 + *
    1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    1.25 + * or visit www.oracle.com if you need additional information or have any
    1.26 + * questions.
    1.27 + */
    1.28 +
    1.29 +package java.io;
    1.30 +
    1.31 +import java.util.ArrayList;
    1.32 +import java.util.Arrays;
    1.33 +import java.util.List;
    1.34 +import org.apidesign.bck2brwsr.emul.lang.System;
    1.35 +
    1.36 +/**
    1.37 + * An ObjectOutputStream writes primitive data types and graphs of Java objects
    1.38 + * to an OutputStream.  The objects can be read (reconstituted) using an
    1.39 + * ObjectInputStream.  Persistent storage of objects can be accomplished by
    1.40 + * using a file for the stream.  If the stream is a network socket stream, the
    1.41 + * objects can be reconstituted on another host or in another process.
    1.42 + *
    1.43 + * <p>Only objects that support the java.io.Serializable interface can be
    1.44 + * written to streams.  The class of each serializable object is encoded
    1.45 + * including the class name and signature of the class, the values of the
    1.46 + * object's fields and arrays, and the closure of any other objects referenced
    1.47 + * from the initial objects.
    1.48 + *
    1.49 + * <p>The method writeObject is used to write an object to the stream.  Any
    1.50 + * object, including Strings and arrays, is written with writeObject. Multiple
    1.51 + * objects or primitives can be written to the stream.  The objects must be
    1.52 + * read back from the corresponding ObjectInputstream with the same types and
    1.53 + * in the same order as they were written.
    1.54 + *
    1.55 + * <p>Primitive data types can also be written to the stream using the
    1.56 + * appropriate methods from DataOutput. Strings can also be written using the
    1.57 + * writeUTF method.
    1.58 + *
    1.59 + * <p>The default serialization mechanism for an object writes the class of the
    1.60 + * object, the class signature, and the values of all non-transient and
    1.61 + * non-static fields.  References to other objects (except in transient or
    1.62 + * static fields) cause those objects to be written also. Multiple references
    1.63 + * to a single object are encoded using a reference sharing mechanism so that
    1.64 + * graphs of objects can be restored to the same shape as when the original was
    1.65 + * written.
    1.66 + *
    1.67 + * <p>For example to write an object that can be read by the example in
    1.68 + * ObjectInputStream:
    1.69 + * <br>
    1.70 + * <pre>
    1.71 + *      FileOutputStream fos = new FileOutputStream("t.tmp");
    1.72 + *      ObjectOutputStream oos = new ObjectOutputStream(fos);
    1.73 + *
    1.74 + *      oos.writeInt(12345);
    1.75 + *      oos.writeObject("Today");
    1.76 + *      oos.writeObject(new Date());
    1.77 + *
    1.78 + *      oos.close();
    1.79 + * </pre>
    1.80 + *
    1.81 + * <p>Classes that require special handling during the serialization and
    1.82 + * deserialization process must implement special methods with these exact
    1.83 + * signatures:
    1.84 + * <br>
    1.85 + * <pre>
    1.86 + * private void readObject(java.io.ObjectInputStream stream)
    1.87 + *     throws IOException, ClassNotFoundException;
    1.88 + * private void writeObject(java.io.ObjectOutputStream stream)
    1.89 + *     throws IOException
    1.90 + * private void readObjectNoData()
    1.91 + *     throws ObjectStreamException;
    1.92 + * </pre>
    1.93 + *
    1.94 + * <p>The writeObject method is responsible for writing the state of the object
    1.95 + * for its particular class so that the corresponding readObject method can
    1.96 + * restore it.  The method does not need to concern itself with the state
    1.97 + * belonging to the object's superclasses or subclasses.  State is saved by
    1.98 + * writing the individual fields to the ObjectOutputStream using the
    1.99 + * writeObject method or by using the methods for primitive data types
   1.100 + * supported by DataOutput.
   1.101 + *
   1.102 + * <p>Serialization does not write out the fields of any object that does not
   1.103 + * implement the java.io.Serializable interface.  Subclasses of Objects that
   1.104 + * are not serializable can be serializable. In this case the non-serializable
   1.105 + * class must have a no-arg constructor to allow its fields to be initialized.
   1.106 + * In this case it is the responsibility of the subclass to save and restore
   1.107 + * the state of the non-serializable class. It is frequently the case that the
   1.108 + * fields of that class are accessible (public, package, or protected) or that
   1.109 + * there are get and set methods that can be used to restore the state.
   1.110 + *
   1.111 + * <p>Serialization of an object can be prevented by implementing writeObject
   1.112 + * and readObject methods that throw the NotSerializableException.  The
   1.113 + * exception will be caught by the ObjectOutputStream and abort the
   1.114 + * serialization process.
   1.115 + *
   1.116 + * <p>Implementing the Externalizable interface allows the object to assume
   1.117 + * complete control over the contents and format of the object's serialized
   1.118 + * form.  The methods of the Externalizable interface, writeExternal and
   1.119 + * readExternal, are called to save and restore the objects state.  When
   1.120 + * implemented by a class they can write and read their own state using all of
   1.121 + * the methods of ObjectOutput and ObjectInput.  It is the responsibility of
   1.122 + * the objects to handle any versioning that occurs.
   1.123 + *
   1.124 + * <p>Enum constants are serialized differently than ordinary serializable or
   1.125 + * externalizable objects.  The serialized form of an enum constant consists
   1.126 + * solely of its name; field values of the constant are not transmitted.  To
   1.127 + * serialize an enum constant, ObjectOutputStream writes the string returned by
   1.128 + * the constant's name method.  Like other serializable or externalizable
   1.129 + * objects, enum constants can function as the targets of back references
   1.130 + * appearing subsequently in the serialization stream.  The process by which
   1.131 + * enum constants are serialized cannot be customized; any class-specific
   1.132 + * writeObject and writeReplace methods defined by enum types are ignored
   1.133 + * during serialization.  Similarly, any serialPersistentFields or
   1.134 + * serialVersionUID field declarations are also ignored--all enum types have a
   1.135 + * fixed serialVersionUID of 0L.
   1.136 + *
   1.137 + * <p>Primitive data, excluding serializable fields and externalizable data, is
   1.138 + * written to the ObjectOutputStream in block-data records. A block data record
   1.139 + * is composed of a header and data. The block data header consists of a marker
   1.140 + * and the number of bytes to follow the header.  Consecutive primitive data
   1.141 + * writes are merged into one block-data record.  The blocking factor used for
   1.142 + * a block-data record will be 1024 bytes.  Each block-data record will be
   1.143 + * filled up to 1024 bytes, or be written whenever there is a termination of
   1.144 + * block-data mode.  Calls to the ObjectOutputStream methods writeObject,
   1.145 + * defaultWriteObject and writeFields initially terminate any existing
   1.146 + * block-data record.
   1.147 + *
   1.148 + * @author      Mike Warres
   1.149 + * @author      Roger Riggs
   1.150 + * @see java.io.DataOutput
   1.151 + * @see java.io.ObjectInputStream
   1.152 + * @see java.io.Serializable
   1.153 + * @see java.io.Externalizable
   1.154 + * @see <a href="../../../platform/serialization/spec/output.html">Object Serialization Specification, Section 2, Object Output Classes</a>
   1.155 + * @since       JDK1.1
   1.156 + */
   1.157 +public class ObjectOutputStream
   1.158 +    extends OutputStream implements ObjectOutput, ObjectStreamConstants
   1.159 +{
   1.160 +    /** filter stream for handling block data conversion */
   1.161 +    private final BlockDataOutputStream bout;
   1.162 +    /** obj -> wire handle map */
   1.163 +    private final HandleTable handles;
   1.164 +    /** obj -> replacement obj map */
   1.165 +    private final ReplaceTable subs;
   1.166 +    /** stream protocol version */
   1.167 +    private int protocol = PROTOCOL_VERSION_2;
   1.168 +    /** recursion depth */
   1.169 +    private int depth;
   1.170 +
   1.171 +    /** buffer for writing primitive field values */
   1.172 +    private byte[] primVals;
   1.173 +
   1.174 +    /** if true, invoke writeObjectOverride() instead of writeObject() */
   1.175 +    private final boolean enableOverride;
   1.176 +    /** if true, invoke replaceObject() */
   1.177 +    private boolean enableReplace;
   1.178 +
   1.179 +    // values below valid only during upcalls to writeObject()/writeExternal()
   1.180 +    /**
   1.181 +     * Context during upcalls to class-defined writeObject methods; holds
   1.182 +     * object currently being serialized and descriptor for current class.
   1.183 +     * Null when not during writeObject upcall.
   1.184 +     */
   1.185 +    private Object curContext;
   1.186 +    /** current PutField object */
   1.187 +    private PutFieldImpl curPut;
   1.188 +
   1.189 +    /** custom storage for debug trace info */
   1.190 +    private final DebugTraceInfoStack debugInfoStack;
   1.191 +
   1.192 +    /**
   1.193 +     * value of "sun.io.serialization.extendedDebugInfo" property,
   1.194 +     * as true or false for extended information about exception's place
   1.195 +     */
   1.196 +    private static final boolean extendedDebugInfo = false;
   1.197 +
   1.198 +    /**
   1.199 +     * Creates an ObjectOutputStream that writes to the specified OutputStream.
   1.200 +     * This constructor writes the serialization stream header to the
   1.201 +     * underlying stream; callers may wish to flush the stream immediately to
   1.202 +     * ensure that constructors for receiving ObjectInputStreams will not block
   1.203 +     * when reading the header.
   1.204 +     *
   1.205 +     * <p>If a security manager is installed, this constructor will check for
   1.206 +     * the "enableSubclassImplementation" SerializablePermission when invoked
   1.207 +     * directly or indirectly by the constructor of a subclass which overrides
   1.208 +     * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared
   1.209 +     * methods.
   1.210 +     *
   1.211 +     * @param   out output stream to write to
   1.212 +     * @throws  IOException if an I/O error occurs while writing stream header
   1.213 +     * @throws  SecurityException if untrusted subclass illegally overrides
   1.214 +     *          security-sensitive methods
   1.215 +     * @throws  NullPointerException if <code>out</code> is <code>null</code>
   1.216 +     * @since   1.4
   1.217 +     * @see     ObjectOutputStream#ObjectOutputStream()
   1.218 +     * @see     ObjectOutputStream#putFields()
   1.219 +     * @see     ObjectInputStream#ObjectInputStream(InputStream)
   1.220 +     */
   1.221 +    public ObjectOutputStream(OutputStream out) throws IOException {
   1.222 +        verifySubclass();
   1.223 +        bout = new BlockDataOutputStream(out);
   1.224 +        handles = new HandleTable(10, (float) 3.00);
   1.225 +        subs = new ReplaceTable(10, (float) 3.00);
   1.226 +        enableOverride = false;
   1.227 +        writeStreamHeader();
   1.228 +        bout.setBlockDataMode(true);
   1.229 +        if (extendedDebugInfo) {
   1.230 +            debugInfoStack = new DebugTraceInfoStack();
   1.231 +        } else {
   1.232 +            debugInfoStack = null;
   1.233 +        }
   1.234 +    }
   1.235 +
   1.236 +    /**
   1.237 +     * Provide a way for subclasses that are completely reimplementing
   1.238 +     * ObjectOutputStream to not have to allocate private data just used by
   1.239 +     * this implementation of ObjectOutputStream.
   1.240 +     *
   1.241 +     * <p>If there is a security manager installed, this method first calls the
   1.242 +     * security manager's <code>checkPermission</code> method with a
   1.243 +     * <code>SerializablePermission("enableSubclassImplementation")</code>
   1.244 +     * permission to ensure it's ok to enable subclassing.
   1.245 +     *
   1.246 +     * @throws  SecurityException if a security manager exists and its
   1.247 +     *          <code>checkPermission</code> method denies enabling
   1.248 +     *          subclassing.
   1.249 +     * @see SecurityManager#checkPermission
   1.250 +     * @see java.io.SerializablePermission
   1.251 +     */
   1.252 +    protected ObjectOutputStream() throws IOException, SecurityException {
   1.253 +        throw new SecurityException();
   1.254 +    }
   1.255 +
   1.256 +    /**
   1.257 +     * Specify stream protocol version to use when writing the stream.
   1.258 +     *
   1.259 +     * <p>This routine provides a hook to enable the current version of
   1.260 +     * Serialization to write in a format that is backwards compatible to a
   1.261 +     * previous version of the stream format.
   1.262 +     *
   1.263 +     * <p>Every effort will be made to avoid introducing additional
   1.264 +     * backwards incompatibilities; however, sometimes there is no
   1.265 +     * other alternative.
   1.266 +     *
   1.267 +     * @param   version use ProtocolVersion from java.io.ObjectStreamConstants.
   1.268 +     * @throws  IllegalStateException if called after any objects
   1.269 +     *          have been serialized.
   1.270 +     * @throws  IllegalArgumentException if invalid version is passed in.
   1.271 +     * @throws  IOException if I/O errors occur
   1.272 +     * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
   1.273 +     * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2
   1.274 +     * @since   1.2
   1.275 +     */
   1.276 +    public void useProtocolVersion(int version) throws IOException {
   1.277 +        if (handles.size() != 0) {
   1.278 +            // REMIND: implement better check for pristine stream?
   1.279 +            throw new IllegalStateException("stream non-empty");
   1.280 +        }
   1.281 +        switch (version) {
   1.282 +            case PROTOCOL_VERSION_1:
   1.283 +            case PROTOCOL_VERSION_2:
   1.284 +                protocol = version;
   1.285 +                break;
   1.286 +
   1.287 +            default:
   1.288 +                throw new IllegalArgumentException(
   1.289 +                    "unknown version: " + version);
   1.290 +        }
   1.291 +    }
   1.292 +
   1.293 +    /**
   1.294 +     * Write the specified object to the ObjectOutputStream.  The class of the
   1.295 +     * object, the signature of the class, and the values of the non-transient
   1.296 +     * and non-static fields of the class and all of its supertypes are
   1.297 +     * written.  Default serialization for a class can be overridden using the
   1.298 +     * writeObject and the readObject methods.  Objects referenced by this
   1.299 +     * object are written transitively so that a complete equivalent graph of
   1.300 +     * objects can be reconstructed by an ObjectInputStream.
   1.301 +     *
   1.302 +     * <p>Exceptions are thrown for problems with the OutputStream and for
   1.303 +     * classes that should not be serialized.  All exceptions are fatal to the
   1.304 +     * OutputStream, which is left in an indeterminate state, and it is up to
   1.305 +     * the caller to ignore or recover the stream state.
   1.306 +     *
   1.307 +     * @throws  InvalidClassException Something is wrong with a class used by
   1.308 +     *          serialization.
   1.309 +     * @throws  NotSerializableException Some object to be serialized does not
   1.310 +     *          implement the java.io.Serializable interface.
   1.311 +     * @throws  IOException Any exception thrown by the underlying
   1.312 +     *          OutputStream.
   1.313 +     */
   1.314 +    public final void writeObject(Object obj) throws IOException {
   1.315 +        if (enableOverride) {
   1.316 +            writeObjectOverride(obj);
   1.317 +            return;
   1.318 +        }
   1.319 +        try {
   1.320 +            writeObject0(obj, false);
   1.321 +        } catch (IOException ex) {
   1.322 +            if (depth == 0) {
   1.323 +                writeFatalException(ex);
   1.324 +            }
   1.325 +            throw ex;
   1.326 +        }
   1.327 +    }
   1.328 +
   1.329 +    /**
   1.330 +     * Method used by subclasses to override the default writeObject method.
   1.331 +     * This method is called by trusted subclasses of ObjectInputStream that
   1.332 +     * constructed ObjectInputStream using the protected no-arg constructor.
   1.333 +     * The subclass is expected to provide an override method with the modifier
   1.334 +     * "final".
   1.335 +     *
   1.336 +     * @param   obj object to be written to the underlying stream
   1.337 +     * @throws  IOException if there are I/O errors while writing to the
   1.338 +     *          underlying stream
   1.339 +     * @see #ObjectOutputStream()
   1.340 +     * @see #writeObject(Object)
   1.341 +     * @since 1.2
   1.342 +     */
   1.343 +    protected void writeObjectOverride(Object obj) throws IOException {
   1.344 +    }
   1.345 +
   1.346 +    /**
   1.347 +     * Writes an "unshared" object to the ObjectOutputStream.  This method is
   1.348 +     * identical to writeObject, except that it always writes the given object
   1.349 +     * as a new, unique object in the stream (as opposed to a back-reference
   1.350 +     * pointing to a previously serialized instance).  Specifically:
   1.351 +     * <ul>
   1.352 +     *   <li>An object written via writeUnshared is always serialized in the
   1.353 +     *       same manner as a newly appearing object (an object that has not
   1.354 +     *       been written to the stream yet), regardless of whether or not the
   1.355 +     *       object has been written previously.
   1.356 +     *
   1.357 +     *   <li>If writeObject is used to write an object that has been previously
   1.358 +     *       written with writeUnshared, the previous writeUnshared operation
   1.359 +     *       is treated as if it were a write of a separate object.  In other
   1.360 +     *       words, ObjectOutputStream will never generate back-references to
   1.361 +     *       object data written by calls to writeUnshared.
   1.362 +     * </ul>
   1.363 +     * While writing an object via writeUnshared does not in itself guarantee a
   1.364 +     * unique reference to the object when it is deserialized, it allows a
   1.365 +     * single object to be defined multiple times in a stream, so that multiple
   1.366 +     * calls to readUnshared by the receiver will not conflict.  Note that the
   1.367 +     * rules described above only apply to the base-level object written with
   1.368 +     * writeUnshared, and not to any transitively referenced sub-objects in the
   1.369 +     * object graph to be serialized.
   1.370 +     *
   1.371 +     * <p>ObjectOutputStream subclasses which override this method can only be
   1.372 +     * constructed in security contexts possessing the
   1.373 +     * "enableSubclassImplementation" SerializablePermission; any attempt to
   1.374 +     * instantiate such a subclass without this permission will cause a
   1.375 +     * SecurityException to be thrown.
   1.376 +     *
   1.377 +     * @param   obj object to write to stream
   1.378 +     * @throws  NotSerializableException if an object in the graph to be
   1.379 +     *          serialized does not implement the Serializable interface
   1.380 +     * @throws  InvalidClassException if a problem exists with the class of an
   1.381 +     *          object to be serialized
   1.382 +     * @throws  IOException if an I/O error occurs during serialization
   1.383 +     * @since 1.4
   1.384 +     */
   1.385 +    public void writeUnshared(Object obj) throws IOException {
   1.386 +        try {
   1.387 +            writeObject0(obj, true);
   1.388 +        } catch (IOException ex) {
   1.389 +            if (depth == 0) {
   1.390 +                writeFatalException(ex);
   1.391 +            }
   1.392 +            throw ex;
   1.393 +        }
   1.394 +    }
   1.395 +
   1.396 +    /**
   1.397 +     * Write the non-static and non-transient fields of the current class to
   1.398 +     * this stream.  This may only be called from the writeObject method of the
   1.399 +     * class being serialized. It will throw the NotActiveException if it is
   1.400 +     * called otherwise.
   1.401 +     *
   1.402 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.403 +     *          <code>OutputStream</code>
   1.404 +     */
   1.405 +    public void defaultWriteObject() throws IOException {
   1.406 +        if ( curContext == null ) {
   1.407 +            throw new NotActiveException("not in call to writeObject");
   1.408 +        }
   1.409 +        Object curObj = null; // curContext.getObj();
   1.410 +        ObjectStreamClass curDesc = null; // curContext.getDesc();
   1.411 +        bout.setBlockDataMode(false);
   1.412 +        defaultWriteFields(curObj, curDesc);
   1.413 +        bout.setBlockDataMode(true);
   1.414 +    }
   1.415 +
   1.416 +    /**
   1.417 +     * Retrieve the object used to buffer persistent fields to be written to
   1.418 +     * the stream.  The fields will be written to the stream when writeFields
   1.419 +     * method is called.
   1.420 +     *
   1.421 +     * @return  an instance of the class Putfield that holds the serializable
   1.422 +     *          fields
   1.423 +     * @throws  IOException if I/O errors occur
   1.424 +     * @since 1.2
   1.425 +     */
   1.426 +    public ObjectOutputStream.PutField putFields() throws IOException {
   1.427 +        if (curPut == null) {
   1.428 +            if (curContext == null) {
   1.429 +                throw new NotActiveException("not in call to writeObject");
   1.430 +            }
   1.431 +            Object curObj = null; // curContext.getObj();
   1.432 +            ObjectStreamClass curDesc = null; // curContext.getDesc();
   1.433 +            curPut = new PutFieldImpl(curDesc);
   1.434 +        }
   1.435 +        return curPut;
   1.436 +    }
   1.437 +
   1.438 +    /**
   1.439 +     * Write the buffered fields to the stream.
   1.440 +     *
   1.441 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.442 +     *          stream
   1.443 +     * @throws  NotActiveException Called when a classes writeObject method was
   1.444 +     *          not called to write the state of the object.
   1.445 +     * @since 1.2
   1.446 +     */
   1.447 +    public void writeFields() throws IOException {
   1.448 +        if (curPut == null) {
   1.449 +            throw new NotActiveException("no current PutField object");
   1.450 +        }
   1.451 +        bout.setBlockDataMode(false);
   1.452 +        curPut.writeFields();
   1.453 +        bout.setBlockDataMode(true);
   1.454 +    }
   1.455 +
   1.456 +    /**
   1.457 +     * Reset will disregard the state of any objects already written to the
   1.458 +     * stream.  The state is reset to be the same as a new ObjectOutputStream.
   1.459 +     * The current point in the stream is marked as reset so the corresponding
   1.460 +     * ObjectInputStream will be reset at the same point.  Objects previously
   1.461 +     * written to the stream will not be refered to as already being in the
   1.462 +     * stream.  They will be written to the stream again.
   1.463 +     *
   1.464 +     * @throws  IOException if reset() is invoked while serializing an object.
   1.465 +     */
   1.466 +    public void reset() throws IOException {
   1.467 +        if (depth != 0) {
   1.468 +            throw new IOException("stream active");
   1.469 +        }
   1.470 +        bout.setBlockDataMode(false);
   1.471 +        bout.writeByte(TC_RESET);
   1.472 +        clear();
   1.473 +        bout.setBlockDataMode(true);
   1.474 +    }
   1.475 +
   1.476 +    /**
   1.477 +     * Subclasses may implement this method to allow class data to be stored in
   1.478 +     * the stream. By default this method does nothing.  The corresponding
   1.479 +     * method in ObjectInputStream is resolveClass.  This method is called
   1.480 +     * exactly once for each unique class in the stream.  The class name and
   1.481 +     * signature will have already been written to the stream.  This method may
   1.482 +     * make free use of the ObjectOutputStream to save any representation of
   1.483 +     * the class it deems suitable (for example, the bytes of the class file).
   1.484 +     * The resolveClass method in the corresponding subclass of
   1.485 +     * ObjectInputStream must read and use any data or objects written by
   1.486 +     * annotateClass.
   1.487 +     *
   1.488 +     * @param   cl the class to annotate custom data for
   1.489 +     * @throws  IOException Any exception thrown by the underlying
   1.490 +     *          OutputStream.
   1.491 +     */
   1.492 +    protected void annotateClass(Class<?> cl) throws IOException {
   1.493 +    }
   1.494 +
   1.495 +    /**
   1.496 +     * Subclasses may implement this method to store custom data in the stream
   1.497 +     * along with descriptors for dynamic proxy classes.
   1.498 +     *
   1.499 +     * <p>This method is called exactly once for each unique proxy class
   1.500 +     * descriptor in the stream.  The default implementation of this method in
   1.501 +     * <code>ObjectOutputStream</code> does nothing.
   1.502 +     *
   1.503 +     * <p>The corresponding method in <code>ObjectInputStream</code> is
   1.504 +     * <code>resolveProxyClass</code>.  For a given subclass of
   1.505 +     * <code>ObjectOutputStream</code> that overrides this method, the
   1.506 +     * <code>resolveProxyClass</code> method in the corresponding subclass of
   1.507 +     * <code>ObjectInputStream</code> must read any data or objects written by
   1.508 +     * <code>annotateProxyClass</code>.
   1.509 +     *
   1.510 +     * @param   cl the proxy class to annotate custom data for
   1.511 +     * @throws  IOException any exception thrown by the underlying
   1.512 +     *          <code>OutputStream</code>
   1.513 +     * @see ObjectInputStream#resolveProxyClass(String[])
   1.514 +     * @since   1.3
   1.515 +     */
   1.516 +    protected void annotateProxyClass(Class<?> cl) throws IOException {
   1.517 +    }
   1.518 +
   1.519 +    /**
   1.520 +     * This method will allow trusted subclasses of ObjectOutputStream to
   1.521 +     * substitute one object for another during serialization. Replacing
   1.522 +     * objects is disabled until enableReplaceObject is called. The
   1.523 +     * enableReplaceObject method checks that the stream requesting to do
   1.524 +     * replacement can be trusted.  The first occurrence of each object written
   1.525 +     * into the serialization stream is passed to replaceObject.  Subsequent
   1.526 +     * references to the object are replaced by the object returned by the
   1.527 +     * original call to replaceObject.  To ensure that the private state of
   1.528 +     * objects is not unintentionally exposed, only trusted streams may use
   1.529 +     * replaceObject.
   1.530 +     *
   1.531 +     * <p>The ObjectOutputStream.writeObject method takes a parameter of type
   1.532 +     * Object (as opposed to type Serializable) to allow for cases where
   1.533 +     * non-serializable objects are replaced by serializable ones.
   1.534 +     *
   1.535 +     * <p>When a subclass is replacing objects it must insure that either a
   1.536 +     * complementary substitution must be made during deserialization or that
   1.537 +     * the substituted object is compatible with every field where the
   1.538 +     * reference will be stored.  Objects whose type is not a subclass of the
   1.539 +     * type of the field or array element abort the serialization by raising an
   1.540 +     * exception and the object is not be stored.
   1.541 +     *
   1.542 +     * <p>This method is called only once when each object is first
   1.543 +     * encountered.  All subsequent references to the object will be redirected
   1.544 +     * to the new object. This method should return the object to be
   1.545 +     * substituted or the original object.
   1.546 +     *
   1.547 +     * <p>Null can be returned as the object to be substituted, but may cause
   1.548 +     * NullReferenceException in classes that contain references to the
   1.549 +     * original object since they may be expecting an object instead of
   1.550 +     * null.
   1.551 +     *
   1.552 +     * @param   obj the object to be replaced
   1.553 +     * @return  the alternate object that replaced the specified one
   1.554 +     * @throws  IOException Any exception thrown by the underlying
   1.555 +     *          OutputStream.
   1.556 +     */
   1.557 +    protected Object replaceObject(Object obj) throws IOException {
   1.558 +        return obj;
   1.559 +    }
   1.560 +
   1.561 +    /**
   1.562 +     * Enable the stream to do replacement of objects in the stream.  When
   1.563 +     * enabled, the replaceObject method is called for every object being
   1.564 +     * serialized.
   1.565 +     *
   1.566 +     * <p>If <code>enable</code> is true, and there is a security manager
   1.567 +     * installed, this method first calls the security manager's
   1.568 +     * <code>checkPermission</code> method with a
   1.569 +     * <code>SerializablePermission("enableSubstitution")</code> permission to
   1.570 +     * ensure it's ok to enable the stream to do replacement of objects in the
   1.571 +     * stream.
   1.572 +     *
   1.573 +     * @param   enable boolean parameter to enable replacement of objects
   1.574 +     * @return  the previous setting before this method was invoked
   1.575 +     * @throws  SecurityException if a security manager exists and its
   1.576 +     *          <code>checkPermission</code> method denies enabling the stream
   1.577 +     *          to do replacement of objects in the stream.
   1.578 +     * @see SecurityManager#checkPermission
   1.579 +     * @see java.io.SerializablePermission
   1.580 +     */
   1.581 +    protected boolean enableReplaceObject(boolean enable)
   1.582 +        throws SecurityException
   1.583 +    {
   1.584 +        throw new SecurityException();
   1.585 +    }
   1.586 +
   1.587 +    /**
   1.588 +     * The writeStreamHeader method is provided so subclasses can append or
   1.589 +     * prepend their own header to the stream.  It writes the magic number and
   1.590 +     * version to the stream.
   1.591 +     *
   1.592 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.593 +     *          stream
   1.594 +     */
   1.595 +    protected void writeStreamHeader() throws IOException {
   1.596 +        bout.writeShort(STREAM_MAGIC);
   1.597 +        bout.writeShort(STREAM_VERSION);
   1.598 +    }
   1.599 +
   1.600 +    /**
   1.601 +     * Write the specified class descriptor to the ObjectOutputStream.  Class
   1.602 +     * descriptors are used to identify the classes of objects written to the
   1.603 +     * stream.  Subclasses of ObjectOutputStream may override this method to
   1.604 +     * customize the way in which class descriptors are written to the
   1.605 +     * serialization stream.  The corresponding method in ObjectInputStream,
   1.606 +     * <code>readClassDescriptor</code>, should then be overridden to
   1.607 +     * reconstitute the class descriptor from its custom stream representation.
   1.608 +     * By default, this method writes class descriptors according to the format
   1.609 +     * defined in the Object Serialization specification.
   1.610 +     *
   1.611 +     * <p>Note that this method will only be called if the ObjectOutputStream
   1.612 +     * is not using the old serialization stream format (set by calling
   1.613 +     * ObjectOutputStream's <code>useProtocolVersion</code> method).  If this
   1.614 +     * serialization stream is using the old format
   1.615 +     * (<code>PROTOCOL_VERSION_1</code>), the class descriptor will be written
   1.616 +     * internally in a manner that cannot be overridden or customized.
   1.617 +     *
   1.618 +     * @param   desc class descriptor to write to the stream
   1.619 +     * @throws  IOException If an I/O error has occurred.
   1.620 +     * @see java.io.ObjectInputStream#readClassDescriptor()
   1.621 +     * @see #useProtocolVersion(int)
   1.622 +     * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
   1.623 +     * @since 1.3
   1.624 +     */
   1.625 +    protected void writeClassDescriptor(ObjectStreamClass desc)
   1.626 +        throws IOException
   1.627 +    {
   1.628 +        desc.writeNonProxy(this);
   1.629 +    }
   1.630 +
   1.631 +    /**
   1.632 +     * Writes a byte. This method will block until the byte is actually
   1.633 +     * written.
   1.634 +     *
   1.635 +     * @param   val the byte to be written to the stream
   1.636 +     * @throws  IOException If an I/O error has occurred.
   1.637 +     */
   1.638 +    public void write(int val) throws IOException {
   1.639 +        bout.write(val);
   1.640 +    }
   1.641 +
   1.642 +    /**
   1.643 +     * Writes an array of bytes. This method will block until the bytes are
   1.644 +     * actually written.
   1.645 +     *
   1.646 +     * @param   buf the data to be written
   1.647 +     * @throws  IOException If an I/O error has occurred.
   1.648 +     */
   1.649 +    public void write(byte[] buf) throws IOException {
   1.650 +        bout.write(buf, 0, buf.length, false);
   1.651 +    }
   1.652 +
   1.653 +    /**
   1.654 +     * Writes a sub array of bytes.
   1.655 +     *
   1.656 +     * @param   buf the data to be written
   1.657 +     * @param   off the start offset in the data
   1.658 +     * @param   len the number of bytes that are written
   1.659 +     * @throws  IOException If an I/O error has occurred.
   1.660 +     */
   1.661 +    public void write(byte[] buf, int off, int len) throws IOException {
   1.662 +        if (buf == null) {
   1.663 +            throw new NullPointerException();
   1.664 +        }
   1.665 +        int endoff = off + len;
   1.666 +        if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
   1.667 +            throw new IndexOutOfBoundsException();
   1.668 +        }
   1.669 +        bout.write(buf, off, len, false);
   1.670 +    }
   1.671 +
   1.672 +    /**
   1.673 +     * Flushes the stream. This will write any buffered output bytes and flush
   1.674 +     * through to the underlying stream.
   1.675 +     *
   1.676 +     * @throws  IOException If an I/O error has occurred.
   1.677 +     */
   1.678 +    public void flush() throws IOException {
   1.679 +        bout.flush();
   1.680 +    }
   1.681 +
   1.682 +    /**
   1.683 +     * Drain any buffered data in ObjectOutputStream.  Similar to flush but
   1.684 +     * does not propagate the flush to the underlying stream.
   1.685 +     *
   1.686 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.687 +     *          stream
   1.688 +     */
   1.689 +    protected void drain() throws IOException {
   1.690 +        bout.drain();
   1.691 +    }
   1.692 +
   1.693 +    /**
   1.694 +     * Closes the stream. This method must be called to release any resources
   1.695 +     * associated with the stream.
   1.696 +     *
   1.697 +     * @throws  IOException If an I/O error has occurred.
   1.698 +     */
   1.699 +    public void close() throws IOException {
   1.700 +        flush();
   1.701 +        clear();
   1.702 +        bout.close();
   1.703 +    }
   1.704 +
   1.705 +    /**
   1.706 +     * Writes a boolean.
   1.707 +     *
   1.708 +     * @param   val the boolean to be written
   1.709 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.710 +     *          stream
   1.711 +     */
   1.712 +    public void writeBoolean(boolean val) throws IOException {
   1.713 +        bout.writeBoolean(val);
   1.714 +    }
   1.715 +
   1.716 +    /**
   1.717 +     * Writes an 8 bit byte.
   1.718 +     *
   1.719 +     * @param   val the byte value to be written
   1.720 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.721 +     *          stream
   1.722 +     */
   1.723 +    public void writeByte(int val) throws IOException  {
   1.724 +        bout.writeByte(val);
   1.725 +    }
   1.726 +
   1.727 +    /**
   1.728 +     * Writes a 16 bit short.
   1.729 +     *
   1.730 +     * @param   val the short value to be written
   1.731 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.732 +     *          stream
   1.733 +     */
   1.734 +    public void writeShort(int val)  throws IOException {
   1.735 +        bout.writeShort(val);
   1.736 +    }
   1.737 +
   1.738 +    /**
   1.739 +     * Writes a 16 bit char.
   1.740 +     *
   1.741 +     * @param   val the char value to be written
   1.742 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.743 +     *          stream
   1.744 +     */
   1.745 +    public void writeChar(int val)  throws IOException {
   1.746 +        bout.writeChar(val);
   1.747 +    }
   1.748 +
   1.749 +    /**
   1.750 +     * Writes a 32 bit int.
   1.751 +     *
   1.752 +     * @param   val the integer value to be written
   1.753 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.754 +     *          stream
   1.755 +     */
   1.756 +    public void writeInt(int val)  throws IOException {
   1.757 +        bout.writeInt(val);
   1.758 +    }
   1.759 +
   1.760 +    /**
   1.761 +     * Writes a 64 bit long.
   1.762 +     *
   1.763 +     * @param   val the long value to be written
   1.764 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.765 +     *          stream
   1.766 +     */
   1.767 +    public void writeLong(long val)  throws IOException {
   1.768 +        bout.writeLong(val);
   1.769 +    }
   1.770 +
   1.771 +    /**
   1.772 +     * Writes a 32 bit float.
   1.773 +     *
   1.774 +     * @param   val the float value to be written
   1.775 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.776 +     *          stream
   1.777 +     */
   1.778 +    public void writeFloat(float val) throws IOException {
   1.779 +        bout.writeFloat(val);
   1.780 +    }
   1.781 +
   1.782 +    /**
   1.783 +     * Writes a 64 bit double.
   1.784 +     *
   1.785 +     * @param   val the double value to be written
   1.786 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.787 +     *          stream
   1.788 +     */
   1.789 +    public void writeDouble(double val) throws IOException {
   1.790 +        bout.writeDouble(val);
   1.791 +    }
   1.792 +
   1.793 +    /**
   1.794 +     * Writes a String as a sequence of bytes.
   1.795 +     *
   1.796 +     * @param   str the String of bytes to be written
   1.797 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.798 +     *          stream
   1.799 +     */
   1.800 +    public void writeBytes(String str) throws IOException {
   1.801 +        bout.writeBytes(str);
   1.802 +    }
   1.803 +
   1.804 +    /**
   1.805 +     * Writes a String as a sequence of chars.
   1.806 +     *
   1.807 +     * @param   str the String of chars to be written
   1.808 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.809 +     *          stream
   1.810 +     */
   1.811 +    public void writeChars(String str) throws IOException {
   1.812 +        bout.writeChars(str);
   1.813 +    }
   1.814 +
   1.815 +    /**
   1.816 +     * Primitive data write of this String in
   1.817 +     * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
   1.818 +     * format.  Note that there is a
   1.819 +     * significant difference between writing a String into the stream as
   1.820 +     * primitive data or as an Object. A String instance written by writeObject
   1.821 +     * is written into the stream as a String initially. Future writeObject()
   1.822 +     * calls write references to the string into the stream.
   1.823 +     *
   1.824 +     * @param   str the String to be written
   1.825 +     * @throws  IOException if I/O errors occur while writing to the underlying
   1.826 +     *          stream
   1.827 +     */
   1.828 +    public void writeUTF(String str) throws IOException {
   1.829 +        bout.writeUTF(str);
   1.830 +    }
   1.831 +
   1.832 +    /**
   1.833 +     * Provide programmatic access to the persistent fields to be written
   1.834 +     * to ObjectOutput.
   1.835 +     *
   1.836 +     * @since 1.2
   1.837 +     */
   1.838 +    public static abstract class PutField {
   1.839 +
   1.840 +        /**
   1.841 +         * Put the value of the named boolean field into the persistent field.
   1.842 +         *
   1.843 +         * @param  name the name of the serializable field
   1.844 +         * @param  val the value to assign to the field
   1.845 +         * @throws IllegalArgumentException if <code>name</code> does not
   1.846 +         * match the name of a serializable field for the class whose fields
   1.847 +         * are being written, or if the type of the named field is not
   1.848 +         * <code>boolean</code>
   1.849 +         */
   1.850 +        public abstract void put(String name, boolean val);
   1.851 +
   1.852 +        /**
   1.853 +         * Put the value of the named byte field into the persistent field.
   1.854 +         *
   1.855 +         * @param  name the name of the serializable field
   1.856 +         * @param  val the value to assign to the field
   1.857 +         * @throws IllegalArgumentException if <code>name</code> does not
   1.858 +         * match the name of a serializable field for the class whose fields
   1.859 +         * are being written, or if the type of the named field is not
   1.860 +         * <code>byte</code>
   1.861 +         */
   1.862 +        public abstract void put(String name, byte val);
   1.863 +
   1.864 +        /**
   1.865 +         * Put the value of the named char field into the persistent field.
   1.866 +         *
   1.867 +         * @param  name the name of the serializable field
   1.868 +         * @param  val the value to assign to the field
   1.869 +         * @throws IllegalArgumentException if <code>name</code> does not
   1.870 +         * match the name of a serializable field for the class whose fields
   1.871 +         * are being written, or if the type of the named field is not
   1.872 +         * <code>char</code>
   1.873 +         */
   1.874 +        public abstract void put(String name, char val);
   1.875 +
   1.876 +        /**
   1.877 +         * Put the value of the named short field into the persistent field.
   1.878 +         *
   1.879 +         * @param  name the name of the serializable field
   1.880 +         * @param  val the value to assign to the field
   1.881 +         * @throws IllegalArgumentException if <code>name</code> does not
   1.882 +         * match the name of a serializable field for the class whose fields
   1.883 +         * are being written, or if the type of the named field is not
   1.884 +         * <code>short</code>
   1.885 +         */
   1.886 +        public abstract void put(String name, short val);
   1.887 +
   1.888 +        /**
   1.889 +         * Put the value of the named int field into the persistent field.
   1.890 +         *
   1.891 +         * @param  name the name of the serializable field
   1.892 +         * @param  val the value to assign to the field
   1.893 +         * @throws IllegalArgumentException if <code>name</code> does not
   1.894 +         * match the name of a serializable field for the class whose fields
   1.895 +         * are being written, or if the type of the named field is not
   1.896 +         * <code>int</code>
   1.897 +         */
   1.898 +        public abstract void put(String name, int val);
   1.899 +
   1.900 +        /**
   1.901 +         * Put the value of the named long field into the persistent field.
   1.902 +         *
   1.903 +         * @param  name the name of the serializable field
   1.904 +         * @param  val the value to assign to the field
   1.905 +         * @throws IllegalArgumentException if <code>name</code> does not
   1.906 +         * match the name of a serializable field for the class whose fields
   1.907 +         * are being written, or if the type of the named field is not
   1.908 +         * <code>long</code>
   1.909 +         */
   1.910 +        public abstract void put(String name, long val);
   1.911 +
   1.912 +        /**
   1.913 +         * Put the value of the named float field into the persistent field.
   1.914 +         *
   1.915 +         * @param  name the name of the serializable field
   1.916 +         * @param  val the value to assign to the field
   1.917 +         * @throws IllegalArgumentException if <code>name</code> does not
   1.918 +         * match the name of a serializable field for the class whose fields
   1.919 +         * are being written, or if the type of the named field is not
   1.920 +         * <code>float</code>
   1.921 +         */
   1.922 +        public abstract void put(String name, float val);
   1.923 +
   1.924 +        /**
   1.925 +         * Put the value of the named double field into the persistent field.
   1.926 +         *
   1.927 +         * @param  name the name of the serializable field
   1.928 +         * @param  val the value to assign to the field
   1.929 +         * @throws IllegalArgumentException if <code>name</code> does not
   1.930 +         * match the name of a serializable field for the class whose fields
   1.931 +         * are being written, or if the type of the named field is not
   1.932 +         * <code>double</code>
   1.933 +         */
   1.934 +        public abstract void put(String name, double val);
   1.935 +
   1.936 +        /**
   1.937 +         * Put the value of the named Object field into the persistent field.
   1.938 +         *
   1.939 +         * @param  name the name of the serializable field
   1.940 +         * @param  val the value to assign to the field
   1.941 +         *         (which may be <code>null</code>)
   1.942 +         * @throws IllegalArgumentException if <code>name</code> does not
   1.943 +         * match the name of a serializable field for the class whose fields
   1.944 +         * are being written, or if the type of the named field is not a
   1.945 +         * reference type
   1.946 +         */
   1.947 +        public abstract void put(String name, Object val);
   1.948 +
   1.949 +        /**
   1.950 +         * Write the data and fields to the specified ObjectOutput stream,
   1.951 +         * which must be the same stream that produced this
   1.952 +         * <code>PutField</code> object.
   1.953 +         *
   1.954 +         * @param  out the stream to write the data and fields to
   1.955 +         * @throws IOException if I/O errors occur while writing to the
   1.956 +         *         underlying stream
   1.957 +         * @throws IllegalArgumentException if the specified stream is not
   1.958 +         *         the same stream that produced this <code>PutField</code>
   1.959 +         *         object
   1.960 +         * @deprecated This method does not write the values contained by this
   1.961 +         *         <code>PutField</code> object in a proper format, and may
   1.962 +         *         result in corruption of the serialization stream.  The
   1.963 +         *         correct way to write <code>PutField</code> data is by
   1.964 +         *         calling the {@link java.io.ObjectOutputStream#writeFields()}
   1.965 +         *         method.
   1.966 +         */
   1.967 +        @Deprecated
   1.968 +        public abstract void write(ObjectOutput out) throws IOException;
   1.969 +    }
   1.970 +
   1.971 +
   1.972 +    /**
   1.973 +     * Returns protocol version in use.
   1.974 +     */
   1.975 +    int getProtocolVersion() {
   1.976 +        return protocol;
   1.977 +    }
   1.978 +
   1.979 +    /**
   1.980 +     * Writes string without allowing it to be replaced in stream.  Used by
   1.981 +     * ObjectStreamClass to write class descriptor type strings.
   1.982 +     */
   1.983 +    void writeTypeString(String str) throws IOException {
   1.984 +        int handle;
   1.985 +        if (str == null) {
   1.986 +            writeNull();
   1.987 +        } else if ((handle = handles.lookup(str)) != -1) {
   1.988 +            writeHandle(handle);
   1.989 +        } else {
   1.990 +            writeString(str, false);
   1.991 +        }
   1.992 +    }
   1.993 +
   1.994 +    /**
   1.995 +     * Verifies that this (possibly subclass) instance can be constructed
   1.996 +     * without violating security constraints: the subclass must not override
   1.997 +     * security-sensitive non-final methods, or else the
   1.998 +     * "enableSubclassImplementation" SerializablePermission is checked.
   1.999 +     */
  1.1000 +    private void verifySubclass() {
  1.1001 +        Class cl = getClass();
  1.1002 +        if (cl == ObjectOutputStream.class) {
  1.1003 +            return;
  1.1004 +        }
  1.1005 +        throw new SecurityException();
  1.1006 +    }
  1.1007 +
  1.1008 +    /**
  1.1009 +     * Clears internal data structures.
  1.1010 +     */
  1.1011 +    private void clear() {
  1.1012 +        subs.clear();
  1.1013 +        handles.clear();
  1.1014 +    }
  1.1015 +
  1.1016 +    /**
  1.1017 +     * Underlying writeObject/writeUnshared implementation.
  1.1018 +     */
  1.1019 +    private void writeObject0(Object obj, boolean unshared)
  1.1020 +        throws IOException
  1.1021 +    {
  1.1022 +        boolean oldMode = bout.setBlockDataMode(false);
  1.1023 +        depth++;
  1.1024 +        try {
  1.1025 +            // handle previously written and non-replaceable objects
  1.1026 +            int h;
  1.1027 +            if ((obj = subs.lookup(obj)) == null) {
  1.1028 +                writeNull();
  1.1029 +                return;
  1.1030 +            } else if (!unshared && (h = handles.lookup(obj)) != -1) {
  1.1031 +                writeHandle(h);
  1.1032 +                return;
  1.1033 +            } else if (obj instanceof Class) {
  1.1034 +                writeClass((Class) obj, unshared);
  1.1035 +                return;
  1.1036 +            } else if (obj instanceof ObjectStreamClass) {
  1.1037 +                writeClassDesc((ObjectStreamClass) obj, unshared);
  1.1038 +                return;
  1.1039 +            }
  1.1040 +
  1.1041 +            // check for replacement object
  1.1042 +            Object orig = obj;
  1.1043 +            Class cl = obj.getClass();
  1.1044 +            ObjectStreamClass desc;
  1.1045 +            for (;;) {
  1.1046 +                // REMIND: skip this check for strings/arrays?
  1.1047 +                Class repCl;
  1.1048 +                desc = ObjectStreamClass.lookup(cl, true);
  1.1049 +                if (!desc.hasWriteReplaceMethod() ||
  1.1050 +                    (obj = desc.invokeWriteReplace(obj)) == null ||
  1.1051 +                    (repCl = obj.getClass()) == cl)
  1.1052 +                {
  1.1053 +                    break;
  1.1054 +                }
  1.1055 +                cl = repCl;
  1.1056 +            }
  1.1057 +            if (enableReplace) {
  1.1058 +                Object rep = replaceObject(obj);
  1.1059 +                if (rep != obj && rep != null) {
  1.1060 +                    cl = rep.getClass();
  1.1061 +                    desc = ObjectStreamClass.lookup(cl, true);
  1.1062 +                }
  1.1063 +                obj = rep;
  1.1064 +            }
  1.1065 +
  1.1066 +            // if object replaced, run through original checks a second time
  1.1067 +            if (obj != orig) {
  1.1068 +                subs.assign(orig, obj);
  1.1069 +                if (obj == null) {
  1.1070 +                    writeNull();
  1.1071 +                    return;
  1.1072 +                } else if (!unshared && (h = handles.lookup(obj)) != -1) {
  1.1073 +                    writeHandle(h);
  1.1074 +                    return;
  1.1075 +                } else if (obj instanceof Class) {
  1.1076 +                    writeClass((Class) obj, unshared);
  1.1077 +                    return;
  1.1078 +                } else if (obj instanceof ObjectStreamClass) {
  1.1079 +                    writeClassDesc((ObjectStreamClass) obj, unshared);
  1.1080 +                    return;
  1.1081 +                }
  1.1082 +            }
  1.1083 +
  1.1084 +            // remaining cases
  1.1085 +            if (obj instanceof String) {
  1.1086 +                writeString((String) obj, unshared);
  1.1087 +            } else if (cl.isArray()) {
  1.1088 +                writeArray(obj, desc, unshared);
  1.1089 +            } else if (obj instanceof Enum) {
  1.1090 +                writeEnum((Enum) obj, desc, unshared);
  1.1091 +            } else if (obj instanceof Serializable) {
  1.1092 +                writeOrdinaryObject(obj, desc, unshared);
  1.1093 +            } else {
  1.1094 +                if (extendedDebugInfo) {
  1.1095 +                    throw new NotSerializableException(
  1.1096 +                        cl.getName() + "\n" + debugInfoStack.toString());
  1.1097 +                } else {
  1.1098 +                    throw new NotSerializableException(cl.getName());
  1.1099 +                }
  1.1100 +            }
  1.1101 +        } finally {
  1.1102 +            depth--;
  1.1103 +            bout.setBlockDataMode(oldMode);
  1.1104 +        }
  1.1105 +    }
  1.1106 +
  1.1107 +    /**
  1.1108 +     * Writes null code to stream.
  1.1109 +     */
  1.1110 +    private void writeNull() throws IOException {
  1.1111 +        bout.writeByte(TC_NULL);
  1.1112 +    }
  1.1113 +
  1.1114 +    /**
  1.1115 +     * Writes given object handle to stream.
  1.1116 +     */
  1.1117 +    private void writeHandle(int handle) throws IOException {
  1.1118 +        bout.writeByte(TC_REFERENCE);
  1.1119 +        bout.writeInt(baseWireHandle + handle);
  1.1120 +    }
  1.1121 +
  1.1122 +    /**
  1.1123 +     * Writes representation of given class to stream.
  1.1124 +     */
  1.1125 +    private void writeClass(Class cl, boolean unshared) throws IOException {
  1.1126 +        bout.writeByte(TC_CLASS);
  1.1127 +        writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
  1.1128 +        handles.assign(unshared ? null : cl);
  1.1129 +    }
  1.1130 +
  1.1131 +    /**
  1.1132 +     * Writes representation of given class descriptor to stream.
  1.1133 +     */
  1.1134 +    private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
  1.1135 +        throws IOException
  1.1136 +    {
  1.1137 +        int handle;
  1.1138 +        if (desc == null) {
  1.1139 +            writeNull();
  1.1140 +        } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
  1.1141 +            writeHandle(handle);
  1.1142 +        } else if (desc.isProxy()) {
  1.1143 +            writeProxyDesc(desc, unshared);
  1.1144 +        } else {
  1.1145 +            writeNonProxyDesc(desc, unshared);
  1.1146 +        }
  1.1147 +    }
  1.1148 +
  1.1149 +    /**
  1.1150 +     * Writes class descriptor representing a dynamic proxy class to stream.
  1.1151 +     */
  1.1152 +    private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
  1.1153 +        throws IOException
  1.1154 +    {
  1.1155 +        bout.writeByte(TC_PROXYCLASSDESC);
  1.1156 +        handles.assign(unshared ? null : desc);
  1.1157 +
  1.1158 +        Class cl = desc.forClass();
  1.1159 +        Class[] ifaces = cl.getInterfaces();
  1.1160 +        bout.writeInt(ifaces.length);
  1.1161 +        for (int i = 0; i < ifaces.length; i++) {
  1.1162 +            bout.writeUTF(ifaces[i].getName());
  1.1163 +        }
  1.1164 +
  1.1165 +        bout.setBlockDataMode(true);
  1.1166 +        annotateProxyClass(cl);
  1.1167 +        bout.setBlockDataMode(false);
  1.1168 +        bout.writeByte(TC_ENDBLOCKDATA);
  1.1169 +
  1.1170 +        writeClassDesc(desc.getSuperDesc(), false);
  1.1171 +    }
  1.1172 +
  1.1173 +    /**
  1.1174 +     * Writes class descriptor representing a standard (i.e., not a dynamic
  1.1175 +     * proxy) class to stream.
  1.1176 +     */
  1.1177 +    private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
  1.1178 +        throws IOException
  1.1179 +    {
  1.1180 +        bout.writeByte(TC_CLASSDESC);
  1.1181 +        handles.assign(unshared ? null : desc);
  1.1182 +
  1.1183 +        if (protocol == PROTOCOL_VERSION_1) {
  1.1184 +            // do not invoke class descriptor write hook with old protocol
  1.1185 +            desc.writeNonProxy(this);
  1.1186 +        } else {
  1.1187 +            writeClassDescriptor(desc);
  1.1188 +        }
  1.1189 +
  1.1190 +        Class cl = desc.forClass();
  1.1191 +        bout.setBlockDataMode(true);
  1.1192 +        annotateClass(cl);
  1.1193 +        bout.setBlockDataMode(false);
  1.1194 +        bout.writeByte(TC_ENDBLOCKDATA);
  1.1195 +
  1.1196 +        writeClassDesc(desc.getSuperDesc(), false);
  1.1197 +    }
  1.1198 +
  1.1199 +    /**
  1.1200 +     * Writes given string to stream, using standard or long UTF format
  1.1201 +     * depending on string length.
  1.1202 +     */
  1.1203 +    private void writeString(String str, boolean unshared) throws IOException {
  1.1204 +        handles.assign(unshared ? null : str);
  1.1205 +        long utflen = bout.getUTFLength(str);
  1.1206 +        if (utflen <= 0xFFFF) {
  1.1207 +            bout.writeByte(TC_STRING);
  1.1208 +            bout.writeUTF(str, utflen);
  1.1209 +        } else {
  1.1210 +            bout.writeByte(TC_LONGSTRING);
  1.1211 +            bout.writeLongUTF(str, utflen);
  1.1212 +        }
  1.1213 +    }
  1.1214 +
  1.1215 +    /**
  1.1216 +     * Writes given array object to stream.
  1.1217 +     */
  1.1218 +    private void writeArray(Object array,
  1.1219 +                            ObjectStreamClass desc,
  1.1220 +                            boolean unshared)
  1.1221 +        throws IOException
  1.1222 +    {
  1.1223 +        bout.writeByte(TC_ARRAY);
  1.1224 +        writeClassDesc(desc, false);
  1.1225 +        handles.assign(unshared ? null : array);
  1.1226 +
  1.1227 +        Class ccl = desc.forClass().getComponentType();
  1.1228 +        if (ccl.isPrimitive()) {
  1.1229 +            if (ccl == Integer.TYPE) {
  1.1230 +                int[] ia = (int[]) array;
  1.1231 +                bout.writeInt(ia.length);
  1.1232 +                bout.writeInts(ia, 0, ia.length);
  1.1233 +            } else if (ccl == Byte.TYPE) {
  1.1234 +                byte[] ba = (byte[]) array;
  1.1235 +                bout.writeInt(ba.length);
  1.1236 +                bout.write(ba, 0, ba.length, true);
  1.1237 +            } else if (ccl == Long.TYPE) {
  1.1238 +                long[] ja = (long[]) array;
  1.1239 +                bout.writeInt(ja.length);
  1.1240 +                bout.writeLongs(ja, 0, ja.length);
  1.1241 +            } else if (ccl == Float.TYPE) {
  1.1242 +                float[] fa = (float[]) array;
  1.1243 +                bout.writeInt(fa.length);
  1.1244 +                bout.writeFloats(fa, 0, fa.length);
  1.1245 +            } else if (ccl == Double.TYPE) {
  1.1246 +                double[] da = (double[]) array;
  1.1247 +                bout.writeInt(da.length);
  1.1248 +                bout.writeDoubles(da, 0, da.length);
  1.1249 +            } else if (ccl == Short.TYPE) {
  1.1250 +                short[] sa = (short[]) array;
  1.1251 +                bout.writeInt(sa.length);
  1.1252 +                bout.writeShorts(sa, 0, sa.length);
  1.1253 +            } else if (ccl == Character.TYPE) {
  1.1254 +                char[] ca = (char[]) array;
  1.1255 +                bout.writeInt(ca.length);
  1.1256 +                bout.writeChars(ca, 0, ca.length);
  1.1257 +            } else if (ccl == Boolean.TYPE) {
  1.1258 +                boolean[] za = (boolean[]) array;
  1.1259 +                bout.writeInt(za.length);
  1.1260 +                bout.writeBooleans(za, 0, za.length);
  1.1261 +            } else {
  1.1262 +                throw new InternalError();
  1.1263 +            }
  1.1264 +        } else {
  1.1265 +            Object[] objs = (Object[]) array;
  1.1266 +            int len = objs.length;
  1.1267 +            bout.writeInt(len);
  1.1268 +            if (extendedDebugInfo) {
  1.1269 +                debugInfoStack.push(
  1.1270 +                    "array (class \"" + array.getClass().getName() +
  1.1271 +                    "\", size: " + len  + ")");
  1.1272 +            }
  1.1273 +            try {
  1.1274 +                for (int i = 0; i < len; i++) {
  1.1275 +                    if (extendedDebugInfo) {
  1.1276 +                        debugInfoStack.push(
  1.1277 +                            "element of array (index: " + i + ")");
  1.1278 +                    }
  1.1279 +                    try {
  1.1280 +                        writeObject0(objs[i], false);
  1.1281 +                    } finally {
  1.1282 +                        if (extendedDebugInfo) {
  1.1283 +                            debugInfoStack.pop();
  1.1284 +                        }
  1.1285 +                    }
  1.1286 +                }
  1.1287 +            } finally {
  1.1288 +                if (extendedDebugInfo) {
  1.1289 +                    debugInfoStack.pop();
  1.1290 +                }
  1.1291 +            }
  1.1292 +        }
  1.1293 +    }
  1.1294 +
  1.1295 +    /**
  1.1296 +     * Writes given enum constant to stream.
  1.1297 +     */
  1.1298 +    private void writeEnum(Enum en,
  1.1299 +                           ObjectStreamClass desc,
  1.1300 +                           boolean unshared)
  1.1301 +        throws IOException
  1.1302 +    {
  1.1303 +        bout.writeByte(TC_ENUM);
  1.1304 +        ObjectStreamClass sdesc = desc.getSuperDesc();
  1.1305 +        writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
  1.1306 +        handles.assign(unshared ? null : en);
  1.1307 +        writeString(en.name(), false);
  1.1308 +    }
  1.1309 +
  1.1310 +    /**
  1.1311 +     * Writes representation of a "ordinary" (i.e., not a String, Class,
  1.1312 +     * ObjectStreamClass, array, or enum constant) serializable object to the
  1.1313 +     * stream.
  1.1314 +     */
  1.1315 +    private void writeOrdinaryObject(Object obj,
  1.1316 +                                     ObjectStreamClass desc,
  1.1317 +                                     boolean unshared)
  1.1318 +        throws IOException
  1.1319 +    {
  1.1320 +        if (extendedDebugInfo) {
  1.1321 +            debugInfoStack.push(
  1.1322 +                (depth == 1 ? "root " : "") + "object (class \"" +
  1.1323 +                obj.getClass().getName() + "\", " + obj.toString() + ")");
  1.1324 +        }
  1.1325 +        try {
  1.1326 +            desc.checkSerialize();
  1.1327 +
  1.1328 +            bout.writeByte(TC_OBJECT);
  1.1329 +            writeClassDesc(desc, false);
  1.1330 +            handles.assign(unshared ? null : obj);
  1.1331 +            if (desc.isExternalizable() && !desc.isProxy()) {
  1.1332 +                writeExternalData((Externalizable) obj);
  1.1333 +            } else {
  1.1334 +                writeSerialData(obj, desc);
  1.1335 +            }
  1.1336 +        } finally {
  1.1337 +            if (extendedDebugInfo) {
  1.1338 +                debugInfoStack.pop();
  1.1339 +            }
  1.1340 +        }
  1.1341 +    }
  1.1342 +
  1.1343 +    /**
  1.1344 +     * Writes externalizable data of given object by invoking its
  1.1345 +     * writeExternal() method.
  1.1346 +     */
  1.1347 +    private void writeExternalData(Externalizable obj) throws IOException {
  1.1348 +        PutFieldImpl oldPut = curPut;
  1.1349 +        curPut = null;
  1.1350 +
  1.1351 +        if (extendedDebugInfo) {
  1.1352 +            debugInfoStack.push("writeExternal data");
  1.1353 +        }
  1.1354 +        Object oldContext = curContext;
  1.1355 +        try {
  1.1356 +            curContext = null;
  1.1357 +            if (protocol == PROTOCOL_VERSION_1) {
  1.1358 +                obj.writeExternal(this);
  1.1359 +            } else {
  1.1360 +                bout.setBlockDataMode(true);
  1.1361 +                obj.writeExternal(this);
  1.1362 +                bout.setBlockDataMode(false);
  1.1363 +                bout.writeByte(TC_ENDBLOCKDATA);
  1.1364 +            }
  1.1365 +        } finally {
  1.1366 +            curContext = oldContext;
  1.1367 +            if (extendedDebugInfo) {
  1.1368 +                debugInfoStack.pop();
  1.1369 +            }
  1.1370 +        }
  1.1371 +
  1.1372 +        curPut = oldPut;
  1.1373 +    }
  1.1374 +
  1.1375 +    /**
  1.1376 +     * Writes instance data for each serializable class of given object, from
  1.1377 +     * superclass to subclass.
  1.1378 +     */
  1.1379 +    private void writeSerialData(Object obj, ObjectStreamClass desc)
  1.1380 +        throws IOException
  1.1381 +    {
  1.1382 +        ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
  1.1383 +        for (int i = 0; i < slots.length; i++) {
  1.1384 +            ObjectStreamClass slotDesc = slots[i].desc;
  1.1385 +            if (slotDesc.hasWriteObjectMethod()) {
  1.1386 +                PutFieldImpl oldPut = curPut;
  1.1387 +                curPut = null;
  1.1388 +                Object oldContext = curContext;
  1.1389 +
  1.1390 +                if (extendedDebugInfo) {
  1.1391 +                    debugInfoStack.push(
  1.1392 +                        "custom writeObject data (class \"" +
  1.1393 +                        slotDesc.getName() + "\")");
  1.1394 +                }
  1.1395 +                try {
  1.1396 +                    curContext = new Object(); //new SerialCallbackContext(obj, slotDesc);
  1.1397 +                    bout.setBlockDataMode(true);
  1.1398 +                    slotDesc.invokeWriteObject(obj, this);
  1.1399 +                    bout.setBlockDataMode(false);
  1.1400 +                    bout.writeByte(TC_ENDBLOCKDATA);
  1.1401 +                } finally {
  1.1402 +                    //curContext.setUsed();
  1.1403 +                    curContext = oldContext;
  1.1404 +                    if (extendedDebugInfo) {
  1.1405 +                        debugInfoStack.pop();
  1.1406 +                    }
  1.1407 +                }
  1.1408 +
  1.1409 +                curPut = oldPut;
  1.1410 +            } else {
  1.1411 +                defaultWriteFields(obj, slotDesc);
  1.1412 +            }
  1.1413 +        }
  1.1414 +    }
  1.1415 +
  1.1416 +    /**
  1.1417 +     * Fetches and writes values of serializable fields of given object to
  1.1418 +     * stream.  The given class descriptor specifies which field values to
  1.1419 +     * write, and in which order they should be written.
  1.1420 +     */
  1.1421 +    private void defaultWriteFields(Object obj, ObjectStreamClass desc)
  1.1422 +        throws IOException
  1.1423 +    {
  1.1424 +        // REMIND: perform conservative isInstance check here?
  1.1425 +        desc.checkDefaultSerialize();
  1.1426 +
  1.1427 +        int primDataSize = desc.getPrimDataSize();
  1.1428 +        if (primVals == null || primVals.length < primDataSize) {
  1.1429 +            primVals = new byte[primDataSize];
  1.1430 +        }
  1.1431 +        desc.getPrimFieldValues(obj, primVals);
  1.1432 +        bout.write(primVals, 0, primDataSize, false);
  1.1433 +
  1.1434 +        ObjectStreamField[] fields = desc.getFields(false);
  1.1435 +        Object[] objVals = new Object[desc.getNumObjFields()];
  1.1436 +        int numPrimFields = fields.length - objVals.length;
  1.1437 +        desc.getObjFieldValues(obj, objVals);
  1.1438 +        for (int i = 0; i < objVals.length; i++) {
  1.1439 +            if (extendedDebugInfo) {
  1.1440 +                debugInfoStack.push(
  1.1441 +                    "field (class \"" + desc.getName() + "\", name: \"" +
  1.1442 +                    fields[numPrimFields + i].getName() + "\", type: \"" +
  1.1443 +                    fields[numPrimFields + i].getType() + "\")");
  1.1444 +            }
  1.1445 +            try {
  1.1446 +                writeObject0(objVals[i],
  1.1447 +                             fields[numPrimFields + i].isUnshared());
  1.1448 +            } finally {
  1.1449 +                if (extendedDebugInfo) {
  1.1450 +                    debugInfoStack.pop();
  1.1451 +                }
  1.1452 +            }
  1.1453 +        }
  1.1454 +    }
  1.1455 +
  1.1456 +    /**
  1.1457 +     * Attempts to write to stream fatal IOException that has caused
  1.1458 +     * serialization to abort.
  1.1459 +     */
  1.1460 +    private void writeFatalException(IOException ex) throws IOException {
  1.1461 +        /*
  1.1462 +         * Note: the serialization specification states that if a second
  1.1463 +         * IOException occurs while attempting to serialize the original fatal
  1.1464 +         * exception to the stream, then a StreamCorruptedException should be
  1.1465 +         * thrown (section 2.1).  However, due to a bug in previous
  1.1466 +         * implementations of serialization, StreamCorruptedExceptions were
  1.1467 +         * rarely (if ever) actually thrown--the "root" exceptions from
  1.1468 +         * underlying streams were thrown instead.  This historical behavior is
  1.1469 +         * followed here for consistency.
  1.1470 +         */
  1.1471 +        clear();
  1.1472 +        boolean oldMode = bout.setBlockDataMode(false);
  1.1473 +        try {
  1.1474 +            bout.writeByte(TC_EXCEPTION);
  1.1475 +            writeObject0(ex, false);
  1.1476 +            clear();
  1.1477 +        } finally {
  1.1478 +            bout.setBlockDataMode(oldMode);
  1.1479 +        }
  1.1480 +    }
  1.1481 +
  1.1482 +    /**
  1.1483 +     * Converts specified span of float values into byte values.
  1.1484 +     */
  1.1485 +    // REMIND: remove once hotspot inlines Float.floatToIntBits
  1.1486 +    private static native void floatsToBytes(float[] src, int srcpos,
  1.1487 +                                             byte[] dst, int dstpos,
  1.1488 +                                             int nfloats);
  1.1489 +
  1.1490 +    /**
  1.1491 +     * Converts specified span of double values into byte values.
  1.1492 +     */
  1.1493 +    // REMIND: remove once hotspot inlines Double.doubleToLongBits
  1.1494 +    private static native void doublesToBytes(double[] src, int srcpos,
  1.1495 +                                              byte[] dst, int dstpos,
  1.1496 +                                              int ndoubles);
  1.1497 +
  1.1498 +    /**
  1.1499 +     * Default PutField implementation.
  1.1500 +     */
  1.1501 +    private class PutFieldImpl extends PutField {
  1.1502 +
  1.1503 +        /** class descriptor describing serializable fields */
  1.1504 +        private final ObjectStreamClass desc;
  1.1505 +        /** primitive field values */
  1.1506 +        private final byte[] primVals;
  1.1507 +        /** object field values */
  1.1508 +        private final Object[] objVals;
  1.1509 +
  1.1510 +        /**
  1.1511 +         * Creates PutFieldImpl object for writing fields defined in given
  1.1512 +         * class descriptor.
  1.1513 +         */
  1.1514 +        PutFieldImpl(ObjectStreamClass desc) {
  1.1515 +            this.desc = desc;
  1.1516 +            primVals = new byte[desc.getPrimDataSize()];
  1.1517 +            objVals = new Object[desc.getNumObjFields()];
  1.1518 +        }
  1.1519 +
  1.1520 +        public void put(String name, boolean val) {
  1.1521 +            Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val);
  1.1522 +        }
  1.1523 +
  1.1524 +        public void put(String name, byte val) {
  1.1525 +            primVals[getFieldOffset(name, Byte.TYPE)] = val;
  1.1526 +        }
  1.1527 +
  1.1528 +        public void put(String name, char val) {
  1.1529 +            Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val);
  1.1530 +        }
  1.1531 +
  1.1532 +        public void put(String name, short val) {
  1.1533 +            Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val);
  1.1534 +        }
  1.1535 +
  1.1536 +        public void put(String name, int val) {
  1.1537 +            Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val);
  1.1538 +        }
  1.1539 +
  1.1540 +        public void put(String name, float val) {
  1.1541 +            Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val);
  1.1542 +        }
  1.1543 +
  1.1544 +        public void put(String name, long val) {
  1.1545 +            Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
  1.1546 +        }
  1.1547 +
  1.1548 +        public void put(String name, double val) {
  1.1549 +            Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val);
  1.1550 +        }
  1.1551 +
  1.1552 +        public void put(String name, Object val) {
  1.1553 +            objVals[getFieldOffset(name, Object.class)] = val;
  1.1554 +        }
  1.1555 +
  1.1556 +        // deprecated in ObjectOutputStream.PutField
  1.1557 +        public void write(ObjectOutput out) throws IOException {
  1.1558 +            /*
  1.1559 +             * Applications should *not* use this method to write PutField
  1.1560 +             * data, as it will lead to stream corruption if the PutField
  1.1561 +             * object writes any primitive data (since block data mode is not
  1.1562 +             * unset/set properly, as is done in OOS.writeFields()).  This
  1.1563 +             * broken implementation is being retained solely for behavioral
  1.1564 +             * compatibility, in order to support applications which use
  1.1565 +             * OOS.PutField.write() for writing only non-primitive data.
  1.1566 +             *
  1.1567 +             * Serialization of unshared objects is not implemented here since
  1.1568 +             * it is not necessary for backwards compatibility; also, unshared
  1.1569 +             * semantics may not be supported by the given ObjectOutput
  1.1570 +             * instance.  Applications which write unshared objects using the
  1.1571 +             * PutField API must use OOS.writeFields().
  1.1572 +             */
  1.1573 +            if (ObjectOutputStream.this != out) {
  1.1574 +                throw new IllegalArgumentException("wrong stream");
  1.1575 +            }
  1.1576 +            out.write(primVals, 0, primVals.length);
  1.1577 +
  1.1578 +            ObjectStreamField[] fields = desc.getFields(false);
  1.1579 +            int numPrimFields = fields.length - objVals.length;
  1.1580 +            // REMIND: warn if numPrimFields > 0?
  1.1581 +            for (int i = 0; i < objVals.length; i++) {
  1.1582 +                if (fields[numPrimFields + i].isUnshared()) {
  1.1583 +                    throw new IOException("cannot write unshared object");
  1.1584 +                }
  1.1585 +                out.writeObject(objVals[i]);
  1.1586 +            }
  1.1587 +        }
  1.1588 +
  1.1589 +        /**
  1.1590 +         * Writes buffered primitive data and object fields to stream.
  1.1591 +         */
  1.1592 +        void writeFields() throws IOException {
  1.1593 +            bout.write(primVals, 0, primVals.length, false);
  1.1594 +
  1.1595 +            ObjectStreamField[] fields = desc.getFields(false);
  1.1596 +            int numPrimFields = fields.length - objVals.length;
  1.1597 +            for (int i = 0; i < objVals.length; i++) {
  1.1598 +                if (extendedDebugInfo) {
  1.1599 +                    debugInfoStack.push(
  1.1600 +                        "field (class \"" + desc.getName() + "\", name: \"" +
  1.1601 +                        fields[numPrimFields + i].getName() + "\", type: \"" +
  1.1602 +                        fields[numPrimFields + i].getType() + "\")");
  1.1603 +                }
  1.1604 +                try {
  1.1605 +                    writeObject0(objVals[i],
  1.1606 +                                 fields[numPrimFields + i].isUnshared());
  1.1607 +                } finally {
  1.1608 +                    if (extendedDebugInfo) {
  1.1609 +                        debugInfoStack.pop();
  1.1610 +                    }
  1.1611 +                }
  1.1612 +            }
  1.1613 +        }
  1.1614 +
  1.1615 +        /**
  1.1616 +         * Returns offset of field with given name and type.  A specified type
  1.1617 +         * of null matches all types, Object.class matches all non-primitive
  1.1618 +         * types, and any other non-null type matches assignable types only.
  1.1619 +         * Throws IllegalArgumentException if no matching field found.
  1.1620 +         */
  1.1621 +        private int getFieldOffset(String name, Class type) {
  1.1622 +            ObjectStreamField field = desc.getField(name, type);
  1.1623 +            if (field == null) {
  1.1624 +                throw new IllegalArgumentException("no such field " + name +
  1.1625 +                                                   " with type " + type);
  1.1626 +            }
  1.1627 +            return field.getOffset();
  1.1628 +        }
  1.1629 +    }
  1.1630 +
  1.1631 +    /**
  1.1632 +     * Buffered output stream with two modes: in default mode, outputs data in
  1.1633 +     * same format as DataOutputStream; in "block data" mode, outputs data
  1.1634 +     * bracketed by block data markers (see object serialization specification
  1.1635 +     * for details).
  1.1636 +     */
  1.1637 +    private static class BlockDataOutputStream
  1.1638 +        extends OutputStream implements DataOutput
  1.1639 +    {
  1.1640 +        /** maximum data block length */
  1.1641 +        private static final int MAX_BLOCK_SIZE = 1024;
  1.1642 +        /** maximum data block header length */
  1.1643 +        private static final int MAX_HEADER_SIZE = 5;
  1.1644 +        /** (tunable) length of char buffer (for writing strings) */
  1.1645 +        private static final int CHAR_BUF_SIZE = 256;
  1.1646 +
  1.1647 +        /** buffer for writing general/block data */
  1.1648 +        private final byte[] buf = new byte[MAX_BLOCK_SIZE];
  1.1649 +        /** buffer for writing block data headers */
  1.1650 +        private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
  1.1651 +        /** char buffer for fast string writes */
  1.1652 +        private final char[] cbuf = new char[CHAR_BUF_SIZE];
  1.1653 +
  1.1654 +        /** block data mode */
  1.1655 +        private boolean blkmode = false;
  1.1656 +        /** current offset into buf */
  1.1657 +        private int pos = 0;
  1.1658 +
  1.1659 +        /** underlying output stream */
  1.1660 +        private final OutputStream out;
  1.1661 +        /** loopback stream (for data writes that span data blocks) */
  1.1662 +        private final DataOutputStream dout;
  1.1663 +
  1.1664 +        /**
  1.1665 +         * Creates new BlockDataOutputStream on top of given underlying stream.
  1.1666 +         * Block data mode is turned off by default.
  1.1667 +         */
  1.1668 +        BlockDataOutputStream(OutputStream out) {
  1.1669 +            this.out = out;
  1.1670 +            dout = new DataOutputStream(this);
  1.1671 +        }
  1.1672 +
  1.1673 +        /**
  1.1674 +         * Sets block data mode to the given mode (true == on, false == off)
  1.1675 +         * and returns the previous mode value.  If the new mode is the same as
  1.1676 +         * the old mode, no action is taken.  If the new mode differs from the
  1.1677 +         * old mode, any buffered data is flushed before switching to the new
  1.1678 +         * mode.
  1.1679 +         */
  1.1680 +        boolean setBlockDataMode(boolean mode) throws IOException {
  1.1681 +            if (blkmode == mode) {
  1.1682 +                return blkmode;
  1.1683 +            }
  1.1684 +            drain();
  1.1685 +            blkmode = mode;
  1.1686 +            return !blkmode;
  1.1687 +        }
  1.1688 +
  1.1689 +        /**
  1.1690 +         * Returns true if the stream is currently in block data mode, false
  1.1691 +         * otherwise.
  1.1692 +         */
  1.1693 +        boolean getBlockDataMode() {
  1.1694 +            return blkmode;
  1.1695 +        }
  1.1696 +
  1.1697 +        /* ----------------- generic output stream methods ----------------- */
  1.1698 +        /*
  1.1699 +         * The following methods are equivalent to their counterparts in
  1.1700 +         * OutputStream, except that they partition written data into data
  1.1701 +         * blocks when in block data mode.
  1.1702 +         */
  1.1703 +
  1.1704 +        public void write(int b) throws IOException {
  1.1705 +            if (pos >= MAX_BLOCK_SIZE) {
  1.1706 +                drain();
  1.1707 +            }
  1.1708 +            buf[pos++] = (byte) b;
  1.1709 +        }
  1.1710 +
  1.1711 +        public void write(byte[] b) throws IOException {
  1.1712 +            write(b, 0, b.length, false);
  1.1713 +        }
  1.1714 +
  1.1715 +        public void write(byte[] b, int off, int len) throws IOException {
  1.1716 +            write(b, off, len, false);
  1.1717 +        }
  1.1718 +
  1.1719 +        public void flush() throws IOException {
  1.1720 +            drain();
  1.1721 +            out.flush();
  1.1722 +        }
  1.1723 +
  1.1724 +        public void close() throws IOException {
  1.1725 +            flush();
  1.1726 +            out.close();
  1.1727 +        }
  1.1728 +
  1.1729 +        /**
  1.1730 +         * Writes specified span of byte values from given array.  If copy is
  1.1731 +         * true, copies the values to an intermediate buffer before writing
  1.1732 +         * them to underlying stream (to avoid exposing a reference to the
  1.1733 +         * original byte array).
  1.1734 +         */
  1.1735 +        void write(byte[] b, int off, int len, boolean copy)
  1.1736 +            throws IOException
  1.1737 +        {
  1.1738 +            if (!(copy || blkmode)) {           // write directly
  1.1739 +                drain();
  1.1740 +                out.write(b, off, len);
  1.1741 +                return;
  1.1742 +            }
  1.1743 +
  1.1744 +            while (len > 0) {
  1.1745 +                if (pos >= MAX_BLOCK_SIZE) {
  1.1746 +                    drain();
  1.1747 +                }
  1.1748 +                if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) {
  1.1749 +                    // avoid unnecessary copy
  1.1750 +                    writeBlockHeader(MAX_BLOCK_SIZE);
  1.1751 +                    out.write(b, off, MAX_BLOCK_SIZE);
  1.1752 +                    off += MAX_BLOCK_SIZE;
  1.1753 +                    len -= MAX_BLOCK_SIZE;
  1.1754 +                } else {
  1.1755 +                    int wlen = Math.min(len, MAX_BLOCK_SIZE - pos);
  1.1756 +                    System.arraycopy(b, off, buf, pos, wlen);
  1.1757 +                    pos += wlen;
  1.1758 +                    off += wlen;
  1.1759 +                    len -= wlen;
  1.1760 +                }
  1.1761 +            }
  1.1762 +        }
  1.1763 +
  1.1764 +        /**
  1.1765 +         * Writes all buffered data from this stream to the underlying stream,
  1.1766 +         * but does not flush underlying stream.
  1.1767 +         */
  1.1768 +        void drain() throws IOException {
  1.1769 +            if (pos == 0) {
  1.1770 +                return;
  1.1771 +            }
  1.1772 +            if (blkmode) {
  1.1773 +                writeBlockHeader(pos);
  1.1774 +            }
  1.1775 +            out.write(buf, 0, pos);
  1.1776 +            pos = 0;
  1.1777 +        }
  1.1778 +
  1.1779 +        /**
  1.1780 +         * Writes block data header.  Data blocks shorter than 256 bytes are
  1.1781 +         * prefixed with a 2-byte header; all others start with a 5-byte
  1.1782 +         * header.
  1.1783 +         */
  1.1784 +        private void writeBlockHeader(int len) throws IOException {
  1.1785 +            if (len <= 0xFF) {
  1.1786 +                hbuf[0] = TC_BLOCKDATA;
  1.1787 +                hbuf[1] = (byte) len;
  1.1788 +                out.write(hbuf, 0, 2);
  1.1789 +            } else {
  1.1790 +                hbuf[0] = TC_BLOCKDATALONG;
  1.1791 +                Bits.putInt(hbuf, 1, len);
  1.1792 +                out.write(hbuf, 0, 5);
  1.1793 +            }
  1.1794 +        }
  1.1795 +
  1.1796 +
  1.1797 +        /* ----------------- primitive data output methods ----------------- */
  1.1798 +        /*
  1.1799 +         * The following methods are equivalent to their counterparts in
  1.1800 +         * DataOutputStream, except that they partition written data into data
  1.1801 +         * blocks when in block data mode.
  1.1802 +         */
  1.1803 +
  1.1804 +        public void writeBoolean(boolean v) throws IOException {
  1.1805 +            if (pos >= MAX_BLOCK_SIZE) {
  1.1806 +                drain();
  1.1807 +            }
  1.1808 +            Bits.putBoolean(buf, pos++, v);
  1.1809 +        }
  1.1810 +
  1.1811 +        public void writeByte(int v) throws IOException {
  1.1812 +            if (pos >= MAX_BLOCK_SIZE) {
  1.1813 +                drain();
  1.1814 +            }
  1.1815 +            buf[pos++] = (byte) v;
  1.1816 +        }
  1.1817 +
  1.1818 +        public void writeChar(int v) throws IOException {
  1.1819 +            if (pos + 2 <= MAX_BLOCK_SIZE) {
  1.1820 +                Bits.putChar(buf, pos, (char) v);
  1.1821 +                pos += 2;
  1.1822 +            } else {
  1.1823 +                dout.writeChar(v);
  1.1824 +            }
  1.1825 +        }
  1.1826 +
  1.1827 +        public void writeShort(int v) throws IOException {
  1.1828 +            if (pos + 2 <= MAX_BLOCK_SIZE) {
  1.1829 +                Bits.putShort(buf, pos, (short) v);
  1.1830 +                pos += 2;
  1.1831 +            } else {
  1.1832 +                dout.writeShort(v);
  1.1833 +            }
  1.1834 +        }
  1.1835 +
  1.1836 +        public void writeInt(int v) throws IOException {
  1.1837 +            if (pos + 4 <= MAX_BLOCK_SIZE) {
  1.1838 +                Bits.putInt(buf, pos, v);
  1.1839 +                pos += 4;
  1.1840 +            } else {
  1.1841 +                dout.writeInt(v);
  1.1842 +            }
  1.1843 +        }
  1.1844 +
  1.1845 +        public void writeFloat(float v) throws IOException {
  1.1846 +            if (pos + 4 <= MAX_BLOCK_SIZE) {
  1.1847 +                Bits.putFloat(buf, pos, v);
  1.1848 +                pos += 4;
  1.1849 +            } else {
  1.1850 +                dout.writeFloat(v);
  1.1851 +            }
  1.1852 +        }
  1.1853 +
  1.1854 +        public void writeLong(long v) throws IOException {
  1.1855 +            if (pos + 8 <= MAX_BLOCK_SIZE) {
  1.1856 +                Bits.putLong(buf, pos, v);
  1.1857 +                pos += 8;
  1.1858 +            } else {
  1.1859 +                dout.writeLong(v);
  1.1860 +            }
  1.1861 +        }
  1.1862 +
  1.1863 +        public void writeDouble(double v) throws IOException {
  1.1864 +            if (pos + 8 <= MAX_BLOCK_SIZE) {
  1.1865 +                Bits.putDouble(buf, pos, v);
  1.1866 +                pos += 8;
  1.1867 +            } else {
  1.1868 +                dout.writeDouble(v);
  1.1869 +            }
  1.1870 +        }
  1.1871 +
  1.1872 +        public void writeBytes(String s) throws IOException {
  1.1873 +            int endoff = s.length();
  1.1874 +            int cpos = 0;
  1.1875 +            int csize = 0;
  1.1876 +            for (int off = 0; off < endoff; ) {
  1.1877 +                if (cpos >= csize) {
  1.1878 +                    cpos = 0;
  1.1879 +                    csize = Math.min(endoff - off, CHAR_BUF_SIZE);
  1.1880 +                    s.getChars(off, off + csize, cbuf, 0);
  1.1881 +                }
  1.1882 +                if (pos >= MAX_BLOCK_SIZE) {
  1.1883 +                    drain();
  1.1884 +                }
  1.1885 +                int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
  1.1886 +                int stop = pos + n;
  1.1887 +                while (pos < stop) {
  1.1888 +                    buf[pos++] = (byte) cbuf[cpos++];
  1.1889 +                }
  1.1890 +                off += n;
  1.1891 +            }
  1.1892 +        }
  1.1893 +
  1.1894 +        public void writeChars(String s) throws IOException {
  1.1895 +            int endoff = s.length();
  1.1896 +            for (int off = 0; off < endoff; ) {
  1.1897 +                int csize = Math.min(endoff - off, CHAR_BUF_SIZE);
  1.1898 +                s.getChars(off, off + csize, cbuf, 0);
  1.1899 +                writeChars(cbuf, 0, csize);
  1.1900 +                off += csize;
  1.1901 +            }
  1.1902 +        }
  1.1903 +
  1.1904 +        public void writeUTF(String s) throws IOException {
  1.1905 +            writeUTF(s, getUTFLength(s));
  1.1906 +        }
  1.1907 +
  1.1908 +
  1.1909 +        /* -------------- primitive data array output methods -------------- */
  1.1910 +        /*
  1.1911 +         * The following methods write out spans of primitive data values.
  1.1912 +         * Though equivalent to calling the corresponding primitive write
  1.1913 +         * methods repeatedly, these methods are optimized for writing groups
  1.1914 +         * of primitive data values more efficiently.
  1.1915 +         */
  1.1916 +
  1.1917 +        void writeBooleans(boolean[] v, int off, int len) throws IOException {
  1.1918 +            int endoff = off + len;
  1.1919 +            while (off < endoff) {
  1.1920 +                if (pos >= MAX_BLOCK_SIZE) {
  1.1921 +                    drain();
  1.1922 +                }
  1.1923 +                int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos));
  1.1924 +                while (off < stop) {
  1.1925 +                    Bits.putBoolean(buf, pos++, v[off++]);
  1.1926 +                }
  1.1927 +            }
  1.1928 +        }
  1.1929 +
  1.1930 +        void writeChars(char[] v, int off, int len) throws IOException {
  1.1931 +            int limit = MAX_BLOCK_SIZE - 2;
  1.1932 +            int endoff = off + len;
  1.1933 +            while (off < endoff) {
  1.1934 +                if (pos <= limit) {
  1.1935 +                    int avail = (MAX_BLOCK_SIZE - pos) >> 1;
  1.1936 +                    int stop = Math.min(endoff, off + avail);
  1.1937 +                    while (off < stop) {
  1.1938 +                        Bits.putChar(buf, pos, v[off++]);
  1.1939 +                        pos += 2;
  1.1940 +                    }
  1.1941 +                } else {
  1.1942 +                    dout.writeChar(v[off++]);
  1.1943 +                }
  1.1944 +            }
  1.1945 +        }
  1.1946 +
  1.1947 +        void writeShorts(short[] v, int off, int len) throws IOException {
  1.1948 +            int limit = MAX_BLOCK_SIZE - 2;
  1.1949 +            int endoff = off + len;
  1.1950 +            while (off < endoff) {
  1.1951 +                if (pos <= limit) {
  1.1952 +                    int avail = (MAX_BLOCK_SIZE - pos) >> 1;
  1.1953 +                    int stop = Math.min(endoff, off + avail);
  1.1954 +                    while (off < stop) {
  1.1955 +                        Bits.putShort(buf, pos, v[off++]);
  1.1956 +                        pos += 2;
  1.1957 +                    }
  1.1958 +                } else {
  1.1959 +                    dout.writeShort(v[off++]);
  1.1960 +                }
  1.1961 +            }
  1.1962 +        }
  1.1963 +
  1.1964 +        void writeInts(int[] v, int off, int len) throws IOException {
  1.1965 +            int limit = MAX_BLOCK_SIZE - 4;
  1.1966 +            int endoff = off + len;
  1.1967 +            while (off < endoff) {
  1.1968 +                if (pos <= limit) {
  1.1969 +                    int avail = (MAX_BLOCK_SIZE - pos) >> 2;
  1.1970 +                    int stop = Math.min(endoff, off + avail);
  1.1971 +                    while (off < stop) {
  1.1972 +                        Bits.putInt(buf, pos, v[off++]);
  1.1973 +                        pos += 4;
  1.1974 +                    }
  1.1975 +                } else {
  1.1976 +                    dout.writeInt(v[off++]);
  1.1977 +                }
  1.1978 +            }
  1.1979 +        }
  1.1980 +
  1.1981 +        void writeFloats(float[] v, int off, int len) throws IOException {
  1.1982 +            int limit = MAX_BLOCK_SIZE - 4;
  1.1983 +            int endoff = off + len;
  1.1984 +            while (off < endoff) {
  1.1985 +                if (pos <= limit) {
  1.1986 +                    int avail = (MAX_BLOCK_SIZE - pos) >> 2;
  1.1987 +                    int chunklen = Math.min(endoff - off, avail);
  1.1988 +                    floatsToBytes(v, off, buf, pos, chunklen);
  1.1989 +                    off += chunklen;
  1.1990 +                    pos += chunklen << 2;
  1.1991 +                } else {
  1.1992 +                    dout.writeFloat(v[off++]);
  1.1993 +                }
  1.1994 +            }
  1.1995 +        }
  1.1996 +
  1.1997 +        void writeLongs(long[] v, int off, int len) throws IOException {
  1.1998 +            int limit = MAX_BLOCK_SIZE - 8;
  1.1999 +            int endoff = off + len;
  1.2000 +            while (off < endoff) {
  1.2001 +                if (pos <= limit) {
  1.2002 +                    int avail = (MAX_BLOCK_SIZE - pos) >> 3;
  1.2003 +                    int stop = Math.min(endoff, off + avail);
  1.2004 +                    while (off < stop) {
  1.2005 +                        Bits.putLong(buf, pos, v[off++]);
  1.2006 +                        pos += 8;
  1.2007 +                    }
  1.2008 +                } else {
  1.2009 +                    dout.writeLong(v[off++]);
  1.2010 +                }
  1.2011 +            }
  1.2012 +        }
  1.2013 +
  1.2014 +        void writeDoubles(double[] v, int off, int len) throws IOException {
  1.2015 +            int limit = MAX_BLOCK_SIZE - 8;
  1.2016 +            int endoff = off + len;
  1.2017 +            while (off < endoff) {
  1.2018 +                if (pos <= limit) {
  1.2019 +                    int avail = (MAX_BLOCK_SIZE - pos) >> 3;
  1.2020 +                    int chunklen = Math.min(endoff - off, avail);
  1.2021 +                    doublesToBytes(v, off, buf, pos, chunklen);
  1.2022 +                    off += chunklen;
  1.2023 +                    pos += chunklen << 3;
  1.2024 +                } else {
  1.2025 +                    dout.writeDouble(v[off++]);
  1.2026 +                }
  1.2027 +            }
  1.2028 +        }
  1.2029 +
  1.2030 +        /**
  1.2031 +         * Returns the length in bytes of the UTF encoding of the given string.
  1.2032 +         */
  1.2033 +        long getUTFLength(String s) {
  1.2034 +            int len = s.length();
  1.2035 +            long utflen = 0;
  1.2036 +            for (int off = 0; off < len; ) {
  1.2037 +                int csize = Math.min(len - off, CHAR_BUF_SIZE);
  1.2038 +                s.getChars(off, off + csize, cbuf, 0);
  1.2039 +                for (int cpos = 0; cpos < csize; cpos++) {
  1.2040 +                    char c = cbuf[cpos];
  1.2041 +                    if (c >= 0x0001 && c <= 0x007F) {
  1.2042 +                        utflen++;
  1.2043 +                    } else if (c > 0x07FF) {
  1.2044 +                        utflen += 3;
  1.2045 +                    } else {
  1.2046 +                        utflen += 2;
  1.2047 +                    }
  1.2048 +                }
  1.2049 +                off += csize;
  1.2050 +            }
  1.2051 +            return utflen;
  1.2052 +        }
  1.2053 +
  1.2054 +        /**
  1.2055 +         * Writes the given string in UTF format.  This method is used in
  1.2056 +         * situations where the UTF encoding length of the string is already
  1.2057 +         * known; specifying it explicitly avoids a prescan of the string to
  1.2058 +         * determine its UTF length.
  1.2059 +         */
  1.2060 +        void writeUTF(String s, long utflen) throws IOException {
  1.2061 +            if (utflen > 0xFFFFL) {
  1.2062 +                throw new UTFDataFormatException();
  1.2063 +            }
  1.2064 +            writeShort((int) utflen);
  1.2065 +            if (utflen == (long) s.length()) {
  1.2066 +                writeBytes(s);
  1.2067 +            } else {
  1.2068 +                writeUTFBody(s);
  1.2069 +            }
  1.2070 +        }
  1.2071 +
  1.2072 +        /**
  1.2073 +         * Writes given string in "long" UTF format.  "Long" UTF format is
  1.2074 +         * identical to standard UTF, except that it uses an 8 byte header
  1.2075 +         * (instead of the standard 2 bytes) to convey the UTF encoding length.
  1.2076 +         */
  1.2077 +        void writeLongUTF(String s) throws IOException {
  1.2078 +            writeLongUTF(s, getUTFLength(s));
  1.2079 +        }
  1.2080 +
  1.2081 +        /**
  1.2082 +         * Writes given string in "long" UTF format, where the UTF encoding
  1.2083 +         * length of the string is already known.
  1.2084 +         */
  1.2085 +        void writeLongUTF(String s, long utflen) throws IOException {
  1.2086 +            writeLong(utflen);
  1.2087 +            if (utflen == (long) s.length()) {
  1.2088 +                writeBytes(s);
  1.2089 +            } else {
  1.2090 +                writeUTFBody(s);
  1.2091 +            }
  1.2092 +        }
  1.2093 +
  1.2094 +        /**
  1.2095 +         * Writes the "body" (i.e., the UTF representation minus the 2-byte or
  1.2096 +         * 8-byte length header) of the UTF encoding for the given string.
  1.2097 +         */
  1.2098 +        private void writeUTFBody(String s) throws IOException {
  1.2099 +            int limit = MAX_BLOCK_SIZE - 3;
  1.2100 +            int len = s.length();
  1.2101 +            for (int off = 0; off < len; ) {
  1.2102 +                int csize = Math.min(len - off, CHAR_BUF_SIZE);
  1.2103 +                s.getChars(off, off + csize, cbuf, 0);
  1.2104 +                for (int cpos = 0; cpos < csize; cpos++) {
  1.2105 +                    char c = cbuf[cpos];
  1.2106 +                    if (pos <= limit) {
  1.2107 +                        if (c <= 0x007F && c != 0) {
  1.2108 +                            buf[pos++] = (byte) c;
  1.2109 +                        } else if (c > 0x07FF) {
  1.2110 +                            buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));
  1.2111 +                            buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
  1.2112 +                            buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));
  1.2113 +                            pos += 3;
  1.2114 +                        } else {
  1.2115 +                            buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));
  1.2116 +                            buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));
  1.2117 +                            pos += 2;
  1.2118 +                        }
  1.2119 +                    } else {    // write one byte at a time to normalize block
  1.2120 +                        if (c <= 0x007F && c != 0) {
  1.2121 +                            write(c);
  1.2122 +                        } else if (c > 0x07FF) {
  1.2123 +                            write(0xE0 | ((c >> 12) & 0x0F));
  1.2124 +                            write(0x80 | ((c >> 6) & 0x3F));
  1.2125 +                            write(0x80 | ((c >> 0) & 0x3F));
  1.2126 +                        } else {
  1.2127 +                            write(0xC0 | ((c >> 6) & 0x1F));
  1.2128 +                            write(0x80 | ((c >> 0) & 0x3F));
  1.2129 +                        }
  1.2130 +                    }
  1.2131 +                }
  1.2132 +                off += csize;
  1.2133 +            }
  1.2134 +        }
  1.2135 +    }
  1.2136 +
  1.2137 +    /**
  1.2138 +     * Lightweight identity hash table which maps objects to integer handles,
  1.2139 +     * assigned in ascending order.
  1.2140 +     */
  1.2141 +    private static class HandleTable {
  1.2142 +
  1.2143 +        /* number of mappings in table/next available handle */
  1.2144 +        private int size;
  1.2145 +        /* size threshold determining when to expand hash spine */
  1.2146 +        private int threshold;
  1.2147 +        /* factor for computing size threshold */
  1.2148 +        private final float loadFactor;
  1.2149 +        /* maps hash value -> candidate handle value */
  1.2150 +        private int[] spine;
  1.2151 +        /* maps handle value -> next candidate handle value */
  1.2152 +        private int[] next;
  1.2153 +        /* maps handle value -> associated object */
  1.2154 +        private Object[] objs;
  1.2155 +
  1.2156 +        /**
  1.2157 +         * Creates new HandleTable with given capacity and load factor.
  1.2158 +         */
  1.2159 +        HandleTable(int initialCapacity, float loadFactor) {
  1.2160 +            this.loadFactor = loadFactor;
  1.2161 +            spine = new int[initialCapacity];
  1.2162 +            next = new int[initialCapacity];
  1.2163 +            objs = new Object[initialCapacity];
  1.2164 +            threshold = (int) (initialCapacity * loadFactor);
  1.2165 +            clear();
  1.2166 +        }
  1.2167 +
  1.2168 +        /**
  1.2169 +         * Assigns next available handle to given object, and returns handle
  1.2170 +         * value.  Handles are assigned in ascending order starting at 0.
  1.2171 +         */
  1.2172 +        int assign(Object obj) {
  1.2173 +            if (size >= next.length) {
  1.2174 +                growEntries();
  1.2175 +            }
  1.2176 +            if (size >= threshold) {
  1.2177 +                growSpine();
  1.2178 +            }
  1.2179 +            insert(obj, size);
  1.2180 +            return size++;
  1.2181 +        }
  1.2182 +
  1.2183 +        /**
  1.2184 +         * Looks up and returns handle associated with given object, or -1 if
  1.2185 +         * no mapping found.
  1.2186 +         */
  1.2187 +        int lookup(Object obj) {
  1.2188 +            if (size == 0) {
  1.2189 +                return -1;
  1.2190 +            }
  1.2191 +            int index = hash(obj) % spine.length;
  1.2192 +            for (int i = spine[index]; i >= 0; i = next[i]) {
  1.2193 +                if (objs[i] == obj) {
  1.2194 +                    return i;
  1.2195 +                }
  1.2196 +            }
  1.2197 +            return -1;
  1.2198 +        }
  1.2199 +
  1.2200 +        /**
  1.2201 +         * Resets table to its initial (empty) state.
  1.2202 +         */
  1.2203 +        void clear() {
  1.2204 +            Arrays.fill(spine, -1);
  1.2205 +            Arrays.fill(objs, 0, size, null);
  1.2206 +            size = 0;
  1.2207 +        }
  1.2208 +
  1.2209 +        /**
  1.2210 +         * Returns the number of mappings currently in table.
  1.2211 +         */
  1.2212 +        int size() {
  1.2213 +            return size;
  1.2214 +        }
  1.2215 +
  1.2216 +        /**
  1.2217 +         * Inserts mapping object -> handle mapping into table.  Assumes table
  1.2218 +         * is large enough to accommodate new mapping.
  1.2219 +         */
  1.2220 +        private void insert(Object obj, int handle) {
  1.2221 +            int index = hash(obj) % spine.length;
  1.2222 +            objs[handle] = obj;
  1.2223 +            next[handle] = spine[index];
  1.2224 +            spine[index] = handle;
  1.2225 +        }
  1.2226 +
  1.2227 +        /**
  1.2228 +         * Expands the hash "spine" -- equivalent to increasing the number of
  1.2229 +         * buckets in a conventional hash table.
  1.2230 +         */
  1.2231 +        private void growSpine() {
  1.2232 +            spine = new int[(spine.length << 1) + 1];
  1.2233 +            threshold = (int) (spine.length * loadFactor);
  1.2234 +            Arrays.fill(spine, -1);
  1.2235 +            for (int i = 0; i < size; i++) {
  1.2236 +                insert(objs[i], i);
  1.2237 +            }
  1.2238 +        }
  1.2239 +
  1.2240 +        /**
  1.2241 +         * Increases hash table capacity by lengthening entry arrays.
  1.2242 +         */
  1.2243 +        private void growEntries() {
  1.2244 +            int newLength = (next.length << 1) + 1;
  1.2245 +            int[] newNext = new int[newLength];
  1.2246 +            System.arraycopy(next, 0, newNext, 0, size);
  1.2247 +            next = newNext;
  1.2248 +
  1.2249 +            Object[] newObjs = new Object[newLength];
  1.2250 +            System.arraycopy(objs, 0, newObjs, 0, size);
  1.2251 +            objs = newObjs;
  1.2252 +        }
  1.2253 +
  1.2254 +        /**
  1.2255 +         * Returns hash value for given object.
  1.2256 +         */
  1.2257 +        private int hash(Object obj) {
  1.2258 +            return System.identityHashCode(obj) & 0x7FFFFFFF;
  1.2259 +        }
  1.2260 +    }
  1.2261 +
  1.2262 +    /**
  1.2263 +     * Lightweight identity hash table which maps objects to replacement
  1.2264 +     * objects.
  1.2265 +     */
  1.2266 +    private static class ReplaceTable {
  1.2267 +
  1.2268 +        /* maps object -> index */
  1.2269 +        private final HandleTable htab;
  1.2270 +        /* maps index -> replacement object */
  1.2271 +        private Object[] reps;
  1.2272 +
  1.2273 +        /**
  1.2274 +         * Creates new ReplaceTable with given capacity and load factor.
  1.2275 +         */
  1.2276 +        ReplaceTable(int initialCapacity, float loadFactor) {
  1.2277 +            htab = new HandleTable(initialCapacity, loadFactor);
  1.2278 +            reps = new Object[initialCapacity];
  1.2279 +        }
  1.2280 +
  1.2281 +        /**
  1.2282 +         * Enters mapping from object to replacement object.
  1.2283 +         */
  1.2284 +        void assign(Object obj, Object rep) {
  1.2285 +            int index = htab.assign(obj);
  1.2286 +            while (index >= reps.length) {
  1.2287 +                grow();
  1.2288 +            }
  1.2289 +            reps[index] = rep;
  1.2290 +        }
  1.2291 +
  1.2292 +        /**
  1.2293 +         * Looks up and returns replacement for given object.  If no
  1.2294 +         * replacement is found, returns the lookup object itself.
  1.2295 +         */
  1.2296 +        Object lookup(Object obj) {
  1.2297 +            int index = htab.lookup(obj);
  1.2298 +            return (index >= 0) ? reps[index] : obj;
  1.2299 +        }
  1.2300 +
  1.2301 +        /**
  1.2302 +         * Resets table to its initial (empty) state.
  1.2303 +         */
  1.2304 +        void clear() {
  1.2305 +            Arrays.fill(reps, 0, htab.size(), null);
  1.2306 +            htab.clear();
  1.2307 +        }
  1.2308 +
  1.2309 +        /**
  1.2310 +         * Returns the number of mappings currently in table.
  1.2311 +         */
  1.2312 +        int size() {
  1.2313 +            return htab.size();
  1.2314 +        }
  1.2315 +
  1.2316 +        /**
  1.2317 +         * Increases table capacity.
  1.2318 +         */
  1.2319 +        private void grow() {
  1.2320 +            Object[] newReps = new Object[(reps.length << 1) + 1];
  1.2321 +            System.arraycopy(reps, 0, newReps, 0, reps.length);
  1.2322 +            reps = newReps;
  1.2323 +        }
  1.2324 +    }
  1.2325 +
  1.2326 +    /**
  1.2327 +     * Stack to keep debug information about the state of the
  1.2328 +     * serialization process, for embedding in exception messages.
  1.2329 +     */
  1.2330 +    private static class DebugTraceInfoStack {
  1.2331 +        private final List<String> stack;
  1.2332 +
  1.2333 +        DebugTraceInfoStack() {
  1.2334 +            stack = new ArrayList<>();
  1.2335 +        }
  1.2336 +
  1.2337 +        /**
  1.2338 +         * Removes all of the elements from enclosed list.
  1.2339 +         */
  1.2340 +        void clear() {
  1.2341 +            stack.clear();
  1.2342 +        }
  1.2343 +
  1.2344 +        /**
  1.2345 +         * Removes the object at the top of enclosed list.
  1.2346 +         */
  1.2347 +        void pop() {
  1.2348 +            stack.remove(stack.size()-1);
  1.2349 +        }
  1.2350 +
  1.2351 +        /**
  1.2352 +         * Pushes a String onto the top of enclosed list.
  1.2353 +         */
  1.2354 +        void push(String entry) {
  1.2355 +            stack.add("\t- " + entry);
  1.2356 +        }
  1.2357 +
  1.2358 +        /**
  1.2359 +         * Returns a string representation of this object
  1.2360 +         */
  1.2361 +        public String toString() {
  1.2362 +            StringBuilder buffer = new StringBuilder();
  1.2363 +            if (!stack.isEmpty()) {
  1.2364 +                for(int i = stack.size(); i > 0; i-- ) {
  1.2365 +                    buffer.append(stack.get(i-1) + ((i != 1) ? "\n" : ""));
  1.2366 +                }
  1.2367 +            }
  1.2368 +            return buffer.toString();
  1.2369 +        }
  1.2370 +    }
  1.2371 +
  1.2372 +}