2 * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.List;
31 import org.apidesign.bck2brwsr.emul.lang.System;
34 * An ObjectOutputStream writes primitive data types and graphs of Java objects
35 * to an OutputStream. The objects can be read (reconstituted) using an
36 * ObjectInputStream. Persistent storage of objects can be accomplished by
37 * using a file for the stream. If the stream is a network socket stream, the
38 * objects can be reconstituted on another host or in another process.
40 * <p>Only objects that support the java.io.Serializable interface can be
41 * written to streams. The class of each serializable object is encoded
42 * including the class name and signature of the class, the values of the
43 * object's fields and arrays, and the closure of any other objects referenced
44 * from the initial objects.
46 * <p>The method writeObject is used to write an object to the stream. Any
47 * object, including Strings and arrays, is written with writeObject. Multiple
48 * objects or primitives can be written to the stream. The objects must be
49 * read back from the corresponding ObjectInputstream with the same types and
50 * in the same order as they were written.
52 * <p>Primitive data types can also be written to the stream using the
53 * appropriate methods from DataOutput. Strings can also be written using the
56 * <p>The default serialization mechanism for an object writes the class of the
57 * object, the class signature, and the values of all non-transient and
58 * non-static fields. References to other objects (except in transient or
59 * static fields) cause those objects to be written also. Multiple references
60 * to a single object are encoded using a reference sharing mechanism so that
61 * graphs of objects can be restored to the same shape as when the original was
64 * <p>For example to write an object that can be read by the example in
68 * FileOutputStream fos = new FileOutputStream("t.tmp");
69 * ObjectOutputStream oos = new ObjectOutputStream(fos);
71 * oos.writeInt(12345);
72 * oos.writeObject("Today");
73 * oos.writeObject(new Date());
78 * <p>Classes that require special handling during the serialization and
79 * deserialization process must implement special methods with these exact
83 * private void readObject(java.io.ObjectInputStream stream)
84 * throws IOException, ClassNotFoundException;
85 * private void writeObject(java.io.ObjectOutputStream stream)
87 * private void readObjectNoData()
88 * throws ObjectStreamException;
91 * <p>The writeObject method is responsible for writing the state of the object
92 * for its particular class so that the corresponding readObject method can
93 * restore it. The method does not need to concern itself with the state
94 * belonging to the object's superclasses or subclasses. State is saved by
95 * writing the individual fields to the ObjectOutputStream using the
96 * writeObject method or by using the methods for primitive data types
97 * supported by DataOutput.
99 * <p>Serialization does not write out the fields of any object that does not
100 * implement the java.io.Serializable interface. Subclasses of Objects that
101 * are not serializable can be serializable. In this case the non-serializable
102 * class must have a no-arg constructor to allow its fields to be initialized.
103 * In this case it is the responsibility of the subclass to save and restore
104 * the state of the non-serializable class. It is frequently the case that the
105 * fields of that class are accessible (public, package, or protected) or that
106 * there are get and set methods that can be used to restore the state.
108 * <p>Serialization of an object can be prevented by implementing writeObject
109 * and readObject methods that throw the NotSerializableException. The
110 * exception will be caught by the ObjectOutputStream and abort the
111 * serialization process.
113 * <p>Implementing the Externalizable interface allows the object to assume
114 * complete control over the contents and format of the object's serialized
115 * form. The methods of the Externalizable interface, writeExternal and
116 * readExternal, are called to save and restore the objects state. When
117 * implemented by a class they can write and read their own state using all of
118 * the methods of ObjectOutput and ObjectInput. It is the responsibility of
119 * the objects to handle any versioning that occurs.
121 * <p>Enum constants are serialized differently than ordinary serializable or
122 * externalizable objects. The serialized form of an enum constant consists
123 * solely of its name; field values of the constant are not transmitted. To
124 * serialize an enum constant, ObjectOutputStream writes the string returned by
125 * the constant's name method. Like other serializable or externalizable
126 * objects, enum constants can function as the targets of back references
127 * appearing subsequently in the serialization stream. The process by which
128 * enum constants are serialized cannot be customized; any class-specific
129 * writeObject and writeReplace methods defined by enum types are ignored
130 * during serialization. Similarly, any serialPersistentFields or
131 * serialVersionUID field declarations are also ignored--all enum types have a
132 * fixed serialVersionUID of 0L.
134 * <p>Primitive data, excluding serializable fields and externalizable data, is
135 * written to the ObjectOutputStream in block-data records. A block data record
136 * is composed of a header and data. The block data header consists of a marker
137 * and the number of bytes to follow the header. Consecutive primitive data
138 * writes are merged into one block-data record. The blocking factor used for
139 * a block-data record will be 1024 bytes. Each block-data record will be
140 * filled up to 1024 bytes, or be written whenever there is a termination of
141 * block-data mode. Calls to the ObjectOutputStream methods writeObject,
142 * defaultWriteObject and writeFields initially terminate any existing
145 * @author Mike Warres
146 * @author Roger Riggs
147 * @see java.io.DataOutput
148 * @see java.io.ObjectInputStream
149 * @see java.io.Serializable
150 * @see java.io.Externalizable
151 * @see <a href="../../../platform/serialization/spec/output.html">Object Serialization Specification, Section 2, Object Output Classes</a>
154 public class ObjectOutputStream
155 extends OutputStream implements ObjectOutput, ObjectStreamConstants
157 /** filter stream for handling block data conversion */
158 private final BlockDataOutputStream bout;
159 /** obj -> wire handle map */
160 private final HandleTable handles;
161 /** obj -> replacement obj map */
162 private final ReplaceTable subs;
163 /** stream protocol version */
164 private int protocol = PROTOCOL_VERSION_2;
165 /** recursion depth */
168 /** buffer for writing primitive field values */
169 private byte[] primVals;
171 /** if true, invoke writeObjectOverride() instead of writeObject() */
172 private final boolean enableOverride;
173 /** if true, invoke replaceObject() */
174 private boolean enableReplace;
176 // values below valid only during upcalls to writeObject()/writeExternal()
178 * Context during upcalls to class-defined writeObject methods; holds
179 * object currently being serialized and descriptor for current class.
180 * Null when not during writeObject upcall.
182 private Object curContext;
183 /** current PutField object */
184 private PutFieldImpl curPut;
186 /** custom storage for debug trace info */
187 private final DebugTraceInfoStack debugInfoStack;
190 * value of "sun.io.serialization.extendedDebugInfo" property,
191 * as true or false for extended information about exception's place
193 private static final boolean extendedDebugInfo = false;
196 * Creates an ObjectOutputStream that writes to the specified OutputStream.
197 * This constructor writes the serialization stream header to the
198 * underlying stream; callers may wish to flush the stream immediately to
199 * ensure that constructors for receiving ObjectInputStreams will not block
200 * when reading the header.
202 * <p>If a security manager is installed, this constructor will check for
203 * the "enableSubclassImplementation" SerializablePermission when invoked
204 * directly or indirectly by the constructor of a subclass which overrides
205 * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared
208 * @param out output stream to write to
209 * @throws IOException if an I/O error occurs while writing stream header
210 * @throws SecurityException if untrusted subclass illegally overrides
211 * security-sensitive methods
212 * @throws NullPointerException if <code>out</code> is <code>null</code>
214 * @see ObjectOutputStream#ObjectOutputStream()
215 * @see ObjectOutputStream#putFields()
216 * @see ObjectInputStream#ObjectInputStream(InputStream)
218 public ObjectOutputStream(OutputStream out) throws IOException {
220 bout = new BlockDataOutputStream(out);
221 handles = new HandleTable(10, (float) 3.00);
222 subs = new ReplaceTable(10, (float) 3.00);
223 enableOverride = false;
225 bout.setBlockDataMode(true);
226 if (extendedDebugInfo) {
227 debugInfoStack = new DebugTraceInfoStack();
229 debugInfoStack = null;
234 * Provide a way for subclasses that are completely reimplementing
235 * ObjectOutputStream to not have to allocate private data just used by
236 * this implementation of ObjectOutputStream.
238 * <p>If there is a security manager installed, this method first calls the
239 * security manager's <code>checkPermission</code> method with a
240 * <code>SerializablePermission("enableSubclassImplementation")</code>
241 * permission to ensure it's ok to enable subclassing.
243 * @throws SecurityException if a security manager exists and its
244 * <code>checkPermission</code> method denies enabling
246 * @see SecurityManager#checkPermission
247 * @see java.io.SerializablePermission
249 protected ObjectOutputStream() throws IOException, SecurityException {
250 throw new SecurityException();
254 * Specify stream protocol version to use when writing the stream.
256 * <p>This routine provides a hook to enable the current version of
257 * Serialization to write in a format that is backwards compatible to a
258 * previous version of the stream format.
260 * <p>Every effort will be made to avoid introducing additional
261 * backwards incompatibilities; however, sometimes there is no
264 * @param version use ProtocolVersion from java.io.ObjectStreamConstants.
265 * @throws IllegalStateException if called after any objects
266 * have been serialized.
267 * @throws IllegalArgumentException if invalid version is passed in.
268 * @throws IOException if I/O errors occur
269 * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
270 * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2
273 public void useProtocolVersion(int version) throws IOException {
274 if (handles.size() != 0) {
275 // REMIND: implement better check for pristine stream?
276 throw new IllegalStateException("stream non-empty");
279 case PROTOCOL_VERSION_1:
280 case PROTOCOL_VERSION_2:
285 throw new IllegalArgumentException(
286 "unknown version: " + version);
291 * Write the specified object to the ObjectOutputStream. The class of the
292 * object, the signature of the class, and the values of the non-transient
293 * and non-static fields of the class and all of its supertypes are
294 * written. Default serialization for a class can be overridden using the
295 * writeObject and the readObject methods. Objects referenced by this
296 * object are written transitively so that a complete equivalent graph of
297 * objects can be reconstructed by an ObjectInputStream.
299 * <p>Exceptions are thrown for problems with the OutputStream and for
300 * classes that should not be serialized. All exceptions are fatal to the
301 * OutputStream, which is left in an indeterminate state, and it is up to
302 * the caller to ignore or recover the stream state.
304 * @throws InvalidClassException Something is wrong with a class used by
306 * @throws NotSerializableException Some object to be serialized does not
307 * implement the java.io.Serializable interface.
308 * @throws IOException Any exception thrown by the underlying
311 public final void writeObject(Object obj) throws IOException {
312 if (enableOverride) {
313 writeObjectOverride(obj);
317 writeObject0(obj, false);
318 } catch (IOException ex) {
320 writeFatalException(ex);
327 * Method used by subclasses to override the default writeObject method.
328 * This method is called by trusted subclasses of ObjectInputStream that
329 * constructed ObjectInputStream using the protected no-arg constructor.
330 * The subclass is expected to provide an override method with the modifier
333 * @param obj object to be written to the underlying stream
334 * @throws IOException if there are I/O errors while writing to the
336 * @see #ObjectOutputStream()
337 * @see #writeObject(Object)
340 protected void writeObjectOverride(Object obj) throws IOException {
344 * Writes an "unshared" object to the ObjectOutputStream. This method is
345 * identical to writeObject, except that it always writes the given object
346 * as a new, unique object in the stream (as opposed to a back-reference
347 * pointing to a previously serialized instance). Specifically:
349 * <li>An object written via writeUnshared is always serialized in the
350 * same manner as a newly appearing object (an object that has not
351 * been written to the stream yet), regardless of whether or not the
352 * object has been written previously.
354 * <li>If writeObject is used to write an object that has been previously
355 * written with writeUnshared, the previous writeUnshared operation
356 * is treated as if it were a write of a separate object. In other
357 * words, ObjectOutputStream will never generate back-references to
358 * object data written by calls to writeUnshared.
360 * While writing an object via writeUnshared does not in itself guarantee a
361 * unique reference to the object when it is deserialized, it allows a
362 * single object to be defined multiple times in a stream, so that multiple
363 * calls to readUnshared by the receiver will not conflict. Note that the
364 * rules described above only apply to the base-level object written with
365 * writeUnshared, and not to any transitively referenced sub-objects in the
366 * object graph to be serialized.
368 * <p>ObjectOutputStream subclasses which override this method can only be
369 * constructed in security contexts possessing the
370 * "enableSubclassImplementation" SerializablePermission; any attempt to
371 * instantiate such a subclass without this permission will cause a
372 * SecurityException to be thrown.
374 * @param obj object to write to stream
375 * @throws NotSerializableException if an object in the graph to be
376 * serialized does not implement the Serializable interface
377 * @throws InvalidClassException if a problem exists with the class of an
378 * object to be serialized
379 * @throws IOException if an I/O error occurs during serialization
382 public void writeUnshared(Object obj) throws IOException {
384 writeObject0(obj, true);
385 } catch (IOException ex) {
387 writeFatalException(ex);
394 * Write the non-static and non-transient fields of the current class to
395 * this stream. This may only be called from the writeObject method of the
396 * class being serialized. It will throw the NotActiveException if it is
399 * @throws IOException if I/O errors occur while writing to the underlying
400 * <code>OutputStream</code>
402 public void defaultWriteObject() throws IOException {
403 if ( curContext == null ) {
404 throw new NotActiveException("not in call to writeObject");
406 Object curObj = null; // curContext.getObj();
407 ObjectStreamClass curDesc = null; // curContext.getDesc();
408 bout.setBlockDataMode(false);
409 defaultWriteFields(curObj, curDesc);
410 bout.setBlockDataMode(true);
414 * Retrieve the object used to buffer persistent fields to be written to
415 * the stream. The fields will be written to the stream when writeFields
418 * @return an instance of the class Putfield that holds the serializable
420 * @throws IOException if I/O errors occur
423 public ObjectOutputStream.PutField putFields() throws IOException {
424 if (curPut == null) {
425 if (curContext == null) {
426 throw new NotActiveException("not in call to writeObject");
428 Object curObj = null; // curContext.getObj();
429 ObjectStreamClass curDesc = null; // curContext.getDesc();
430 curPut = new PutFieldImpl(curDesc);
436 * Write the buffered fields to the stream.
438 * @throws IOException if I/O errors occur while writing to the underlying
440 * @throws NotActiveException Called when a classes writeObject method was
441 * not called to write the state of the object.
444 public void writeFields() throws IOException {
445 if (curPut == null) {
446 throw new NotActiveException("no current PutField object");
448 bout.setBlockDataMode(false);
449 curPut.writeFields();
450 bout.setBlockDataMode(true);
454 * Reset will disregard the state of any objects already written to the
455 * stream. The state is reset to be the same as a new ObjectOutputStream.
456 * The current point in the stream is marked as reset so the corresponding
457 * ObjectInputStream will be reset at the same point. Objects previously
458 * written to the stream will not be refered to as already being in the
459 * stream. They will be written to the stream again.
461 * @throws IOException if reset() is invoked while serializing an object.
463 public void reset() throws IOException {
465 throw new IOException("stream active");
467 bout.setBlockDataMode(false);
468 bout.writeByte(TC_RESET);
470 bout.setBlockDataMode(true);
474 * Subclasses may implement this method to allow class data to be stored in
475 * the stream. By default this method does nothing. The corresponding
476 * method in ObjectInputStream is resolveClass. This method is called
477 * exactly once for each unique class in the stream. The class name and
478 * signature will have already been written to the stream. This method may
479 * make free use of the ObjectOutputStream to save any representation of
480 * the class it deems suitable (for example, the bytes of the class file).
481 * The resolveClass method in the corresponding subclass of
482 * ObjectInputStream must read and use any data or objects written by
485 * @param cl the class to annotate custom data for
486 * @throws IOException Any exception thrown by the underlying
489 protected void annotateClass(Class<?> cl) throws IOException {
493 * Subclasses may implement this method to store custom data in the stream
494 * along with descriptors for dynamic proxy classes.
496 * <p>This method is called exactly once for each unique proxy class
497 * descriptor in the stream. The default implementation of this method in
498 * <code>ObjectOutputStream</code> does nothing.
500 * <p>The corresponding method in <code>ObjectInputStream</code> is
501 * <code>resolveProxyClass</code>. For a given subclass of
502 * <code>ObjectOutputStream</code> that overrides this method, the
503 * <code>resolveProxyClass</code> method in the corresponding subclass of
504 * <code>ObjectInputStream</code> must read any data or objects written by
505 * <code>annotateProxyClass</code>.
507 * @param cl the proxy class to annotate custom data for
508 * @throws IOException any exception thrown by the underlying
509 * <code>OutputStream</code>
510 * @see ObjectInputStream#resolveProxyClass(String[])
513 protected void annotateProxyClass(Class<?> cl) throws IOException {
517 * This method will allow trusted subclasses of ObjectOutputStream to
518 * substitute one object for another during serialization. Replacing
519 * objects is disabled until enableReplaceObject is called. The
520 * enableReplaceObject method checks that the stream requesting to do
521 * replacement can be trusted. The first occurrence of each object written
522 * into the serialization stream is passed to replaceObject. Subsequent
523 * references to the object are replaced by the object returned by the
524 * original call to replaceObject. To ensure that the private state of
525 * objects is not unintentionally exposed, only trusted streams may use
528 * <p>The ObjectOutputStream.writeObject method takes a parameter of type
529 * Object (as opposed to type Serializable) to allow for cases where
530 * non-serializable objects are replaced by serializable ones.
532 * <p>When a subclass is replacing objects it must insure that either a
533 * complementary substitution must be made during deserialization or that
534 * the substituted object is compatible with every field where the
535 * reference will be stored. Objects whose type is not a subclass of the
536 * type of the field or array element abort the serialization by raising an
537 * exception and the object is not be stored.
539 * <p>This method is called only once when each object is first
540 * encountered. All subsequent references to the object will be redirected
541 * to the new object. This method should return the object to be
542 * substituted or the original object.
544 * <p>Null can be returned as the object to be substituted, but may cause
545 * NullReferenceException in classes that contain references to the
546 * original object since they may be expecting an object instead of
549 * @param obj the object to be replaced
550 * @return the alternate object that replaced the specified one
551 * @throws IOException Any exception thrown by the underlying
554 protected Object replaceObject(Object obj) throws IOException {
559 * Enable the stream to do replacement of objects in the stream. When
560 * enabled, the replaceObject method is called for every object being
563 * <p>If <code>enable</code> is true, and there is a security manager
564 * installed, this method first calls the security manager's
565 * <code>checkPermission</code> method with a
566 * <code>SerializablePermission("enableSubstitution")</code> permission to
567 * ensure it's ok to enable the stream to do replacement of objects in the
570 * @param enable boolean parameter to enable replacement of objects
571 * @return the previous setting before this method was invoked
572 * @throws SecurityException if a security manager exists and its
573 * <code>checkPermission</code> method denies enabling the stream
574 * to do replacement of objects in the stream.
575 * @see SecurityManager#checkPermission
576 * @see java.io.SerializablePermission
578 protected boolean enableReplaceObject(boolean enable)
579 throws SecurityException
581 throw new SecurityException();
585 * The writeStreamHeader method is provided so subclasses can append or
586 * prepend their own header to the stream. It writes the magic number and
587 * version to the stream.
589 * @throws IOException if I/O errors occur while writing to the underlying
592 protected void writeStreamHeader() throws IOException {
593 bout.writeShort(STREAM_MAGIC);
594 bout.writeShort(STREAM_VERSION);
598 * Write the specified class descriptor to the ObjectOutputStream. Class
599 * descriptors are used to identify the classes of objects written to the
600 * stream. Subclasses of ObjectOutputStream may override this method to
601 * customize the way in which class descriptors are written to the
602 * serialization stream. The corresponding method in ObjectInputStream,
603 * <code>readClassDescriptor</code>, should then be overridden to
604 * reconstitute the class descriptor from its custom stream representation.
605 * By default, this method writes class descriptors according to the format
606 * defined in the Object Serialization specification.
608 * <p>Note that this method will only be called if the ObjectOutputStream
609 * is not using the old serialization stream format (set by calling
610 * ObjectOutputStream's <code>useProtocolVersion</code> method). If this
611 * serialization stream is using the old format
612 * (<code>PROTOCOL_VERSION_1</code>), the class descriptor will be written
613 * internally in a manner that cannot be overridden or customized.
615 * @param desc class descriptor to write to the stream
616 * @throws IOException If an I/O error has occurred.
617 * @see java.io.ObjectInputStream#readClassDescriptor()
618 * @see #useProtocolVersion(int)
619 * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
622 protected void writeClassDescriptor(ObjectStreamClass desc)
625 desc.writeNonProxy(this);
629 * Writes a byte. This method will block until the byte is actually
632 * @param val the byte to be written to the stream
633 * @throws IOException If an I/O error has occurred.
635 public void write(int val) throws IOException {
640 * Writes an array of bytes. This method will block until the bytes are
643 * @param buf the data to be written
644 * @throws IOException If an I/O error has occurred.
646 public void write(byte[] buf) throws IOException {
647 bout.write(buf, 0, buf.length, false);
651 * Writes a sub array of bytes.
653 * @param buf the data to be written
654 * @param off the start offset in the data
655 * @param len the number of bytes that are written
656 * @throws IOException If an I/O error has occurred.
658 public void write(byte[] buf, int off, int len) throws IOException {
660 throw new NullPointerException();
662 int endoff = off + len;
663 if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
664 throw new IndexOutOfBoundsException();
666 bout.write(buf, off, len, false);
670 * Flushes the stream. This will write any buffered output bytes and flush
671 * through to the underlying stream.
673 * @throws IOException If an I/O error has occurred.
675 public void flush() throws IOException {
680 * Drain any buffered data in ObjectOutputStream. Similar to flush but
681 * does not propagate the flush to the underlying stream.
683 * @throws IOException if I/O errors occur while writing to the underlying
686 protected void drain() throws IOException {
691 * Closes the stream. This method must be called to release any resources
692 * associated with the stream.
694 * @throws IOException If an I/O error has occurred.
696 public void close() throws IOException {
705 * @param val the boolean to be written
706 * @throws IOException if I/O errors occur while writing to the underlying
709 public void writeBoolean(boolean val) throws IOException {
710 bout.writeBoolean(val);
714 * Writes an 8 bit byte.
716 * @param val the byte value to be written
717 * @throws IOException if I/O errors occur while writing to the underlying
720 public void writeByte(int val) throws IOException {
725 * Writes a 16 bit short.
727 * @param val the short value to be written
728 * @throws IOException if I/O errors occur while writing to the underlying
731 public void writeShort(int val) throws IOException {
732 bout.writeShort(val);
736 * Writes a 16 bit char.
738 * @param val the char value to be written
739 * @throws IOException if I/O errors occur while writing to the underlying
742 public void writeChar(int val) throws IOException {
747 * Writes a 32 bit int.
749 * @param val the integer value to be written
750 * @throws IOException if I/O errors occur while writing to the underlying
753 public void writeInt(int val) throws IOException {
758 * Writes a 64 bit long.
760 * @param val the long value to be written
761 * @throws IOException if I/O errors occur while writing to the underlying
764 public void writeLong(long val) throws IOException {
769 * Writes a 32 bit float.
771 * @param val the float value to be written
772 * @throws IOException if I/O errors occur while writing to the underlying
775 public void writeFloat(float val) throws IOException {
776 bout.writeFloat(val);
780 * Writes a 64 bit double.
782 * @param val the double value to be written
783 * @throws IOException if I/O errors occur while writing to the underlying
786 public void writeDouble(double val) throws IOException {
787 bout.writeDouble(val);
791 * Writes a String as a sequence of bytes.
793 * @param str the String of bytes to be written
794 * @throws IOException if I/O errors occur while writing to the underlying
797 public void writeBytes(String str) throws IOException {
798 bout.writeBytes(str);
802 * Writes a String as a sequence of chars.
804 * @param str the String of chars to be written
805 * @throws IOException if I/O errors occur while writing to the underlying
808 public void writeChars(String str) throws IOException {
809 bout.writeChars(str);
813 * Primitive data write of this String in
814 * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
815 * format. Note that there is a
816 * significant difference between writing a String into the stream as
817 * primitive data or as an Object. A String instance written by writeObject
818 * is written into the stream as a String initially. Future writeObject()
819 * calls write references to the string into the stream.
821 * @param str the String to be written
822 * @throws IOException if I/O errors occur while writing to the underlying
825 public void writeUTF(String str) throws IOException {
830 * Provide programmatic access to the persistent fields to be written
835 public static abstract class PutField {
838 * Put the value of the named boolean field into the persistent field.
840 * @param name the name of the serializable field
841 * @param val the value to assign to the field
842 * @throws IllegalArgumentException if <code>name</code> does not
843 * match the name of a serializable field for the class whose fields
844 * are being written, or if the type of the named field is not
845 * <code>boolean</code>
847 public abstract void put(String name, boolean val);
850 * Put the value of the named byte field into the persistent field.
852 * @param name the name of the serializable field
853 * @param val the value to assign to the field
854 * @throws IllegalArgumentException if <code>name</code> does not
855 * match the name of a serializable field for the class whose fields
856 * are being written, or if the type of the named field is not
859 public abstract void put(String name, byte val);
862 * Put the value of the named char field into the persistent field.
864 * @param name the name of the serializable field
865 * @param val the value to assign to the field
866 * @throws IllegalArgumentException if <code>name</code> does not
867 * match the name of a serializable field for the class whose fields
868 * are being written, or if the type of the named field is not
871 public abstract void put(String name, char val);
874 * Put the value of the named short field into the persistent field.
876 * @param name the name of the serializable field
877 * @param val the value to assign to the field
878 * @throws IllegalArgumentException if <code>name</code> does not
879 * match the name of a serializable field for the class whose fields
880 * are being written, or if the type of the named field is not
883 public abstract void put(String name, short val);
886 * Put the value of the named int field into the persistent field.
888 * @param name the name of the serializable field
889 * @param val the value to assign to the field
890 * @throws IllegalArgumentException if <code>name</code> does not
891 * match the name of a serializable field for the class whose fields
892 * are being written, or if the type of the named field is not
895 public abstract void put(String name, int val);
898 * Put the value of the named long field into the persistent field.
900 * @param name the name of the serializable field
901 * @param val the value to assign to the field
902 * @throws IllegalArgumentException if <code>name</code> does not
903 * match the name of a serializable field for the class whose fields
904 * are being written, or if the type of the named field is not
907 public abstract void put(String name, long val);
910 * Put the value of the named float field into the persistent field.
912 * @param name the name of the serializable field
913 * @param val the value to assign to the field
914 * @throws IllegalArgumentException if <code>name</code> does not
915 * match the name of a serializable field for the class whose fields
916 * are being written, or if the type of the named field is not
919 public abstract void put(String name, float val);
922 * Put the value of the named double field into the persistent field.
924 * @param name the name of the serializable field
925 * @param val the value to assign to the field
926 * @throws IllegalArgumentException if <code>name</code> does not
927 * match the name of a serializable field for the class whose fields
928 * are being written, or if the type of the named field is not
929 * <code>double</code>
931 public abstract void put(String name, double val);
934 * Put the value of the named Object field into the persistent field.
936 * @param name the name of the serializable field
937 * @param val the value to assign to the field
938 * (which may be <code>null</code>)
939 * @throws IllegalArgumentException if <code>name</code> does not
940 * match the name of a serializable field for the class whose fields
941 * are being written, or if the type of the named field is not a
944 public abstract void put(String name, Object val);
947 * Write the data and fields to the specified ObjectOutput stream,
948 * which must be the same stream that produced this
949 * <code>PutField</code> object.
951 * @param out the stream to write the data and fields to
952 * @throws IOException if I/O errors occur while writing to the
954 * @throws IllegalArgumentException if the specified stream is not
955 * the same stream that produced this <code>PutField</code>
957 * @deprecated This method does not write the values contained by this
958 * <code>PutField</code> object in a proper format, and may
959 * result in corruption of the serialization stream. The
960 * correct way to write <code>PutField</code> data is by
961 * calling the {@link java.io.ObjectOutputStream#writeFields()}
965 public abstract void write(ObjectOutput out) throws IOException;
970 * Returns protocol version in use.
972 int getProtocolVersion() {
977 * Writes string without allowing it to be replaced in stream. Used by
978 * ObjectStreamClass to write class descriptor type strings.
980 void writeTypeString(String str) throws IOException {
984 } else if ((handle = handles.lookup(str)) != -1) {
987 writeString(str, false);
992 * Verifies that this (possibly subclass) instance can be constructed
993 * without violating security constraints: the subclass must not override
994 * security-sensitive non-final methods, or else the
995 * "enableSubclassImplementation" SerializablePermission is checked.
997 private void verifySubclass() {
998 Class cl = getClass();
999 if (cl == ObjectOutputStream.class) {
1002 throw new SecurityException();
1006 * Clears internal data structures.
1008 private void clear() {
1014 * Underlying writeObject/writeUnshared implementation.
1016 private void writeObject0(Object obj, boolean unshared)
1019 boolean oldMode = bout.setBlockDataMode(false);
1022 // handle previously written and non-replaceable objects
1024 if ((obj = subs.lookup(obj)) == null) {
1027 } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1030 } else if (obj instanceof Class) {
1031 writeClass((Class) obj, unshared);
1033 } else if (obj instanceof ObjectStreamClass) {
1034 writeClassDesc((ObjectStreamClass) obj, unshared);
1038 // check for replacement object
1040 Class cl = obj.getClass();
1041 ObjectStreamClass desc;
1043 // REMIND: skip this check for strings/arrays?
1045 desc = ObjectStreamClass.lookup(cl, true);
1046 if (!desc.hasWriteReplaceMethod() ||
1047 (obj = desc.invokeWriteReplace(obj)) == null ||
1048 (repCl = obj.getClass()) == cl)
1054 if (enableReplace) {
1055 Object rep = replaceObject(obj);
1056 if (rep != obj && rep != null) {
1057 cl = rep.getClass();
1058 desc = ObjectStreamClass.lookup(cl, true);
1063 // if object replaced, run through original checks a second time
1065 subs.assign(orig, obj);
1069 } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1072 } else if (obj instanceof Class) {
1073 writeClass((Class) obj, unshared);
1075 } else if (obj instanceof ObjectStreamClass) {
1076 writeClassDesc((ObjectStreamClass) obj, unshared);
1082 if (obj instanceof String) {
1083 writeString((String) obj, unshared);
1084 } else if (cl.isArray()) {
1085 writeArray(obj, desc, unshared);
1086 } else if (obj instanceof Enum) {
1087 writeEnum((Enum) obj, desc, unshared);
1088 } else if (obj instanceof Serializable) {
1089 writeOrdinaryObject(obj, desc, unshared);
1091 if (extendedDebugInfo) {
1092 throw new NotSerializableException(
1093 cl.getName() + "\n" + debugInfoStack.toString());
1095 throw new NotSerializableException(cl.getName());
1100 bout.setBlockDataMode(oldMode);
1105 * Writes null code to stream.
1107 private void writeNull() throws IOException {
1108 bout.writeByte(TC_NULL);
1112 * Writes given object handle to stream.
1114 private void writeHandle(int handle) throws IOException {
1115 bout.writeByte(TC_REFERENCE);
1116 bout.writeInt(baseWireHandle + handle);
1120 * Writes representation of given class to stream.
1122 private void writeClass(Class cl, boolean unshared) throws IOException {
1123 bout.writeByte(TC_CLASS);
1124 writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
1125 handles.assign(unshared ? null : cl);
1129 * Writes representation of given class descriptor to stream.
1131 private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
1137 } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
1138 writeHandle(handle);
1139 } else if (desc.isProxy()) {
1140 writeProxyDesc(desc, unshared);
1142 writeNonProxyDesc(desc, unshared);
1147 * Writes class descriptor representing a dynamic proxy class to stream.
1149 private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
1152 bout.writeByte(TC_PROXYCLASSDESC);
1153 handles.assign(unshared ? null : desc);
1155 Class cl = desc.forClass();
1156 Class[] ifaces = cl.getInterfaces();
1157 bout.writeInt(ifaces.length);
1158 for (int i = 0; i < ifaces.length; i++) {
1159 bout.writeUTF(ifaces[i].getName());
1162 bout.setBlockDataMode(true);
1163 annotateProxyClass(cl);
1164 bout.setBlockDataMode(false);
1165 bout.writeByte(TC_ENDBLOCKDATA);
1167 writeClassDesc(desc.getSuperDesc(), false);
1171 * Writes class descriptor representing a standard (i.e., not a dynamic
1172 * proxy) class to stream.
1174 private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
1177 bout.writeByte(TC_CLASSDESC);
1178 handles.assign(unshared ? null : desc);
1180 if (protocol == PROTOCOL_VERSION_1) {
1181 // do not invoke class descriptor write hook with old protocol
1182 desc.writeNonProxy(this);
1184 writeClassDescriptor(desc);
1187 Class cl = desc.forClass();
1188 bout.setBlockDataMode(true);
1190 bout.setBlockDataMode(false);
1191 bout.writeByte(TC_ENDBLOCKDATA);
1193 writeClassDesc(desc.getSuperDesc(), false);
1197 * Writes given string to stream, using standard or long UTF format
1198 * depending on string length.
1200 private void writeString(String str, boolean unshared) throws IOException {
1201 handles.assign(unshared ? null : str);
1202 long utflen = bout.getUTFLength(str);
1203 if (utflen <= 0xFFFF) {
1204 bout.writeByte(TC_STRING);
1205 bout.writeUTF(str, utflen);
1207 bout.writeByte(TC_LONGSTRING);
1208 bout.writeLongUTF(str, utflen);
1213 * Writes given array object to stream.
1215 private void writeArray(Object array,
1216 ObjectStreamClass desc,
1220 bout.writeByte(TC_ARRAY);
1221 writeClassDesc(desc, false);
1222 handles.assign(unshared ? null : array);
1224 Class ccl = desc.forClass().getComponentType();
1225 if (ccl.isPrimitive()) {
1226 if (ccl == Integer.TYPE) {
1227 int[] ia = (int[]) array;
1228 bout.writeInt(ia.length);
1229 bout.writeInts(ia, 0, ia.length);
1230 } else if (ccl == Byte.TYPE) {
1231 byte[] ba = (byte[]) array;
1232 bout.writeInt(ba.length);
1233 bout.write(ba, 0, ba.length, true);
1234 } else if (ccl == Long.TYPE) {
1235 long[] ja = (long[]) array;
1236 bout.writeInt(ja.length);
1237 bout.writeLongs(ja, 0, ja.length);
1238 } else if (ccl == Float.TYPE) {
1239 float[] fa = (float[]) array;
1240 bout.writeInt(fa.length);
1241 bout.writeFloats(fa, 0, fa.length);
1242 } else if (ccl == Double.TYPE) {
1243 double[] da = (double[]) array;
1244 bout.writeInt(da.length);
1245 bout.writeDoubles(da, 0, da.length);
1246 } else if (ccl == Short.TYPE) {
1247 short[] sa = (short[]) array;
1248 bout.writeInt(sa.length);
1249 bout.writeShorts(sa, 0, sa.length);
1250 } else if (ccl == Character.TYPE) {
1251 char[] ca = (char[]) array;
1252 bout.writeInt(ca.length);
1253 bout.writeChars(ca, 0, ca.length);
1254 } else if (ccl == Boolean.TYPE) {
1255 boolean[] za = (boolean[]) array;
1256 bout.writeInt(za.length);
1257 bout.writeBooleans(za, 0, za.length);
1259 throw new InternalError();
1262 Object[] objs = (Object[]) array;
1263 int len = objs.length;
1265 if (extendedDebugInfo) {
1266 debugInfoStack.push(
1267 "array (class \"" + array.getClass().getName() +
1268 "\", size: " + len + ")");
1271 for (int i = 0; i < len; i++) {
1272 if (extendedDebugInfo) {
1273 debugInfoStack.push(
1274 "element of array (index: " + i + ")");
1277 writeObject0(objs[i], false);
1279 if (extendedDebugInfo) {
1280 debugInfoStack.pop();
1285 if (extendedDebugInfo) {
1286 debugInfoStack.pop();
1293 * Writes given enum constant to stream.
1295 private void writeEnum(Enum en,
1296 ObjectStreamClass desc,
1300 bout.writeByte(TC_ENUM);
1301 ObjectStreamClass sdesc = desc.getSuperDesc();
1302 writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
1303 handles.assign(unshared ? null : en);
1304 writeString(en.name(), false);
1308 * Writes representation of a "ordinary" (i.e., not a String, Class,
1309 * ObjectStreamClass, array, or enum constant) serializable object to the
1312 private void writeOrdinaryObject(Object obj,
1313 ObjectStreamClass desc,
1317 if (extendedDebugInfo) {
1318 debugInfoStack.push(
1319 (depth == 1 ? "root " : "") + "object (class \"" +
1320 obj.getClass().getName() + "\", " + obj.toString() + ")");
1323 desc.checkSerialize();
1325 bout.writeByte(TC_OBJECT);
1326 writeClassDesc(desc, false);
1327 handles.assign(unshared ? null : obj);
1328 if (desc.isExternalizable() && !desc.isProxy()) {
1329 writeExternalData((Externalizable) obj);
1331 writeSerialData(obj, desc);
1334 if (extendedDebugInfo) {
1335 debugInfoStack.pop();
1341 * Writes externalizable data of given object by invoking its
1342 * writeExternal() method.
1344 private void writeExternalData(Externalizable obj) throws IOException {
1345 PutFieldImpl oldPut = curPut;
1348 if (extendedDebugInfo) {
1349 debugInfoStack.push("writeExternal data");
1351 Object oldContext = curContext;
1354 if (protocol == PROTOCOL_VERSION_1) {
1355 obj.writeExternal(this);
1357 bout.setBlockDataMode(true);
1358 obj.writeExternal(this);
1359 bout.setBlockDataMode(false);
1360 bout.writeByte(TC_ENDBLOCKDATA);
1363 curContext = oldContext;
1364 if (extendedDebugInfo) {
1365 debugInfoStack.pop();
1373 * Writes instance data for each serializable class of given object, from
1374 * superclass to subclass.
1376 private void writeSerialData(Object obj, ObjectStreamClass desc)
1379 ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
1380 for (int i = 0; i < slots.length; i++) {
1381 ObjectStreamClass slotDesc = slots[i].desc;
1382 if (slotDesc.hasWriteObjectMethod()) {
1383 PutFieldImpl oldPut = curPut;
1385 Object oldContext = curContext;
1387 if (extendedDebugInfo) {
1388 debugInfoStack.push(
1389 "custom writeObject data (class \"" +
1390 slotDesc.getName() + "\")");
1393 curContext = new Object(); //new SerialCallbackContext(obj, slotDesc);
1394 bout.setBlockDataMode(true);
1395 slotDesc.invokeWriteObject(obj, this);
1396 bout.setBlockDataMode(false);
1397 bout.writeByte(TC_ENDBLOCKDATA);
1399 //curContext.setUsed();
1400 curContext = oldContext;
1401 if (extendedDebugInfo) {
1402 debugInfoStack.pop();
1408 defaultWriteFields(obj, slotDesc);
1414 * Fetches and writes values of serializable fields of given object to
1415 * stream. The given class descriptor specifies which field values to
1416 * write, and in which order they should be written.
1418 private void defaultWriteFields(Object obj, ObjectStreamClass desc)
1421 // REMIND: perform conservative isInstance check here?
1422 desc.checkDefaultSerialize();
1424 int primDataSize = desc.getPrimDataSize();
1425 if (primVals == null || primVals.length < primDataSize) {
1426 primVals = new byte[primDataSize];
1428 desc.getPrimFieldValues(obj, primVals);
1429 bout.write(primVals, 0, primDataSize, false);
1431 ObjectStreamField[] fields = desc.getFields(false);
1432 Object[] objVals = new Object[desc.getNumObjFields()];
1433 int numPrimFields = fields.length - objVals.length;
1434 desc.getObjFieldValues(obj, objVals);
1435 for (int i = 0; i < objVals.length; i++) {
1436 if (extendedDebugInfo) {
1437 debugInfoStack.push(
1438 "field (class \"" + desc.getName() + "\", name: \"" +
1439 fields[numPrimFields + i].getName() + "\", type: \"" +
1440 fields[numPrimFields + i].getType() + "\")");
1443 writeObject0(objVals[i],
1444 fields[numPrimFields + i].isUnshared());
1446 if (extendedDebugInfo) {
1447 debugInfoStack.pop();
1454 * Attempts to write to stream fatal IOException that has caused
1455 * serialization to abort.
1457 private void writeFatalException(IOException ex) throws IOException {
1459 * Note: the serialization specification states that if a second
1460 * IOException occurs while attempting to serialize the original fatal
1461 * exception to the stream, then a StreamCorruptedException should be
1462 * thrown (section 2.1). However, due to a bug in previous
1463 * implementations of serialization, StreamCorruptedExceptions were
1464 * rarely (if ever) actually thrown--the "root" exceptions from
1465 * underlying streams were thrown instead. This historical behavior is
1466 * followed here for consistency.
1469 boolean oldMode = bout.setBlockDataMode(false);
1471 bout.writeByte(TC_EXCEPTION);
1472 writeObject0(ex, false);
1475 bout.setBlockDataMode(oldMode);
1480 * Converts specified span of float values into byte values.
1482 // REMIND: remove once hotspot inlines Float.floatToIntBits
1483 private static native void floatsToBytes(float[] src, int srcpos,
1484 byte[] dst, int dstpos,
1488 * Converts specified span of double values into byte values.
1490 // REMIND: remove once hotspot inlines Double.doubleToLongBits
1491 private static native void doublesToBytes(double[] src, int srcpos,
1492 byte[] dst, int dstpos,
1496 * Default PutField implementation.
1498 private class PutFieldImpl extends PutField {
1500 /** class descriptor describing serializable fields */
1501 private final ObjectStreamClass desc;
1502 /** primitive field values */
1503 private final byte[] primVals;
1504 /** object field values */
1505 private final Object[] objVals;
1508 * Creates PutFieldImpl object for writing fields defined in given
1511 PutFieldImpl(ObjectStreamClass desc) {
1513 primVals = new byte[desc.getPrimDataSize()];
1514 objVals = new Object[desc.getNumObjFields()];
1517 public void put(String name, boolean val) {
1518 Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val);
1521 public void put(String name, byte val) {
1522 primVals[getFieldOffset(name, Byte.TYPE)] = val;
1525 public void put(String name, char val) {
1526 Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val);
1529 public void put(String name, short val) {
1530 Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val);
1533 public void put(String name, int val) {
1534 Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val);
1537 public void put(String name, float val) {
1538 Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val);
1541 public void put(String name, long val) {
1542 Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
1545 public void put(String name, double val) {
1546 Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val);
1549 public void put(String name, Object val) {
1550 objVals[getFieldOffset(name, Object.class)] = val;
1553 // deprecated in ObjectOutputStream.PutField
1554 public void write(ObjectOutput out) throws IOException {
1556 * Applications should *not* use this method to write PutField
1557 * data, as it will lead to stream corruption if the PutField
1558 * object writes any primitive data (since block data mode is not
1559 * unset/set properly, as is done in OOS.writeFields()). This
1560 * broken implementation is being retained solely for behavioral
1561 * compatibility, in order to support applications which use
1562 * OOS.PutField.write() for writing only non-primitive data.
1564 * Serialization of unshared objects is not implemented here since
1565 * it is not necessary for backwards compatibility; also, unshared
1566 * semantics may not be supported by the given ObjectOutput
1567 * instance. Applications which write unshared objects using the
1568 * PutField API must use OOS.writeFields().
1570 if (ObjectOutputStream.this != out) {
1571 throw new IllegalArgumentException("wrong stream");
1573 out.write(primVals, 0, primVals.length);
1575 ObjectStreamField[] fields = desc.getFields(false);
1576 int numPrimFields = fields.length - objVals.length;
1577 // REMIND: warn if numPrimFields > 0?
1578 for (int i = 0; i < objVals.length; i++) {
1579 if (fields[numPrimFields + i].isUnshared()) {
1580 throw new IOException("cannot write unshared object");
1582 out.writeObject(objVals[i]);
1587 * Writes buffered primitive data and object fields to stream.
1589 void writeFields() throws IOException {
1590 bout.write(primVals, 0, primVals.length, false);
1592 ObjectStreamField[] fields = desc.getFields(false);
1593 int numPrimFields = fields.length - objVals.length;
1594 for (int i = 0; i < objVals.length; i++) {
1595 if (extendedDebugInfo) {
1596 debugInfoStack.push(
1597 "field (class \"" + desc.getName() + "\", name: \"" +
1598 fields[numPrimFields + i].getName() + "\", type: \"" +
1599 fields[numPrimFields + i].getType() + "\")");
1602 writeObject0(objVals[i],
1603 fields[numPrimFields + i].isUnshared());
1605 if (extendedDebugInfo) {
1606 debugInfoStack.pop();
1613 * Returns offset of field with given name and type. A specified type
1614 * of null matches all types, Object.class matches all non-primitive
1615 * types, and any other non-null type matches assignable types only.
1616 * Throws IllegalArgumentException if no matching field found.
1618 private int getFieldOffset(String name, Class type) {
1619 ObjectStreamField field = desc.getField(name, type);
1620 if (field == null) {
1621 throw new IllegalArgumentException("no such field " + name +
1622 " with type " + type);
1624 return field.getOffset();
1629 * Buffered output stream with two modes: in default mode, outputs data in
1630 * same format as DataOutputStream; in "block data" mode, outputs data
1631 * bracketed by block data markers (see object serialization specification
1634 private static class BlockDataOutputStream
1635 extends OutputStream implements DataOutput
1637 /** maximum data block length */
1638 private static final int MAX_BLOCK_SIZE = 1024;
1639 /** maximum data block header length */
1640 private static final int MAX_HEADER_SIZE = 5;
1641 /** (tunable) length of char buffer (for writing strings) */
1642 private static final int CHAR_BUF_SIZE = 256;
1644 /** buffer for writing general/block data */
1645 private final byte[] buf = new byte[MAX_BLOCK_SIZE];
1646 /** buffer for writing block data headers */
1647 private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
1648 /** char buffer for fast string writes */
1649 private final char[] cbuf = new char[CHAR_BUF_SIZE];
1651 /** block data mode */
1652 private boolean blkmode = false;
1653 /** current offset into buf */
1654 private int pos = 0;
1656 /** underlying output stream */
1657 private final OutputStream out;
1658 /** loopback stream (for data writes that span data blocks) */
1659 private final DataOutputStream dout;
1662 * Creates new BlockDataOutputStream on top of given underlying stream.
1663 * Block data mode is turned off by default.
1665 BlockDataOutputStream(OutputStream out) {
1667 dout = new DataOutputStream(this);
1671 * Sets block data mode to the given mode (true == on, false == off)
1672 * and returns the previous mode value. If the new mode is the same as
1673 * the old mode, no action is taken. If the new mode differs from the
1674 * old mode, any buffered data is flushed before switching to the new
1677 boolean setBlockDataMode(boolean mode) throws IOException {
1678 if (blkmode == mode) {
1687 * Returns true if the stream is currently in block data mode, false
1690 boolean getBlockDataMode() {
1694 /* ----------------- generic output stream methods ----------------- */
1696 * The following methods are equivalent to their counterparts in
1697 * OutputStream, except that they partition written data into data
1698 * blocks when in block data mode.
1701 public void write(int b) throws IOException {
1702 if (pos >= MAX_BLOCK_SIZE) {
1705 buf[pos++] = (byte) b;
1708 public void write(byte[] b) throws IOException {
1709 write(b, 0, b.length, false);
1712 public void write(byte[] b, int off, int len) throws IOException {
1713 write(b, off, len, false);
1716 public void flush() throws IOException {
1721 public void close() throws IOException {
1727 * Writes specified span of byte values from given array. If copy is
1728 * true, copies the values to an intermediate buffer before writing
1729 * them to underlying stream (to avoid exposing a reference to the
1730 * original byte array).
1732 void write(byte[] b, int off, int len, boolean copy)
1735 if (!(copy || blkmode)) { // write directly
1737 out.write(b, off, len);
1742 if (pos >= MAX_BLOCK_SIZE) {
1745 if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) {
1746 // avoid unnecessary copy
1747 writeBlockHeader(MAX_BLOCK_SIZE);
1748 out.write(b, off, MAX_BLOCK_SIZE);
1749 off += MAX_BLOCK_SIZE;
1750 len -= MAX_BLOCK_SIZE;
1752 int wlen = Math.min(len, MAX_BLOCK_SIZE - pos);
1753 System.arraycopy(b, off, buf, pos, wlen);
1762 * Writes all buffered data from this stream to the underlying stream,
1763 * but does not flush underlying stream.
1765 void drain() throws IOException {
1770 writeBlockHeader(pos);
1772 out.write(buf, 0, pos);
1777 * Writes block data header. Data blocks shorter than 256 bytes are
1778 * prefixed with a 2-byte header; all others start with a 5-byte
1781 private void writeBlockHeader(int len) throws IOException {
1783 hbuf[0] = TC_BLOCKDATA;
1784 hbuf[1] = (byte) len;
1785 out.write(hbuf, 0, 2);
1787 hbuf[0] = TC_BLOCKDATALONG;
1788 Bits.putInt(hbuf, 1, len);
1789 out.write(hbuf, 0, 5);
1794 /* ----------------- primitive data output methods ----------------- */
1796 * The following methods are equivalent to their counterparts in
1797 * DataOutputStream, except that they partition written data into data
1798 * blocks when in block data mode.
1801 public void writeBoolean(boolean v) throws IOException {
1802 if (pos >= MAX_BLOCK_SIZE) {
1805 Bits.putBoolean(buf, pos++, v);
1808 public void writeByte(int v) throws IOException {
1809 if (pos >= MAX_BLOCK_SIZE) {
1812 buf[pos++] = (byte) v;
1815 public void writeChar(int v) throws IOException {
1816 if (pos + 2 <= MAX_BLOCK_SIZE) {
1817 Bits.putChar(buf, pos, (char) v);
1824 public void writeShort(int v) throws IOException {
1825 if (pos + 2 <= MAX_BLOCK_SIZE) {
1826 Bits.putShort(buf, pos, (short) v);
1833 public void writeInt(int v) throws IOException {
1834 if (pos + 4 <= MAX_BLOCK_SIZE) {
1835 Bits.putInt(buf, pos, v);
1842 public void writeFloat(float v) throws IOException {
1843 if (pos + 4 <= MAX_BLOCK_SIZE) {
1844 Bits.putFloat(buf, pos, v);
1851 public void writeLong(long v) throws IOException {
1852 if (pos + 8 <= MAX_BLOCK_SIZE) {
1853 Bits.putLong(buf, pos, v);
1860 public void writeDouble(double v) throws IOException {
1861 if (pos + 8 <= MAX_BLOCK_SIZE) {
1862 Bits.putDouble(buf, pos, v);
1865 dout.writeDouble(v);
1869 public void writeBytes(String s) throws IOException {
1870 int endoff = s.length();
1873 for (int off = 0; off < endoff; ) {
1874 if (cpos >= csize) {
1876 csize = Math.min(endoff - off, CHAR_BUF_SIZE);
1877 s.getChars(off, off + csize, cbuf, 0);
1879 if (pos >= MAX_BLOCK_SIZE) {
1882 int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
1884 while (pos < stop) {
1885 buf[pos++] = (byte) cbuf[cpos++];
1891 public void writeChars(String s) throws IOException {
1892 int endoff = s.length();
1893 for (int off = 0; off < endoff; ) {
1894 int csize = Math.min(endoff - off, CHAR_BUF_SIZE);
1895 s.getChars(off, off + csize, cbuf, 0);
1896 writeChars(cbuf, 0, csize);
1901 public void writeUTF(String s) throws IOException {
1902 writeUTF(s, getUTFLength(s));
1906 /* -------------- primitive data array output methods -------------- */
1908 * The following methods write out spans of primitive data values.
1909 * Though equivalent to calling the corresponding primitive write
1910 * methods repeatedly, these methods are optimized for writing groups
1911 * of primitive data values more efficiently.
1914 void writeBooleans(boolean[] v, int off, int len) throws IOException {
1915 int endoff = off + len;
1916 while (off < endoff) {
1917 if (pos >= MAX_BLOCK_SIZE) {
1920 int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos));
1921 while (off < stop) {
1922 Bits.putBoolean(buf, pos++, v[off++]);
1927 void writeChars(char[] v, int off, int len) throws IOException {
1928 int limit = MAX_BLOCK_SIZE - 2;
1929 int endoff = off + len;
1930 while (off < endoff) {
1932 int avail = (MAX_BLOCK_SIZE - pos) >> 1;
1933 int stop = Math.min(endoff, off + avail);
1934 while (off < stop) {
1935 Bits.putChar(buf, pos, v[off++]);
1939 dout.writeChar(v[off++]);
1944 void writeShorts(short[] v, int off, int len) throws IOException {
1945 int limit = MAX_BLOCK_SIZE - 2;
1946 int endoff = off + len;
1947 while (off < endoff) {
1949 int avail = (MAX_BLOCK_SIZE - pos) >> 1;
1950 int stop = Math.min(endoff, off + avail);
1951 while (off < stop) {
1952 Bits.putShort(buf, pos, v[off++]);
1956 dout.writeShort(v[off++]);
1961 void writeInts(int[] v, int off, int len) throws IOException {
1962 int limit = MAX_BLOCK_SIZE - 4;
1963 int endoff = off + len;
1964 while (off < endoff) {
1966 int avail = (MAX_BLOCK_SIZE - pos) >> 2;
1967 int stop = Math.min(endoff, off + avail);
1968 while (off < stop) {
1969 Bits.putInt(buf, pos, v[off++]);
1973 dout.writeInt(v[off++]);
1978 void writeFloats(float[] v, int off, int len) throws IOException {
1979 int limit = MAX_BLOCK_SIZE - 4;
1980 int endoff = off + len;
1981 while (off < endoff) {
1983 int avail = (MAX_BLOCK_SIZE - pos) >> 2;
1984 int chunklen = Math.min(endoff - off, avail);
1985 floatsToBytes(v, off, buf, pos, chunklen);
1987 pos += chunklen << 2;
1989 dout.writeFloat(v[off++]);
1994 void writeLongs(long[] v, int off, int len) throws IOException {
1995 int limit = MAX_BLOCK_SIZE - 8;
1996 int endoff = off + len;
1997 while (off < endoff) {
1999 int avail = (MAX_BLOCK_SIZE - pos) >> 3;
2000 int stop = Math.min(endoff, off + avail);
2001 while (off < stop) {
2002 Bits.putLong(buf, pos, v[off++]);
2006 dout.writeLong(v[off++]);
2011 void writeDoubles(double[] v, int off, int len) throws IOException {
2012 int limit = MAX_BLOCK_SIZE - 8;
2013 int endoff = off + len;
2014 while (off < endoff) {
2016 int avail = (MAX_BLOCK_SIZE - pos) >> 3;
2017 int chunklen = Math.min(endoff - off, avail);
2018 doublesToBytes(v, off, buf, pos, chunklen);
2020 pos += chunklen << 3;
2022 dout.writeDouble(v[off++]);
2028 * Returns the length in bytes of the UTF encoding of the given string.
2030 long getUTFLength(String s) {
2031 int len = s.length();
2033 for (int off = 0; off < len; ) {
2034 int csize = Math.min(len - off, CHAR_BUF_SIZE);
2035 s.getChars(off, off + csize, cbuf, 0);
2036 for (int cpos = 0; cpos < csize; cpos++) {
2037 char c = cbuf[cpos];
2038 if (c >= 0x0001 && c <= 0x007F) {
2040 } else if (c > 0x07FF) {
2052 * Writes the given string in UTF format. This method is used in
2053 * situations where the UTF encoding length of the string is already
2054 * known; specifying it explicitly avoids a prescan of the string to
2055 * determine its UTF length.
2057 void writeUTF(String s, long utflen) throws IOException {
2058 if (utflen > 0xFFFFL) {
2059 throw new UTFDataFormatException();
2061 writeShort((int) utflen);
2062 if (utflen == (long) s.length()) {
2070 * Writes given string in "long" UTF format. "Long" UTF format is
2071 * identical to standard UTF, except that it uses an 8 byte header
2072 * (instead of the standard 2 bytes) to convey the UTF encoding length.
2074 void writeLongUTF(String s) throws IOException {
2075 writeLongUTF(s, getUTFLength(s));
2079 * Writes given string in "long" UTF format, where the UTF encoding
2080 * length of the string is already known.
2082 void writeLongUTF(String s, long utflen) throws IOException {
2084 if (utflen == (long) s.length()) {
2092 * Writes the "body" (i.e., the UTF representation minus the 2-byte or
2093 * 8-byte length header) of the UTF encoding for the given string.
2095 private void writeUTFBody(String s) throws IOException {
2096 int limit = MAX_BLOCK_SIZE - 3;
2097 int len = s.length();
2098 for (int off = 0; off < len; ) {
2099 int csize = Math.min(len - off, CHAR_BUF_SIZE);
2100 s.getChars(off, off + csize, cbuf, 0);
2101 for (int cpos = 0; cpos < csize; cpos++) {
2102 char c = cbuf[cpos];
2104 if (c <= 0x007F && c != 0) {
2105 buf[pos++] = (byte) c;
2106 } else if (c > 0x07FF) {
2107 buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));
2108 buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
2109 buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));
2112 buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));
2113 buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));
2116 } else { // write one byte at a time to normalize block
2117 if (c <= 0x007F && c != 0) {
2119 } else if (c > 0x07FF) {
2120 write(0xE0 | ((c >> 12) & 0x0F));
2121 write(0x80 | ((c >> 6) & 0x3F));
2122 write(0x80 | ((c >> 0) & 0x3F));
2124 write(0xC0 | ((c >> 6) & 0x1F));
2125 write(0x80 | ((c >> 0) & 0x3F));
2135 * Lightweight identity hash table which maps objects to integer handles,
2136 * assigned in ascending order.
2138 private static class HandleTable {
2140 /* number of mappings in table/next available handle */
2142 /* size threshold determining when to expand hash spine */
2143 private int threshold;
2144 /* factor for computing size threshold */
2145 private final float loadFactor;
2146 /* maps hash value -> candidate handle value */
2147 private int[] spine;
2148 /* maps handle value -> next candidate handle value */
2150 /* maps handle value -> associated object */
2151 private Object[] objs;
2154 * Creates new HandleTable with given capacity and load factor.
2156 HandleTable(int initialCapacity, float loadFactor) {
2157 this.loadFactor = loadFactor;
2158 spine = new int[initialCapacity];
2159 next = new int[initialCapacity];
2160 objs = new Object[initialCapacity];
2161 threshold = (int) (initialCapacity * loadFactor);
2166 * Assigns next available handle to given object, and returns handle
2167 * value. Handles are assigned in ascending order starting at 0.
2169 int assign(Object obj) {
2170 if (size >= next.length) {
2173 if (size >= threshold) {
2181 * Looks up and returns handle associated with given object, or -1 if
2184 int lookup(Object obj) {
2188 int index = hash(obj) % spine.length;
2189 for (int i = spine[index]; i >= 0; i = next[i]) {
2190 if (objs[i] == obj) {
2198 * Resets table to its initial (empty) state.
2201 Arrays.fill(spine, -1);
2202 Arrays.fill(objs, 0, size, null);
2207 * Returns the number of mappings currently in table.
2214 * Inserts mapping object -> handle mapping into table. Assumes table
2215 * is large enough to accommodate new mapping.
2217 private void insert(Object obj, int handle) {
2218 int index = hash(obj) % spine.length;
2220 next[handle] = spine[index];
2221 spine[index] = handle;
2225 * Expands the hash "spine" -- equivalent to increasing the number of
2226 * buckets in a conventional hash table.
2228 private void growSpine() {
2229 spine = new int[(spine.length << 1) + 1];
2230 threshold = (int) (spine.length * loadFactor);
2231 Arrays.fill(spine, -1);
2232 for (int i = 0; i < size; i++) {
2238 * Increases hash table capacity by lengthening entry arrays.
2240 private void growEntries() {
2241 int newLength = (next.length << 1) + 1;
2242 int[] newNext = new int[newLength];
2243 System.arraycopy(next, 0, newNext, 0, size);
2246 Object[] newObjs = new Object[newLength];
2247 System.arraycopy(objs, 0, newObjs, 0, size);
2252 * Returns hash value for given object.
2254 private int hash(Object obj) {
2255 return System.identityHashCode(obj) & 0x7FFFFFFF;
2260 * Lightweight identity hash table which maps objects to replacement
2263 private static class ReplaceTable {
2265 /* maps object -> index */
2266 private final HandleTable htab;
2267 /* maps index -> replacement object */
2268 private Object[] reps;
2271 * Creates new ReplaceTable with given capacity and load factor.
2273 ReplaceTable(int initialCapacity, float loadFactor) {
2274 htab = new HandleTable(initialCapacity, loadFactor);
2275 reps = new Object[initialCapacity];
2279 * Enters mapping from object to replacement object.
2281 void assign(Object obj, Object rep) {
2282 int index = htab.assign(obj);
2283 while (index >= reps.length) {
2290 * Looks up and returns replacement for given object. If no
2291 * replacement is found, returns the lookup object itself.
2293 Object lookup(Object obj) {
2294 int index = htab.lookup(obj);
2295 return (index >= 0) ? reps[index] : obj;
2299 * Resets table to its initial (empty) state.
2302 Arrays.fill(reps, 0, htab.size(), null);
2307 * Returns the number of mappings currently in table.
2314 * Increases table capacity.
2316 private void grow() {
2317 Object[] newReps = new Object[(reps.length << 1) + 1];
2318 System.arraycopy(reps, 0, newReps, 0, reps.length);
2324 * Stack to keep debug information about the state of the
2325 * serialization process, for embedding in exception messages.
2327 private static class DebugTraceInfoStack {
2328 private final List<String> stack;
2330 DebugTraceInfoStack() {
2331 stack = new ArrayList<>();
2335 * Removes all of the elements from enclosed list.
2342 * Removes the object at the top of enclosed list.
2345 stack.remove(stack.size()-1);
2349 * Pushes a String onto the top of enclosed list.
2351 void push(String entry) {
2352 stack.add("\t- " + entry);
2356 * Returns a string representation of this object
2358 public String toString() {
2359 StringBuilder buffer = new StringBuilder();
2360 if (!stack.isEmpty()) {
2361 for(int i = stack.size(); i > 0; i-- ) {
2362 buffer.append(stack.get(i-1) + ((i != 1) ? "\n" : ""));
2365 return buffer.toString();