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