0001 /*
0002 * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package java.io;
0027
0028 import java.io.ObjectStreamClass.WeakClassKey;
0029 import java.lang.ref.ReferenceQueue;
0030 import java.security.AccessController;
0031 import java.security.PrivilegedAction;
0032 import java.util.ArrayList;
0033 import java.util.Arrays;
0034 import java.util.List;
0035 import java.util.concurrent.ConcurrentHashMap;
0036 import java.util.concurrent.ConcurrentMap;
0037 import static java.io.ObjectStreamClass.processQueue;
0038
0039 /**
0040 * An ObjectOutputStream writes primitive data types and graphs of Java objects
0041 * to an OutputStream. The objects can be read (reconstituted) using an
0042 * ObjectInputStream. Persistent storage of objects can be accomplished by
0043 * using a file for the stream. If the stream is a network socket stream, the
0044 * objects can be reconstituted on another host or in another process.
0045 *
0046 * <p>Only objects that support the java.io.Serializable interface can be
0047 * written to streams. The class of each serializable object is encoded
0048 * including the class name and signature of the class, the values of the
0049 * object's fields and arrays, and the closure of any other objects referenced
0050 * from the initial objects.
0051 *
0052 * <p>The method writeObject is used to write an object to the stream. Any
0053 * object, including Strings and arrays, is written with writeObject. Multiple
0054 * objects or primitives can be written to the stream. The objects must be
0055 * read back from the corresponding ObjectInputstream with the same types and
0056 * in the same order as they were written.
0057 *
0058 * <p>Primitive data types can also be written to the stream using the
0059 * appropriate methods from DataOutput. Strings can also be written using the
0060 * writeUTF method.
0061 *
0062 * <p>The default serialization mechanism for an object writes the class of the
0063 * object, the class signature, and the values of all non-transient and
0064 * non-static fields. References to other objects (except in transient or
0065 * static fields) cause those objects to be written also. Multiple references
0066 * to a single object are encoded using a reference sharing mechanism so that
0067 * graphs of objects can be restored to the same shape as when the original was
0068 * written.
0069 *
0070 * <p>For example to write an object that can be read by the example in
0071 * ObjectInputStream:
0072 * <br>
0073 * <pre>
0074 * FileOutputStream fos = new FileOutputStream("t.tmp");
0075 * ObjectOutputStream oos = new ObjectOutputStream(fos);
0076 *
0077 * oos.writeInt(12345);
0078 * oos.writeObject("Today");
0079 * oos.writeObject(new Date());
0080 *
0081 * oos.close();
0082 * </pre>
0083 *
0084 * <p>Classes that require special handling during the serialization and
0085 * deserialization process must implement special methods with these exact
0086 * signatures:
0087 * <br>
0088 * <pre>
0089 * private void readObject(java.io.ObjectInputStream stream)
0090 * throws IOException, ClassNotFoundException;
0091 * private void writeObject(java.io.ObjectOutputStream stream)
0092 * throws IOException
0093 * private void readObjectNoData()
0094 * throws ObjectStreamException;
0095 * </pre>
0096 *
0097 * <p>The writeObject method is responsible for writing the state of the object
0098 * for its particular class so that the corresponding readObject method can
0099 * restore it. The method does not need to concern itself with the state
0100 * belonging to the object's superclasses or subclasses. State is saved by
0101 * writing the individual fields to the ObjectOutputStream using the
0102 * writeObject method or by using the methods for primitive data types
0103 * supported by DataOutput.
0104 *
0105 * <p>Serialization does not write out the fields of any object that does not
0106 * implement the java.io.Serializable interface. Subclasses of Objects that
0107 * are not serializable can be serializable. In this case the non-serializable
0108 * class must have a no-arg constructor to allow its fields to be initialized.
0109 * In this case it is the responsibility of the subclass to save and restore
0110 * the state of the non-serializable class. It is frequently the case that the
0111 * fields of that class are accessible (public, package, or protected) or that
0112 * there are get and set methods that can be used to restore the state.
0113 *
0114 * <p>Serialization of an object can be prevented by implementing writeObject
0115 * and readObject methods that throw the NotSerializableException. The
0116 * exception will be caught by the ObjectOutputStream and abort the
0117 * serialization process.
0118 *
0119 * <p>Implementing the Externalizable interface allows the object to assume
0120 * complete control over the contents and format of the object's serialized
0121 * form. The methods of the Externalizable interface, writeExternal and
0122 * readExternal, are called to save and restore the objects state. When
0123 * implemented by a class they can write and read their own state using all of
0124 * the methods of ObjectOutput and ObjectInput. It is the responsibility of
0125 * the objects to handle any versioning that occurs.
0126 *
0127 * <p>Enum constants are serialized differently than ordinary serializable or
0128 * externalizable objects. The serialized form of an enum constant consists
0129 * solely of its name; field values of the constant are not transmitted. To
0130 * serialize an enum constant, ObjectOutputStream writes the string returned by
0131 * the constant's name method. Like other serializable or externalizable
0132 * objects, enum constants can function as the targets of back references
0133 * appearing subsequently in the serialization stream. The process by which
0134 * enum constants are serialized cannot be customized; any class-specific
0135 * writeObject and writeReplace methods defined by enum types are ignored
0136 * during serialization. Similarly, any serialPersistentFields or
0137 * serialVersionUID field declarations are also ignored--all enum types have a
0138 * fixed serialVersionUID of 0L.
0139 *
0140 * <p>Primitive data, excluding serializable fields and externalizable data, is
0141 * written to the ObjectOutputStream in block-data records. A block data record
0142 * is composed of a header and data. The block data header consists of a marker
0143 * and the number of bytes to follow the header. Consecutive primitive data
0144 * writes are merged into one block-data record. The blocking factor used for
0145 * a block-data record will be 1024 bytes. Each block-data record will be
0146 * filled up to 1024 bytes, or be written whenever there is a termination of
0147 * block-data mode. Calls to the ObjectOutputStream methods writeObject,
0148 * defaultWriteObject and writeFields initially terminate any existing
0149 * block-data record.
0150 *
0151 * @author Mike Warres
0152 * @author Roger Riggs
0153 * @version 1.165, 07/05/05
0154 * @see java.io.DataOutput
0155 * @see java.io.ObjectInputStream
0156 * @see java.io.Serializable
0157 * @see java.io.Externalizable
0158 * @see <a href="../../../platform/serialization/spec/output.html">Object Serialization Specification, Section 2, Object Output Classes</a>
0159 * @since JDK1.1
0160 */
0161 public class ObjectOutputStream extends OutputStream implements
0162 ObjectOutput, ObjectStreamConstants {
0163
0164 private static class Caches {
0165 /** cache of subclass security audit results */
0166 static final ConcurrentMap<WeakClassKey, Boolean> subclassAudits = new ConcurrentHashMap<WeakClassKey, Boolean>();
0167
0168 /** queue for WeakReferences to audited subclasses */
0169 static final ReferenceQueue<Class<?>> subclassAuditsQueue = new ReferenceQueue<Class<?>>();
0170 }
0171
0172 /** filter stream for handling block data conversion */
0173 private final BlockDataOutputStream bout;
0174 /** obj -> wire handle map */
0175 private final HandleTable handles;
0176 /** obj -> replacement obj map */
0177 private final ReplaceTable subs;
0178 /** stream protocol version */
0179 private int protocol = PROTOCOL_VERSION_2;
0180 /** recursion depth */
0181 private int depth;
0182
0183 /** buffer for writing primitive field values */
0184 private byte[] primVals;
0185
0186 /** if true, invoke writeObjectOverride() instead of writeObject() */
0187 private final boolean enableOverride;
0188 /** if true, invoke replaceObject() */
0189 private boolean enableReplace;
0190
0191 // values below valid only during upcalls to writeObject()/writeExternal()
0192 /** object currently being serialized */
0193 private Object curObj;
0194 /** descriptor for current class (null if in writeExternal()) */
0195 private ObjectStreamClass curDesc;
0196 /** current PutField object */
0197 private PutFieldImpl curPut;
0198
0199 /** custom storage for debug trace info */
0200 private final DebugTraceInfoStack debugInfoStack;
0201
0202 /**
0203 * value of "sun.io.serialization.extendedDebugInfo" property,
0204 * as true or false for extended information about exception's place
0205 */
0206 private static final boolean extendedDebugInfo = ((Boolean) java.security.AccessController
0207 .doPrivileged(new sun.security.action.GetBooleanAction(
0208 "sun.io.serialization.extendedDebugInfo")))
0209 .booleanValue();
0210
0211 /**
0212 * Creates an ObjectOutputStream that writes to the specified OutputStream.
0213 * This constructor writes the serialization stream header to the
0214 * underlying stream; callers may wish to flush the stream immediately to
0215 * ensure that constructors for receiving ObjectInputStreams will not block
0216 * when reading the header.
0217 *
0218 * <p>If a security manager is installed, this constructor will check for
0219 * the "enableSubclassImplementation" SerializablePermission when invoked
0220 * directly or indirectly by the constructor of a subclass which overrides
0221 * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared
0222 * methods.
0223 *
0224 * @param out output stream to write to
0225 * @throws IOException if an I/O error occurs while writing stream header
0226 * @throws SecurityException if untrusted subclass illegally overrides
0227 * security-sensitive methods
0228 * @throws NullPointerException if <code>out</code> is <code>null</code>
0229 * @since 1.4
0230 * @see ObjectOutputStream#ObjectOutputStream()
0231 * @see ObjectOutputStream#putFields()
0232 * @see ObjectInputStream#ObjectInputStream(InputStream)
0233 */
0234 public ObjectOutputStream(OutputStream out) throws IOException {
0235 verifySubclass();
0236 bout = new BlockDataOutputStream(out);
0237 handles = new HandleTable(10, (float) 3.00);
0238 subs = new ReplaceTable(10, (float) 3.00);
0239 enableOverride = false;
0240 writeStreamHeader();
0241 bout.setBlockDataMode(true);
0242 if (extendedDebugInfo) {
0243 debugInfoStack = new DebugTraceInfoStack();
0244 } else {
0245 debugInfoStack = null;
0246 }
0247 }
0248
0249 /**
0250 * Provide a way for subclasses that are completely reimplementing
0251 * ObjectOutputStream to not have to allocate private data just used by
0252 * this implementation of ObjectOutputStream.
0253 *
0254 * <p>If there is a security manager installed, this method first calls the
0255 * security manager's <code>checkPermission</code> method with a
0256 * <code>SerializablePermission("enableSubclassImplementation")</code>
0257 * permission to ensure it's ok to enable subclassing.
0258 *
0259 * @throws SecurityException if a security manager exists and its
0260 * <code>checkPermission</code> method denies enabling
0261 * subclassing.
0262 * @see SecurityManager#checkPermission
0263 * @see java.io.SerializablePermission
0264 */
0265 protected ObjectOutputStream() throws IOException,
0266 SecurityException {
0267 SecurityManager sm = System.getSecurityManager();
0268 if (sm != null) {
0269 sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
0270 }
0271 bout = null;
0272 handles = null;
0273 subs = null;
0274 enableOverride = true;
0275 debugInfoStack = null;
0276 }
0277
0278 /**
0279 * Specify stream protocol version to use when writing the stream.
0280 *
0281 * <p>This routine provides a hook to enable the current version of
0282 * Serialization to write in a format that is backwards compatible to a
0283 * previous version of the stream format.
0284 *
0285 * <p>Every effort will be made to avoid introducing additional
0286 * backwards incompatibilities; however, sometimes there is no
0287 * other alternative.
0288 *
0289 * @param version use ProtocolVersion from java.io.ObjectStreamConstants.
0290 * @throws IllegalStateException if called after any objects
0291 * have been serialized.
0292 * @throws IllegalArgumentException if invalid version is passed in.
0293 * @throws IOException if I/O errors occur
0294 * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
0295 * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2
0296 * @since 1.2
0297 */
0298 public void useProtocolVersion(int version) throws IOException {
0299 if (handles.size() != 0) {
0300 // REMIND: implement better check for pristine stream?
0301 throw new IllegalStateException("stream non-empty");
0302 }
0303 switch (version) {
0304 case PROTOCOL_VERSION_1:
0305 case PROTOCOL_VERSION_2:
0306 protocol = version;
0307 break;
0308
0309 default:
0310 throw new IllegalArgumentException("unknown version: "
0311 + version);
0312 }
0313 }
0314
0315 /**
0316 * Write the specified object to the ObjectOutputStream. The class of the
0317 * object, the signature of the class, and the values of the non-transient
0318 * and non-static fields of the class and all of its supertypes are
0319 * written. Default serialization for a class can be overridden using the
0320 * writeObject and the readObject methods. Objects referenced by this
0321 * object are written transitively so that a complete equivalent graph of
0322 * objects can be reconstructed by an ObjectInputStream.
0323 *
0324 * <p>Exceptions are thrown for problems with the OutputStream and for
0325 * classes that should not be serialized. All exceptions are fatal to the
0326 * OutputStream, which is left in an indeterminate state, and it is up to
0327 * the caller to ignore or recover the stream state.
0328 *
0329 * @throws InvalidClassException Something is wrong with a class used by
0330 * serialization.
0331 * @throws NotSerializableException Some object to be serialized does not
0332 * implement the java.io.Serializable interface.
0333 * @throws IOException Any exception thrown by the underlying
0334 * OutputStream.
0335 */
0336 public final void writeObject(Object obj) throws IOException {
0337 if (enableOverride) {
0338 writeObjectOverride(obj);
0339 return;
0340 }
0341 try {
0342 writeObject0(obj, false);
0343 } catch (IOException ex) {
0344 if (depth == 0) {
0345 writeFatalException(ex);
0346 }
0347 throw ex;
0348 }
0349 }
0350
0351 /**
0352 * Method used by subclasses to override the default writeObject method.
0353 * This method is called by trusted subclasses of ObjectInputStream that
0354 * constructed ObjectInputStream using the protected no-arg constructor.
0355 * The subclass is expected to provide an override method with the modifier
0356 * "final".
0357 *
0358 * @param obj object to be written to the underlying stream
0359 * @throws IOException if there are I/O errors while writing to the
0360 * underlying stream
0361 * @see #ObjectOutputStream()
0362 * @see #writeObject(Object)
0363 * @since 1.2
0364 */
0365 protected void writeObjectOverride(Object obj) throws IOException {
0366 }
0367
0368 /**
0369 * Writes an "unshared" object to the ObjectOutputStream. This method is
0370 * identical to writeObject, except that it always writes the given object
0371 * as a new, unique object in the stream (as opposed to a back-reference
0372 * pointing to a previously serialized instance). Specifically:
0373 * <ul>
0374 * <li>An object written via writeUnshared is always serialized in the
0375 * same manner as a newly appearing object (an object that has not
0376 * been written to the stream yet), regardless of whether or not the
0377 * object has been written previously.
0378 *
0379 * <li>If writeObject is used to write an object that has been previously
0380 * written with writeUnshared, the previous writeUnshared operation
0381 * is treated as if it were a write of a separate object. In other
0382 * words, ObjectOutputStream will never generate back-references to
0383 * object data written by calls to writeUnshared.
0384 * </ul>
0385 * While writing an object via writeUnshared does not in itself guarantee a
0386 * unique reference to the object when it is deserialized, it allows a
0387 * single object to be defined multiple times in a stream, so that multiple
0388 * calls to readUnshared by the receiver will not conflict. Note that the
0389 * rules described above only apply to the base-level object written with
0390 * writeUnshared, and not to any transitively referenced sub-objects in the
0391 * object graph to be serialized.
0392 *
0393 * <p>ObjectOutputStream subclasses which override this method can only be
0394 * constructed in security contexts possessing the
0395 * "enableSubclassImplementation" SerializablePermission; any attempt to
0396 * instantiate such a subclass without this permission will cause a
0397 * SecurityException to be thrown.
0398 *
0399 * @param obj object to write to stream
0400 * @throws NotSerializableException if an object in the graph to be
0401 * serialized does not implement the Serializable interface
0402 * @throws InvalidClassException if a problem exists with the class of an
0403 * object to be serialized
0404 * @throws IOException if an I/O error occurs during serialization
0405 * @since 1.4
0406 */
0407 public void writeUnshared(Object obj) throws IOException {
0408 try {
0409 writeObject0(obj, true);
0410 } catch (IOException ex) {
0411 if (depth == 0) {
0412 writeFatalException(ex);
0413 }
0414 throw ex;
0415 }
0416 }
0417
0418 /**
0419 * Write the non-static and non-transient fields of the current class to
0420 * this stream. This may only be called from the writeObject method of the
0421 * class being serialized. It will throw the NotActiveException if it is
0422 * called otherwise.
0423 *
0424 * @throws IOException if I/O errors occur while writing to the underlying
0425 * <code>OutputStream</code>
0426 */
0427 public void defaultWriteObject() throws IOException {
0428 if (curObj == null || curDesc == null) {
0429 throw new NotActiveException("not in call to writeObject");
0430 }
0431 bout.setBlockDataMode(false);
0432 defaultWriteFields(curObj, curDesc);
0433 bout.setBlockDataMode(true);
0434 }
0435
0436 /**
0437 * Retrieve the object used to buffer persistent fields to be written to
0438 * the stream. The fields will be written to the stream when writeFields
0439 * method is called.
0440 *
0441 * @return an instance of the class Putfield that holds the serializable
0442 * fields
0443 * @throws IOException if I/O errors occur
0444 * @since 1.2
0445 */
0446 public ObjectOutputStream.PutField putFields() throws IOException {
0447 if (curPut == null) {
0448 if (curObj == null || curDesc == null) {
0449 throw new NotActiveException(
0450 "not in call to writeObject");
0451 }
0452 curPut = new PutFieldImpl(curDesc);
0453 }
0454 return curPut;
0455 }
0456
0457 /**
0458 * Write the buffered fields to the stream.
0459 *
0460 * @throws IOException if I/O errors occur while writing to the underlying
0461 * stream
0462 * @throws NotActiveException Called when a classes writeObject method was
0463 * not called to write the state of the object.
0464 * @since 1.2
0465 */
0466 public void writeFields() throws IOException {
0467 if (curPut == null) {
0468 throw new NotActiveException("no current PutField object");
0469 }
0470 bout.setBlockDataMode(false);
0471 curPut.writeFields();
0472 bout.setBlockDataMode(true);
0473 }
0474
0475 /**
0476 * Reset will disregard the state of any objects already written to the
0477 * stream. The state is reset to be the same as a new ObjectOutputStream.
0478 * The current point in the stream is marked as reset so the corresponding
0479 * ObjectInputStream will be reset at the same point. Objects previously
0480 * written to the stream will not be refered to as already being in the
0481 * stream. They will be written to the stream again.
0482 *
0483 * @throws IOException if reset() is invoked while serializing an object.
0484 */
0485 public void reset() throws IOException {
0486 if (depth != 0) {
0487 throw new IOException("stream active");
0488 }
0489 bout.setBlockDataMode(false);
0490 bout.writeByte(TC_RESET);
0491 clear();
0492 bout.setBlockDataMode(true);
0493 }
0494
0495 /**
0496 * Subclasses may implement this method to allow class data to be stored in
0497 * the stream. By default this method does nothing. The corresponding
0498 * method in ObjectInputStream is resolveClass. This method is called
0499 * exactly once for each unique class in the stream. The class name and
0500 * signature will have already been written to the stream. This method may
0501 * make free use of the ObjectOutputStream to save any representation of
0502 * the class it deems suitable (for example, the bytes of the class file).
0503 * The resolveClass method in the corresponding subclass of
0504 * ObjectInputStream must read and use any data or objects written by
0505 * annotateClass.
0506 *
0507 * @param cl the class to annotate custom data for
0508 * @throws IOException Any exception thrown by the underlying
0509 * OutputStream.
0510 */
0511 protected void annotateClass(Class<?> cl) throws IOException {
0512 }
0513
0514 /**
0515 * Subclasses may implement this method to store custom data in the stream
0516 * along with descriptors for dynamic proxy classes.
0517 *
0518 * <p>This method is called exactly once for each unique proxy class
0519 * descriptor in the stream. The default implementation of this method in
0520 * <code>ObjectOutputStream</code> does nothing.
0521 *
0522 * <p>The corresponding method in <code>ObjectInputStream</code> is
0523 * <code>resolveProxyClass</code>. For a given subclass of
0524 * <code>ObjectOutputStream</code> that overrides this method, the
0525 * <code>resolveProxyClass</code> method in the corresponding subclass of
0526 * <code>ObjectInputStream</code> must read any data or objects written by
0527 * <code>annotateProxyClass</code>.
0528 *
0529 * @param cl the proxy class to annotate custom data for
0530 * @throws IOException any exception thrown by the underlying
0531 * <code>OutputStream</code>
0532 * @see ObjectInputStream#resolveProxyClass(String[])
0533 * @since 1.3
0534 */
0535 protected void annotateProxyClass(Class<?> cl) throws IOException {
0536 }
0537
0538 /**
0539 * This method will allow trusted subclasses of ObjectOutputStream to
0540 * substitute one object for another during serialization. Replacing
0541 * objects is disabled until enableReplaceObject is called. The
0542 * enableReplaceObject method checks that the stream requesting to do
0543 * replacement can be trusted. The first occurrence of each object written
0544 * into the serialization stream is passed to replaceObject. Subsequent
0545 * references to the object are replaced by the object returned by the
0546 * original call to replaceObject. To ensure that the private state of
0547 * objects is not unintentionally exposed, only trusted streams may use
0548 * replaceObject.
0549 *
0550 * <p>The ObjectOutputStream.writeObject method takes a parameter of type
0551 * Object (as opposed to type Serializable) to allow for cases where
0552 * non-serializable objects are replaced by serializable ones.
0553 *
0554 * <p>When a subclass is replacing objects it must insure that either a
0555 * complementary substitution must be made during deserialization or that
0556 * the substituted object is compatible with every field where the
0557 * reference will be stored. Objects whose type is not a subclass of the
0558 * type of the field or array element abort the serialization by raising an
0559 * exception and the object is not be stored.
0560 *
0561 * <p>This method is called only once when each object is first
0562 * encountered. All subsequent references to the object will be redirected
0563 * to the new object. This method should return the object to be
0564 * substituted or the original object.
0565 *
0566 * <p>Null can be returned as the object to be substituted, but may cause
0567 * NullReferenceException in classes that contain references to the
0568 * original object since they may be expecting an object instead of
0569 * null.
0570 *
0571 * @param obj the object to be replaced
0572 * @return the alternate object that replaced the specified one
0573 * @throws IOException Any exception thrown by the underlying
0574 * OutputStream.
0575 */
0576 protected Object replaceObject(Object obj) throws IOException {
0577 return obj;
0578 }
0579
0580 /**
0581 * Enable the stream to do replacement of objects in the stream. When
0582 * enabled, the replaceObject method is called for every object being
0583 * serialized.
0584 *
0585 * <p>If <code>enable</code> is true, and there is a security manager
0586 * installed, this method first calls the security manager's
0587 * <code>checkPermission</code> method with a
0588 * <code>SerializablePermission("enableSubstitution")</code> permission to
0589 * ensure it's ok to enable the stream to do replacement of objects in the
0590 * stream.
0591 *
0592 * @param enable boolean parameter to enable replacement of objects
0593 * @return the previous setting before this method was invoked
0594 * @throws SecurityException if a security manager exists and its
0595 * <code>checkPermission</code> method denies enabling the stream
0596 * to do replacement of objects in the stream.
0597 * @see SecurityManager#checkPermission
0598 * @see java.io.SerializablePermission
0599 */
0600 protected boolean enableReplaceObject(boolean enable)
0601 throws SecurityException {
0602 if (enable == enableReplace) {
0603 return enable;
0604 }
0605 if (enable) {
0606 SecurityManager sm = System.getSecurityManager();
0607 if (sm != null) {
0608 sm.checkPermission(SUBSTITUTION_PERMISSION);
0609 }
0610 }
0611 enableReplace = enable;
0612 return !enableReplace;
0613 }
0614
0615 /**
0616 * The writeStreamHeader method is provided so subclasses can append or
0617 * prepend their own header to the stream. It writes the magic number and
0618 * version to the stream.
0619 *
0620 * @throws IOException if I/O errors occur while writing to the underlying
0621 * stream
0622 */
0623 protected void writeStreamHeader() throws IOException {
0624 bout.writeShort(STREAM_MAGIC);
0625 bout.writeShort(STREAM_VERSION);
0626 }
0627
0628 /**
0629 * Write the specified class descriptor to the ObjectOutputStream. Class
0630 * descriptors are used to identify the classes of objects written to the
0631 * stream. Subclasses of ObjectOutputStream may override this method to
0632 * customize the way in which class descriptors are written to the
0633 * serialization stream. The corresponding method in ObjectInputStream,
0634 * <code>readClassDescriptor</code>, should then be overridden to
0635 * reconstitute the class descriptor from its custom stream representation.
0636 * By default, this method writes class descriptors according to the format
0637 * defined in the Object Serialization specification.
0638 *
0639 * <p>Note that this method will only be called if the ObjectOutputStream
0640 * is not using the old serialization stream format (set by calling
0641 * ObjectOutputStream's <code>useProtocolVersion</code> method). If this
0642 * serialization stream is using the old format
0643 * (<code>PROTOCOL_VERSION_1</code>), the class descriptor will be written
0644 * internally in a manner that cannot be overridden or customized.
0645 *
0646 * @param desc class descriptor to write to the stream
0647 * @throws IOException If an I/O error has occurred.
0648 * @see java.io.ObjectInputStream#readClassDescriptor()
0649 * @see #useProtocolVersion(int)
0650 * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
0651 * @since 1.3
0652 */
0653 protected void writeClassDescriptor(ObjectStreamClass desc)
0654 throws IOException {
0655 desc.writeNonProxy(this );
0656 }
0657
0658 /**
0659 * Writes a byte. This method will block until the byte is actually
0660 * written.
0661 *
0662 * @param val the byte to be written to the stream
0663 * @throws IOException If an I/O error has occurred.
0664 */
0665 public void write(int val) throws IOException {
0666 bout.write(val);
0667 }
0668
0669 /**
0670 * Writes an array of bytes. This method will block until the bytes are
0671 * actually written.
0672 *
0673 * @param buf the data to be written
0674 * @throws IOException If an I/O error has occurred.
0675 */
0676 public void write(byte[] buf) throws IOException {
0677 bout.write(buf, 0, buf.length, false);
0678 }
0679
0680 /**
0681 * Writes a sub array of bytes.
0682 *
0683 * @param buf the data to be written
0684 * @param off the start offset in the data
0685 * @param len the number of bytes that are written
0686 * @throws IOException If an I/O error has occurred.
0687 */
0688 public void write(byte[] buf, int off, int len) throws IOException {
0689 if (buf == null) {
0690 throw new NullPointerException();
0691 }
0692 int endoff = off + len;
0693 if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
0694 throw new IndexOutOfBoundsException();
0695 }
0696 bout.write(buf, off, len, false);
0697 }
0698
0699 /**
0700 * Flushes the stream. This will write any buffered output bytes and flush
0701 * through to the underlying stream.
0702 *
0703 * @throws IOException If an I/O error has occurred.
0704 */
0705 public void flush() throws IOException {
0706 bout.flush();
0707 }
0708
0709 /**
0710 * Drain any buffered data in ObjectOutputStream. Similar to flush but
0711 * does not propagate the flush to the underlying stream.
0712 *
0713 * @throws IOException if I/O errors occur while writing to the underlying
0714 * stream
0715 */
0716 protected void drain() throws IOException {
0717 bout.drain();
0718 }
0719
0720 /**
0721 * Closes the stream. This method must be called to release any resources
0722 * associated with the stream.
0723 *
0724 * @throws IOException If an I/O error has occurred.
0725 */
0726 public void close() throws IOException {
0727 flush();
0728 clear();
0729 bout.close();
0730 }
0731
0732 /**
0733 * Writes a boolean.
0734 *
0735 * @param val the boolean to be written
0736 * @throws IOException if I/O errors occur while writing to the underlying
0737 * stream
0738 */
0739 public void writeBoolean(boolean val) throws IOException {
0740 bout.writeBoolean(val);
0741 }
0742
0743 /**
0744 * Writes an 8 bit byte.
0745 *
0746 * @param val the byte value to be written
0747 * @throws IOException if I/O errors occur while writing to the underlying
0748 * stream
0749 */
0750 public void writeByte(int val) throws IOException {
0751 bout.writeByte(val);
0752 }
0753
0754 /**
0755 * Writes a 16 bit short.
0756 *
0757 * @param val the short value to be written
0758 * @throws IOException if I/O errors occur while writing to the underlying
0759 * stream
0760 */
0761 public void writeShort(int val) throws IOException {
0762 bout.writeShort(val);
0763 }
0764
0765 /**
0766 * Writes a 16 bit char.
0767 *
0768 * @param val the char value to be written
0769 * @throws IOException if I/O errors occur while writing to the underlying
0770 * stream
0771 */
0772 public void writeChar(int val) throws IOException {
0773 bout.writeChar(val);
0774 }
0775
0776 /**
0777 * Writes a 32 bit int.
0778 *
0779 * @param val the integer value to be written
0780 * @throws IOException if I/O errors occur while writing to the underlying
0781 * stream
0782 */
0783 public void writeInt(int val) throws IOException {
0784 bout.writeInt(val);
0785 }
0786
0787 /**
0788 * Writes a 64 bit long.
0789 *
0790 * @param val the long value to be written
0791 * @throws IOException if I/O errors occur while writing to the underlying
0792 * stream
0793 */
0794 public void writeLong(long val) throws IOException {
0795 bout.writeLong(val);
0796 }
0797
0798 /**
0799 * Writes a 32 bit float.
0800 *
0801 * @param val the float value to be written
0802 * @throws IOException if I/O errors occur while writing to the underlying
0803 * stream
0804 */
0805 public void writeFloat(float val) throws IOException {
0806 bout.writeFloat(val);
0807 }
0808
0809 /**
0810 * Writes a 64 bit double.
0811 *
0812 * @param val the double value to be written
0813 * @throws IOException if I/O errors occur while writing to the underlying
0814 * stream
0815 */
0816 public void writeDouble(double val) throws IOException {
0817 bout.writeDouble(val);
0818 }
0819
0820 /**
0821 * Writes a String as a sequence of bytes.
0822 *
0823 * @param str the String of bytes to be written
0824 * @throws IOException if I/O errors occur while writing to the underlying
0825 * stream
0826 */
0827 public void writeBytes(String str) throws IOException {
0828 bout.writeBytes(str);
0829 }
0830
0831 /**
0832 * Writes a String as a sequence of chars.
0833 *
0834 * @param str the String of chars to be written
0835 * @throws IOException if I/O errors occur while writing to the underlying
0836 * stream
0837 */
0838 public void writeChars(String str) throws IOException {
0839 bout.writeChars(str);
0840 }
0841
0842 /**
0843 * Primitive data write of this String in
0844 * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
0845 * format. Note that there is a
0846 * significant difference between writing a String into the stream as
0847 * primitive data or as an Object. A String instance written by writeObject
0848 * is written into the stream as a String initially. Future writeObject()
0849 * calls write references to the string into the stream.
0850 *
0851 * @param str the String to be written
0852 * @throws IOException if I/O errors occur while writing to the underlying
0853 * stream
0854 */
0855 public void writeUTF(String str) throws IOException {
0856 bout.writeUTF(str);
0857 }
0858
0859 /**
0860 * Provide programmatic access to the persistent fields to be written
0861 * to ObjectOutput.
0862 *
0863 * @since 1.2
0864 */
0865 public static abstract class PutField {
0866
0867 /**
0868 * Put the value of the named boolean field into the persistent field.
0869 *
0870 * @param name the name of the serializable field
0871 * @param val the value to assign to the field
0872 * @throws IllegalArgumentException if <code>name</code> does not
0873 * match the name of a serializable field for the class whose fields
0874 * are being written, or if the type of the named field is not
0875 * <code>boolean</code>
0876 */
0877 public abstract void put(String name, boolean val);
0878
0879 /**
0880 * Put the value of the named byte field into the persistent field.
0881 *
0882 * @param name the name of the serializable field
0883 * @param val the value to assign to the field
0884 * @throws IllegalArgumentException if <code>name</code> does not
0885 * match the name of a serializable field for the class whose fields
0886 * are being written, or if the type of the named field is not
0887 * <code>byte</code>
0888 */
0889 public abstract void put(String name, byte val);
0890
0891 /**
0892 * Put the value of the named char field into the persistent field.
0893 *
0894 * @param name the name of the serializable field
0895 * @param val the value to assign to the field
0896 * @throws IllegalArgumentException if <code>name</code> does not
0897 * match the name of a serializable field for the class whose fields
0898 * are being written, or if the type of the named field is not
0899 * <code>char</code>
0900 */
0901 public abstract void put(String name, char val);
0902
0903 /**
0904 * Put the value of the named short field into the persistent field.
0905 *
0906 * @param name the name of the serializable field
0907 * @param val the value to assign to the field
0908 * @throws IllegalArgumentException if <code>name</code> does not
0909 * match the name of a serializable field for the class whose fields
0910 * are being written, or if the type of the named field is not
0911 * <code>short</code>
0912 */
0913 public abstract void put(String name, short val);
0914
0915 /**
0916 * Put the value of the named int field into the persistent field.
0917 *
0918 * @param name the name of the serializable field
0919 * @param val the value to assign to the field
0920 * @throws IllegalArgumentException if <code>name</code> does not
0921 * match the name of a serializable field for the class whose fields
0922 * are being written, or if the type of the named field is not
0923 * <code>int</code>
0924 */
0925 public abstract void put(String name, int val);
0926
0927 /**
0928 * Put the value of the named long field into the persistent field.
0929 *
0930 * @param name the name of the serializable field
0931 * @param val the value to assign to the field
0932 * @throws IllegalArgumentException if <code>name</code> does not
0933 * match the name of a serializable field for the class whose fields
0934 * are being written, or if the type of the named field is not
0935 * <code>long</code>
0936 */
0937 public abstract void put(String name, long val);
0938
0939 /**
0940 * Put the value of the named float field into the persistent field.
0941 *
0942 * @param name the name of the serializable field
0943 * @param val the value to assign to the field
0944 * @throws IllegalArgumentException if <code>name</code> does not
0945 * match the name of a serializable field for the class whose fields
0946 * are being written, or if the type of the named field is not
0947 * <code>float</code>
0948 */
0949 public abstract void put(String name, float val);
0950
0951 /**
0952 * Put the value of the named double field into the persistent field.
0953 *
0954 * @param name the name of the serializable field
0955 * @param val the value to assign to the field
0956 * @throws IllegalArgumentException if <code>name</code> does not
0957 * match the name of a serializable field for the class whose fields
0958 * are being written, or if the type of the named field is not
0959 * <code>double</code>
0960 */
0961 public abstract void put(String name, double val);
0962
0963 /**
0964 * Put the value of the named Object field into the persistent field.
0965 *
0966 * @param name the name of the serializable field
0967 * @param val the value to assign to the field
0968 * (which may be <code>null</code>)
0969 * @throws IllegalArgumentException if <code>name</code> does not
0970 * match the name of a serializable field for the class whose fields
0971 * are being written, or if the type of the named field is not a
0972 * reference type
0973 */
0974 public abstract void put(String name, Object val);
0975
0976 /**
0977 * Write the data and fields to the specified ObjectOutput stream,
0978 * which must be the same stream that produced this
0979 * <code>PutField</code> object.
0980 *
0981 * @param out the stream to write the data and fields to
0982 * @throws IOException if I/O errors occur while writing to the
0983 * underlying stream
0984 * @throws IllegalArgumentException if the specified stream is not
0985 * the same stream that produced this <code>PutField</code>
0986 * object
0987 * @deprecated This method does not write the values contained by this
0988 * <code>PutField</code> object in a proper format, and may
0989 * result in corruption of the serialization stream. The
0990 * correct way to write <code>PutField</code> data is by
0991 * calling the {@link java.io.ObjectOutputStream#writeFields()}
0992 * method.
0993 */
0994 @Deprecated
0995 public abstract void write(ObjectOutput out) throws IOException;
0996 }
0997
0998 /**
0999 * Returns protocol version in use.
1000 */
1001 int getProtocolVersion() {
1002 return protocol;
1003 }
1004
1005 /**
1006 * Writes string without allowing it to be replaced in stream. Used by
1007 * ObjectStreamClass to write class descriptor type strings.
1008 */
1009 void writeTypeString(String str) throws IOException {
1010 int handle;
1011 if (str == null) {
1012 writeNull();
1013 } else if ((handle = handles.lookup(str)) != -1) {
1014 writeHandle(handle);
1015 } else {
1016 writeString(str, false);
1017 }
1018 }
1019
1020 /**
1021 * Verifies that this (possibly subclass) instance can be constructed
1022 * without violating security constraints: the subclass must not override
1023 * security-sensitive non-final methods, or else the
1024 * "enableSubclassImplementation" SerializablePermission is checked.
1025 */
1026 private void verifySubclass() {
1027 Class cl = getClass();
1028 if (cl == ObjectOutputStream.class) {
1029 return;
1030 }
1031 SecurityManager sm = System.getSecurityManager();
1032 if (sm == null) {
1033 return;
1034 }
1035 processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
1036 WeakClassKey key = new WeakClassKey(cl,
1037 Caches.subclassAuditsQueue);
1038 Boolean result = Caches.subclassAudits.get(key);
1039 if (result == null) {
1040 result = Boolean.valueOf(auditSubclass(cl));
1041 Caches.subclassAudits.putIfAbsent(key, result);
1042 }
1043 if (result.booleanValue()) {
1044 return;
1045 }
1046 sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
1047 }
1048
1049 /**
1050 * Performs reflective checks on given subclass to verify that it doesn't
1051 * override security-sensitive non-final methods. Returns true if subclass
1052 * is "safe", false otherwise.
1053 */
1054 private static boolean auditSubclass(final Class subcl) {
1055 Boolean result = (Boolean) AccessController
1056 .doPrivileged(new PrivilegedAction() {
1057 public Object run() {
1058 for (Class cl = subcl; cl != ObjectOutputStream.class; cl = cl
1059 .getSuperclass()) {
1060 try {
1061 cl.getDeclaredMethod("writeUnshared",
1062 new Class[] { Object.class });
1063 return Boolean.FALSE;
1064 } catch (NoSuchMethodException ex) {
1065 }
1066 try {
1067 cl.getDeclaredMethod("putFields",
1068 (Class[]) null);
1069 return Boolean.FALSE;
1070 } catch (NoSuchMethodException ex) {
1071 }
1072 }
1073 return Boolean.TRUE;
1074 }
1075 });
1076 return result.booleanValue();
1077 }
1078
1079 /**
1080 * Clears internal data structures.
1081 */
1082 private void clear() {
1083 subs.clear();
1084 handles.clear();
1085 }
1086
1087 /**
1088 * Underlying writeObject/writeUnshared implementation.
1089 */
1090 private void writeObject0(Object obj, boolean unshared)
1091 throws IOException {
1092 boolean oldMode = bout.setBlockDataMode(false);
1093 depth++;
1094 try {
1095 // handle previously written and non-replaceable objects
1096 int h;
1097 if ((obj = subs.lookup(obj)) == null) {
1098 writeNull();
1099 return;
1100 } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1101 writeHandle(h);
1102 return;
1103 } else if (obj instanceof Class) {
1104 writeClass((Class) obj, unshared);
1105 return;
1106 } else if (obj instanceof ObjectStreamClass) {
1107 writeClassDesc((ObjectStreamClass) obj, unshared);
1108 return;
1109 }
1110
1111 // check for replacement object
1112 Object orig = obj;
1113 Class cl = obj.getClass();
1114 ObjectStreamClass desc;
1115 for (;;) {
1116 // REMIND: skip this check for strings/arrays?
1117 Class repCl;
1118 desc = ObjectStreamClass.lookup(cl, true);
1119 if (!desc.hasWriteReplaceMethod()
1120 || (obj = desc.invokeWriteReplace(obj)) == null
1121 || (repCl = obj.getClass()) == cl) {
1122 break;
1123 }
1124 cl = repCl;
1125 }
1126 if (enableReplace) {
1127 Object rep = replaceObject(obj);
1128 if (rep != obj && rep != null) {
1129 cl = rep.getClass();
1130 desc = ObjectStreamClass.lookup(cl, true);
1131 }
1132 obj = rep;
1133 }
1134
1135 // if object replaced, run through original checks a second time
1136 if (obj != orig) {
1137 subs.assign(orig, obj);
1138 if (obj == null) {
1139 writeNull();
1140 return;
1141 } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1142 writeHandle(h);
1143 return;
1144 } else if (obj instanceof Class) {
1145 writeClass((Class) obj, unshared);
1146 return;
1147 } else if (obj instanceof ObjectStreamClass) {
1148 writeClassDesc((ObjectStreamClass) obj, unshared);
1149 return;
1150 }
1151 }
1152
1153 // remaining cases
1154 if (obj instanceof String) {
1155 writeString((String) obj, unshared);
1156 } else if (cl.isArray()) {
1157 writeArray(obj, desc, unshared);
1158 } else if (obj instanceof Enum) {
1159 writeEnum((Enum) obj, desc, unshared);
1160 } else if (obj instanceof Serializable) {
1161 writeOrdinaryObject(obj, desc, unshared);
1162 } else {
1163 if (extendedDebugInfo) {
1164 throw new NotSerializableException(cl.getName()
1165 + "\n" + debugInfoStack.toString());
1166 } else {
1167 throw new NotSerializableException(cl.getName());
1168 }
1169 }
1170 } finally {
1171 depth--;
1172 bout.setBlockDataMode(oldMode);
1173 }
1174 }
1175
1176 /**
1177 * Writes null code to stream.
1178 */
1179 private void writeNull() throws IOException {
1180 bout.writeByte(TC_NULL);
1181 }
1182
1183 /**
1184 * Writes given object handle to stream.
1185 */
1186 private void writeHandle(int handle) throws IOException {
1187 bout.writeByte(TC_REFERENCE);
1188 bout.writeInt(baseWireHandle + handle);
1189 }
1190
1191 /**
1192 * Writes representation of given class to stream.
1193 */
1194 private void writeClass(Class cl, boolean unshared)
1195 throws IOException {
1196 bout.writeByte(TC_CLASS);
1197 writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
1198 handles.assign(unshared ? null : cl);
1199 }
1200
1201 /**
1202 * Writes representation of given class descriptor to stream.
1203 */
1204 private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
1205 throws IOException {
1206 int handle;
1207 if (desc == null) {
1208 writeNull();
1209 } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
1210 writeHandle(handle);
1211 } else if (desc.isProxy()) {
1212 writeProxyDesc(desc, unshared);
1213 } else {
1214 writeNonProxyDesc(desc, unshared);
1215 }
1216 }
1217
1218 /**
1219 * Writes class descriptor representing a dynamic proxy class to stream.
1220 */
1221 private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
1222 throws IOException {
1223 bout.writeByte(TC_PROXYCLASSDESC);
1224 handles.assign(unshared ? null : desc);
1225
1226 Class cl = desc.forClass();
1227 Class[] ifaces = cl.getInterfaces();
1228 bout.writeInt(ifaces.length);
1229 for (int i = 0; i < ifaces.length; i++) {
1230 bout.writeUTF(ifaces[i].getName());
1231 }
1232
1233 bout.setBlockDataMode(true);
1234 annotateProxyClass(cl);
1235 bout.setBlockDataMode(false);
1236 bout.writeByte(TC_ENDBLOCKDATA);
1237
1238 writeClassDesc(desc.getSuperDesc(), false);
1239 }
1240
1241 /**
1242 * Writes class descriptor representing a standard (i.e., not a dynamic
1243 * proxy) class to stream.
1244 */
1245 private void writeNonProxyDesc(ObjectStreamClass desc,
1246 boolean unshared) throws IOException {
1247 bout.writeByte(TC_CLASSDESC);
1248 handles.assign(unshared ? null : desc);
1249
1250 if (protocol == PROTOCOL_VERSION_1) {
1251 // do not invoke class descriptor write hook with old protocol
1252 desc.writeNonProxy(this );
1253 } else {
1254 writeClassDescriptor(desc);
1255 }
1256
1257 Class cl = desc.forClass();
1258 bout.setBlockDataMode(true);
1259 annotateClass(cl);
1260 bout.setBlockDataMode(false);
1261 bout.writeByte(TC_ENDBLOCKDATA);
1262
1263 writeClassDesc(desc.getSuperDesc(), false);
1264 }
1265
1266 /**
1267 * Writes given string to stream, using standard or long UTF format
1268 * depending on string length.
1269 */
1270 private void writeString(String str, boolean unshared)
1271 throws IOException {
1272 handles.assign(unshared ? null : str);
1273 long utflen = bout.getUTFLength(str);
1274 if (utflen <= 0xFFFF) {
1275 bout.writeByte(TC_STRING);
1276 bout.writeUTF(str, utflen);
1277 } else {
1278 bout.writeByte(TC_LONGSTRING);
1279 bout.writeLongUTF(str, utflen);
1280 }
1281 }
1282
1283 /**
1284 * Writes given array object to stream.
1285 */
1286 private void writeArray(Object array, ObjectStreamClass desc,
1287 boolean unshared) throws IOException {
1288 bout.writeByte(TC_ARRAY);
1289 writeClassDesc(desc, false);
1290 handles.assign(unshared ? null : array);
1291
1292 Class ccl = desc.forClass().getComponentType();
1293 if (ccl.isPrimitive()) {
1294 if (ccl == Integer.TYPE) {
1295 int[] ia = (int[]) array;
1296 bout.writeInt(ia.length);
1297 bout.writeInts(ia, 0, ia.length);
1298 } else if (ccl == Byte.TYPE) {
1299 byte[] ba = (byte[]) array;
1300 bout.writeInt(ba.length);
1301 bout.write(ba, 0, ba.length, true);
1302 } else if (ccl == Long.TYPE) {
1303 long[] ja = (long[]) array;
1304 bout.writeInt(ja.length);
1305 bout.writeLongs(ja, 0, ja.length);
1306 } else if (ccl == Float.TYPE) {
1307 float[] fa = (float[]) array;
1308 bout.writeInt(fa.length);
1309 bout.writeFloats(fa, 0, fa.length);
1310 } else if (ccl == Double.TYPE) {
1311 double[] da = (double[]) array;
1312 bout.writeInt(da.length);
1313 bout.writeDoubles(da, 0, da.length);
1314 } else if (ccl == Short.TYPE) {
1315 short[] sa = (short[]) array;
1316 bout.writeInt(sa.length);
1317 bout.writeShorts(sa, 0, sa.length);
1318 } else if (ccl == Character.TYPE) {
1319 char[] ca = (char[]) array;
1320 bout.writeInt(ca.length);
1321 bout.writeChars(ca, 0, ca.length);
1322 } else if (ccl == Boolean.TYPE) {
1323 boolean[] za = (boolean[]) array;
1324 bout.writeInt(za.length);
1325 bout.writeBooleans(za, 0, za.length);
1326 } else {
1327 throw new InternalError();
1328 }
1329 } else {
1330 Object[] objs = (Object[]) array;
1331 int len = objs.length;
1332 bout.writeInt(len);
1333 if (extendedDebugInfo) {
1334 debugInfoStack.push("array (class \""
1335 + array.getClass().getName() + "\", size: "
1336 + len + ")");
1337 }
1338 try {
1339 for (int i = 0; i < len; i++) {
1340 if (extendedDebugInfo) {
1341 debugInfoStack.push("element of array (index: "
1342 + i + ")");
1343 }
1344 try {
1345 writeObject0(objs[i], false);
1346 } finally {
1347 if (extendedDebugInfo) {
1348 debugInfoStack.pop();
1349 }
1350 }
1351 }
1352 } finally {
1353 if (extendedDebugInfo) {
1354 debugInfoStack.pop();
1355 }
1356 }
1357 }
1358 }
1359
1360 /**
1361 * Writes given enum constant to stream.
1362 */
1363 private void writeEnum(Enum en, ObjectStreamClass desc,
1364 boolean unshared) throws IOException {
1365 bout.writeByte(TC_ENUM);
1366 ObjectStreamClass sdesc = desc.getSuperDesc();
1367 writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc,
1368 false);
1369 handles.assign(unshared ? null : en);
1370 writeString(en.name(), false);
1371 }
1372
1373 /**
1374 * Writes representation of a "ordinary" (i.e., not a String, Class,
1375 * ObjectStreamClass, array, or enum constant) serializable object to the
1376 * stream.
1377 */
1378 private void writeOrdinaryObject(Object obj,
1379 ObjectStreamClass desc, boolean unshared)
1380 throws IOException {
1381 if (extendedDebugInfo) {
1382 debugInfoStack.push((depth == 1 ? "root " : "")
1383 + "object (class \"" + obj.getClass().getName()
1384 + "\", " + obj.toString() + ")");
1385 }
1386 try {
1387 desc.checkSerialize();
1388
1389 bout.writeByte(TC_OBJECT);
1390 writeClassDesc(desc, false);
1391 handles.assign(unshared ? null : obj);
1392 if (desc.isExternalizable() && !desc.isProxy()) {
1393 writeExternalData((Externalizable) obj);
1394 } else {
1395 writeSerialData(obj, desc);
1396 }
1397 } finally {
1398 if (extendedDebugInfo) {
1399 debugInfoStack.pop();
1400 }
1401 }
1402 }
1403
1404 /**
1405 * Writes externalizable data of given object by invoking its
1406 * writeExternal() method.
1407 */
1408 private void writeExternalData(Externalizable obj)
1409 throws IOException {
1410 Object oldObj = curObj;
1411 ObjectStreamClass oldDesc = curDesc;
1412 PutFieldImpl oldPut = curPut;
1413 curObj = obj;
1414 curDesc = null;
1415 curPut = null;
1416
1417 if (extendedDebugInfo) {
1418 debugInfoStack.push("writeExternal data");
1419 }
1420 try {
1421 if (protocol == PROTOCOL_VERSION_1) {
1422 obj.writeExternal(this );
1423 } else {
1424 bout.setBlockDataMode(true);
1425 obj.writeExternal(this );
1426 bout.setBlockDataMode(false);
1427 bout.writeByte(TC_ENDBLOCKDATA);
1428 }
1429 } finally {
1430 if (extendedDebugInfo) {
1431 debugInfoStack.pop();
1432 }
1433 }
1434
1435 curObj = oldObj;
1436 curDesc = oldDesc;
1437 curPut = oldPut;
1438 }
1439
1440 /**
1441 * Writes instance data for each serializable class of given object, from
1442 * superclass to subclass.
1443 */
1444 private void writeSerialData(Object obj, ObjectStreamClass desc)
1445 throws IOException {
1446 ObjectStreamClass.ClassDataSlot[] slots = desc
1447 .getClassDataLayout();
1448 for (int i = 0; i < slots.length; i++) {
1449 ObjectStreamClass slotDesc = slots[i].desc;
1450 if (slotDesc.hasWriteObjectMethod()) {
1451 Object oldObj = curObj;
1452 ObjectStreamClass oldDesc = curDesc;
1453 PutFieldImpl oldPut = curPut;
1454 curObj = obj;
1455 curDesc = slotDesc;
1456 curPut = null;
1457
1458 if (extendedDebugInfo) {
1459 debugInfoStack
1460 .push("custom writeObject data (class \""
1461 + slotDesc.getName() + "\")");
1462 }
1463 try {
1464 bout.setBlockDataMode(true);
1465 slotDesc.invokeWriteObject(obj, this );
1466 bout.setBlockDataMode(false);
1467 bout.writeByte(TC_ENDBLOCKDATA);
1468 } finally {
1469 if (extendedDebugInfo) {
1470 debugInfoStack.pop();
1471 }
1472 }
1473
1474 curObj = oldObj;
1475 curDesc = oldDesc;
1476 curPut = oldPut;
1477 } else {
1478 defaultWriteFields(obj, slotDesc);
1479 }
1480 }
1481 }
1482
1483 /**
1484 * Fetches and writes values of serializable fields of given object to
1485 * stream. The given class descriptor specifies which field values to
1486 * write, and in which order they should be written.
1487 */
1488 private void defaultWriteFields(Object obj, ObjectStreamClass desc)
1489 throws IOException {
1490 // REMIND: perform conservative isInstance check here?
1491 desc.checkDefaultSerialize();
1492
1493 int primDataSize = desc.getPrimDataSize();
1494 if (primVals == null || primVals.length < primDataSize) {
1495 primVals = new byte[primDataSize];
1496 }
1497 desc.getPrimFieldValues(obj, primVals);
1498 bout.write(primVals, 0, primDataSize, false);
1499
1500 ObjectStreamField[] fields = desc.getFields(false);
1501 Object[] objVals = new Object[desc.getNumObjFields()];
1502 int numPrimFields = fields.length - objVals.length;
1503 desc.getObjFieldValues(obj, objVals);
1504 for (int i = 0; i < objVals.length; i++) {
1505 if (extendedDebugInfo) {
1506 debugInfoStack.push("field (class \"" + desc.getName()
1507 + "\", name: \""
1508 + fields[numPrimFields + i].getName()
1509 + "\", type: \""
1510 + fields[numPrimFields + i].getType() + "\")");
1511 }
1512 try {
1513 writeObject0(objVals[i], fields[numPrimFields + i]
1514 .isUnshared());
1515 } finally {
1516 if (extendedDebugInfo) {
1517 debugInfoStack.pop();
1518 }
1519 }
1520 }
1521 }
1522
1523 /**
1524 * Attempts to write to stream fatal IOException that has caused
1525 * serialization to abort.
1526 */
1527 private void writeFatalException(IOException ex) throws IOException {
1528 /*
1529 * Note: the serialization specification states that if a second
1530 * IOException occurs while attempting to serialize the original fatal
1531 * exception to the stream, then a StreamCorruptedException should be
1532 * thrown (section 2.1). However, due to a bug in previous
1533 * implementations of serialization, StreamCorruptedExceptions were
1534 * rarely (if ever) actually thrown--the "root" exceptions from
1535 * underlying streams were thrown instead. This historical behavior is
1536 * followed here for consistency.
1537 */
1538 clear();
1539 boolean oldMode = bout.setBlockDataMode(false);
1540 try {
1541 bout.writeByte(TC_EXCEPTION);
1542 writeObject0(ex, false);
1543 clear();
1544 } finally {
1545 bout.setBlockDataMode(oldMode);
1546 }
1547 }
1548
1549 /**
1550 * Converts specified span of float values into byte values.
1551 */
1552 // REMIND: remove once hotspot inlines Float.floatToIntBits
1553 private static native void floatsToBytes(float[] src, int srcpos,
1554 byte[] dst, int dstpos, int nfloats);
1555
1556 /**
1557 * Converts specified span of double values into byte values.
1558 */
1559 // REMIND: remove once hotspot inlines Double.doubleToLongBits
1560 private static native void doublesToBytes(double[] src, int srcpos,
1561 byte[] dst, int dstpos, int ndoubles);
1562
1563 /**
1564 * Default PutField implementation.
1565 */
1566 private class PutFieldImpl extends PutField {
1567
1568 /** class descriptor describing serializable fields */
1569 private final ObjectStreamClass desc;
1570 /** primitive field values */
1571 private final byte[] primVals;
1572 /** object field values */
1573 private final Object[] objVals;
1574
1575 /**
1576 * Creates PutFieldImpl object for writing fields defined in given
1577 * class descriptor.
1578 */
1579 PutFieldImpl(ObjectStreamClass desc) {
1580 this .desc = desc;
1581 primVals = new byte[desc.getPrimDataSize()];
1582 objVals = new Object[desc.getNumObjFields()];
1583 }
1584
1585 public void put(String name, boolean val) {
1586 Bits.putBoolean(primVals,
1587 getFieldOffset(name, Boolean.TYPE), val);
1588 }
1589
1590 public void put(String name, byte val) {
1591 primVals[getFieldOffset(name, Byte.TYPE)] = val;
1592 }
1593
1594 public void put(String name, char val) {
1595 Bits.putChar(primVals,
1596 getFieldOffset(name, Character.TYPE), val);
1597 }
1598
1599 public void put(String name, short val) {
1600 Bits.putShort(primVals, getFieldOffset(name, Short.TYPE),
1601 val);
1602 }
1603
1604 public void put(String name, int val) {
1605 Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE),
1606 val);
1607 }
1608
1609 public void put(String name, float val) {
1610 Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE),
1611 val);
1612 }
1613
1614 public void put(String name, long val) {
1615 Bits
1616 .putLong(primVals, getFieldOffset(name, Long.TYPE),
1617 val);
1618 }
1619
1620 public void put(String name, double val) {
1621 Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE),
1622 val);
1623 }
1624
1625 public void put(String name, Object val) {
1626 objVals[getFieldOffset(name, Object.class)] = val;
1627 }
1628
1629 // deprecated in ObjectOutputStream.PutField
1630 public void write(ObjectOutput out) throws IOException {
1631 /*
1632 * Applications should *not* use this method to write PutField
1633 * data, as it will lead to stream corruption if the PutField
1634 * object writes any primitive data (since block data mode is not
1635 * unset/set properly, as is done in OOS.writeFields()). This
1636 * broken implementation is being retained solely for behavioral
1637 * compatibility, in order to support applications which use
1638 * OOS.PutField.write() for writing only non-primitive data.
1639 *
1640 * Serialization of unshared objects is not implemented here since
1641 * it is not necessary for backwards compatibility; also, unshared
1642 * semantics may not be supported by the given ObjectOutput
1643 * instance. Applications which write unshared objects using the
1644 * PutField API must use OOS.writeFields().
1645 */
1646 if (ObjectOutputStream.this != out) {
1647 throw new IllegalArgumentException("wrong stream");
1648 }
1649 out.write(primVals, 0, primVals.length);
1650
1651 ObjectStreamField[] fields = desc.getFields(false);
1652 int numPrimFields = fields.length - objVals.length;
1653 // REMIND: warn if numPrimFields > 0?
1654 for (int i = 0; i < objVals.length; i++) {
1655 if (fields[numPrimFields + i].isUnshared()) {
1656 throw new IOException(
1657 "cannot write unshared object");
1658 }
1659 out.writeObject(objVals[i]);
1660 }
1661 }
1662
1663 /**
1664 * Writes buffered primitive data and object fields to stream.
1665 */
1666 void writeFields() throws IOException {
1667 bout.write(primVals, 0, primVals.length, false);
1668
1669 ObjectStreamField[] fields = desc.getFields(false);
1670 int numPrimFields = fields.length - objVals.length;
1671 for (int i = 0; i < objVals.length; i++) {
1672 if (extendedDebugInfo) {
1673 debugInfoStack.push("field (class \""
1674 + desc.getName() + "\", name: \""
1675 + fields[numPrimFields + i].getName()
1676 + "\", type: \""
1677 + fields[numPrimFields + i].getType()
1678 + "\")");
1679 }
1680 try {
1681 writeObject0(objVals[i], fields[numPrimFields + i]
1682 .isUnshared());
1683 } finally {
1684 if (extendedDebugInfo) {
1685 debugInfoStack.pop();
1686 }
1687 }
1688 }
1689 }
1690
1691 /**
1692 * Returns offset of field with given name and type. A specified type
1693 * of null matches all types, Object.class matches all non-primitive
1694 * types, and any other non-null type matches assignable types only.
1695 * Throws IllegalArgumentException if no matching field found.
1696 */
1697 private int getFieldOffset(String name, Class type) {
1698 ObjectStreamField field = desc.getField(name, type);
1699 if (field == null) {
1700 throw new IllegalArgumentException("no such field "
1701 + name + " with type " + type);
1702 }
1703 return field.getOffset();
1704 }
1705 }
1706
1707 /**
1708 * Buffered output stream with two modes: in default mode, outputs data in
1709 * same format as DataOutputStream; in "block data" mode, outputs data
1710 * bracketed by block data markers (see object serialization specification
1711 * for details).
1712 */
1713 private static class BlockDataOutputStream extends OutputStream
1714 implements DataOutput {
1715 /** maximum data block length */
1716 private static final int MAX_BLOCK_SIZE = 1024;
1717 /** maximum data block header length */
1718 private static final int MAX_HEADER_SIZE = 5;
1719 /** (tunable) length of char buffer (for writing strings) */
1720 private static final int CHAR_BUF_SIZE = 256;
1721
1722 /** buffer for writing general/block data */
1723 private final byte[] buf = new byte[MAX_BLOCK_SIZE];
1724 /** buffer for writing block data headers */
1725 private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
1726 /** char buffer for fast string writes */
1727 private final char[] cbuf = new char[CHAR_BUF_SIZE];
1728
1729 /** block data mode */
1730 private boolean blkmode = false;
1731 /** current offset into buf */
1732 private int pos = 0;
1733
1734 /** underlying output stream */
1735 private final OutputStream out;
1736 /** loopback stream (for data writes that span data blocks) */
1737 private final DataOutputStream dout;
1738
1739 /**
1740 * Creates new BlockDataOutputStream on top of given underlying stream.
1741 * Block data mode is turned off by default.
1742 */
1743 BlockDataOutputStream(OutputStream out) {
1744 this .out = out;
1745 dout = new DataOutputStream(this );
1746 }
1747
1748 /**
1749 * Sets block data mode to the given mode (true == on, false == off)
1750 * and returns the previous mode value. If the new mode is the same as
1751 * the old mode, no action is taken. If the new mode differs from the
1752 * old mode, any buffered data is flushed before switching to the new
1753 * mode.
1754 */
1755 boolean setBlockDataMode(boolean mode) throws IOException {
1756 if (blkmode == mode) {
1757 return blkmode;
1758 }
1759 drain();
1760 blkmode = mode;
1761 return !blkmode;
1762 }
1763
1764 /**
1765 * Returns true if the stream is currently in block data mode, false
1766 * otherwise.
1767 */
1768 boolean getBlockDataMode() {
1769 return blkmode;
1770 }
1771
1772 /* ----------------- generic output stream methods ----------------- */
1773 /*
1774 * The following methods are equivalent to their counterparts in
1775 * OutputStream, except that they partition written data into data
1776 * blocks when in block data mode.
1777 */
1778
1779 public void write(int b) throws IOException {
1780 if (pos >= MAX_BLOCK_SIZE) {
1781 drain();
1782 }
1783 buf[pos++] = (byte) b;
1784 }
1785
1786 public void write(byte[] b) throws IOException {
1787 write(b, 0, b.length, false);
1788 }
1789
1790 public void write(byte[] b, int off, int len)
1791 throws IOException {
1792 write(b, off, len, false);
1793 }
1794
1795 public void flush() throws IOException {
1796 drain();
1797 out.flush();
1798 }
1799
1800 public void close() throws IOException {
1801 flush();
1802 out.close();
1803 }
1804
1805 /**
1806 * Writes specified span of byte values from given array. If copy is
1807 * true, copies the values to an intermediate buffer before writing
1808 * them to underlying stream (to avoid exposing a reference to the
1809 * original byte array).
1810 */
1811 void write(byte[] b, int off, int len, boolean copy)
1812 throws IOException {
1813 if (!(copy || blkmode)) { // write directly
1814 drain();
1815 out.write(b, off, len);
1816 return;
1817 }
1818
1819 while (len > 0) {
1820 if (pos >= MAX_BLOCK_SIZE) {
1821 drain();
1822 }
1823 if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) {
1824 // avoid unnecessary copy
1825 writeBlockHeader(MAX_BLOCK_SIZE);
1826 out.write(b, off, MAX_BLOCK_SIZE);
1827 off += MAX_BLOCK_SIZE;
1828 len -= MAX_BLOCK_SIZE;
1829 } else {
1830 int wlen = Math.min(len, MAX_BLOCK_SIZE - pos);
1831 System.arraycopy(b, off, buf, pos, wlen);
1832 pos += wlen;
1833 off += wlen;
1834 len -= wlen;
1835 }
1836 }
1837 }
1838
1839 /**
1840 * Writes all buffered data from this stream to the underlying stream,
1841 * but does not flush underlying stream.
1842 */
1843 void drain() throws IOException {
1844 if (pos == 0) {
1845 return;
1846 }
1847 if (blkmode) {
1848 writeBlockHeader(pos);
1849 }
1850 out.write(buf, 0, pos);
1851 pos = 0;
1852 }
1853
1854 /**
1855 * Writes block data header. Data blocks shorter than 256 bytes are
1856 * prefixed with a 2-byte header; all others start with a 5-byte
1857 * header.
1858 */
1859 private void writeBlockHeader(int len) throws IOException {
1860 if (len <= 0xFF) {
1861 hbuf[0] = TC_BLOCKDATA;
1862 hbuf[1] = (byte) len;
1863 out.write(hbuf, 0, 2);
1864 } else {
1865 hbuf[0] = TC_BLOCKDATALONG;
1866 Bits.putInt(hbuf, 1, len);
1867 out.write(hbuf, 0, 5);
1868 }
1869 }
1870
1871 /* ----------------- primitive data output methods ----------------- */
1872 /*
1873 * The following methods are equivalent to their counterparts in
1874 * DataOutputStream, except that they partition written data into data
1875 * blocks when in block data mode.
1876 */
1877
1878 public void writeBoolean(boolean v) throws IOException {
1879 if (pos >= MAX_BLOCK_SIZE) {
1880 drain();
1881 }
1882 Bits.putBoolean(buf, pos++, v);
1883 }
1884
1885 public void writeByte(int v) throws IOException {
1886 if (pos >= MAX_BLOCK_SIZE) {
1887 drain();
1888 }
1889 buf[pos++] = (byte) v;
1890 }
1891
1892 public void writeChar(int v) throws IOException {
1893 if (pos + 2 <= MAX_BLOCK_SIZE) {
1894 Bits.putChar(buf, pos, (char) v);
1895 pos += 2;
1896 } else {
1897 dout.writeChar(v);
1898 }
1899 }
1900
1901 public void writeShort(int v) throws IOException {
1902 if (pos + 2 <= MAX_BLOCK_SIZE) {
1903 Bits.putShort(buf, pos, (short) v);
1904 pos += 2;
1905 } else {
1906 dout.writeShort(v);
1907 }
1908 }
1909
1910 public void writeInt(int v) throws IOException {
1911 if (pos + 4 <= MAX_BLOCK_SIZE) {
1912 Bits.putInt(buf, pos, v);
1913 pos += 4;
1914 } else {
1915 dout.writeInt(v);
1916 }
1917 }
1918
1919 public void writeFloat(float v) throws IOException {
1920 if (pos + 4 <= MAX_BLOCK_SIZE) {
1921 Bits.putFloat(buf, pos, v);
1922 pos += 4;
1923 } else {
1924 dout.writeFloat(v);
1925 }
1926 }
1927
1928 public void writeLong(long v) throws IOException {
1929 if (pos + 8 <= MAX_BLOCK_SIZE) {
1930 Bits.putLong(buf, pos, v);
1931 pos += 8;
1932 } else {
1933 dout.writeLong(v);
1934 }
1935 }
1936
1937 public void writeDouble(double v) throws IOException {
1938 if (pos + 8 <= MAX_BLOCK_SIZE) {
1939 Bits.putDouble(buf, pos, v);
1940 pos += 8;
1941 } else {
1942 dout.writeDouble(v);
1943 }
1944 }
1945
1946 public void writeBytes(String s) throws IOException {
1947 int endoff = s.length();
1948 int cpos = 0;
1949 int csize = 0;
1950 for (int off = 0; off < endoff;) {
1951 if (cpos >= csize) {
1952 cpos = 0;
1953 csize = Math.min(endoff - off, CHAR_BUF_SIZE);
1954 s.getChars(off, off + csize, cbuf, 0);
1955 }
1956 if (pos >= MAX_BLOCK_SIZE) {
1957 drain();
1958 }
1959 int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
1960 int stop = pos + n;
1961 while (pos < stop) {
1962 buf[pos++] = (byte) cbuf[cpos++];
1963 }
1964 off += n;
1965 }
1966 }
1967
1968 public void writeChars(String s) throws IOException {
1969 int endoff = s.length();
1970 for (int off = 0; off < endoff;) {
1971 int csize = Math.min(endoff - off, CHAR_BUF_SIZE);
1972 s.getChars(off, off + csize, cbuf, 0);
1973 writeChars(cbuf, 0, csize);
1974 off += csize;
1975 }
1976 }
1977
1978 public void writeUTF(String s) throws IOException {
1979 writeUTF(s, getUTFLength(s));
1980 }
1981
1982 /* -------------- primitive data array output methods -------------- */
1983 /*
1984 * The following methods write out spans of primitive data values.
1985 * Though equivalent to calling the corresponding primitive write
1986 * methods repeatedly, these methods are optimized for writing groups
1987 * of primitive data values more efficiently.
1988 */
1989
1990 void writeBooleans(boolean[] v, int off, int len)
1991 throws IOException {
1992 int endoff = off + len;
1993 while (off < endoff) {
1994 if (pos >= MAX_BLOCK_SIZE) {
1995 drain();
1996 }
1997 int stop = Math.min(endoff, off
1998 + (MAX_BLOCK_SIZE - pos));
1999 while (off < stop) {
2000 Bits.putBoolean(buf, pos++, v[off++]);
2001 }
2002 }
2003 }
2004
2005 void writeChars(char[] v, int off, int len) throws IOException {
2006 int limit = MAX_BLOCK_SIZE - 2;
2007 int endoff = off + len;
2008 while (off < endoff) {
2009 if (pos <= limit) {
2010 int avail = (MAX_BLOCK_SIZE - pos) >> 1;
2011 int stop = Math.min(endoff, off + avail);
2012 while (off < stop) {
2013 Bits.putChar(buf, pos, v[off++]);
2014 pos += 2;
2015 }
2016 } else {
2017 dout.writeChar(v[off++]);
2018 }
2019 }
2020 }
2021
2022 void writeShorts(short[] v, int off, int len)
2023 throws IOException {
2024 int limit = MAX_BLOCK_SIZE - 2;
2025 int endoff = off + len;
2026 while (off < endoff) {
2027 if (pos <= limit) {
2028 int avail = (MAX_BLOCK_SIZE - pos) >> 1;
2029 int stop = Math.min(endoff, off + avail);
2030 while (off < stop) {
2031 Bits.putShort(buf, pos, v[off++]);
2032 pos += 2;
2033 }
2034 } else {
2035 dout.writeShort(v[off++]);
2036 }
2037 }
2038 }
2039
2040 void writeInts(int[] v, int off, int len) throws IOException {
2041 int limit = MAX_BLOCK_SIZE - 4;
2042 int endoff = off + len;
2043 while (off < endoff) {
2044 if (pos <= limit) {
2045 int avail = (MAX_BLOCK_SIZE - pos) >> 2;
2046 int stop = Math.min(endoff, off + avail);
2047 while (off < stop) {
2048 Bits.putInt(buf, pos, v[off++]);
2049 pos += 4;
2050 }
2051 } else {
2052 dout.writeInt(v[off++]);
2053 }
2054 }
2055 }
2056
2057 void writeFloats(float[] v, int off, int len)
2058 throws IOException {
2059 int limit = MAX_BLOCK_SIZE - 4;
2060 int endoff = off + len;
2061 while (off < endoff) {
2062 if (pos <= limit) {
2063 int avail = (MAX_BLOCK_SIZE - pos) >> 2;
2064 int chunklen = Math.min(endoff - off, avail);
2065 floatsToBytes(v, off, buf, pos, chunklen);
2066 off += chunklen;
2067 pos += chunklen << 2;
2068 } else {
2069 dout.writeFloat(v[off++]);
2070 }
2071 }
2072 }
2073
2074 void writeLongs(long[] v, int off, int len) throws IOException {
2075 int limit = MAX_BLOCK_SIZE - 8;
2076 int endoff = off + len;
2077 while (off < endoff) {
2078 if (pos <= limit) {
2079 int avail = (MAX_BLOCK_SIZE - pos) >> 3;
2080 int stop = Math.min(endoff, off + avail);
2081 while (off < stop) {
2082 Bits.putLong(buf, pos, v[off++]);
2083 pos += 8;
2084 }
2085 } else {
2086 dout.writeLong(v[off++]);
2087 }
2088 }
2089 }
2090
2091 void writeDoubles(double[] v, int off, int len)
2092 throws IOException {
2093 int limit = MAX_BLOCK_SIZE - 8;
2094 int endoff = off + len;
2095 while (off < endoff) {
2096 if (pos <= limit) {
2097 int avail = (MAX_BLOCK_SIZE - pos) >> 3;
2098 int chunklen = Math.min(endoff - off, avail);
2099 doublesToBytes(v, off, buf, pos, chunklen);
2100 off += chunklen;
2101 pos += chunklen << 3;
2102 } else {
2103 dout.writeDouble(v[off++]);
2104 }
2105 }
2106 }
2107
2108 /**
2109 * Returns the length in bytes of the UTF encoding of the given string.
2110 */
2111 long getUTFLength(String s) {
2112 int len = s.length();
2113 long utflen = 0;
2114 for (int off = 0; off < len;) {
2115 int csize = Math.min(len - off, CHAR_BUF_SIZE);
2116 s.getChars(off, off + csize, cbuf, 0);
2117 for (int cpos = 0; cpos < csize; cpos++) {
2118 char c = cbuf[cpos];
2119 if (c >= 0x0001 && c <= 0x007F) {
2120 utflen++;
2121 } else if (c > 0x07FF) {
2122 utflen += 3;
2123 } else {
2124 utflen += 2;
2125 }
2126 }
2127 off += csize;
2128 }
2129 return utflen;
2130 }
2131
2132 /**
2133 * Writes the given string in UTF format. This method is used in
2134 * situations where the UTF encoding length of the string is already
2135 * known; specifying it explicitly avoids a prescan of the string to
2136 * determine its UTF length.
2137 */
2138 void writeUTF(String s, long utflen) throws IOException {
2139 if (utflen > 0xFFFFL) {
2140 throw new UTFDataFormatException();
2141 }
2142 writeShort((int) utflen);
2143 if (utflen == (long) s.length()) {
2144 writeBytes(s);
2145 } else {
2146 writeUTFBody(s);
2147 }
2148 }
2149
2150 /**
2151 * Writes given string in "long" UTF format. "Long" UTF format is
2152 * identical to standard UTF, except that it uses an 8 byte header
2153 * (instead of the standard 2 bytes) to convey the UTF encoding length.
2154 */
2155 void writeLongUTF(String s) throws IOException {
2156 writeLongUTF(s, getUTFLength(s));
2157 }
2158
2159 /**
2160 * Writes given string in "long" UTF format, where the UTF encoding
2161 * length of the string is already known.
2162 */
2163 void writeLongUTF(String s, long utflen) throws IOException {
2164 writeLong(utflen);
2165 if (utflen == (long) s.length()) {
2166 writeBytes(s);
2167 } else {
2168 writeUTFBody(s);
2169 }
2170 }
2171
2172 /**
2173 * Writes the "body" (i.e., the UTF representation minus the 2-byte or
2174 * 8-byte length header) of the UTF encoding for the given string.
2175 */
2176 private void writeUTFBody(String s) throws IOException {
2177 int limit = MAX_BLOCK_SIZE - 3;
2178 int len = s.length();
2179 for (int off = 0; off < len;) {
2180 int csize = Math.min(len - off, CHAR_BUF_SIZE);
2181 s.getChars(off, off + csize, cbuf, 0);
2182 for (int cpos = 0; cpos < csize; cpos++) {
2183 char c = cbuf[cpos];
2184 if (pos <= limit) {
2185 if (c <= 0x007F && c != 0) {
2186 buf[pos++] = (byte) c;
2187 } else if (c > 0x07FF) {
2188 buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));
2189 buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
2190 buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));
2191 pos += 3;
2192 } else {
2193 buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));
2194 buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));
2195 pos += 2;
2196 }
2197 } else { // write one byte at a time to normalize block
2198 if (c <= 0x007F && c != 0) {
2199 write(c);
2200 } else if (c > 0x07FF) {
2201 write(0xE0 | ((c >> 12) & 0x0F));
2202 write(0x80 | ((c >> 6) & 0x3F));
2203 write(0x80 | ((c >> 0) & 0x3F));
2204 } else {
2205 write(0xC0 | ((c >> 6) & 0x1F));
2206 write(0x80 | ((c >> 0) & 0x3F));
2207 }
2208 }
2209 }
2210 off += csize;
2211 }
2212 }
2213 }
2214
2215 /**
2216 * Lightweight identity hash table which maps objects to integer handles,
2217 * assigned in ascending order.
2218 */
2219 private static class HandleTable {
2220
2221 /* number of mappings in table/next available handle */
2222 private int size;
2223 /* size threshold determining when to expand hash spine */
2224 private int threshold;
2225 /* factor for computing size threshold */
2226 private final float loadFactor;
2227 /* maps hash value -> candidate handle value */
2228 private int[] spine;
2229 /* maps handle value -> next candidate handle value */
2230 private int[] next;
2231 /* maps handle value -> associated object */
2232 private Object[] objs;
2233
2234 /**
2235 * Creates new HandleTable with given capacity and load factor.
2236 */
2237 HandleTable(int initialCapacity, float loadFactor) {
2238 this .loadFactor = loadFactor;
2239 spine = new int[initialCapacity];
2240 next = new int[initialCapacity];
2241 objs = new Object[initialCapacity];
2242 threshold = (int) (initialCapacity * loadFactor);
2243 clear();
2244 }
2245
2246 /**
2247 * Assigns next available handle to given object, and returns handle
2248 * value. Handles are assigned in ascending order starting at 0.
2249 */
2250 int assign(Object obj) {
2251 if (size >= next.length) {
2252 growEntries();
2253 }
2254 if (size >= threshold) {
2255 growSpine();
2256 }
2257 insert(obj, size);
2258 return size++;
2259 }
2260
2261 /**
2262 * Looks up and returns handle associated with given object, or -1 if
2263 * no mapping found.
2264 */
2265 int lookup(Object obj) {
2266 if (size == 0) {
2267 return -1;
2268 }
2269 int index = hash(obj) % spine.length;
2270 for (int i = spine[index]; i >= 0; i = next[i]) {
2271 if (objs[i] == obj) {
2272 return i;
2273 }
2274 }
2275 return -1;
2276 }
2277
2278 /**
2279 * Resets table to its initial (empty) state.
2280 */
2281 void clear() {
2282 Arrays.fill(spine, -1);
2283 Arrays.fill(objs, 0, size, null);
2284 size = 0;
2285 }
2286
2287 /**
2288 * Returns the number of mappings currently in table.
2289 */
2290 int size() {
2291 return size;
2292 }
2293
2294 /**
2295 * Inserts mapping object -> handle mapping into table. Assumes table
2296 * is large enough to accommodate new mapping.
2297 */
2298 private void insert(Object obj, int handle) {
2299 int index = hash(obj) % spine.length;
2300 objs[handle] = obj;
2301 next[handle] = spine[index];
2302 spine[index] = handle;
2303 }
2304
2305 /**
2306 * Expands the hash "spine" -- equivalent to increasing the number of
2307 * buckets in a conventional hash table.
2308 */
2309 private void growSpine() {
2310 spine = new int[(spine.length << 1) + 1];
2311 threshold = (int) (spine.length * loadFactor);
2312 Arrays.fill(spine, -1);
2313 for (int i = 0; i < size; i++) {
2314 insert(objs[i], i);
2315 }
2316 }
2317
2318 /**
2319 * Increases hash table capacity by lengthening entry arrays.
2320 */
2321 private void growEntries() {
2322 int newLength = (next.length << 1) + 1;
2323 int[] newNext = new int[newLength];
2324 System.arraycopy(next, 0, newNext, 0, size);
2325 next = newNext;
2326
2327 Object[] newObjs = new Object[newLength];
2328 System.arraycopy(objs, 0, newObjs, 0, size);
2329 objs = newObjs;
2330 }
2331
2332 /**
2333 * Returns hash value for given object.
2334 */
2335 private int hash(Object obj) {
2336 return System.identityHashCode(obj) & 0x7FFFFFFF;
2337 }
2338 }
2339
2340 /**
2341 * Lightweight identity hash table which maps objects to replacement
2342 * objects.
2343 */
2344 private static class ReplaceTable {
2345
2346 /* maps object -> index */
2347 private final HandleTable htab;
2348 /* maps index -> replacement object */
2349 private Object[] reps;
2350
2351 /**
2352 * Creates new ReplaceTable with given capacity and load factor.
2353 */
2354 ReplaceTable(int initialCapacity, float loadFactor) {
2355 htab = new HandleTable(initialCapacity, loadFactor);
2356 reps = new Object[initialCapacity];
2357 }
2358
2359 /**
2360 * Enters mapping from object to replacement object.
2361 */
2362 void assign(Object obj, Object rep) {
2363 int index = htab.assign(obj);
2364 while (index >= reps.length) {
2365 grow();
2366 }
2367 reps[index] = rep;
2368 }
2369
2370 /**
2371 * Looks up and returns replacement for given object. If no
2372 * replacement is found, returns the lookup object itself.
2373 */
2374 Object lookup(Object obj) {
2375 int index = htab.lookup(obj);
2376 return (index >= 0) ? reps[index] : obj;
2377 }
2378
2379 /**
2380 * Resets table to its initial (empty) state.
2381 */
2382 void clear() {
2383 Arrays.fill(reps, 0, htab.size(), null);
2384 htab.clear();
2385 }
2386
2387 /**
2388 * Returns the number of mappings currently in table.
2389 */
2390 int size() {
2391 return htab.size();
2392 }
2393
2394 /**
2395 * Increases table capacity.
2396 */
2397 private void grow() {
2398 Object[] newReps = new Object[(reps.length << 1) + 1];
2399 System.arraycopy(reps, 0, newReps, 0, reps.length);
2400 reps = newReps;
2401 }
2402 }
2403
2404 /**
2405 * Stack to keep debug information about the state of the
2406 * serialization process, for embedding in exception messages.
2407 */
2408 private static class DebugTraceInfoStack {
2409 private final List<String> stack;
2410
2411 DebugTraceInfoStack() {
2412 stack = new ArrayList<String>();
2413 }
2414
2415 /**
2416 * Removes all of the elements from enclosed list.
2417 */
2418 void clear() {
2419 stack.clear();
2420 }
2421
2422 /**
2423 * Removes the object at the top of enclosed list.
2424 */
2425 void pop() {
2426 stack.remove(stack.size() - 1);
2427 }
2428
2429 /**
2430 * Pushes a String onto the top of enclosed list.
2431 */
2432 void push(String entry) {
2433 stack.add("\t- " + entry);
2434 }
2435
2436 /**
2437 * Returns a string representation of this object
2438 */
2439 public String toString() {
2440 StringBuilder buffer = new StringBuilder();
2441 if (!stack.isEmpty()) {
2442 for (int i = stack.size(); i > 0; i--) {
2443 buffer.append(stack.get(i - 1)
2444 + ((i != 1) ? "\n" : ""));
2445 }
2446 }
2447 return buffer.toString();
2448 }
2449 }
2450
2451 }
|