emul/compact/src/main/java/java/io/ObjectOutputStream.java
branchmodel
changeset 878 ecbd252fd3a7
parent 877 3392f250c784
parent 871 6168fb585ab4
child 879 af170d42b5b3
     1.1 --- a/emul/compact/src/main/java/java/io/ObjectOutputStream.java	Fri Mar 22 16:59:47 2013 +0100
     1.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.3 @@ -1,2369 +0,0 @@
     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 -}