History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/io/ObjectOutputStream.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/io/ObjectOutputStream.java+2454−0
@@ -0,0 +1,2454 @@
1+/*
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.
4+ *
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.
10+ *
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).
16+ *
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.
20+ *
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
23+ * questions.
24+ */
25+
26+package java.io;
27+
28+import java.io.ObjectStreamClass.WeakClassKey;
29+import java.lang.ref.ReferenceQueue;
30+import java.security.AccessController;
31+import java.security.PrivilegedAction;
32+import java.util.ArrayList;
33+import java.util.Arrays;
34+import java.util.List;
35+import java.util.concurrent.ConcurrentHashMap;
36+import java.util.concurrent.ConcurrentMap;
37+import static java.io.ObjectStreamClass.processQueue;
38+import java.io.SerialCallbackContext;
39+
40+/**
41+ * An ObjectOutputStream writes primitive data types and graphs of Java objects
42+ * to an OutputStream. The objects can be read (reconstituted) using an
43+ * ObjectInputStream. Persistent storage of objects can be accomplished by
44+ * using a file for the stream. If the stream is a network socket stream, the
45+ * objects can be reconstituted on another host or in another process.
46+ *
47+ * <p>Only objects that support the java.io.Serializable interface can be
48+ * written to streams. The class of each serializable object is encoded
49+ * including the class name and signature of the class, the values of the
50+ * object's fields and arrays, and the closure of any other objects referenced
51+ * from the initial objects.
52+ *
53+ * <p>The method writeObject is used to write an object to the stream. Any
54+ * object, including Strings and arrays, is written with writeObject. Multiple
55+ * objects or primitives can be written to the stream. The objects must be
56+ * read back from the corresponding ObjectInputstream with the same types and
57+ * in the same order as they were written.
58+ *
59+ * <p>Primitive data types can also be written to the stream using the
60+ * appropriate methods from DataOutput. Strings can also be written using the
61+ * writeUTF method.
62+ *
63+ * <p>The default serialization mechanism for an object writes the class of the
64+ * object, the class signature, and the values of all non-transient and
65+ * non-static fields. References to other objects (except in transient or
66+ * static fields) cause those objects to be written also. Multiple references
67+ * to a single object are encoded using a reference sharing mechanism so that
68+ * graphs of objects can be restored to the same shape as when the original was
69+ * written.
70+ *
71+ * <p>For example to write an object that can be read by the example in
72+ * ObjectInputStream:
73+ * <br>
74+ * <pre>
75+ * FileOutputStream fos = new FileOutputStream("t.tmp");
76+ * ObjectOutputStream oos = new ObjectOutputStream(fos);
77+ *
78+ * oos.writeInt(12345);
79+ * oos.writeObject("Today");
80+ * oos.writeObject(new Date());
81+ *
82+ * oos.close();
83+ * </pre>
84+ *
85+ * <p>Classes that require special handling during the serialization and
86+ * deserialization process must implement special methods with these exact
87+ * signatures:
88+ * <br>
89+ * <pre>
90+ * private void readObject(java.io.ObjectInputStream stream)
91+ * throws IOException, ClassNotFoundException;
92+ * private void writeObject(java.io.ObjectOutputStream stream)
93+ * throws IOException
94+ * private void readObjectNoData()
95+ * throws ObjectStreamException;
96+ * </pre>
97+ *
98+ * <p>The writeObject method is responsible for writing the state of the object
99+ * for its particular class so that the corresponding readObject method can
100+ * restore it. The method does not need to concern itself with the state
101+ * belonging to the object's superclasses or subclasses. State is saved by
102+ * writing the individual fields to the ObjectOutputStream using the
103+ * writeObject method or by using the methods for primitive data types
104+ * supported by DataOutput.
105+ *
106+ * <p>Serialization does not write out the fields of any object that does not
107+ * implement the java.io.Serializable interface. Subclasses of Objects that
108+ * are not serializable can be serializable. In this case the non-serializable
109+ * class must have a no-arg constructor to allow its fields to be initialized.
110+ * In this case it is the responsibility of the subclass to save and restore
111+ * the state of the non-serializable class. It is frequently the case that the
112+ * fields of that class are accessible (public, package, or protected) or that
113+ * there are get and set methods that can be used to restore the state.
114+ *
115+ * <p>Serialization of an object can be prevented by implementing writeObject
116+ * and readObject methods that throw the NotSerializableException. The
117+ * exception will be caught by the ObjectOutputStream and abort the
118+ * serialization process.
119+ *
120+ * <p>Implementing the Externalizable interface allows the object to assume
121+ * complete control over the contents and format of the object's serialized
122+ * form. The methods of the Externalizable interface, writeExternal and
123+ * readExternal, are called to save and restore the objects state. When
124+ * implemented by a class they can write and read their own state using all of
125+ * the methods of ObjectOutput and ObjectInput. It is the responsibility of
126+ * the objects to handle any versioning that occurs.
127+ *
128+ * <p>Enum constants are serialized differently than ordinary serializable or
129+ * externalizable objects. The serialized form of an enum constant consists
130+ * solely of its name; field values of the constant are not transmitted. To
131+ * serialize an enum constant, ObjectOutputStream writes the string returned by
132+ * the constant's name method. Like other serializable or externalizable
133+ * objects, enum constants can function as the targets of back references
134+ * appearing subsequently in the serialization stream. The process by which
135+ * enum constants are serialized cannot be customized; any class-specific
136+ * writeObject and writeReplace methods defined by enum types are ignored
137+ * during serialization. Similarly, any serialPersistentFields or
138+ * serialVersionUID field declarations are also ignored--all enum types have a
139+ * fixed serialVersionUID of 0L.
140+ *
141+ * <p>Primitive data, excluding serializable fields and externalizable data, is
142+ * written to the ObjectOutputStream in block-data records. A block data record
143+ * is composed of a header and data. The block data header consists of a marker
144+ * and the number of bytes to follow the header. Consecutive primitive data
145+ * writes are merged into one block-data record. The blocking factor used for
146+ * a block-data record will be 1024 bytes. Each block-data record will be
147+ * filled up to 1024 bytes, or be written whenever there is a termination of
148+ * block-data mode. Calls to the ObjectOutputStream methods writeObject,
149+ * defaultWriteObject and writeFields initially terminate any existing
150+ * block-data record.
151+ *
152+ * @author Mike Warres
153+ * @author Roger Riggs
154+ * @see java.io.DataOutput
155+ * @see java.io.ObjectInputStream
156+ * @see java.io.Serializable
157+ * @see java.io.Externalizable
158+ * @see <a href="../../../platform/serialization/spec/output.html">Object Serialization Specification, Section 2, Object Output Classes</a>
159+ * @since JDK1.1
160+ */
161+public class ObjectOutputStream
162+ extends OutputStream implements ObjectOutput, ObjectStreamConstants
163+{
164+
165+ private static class Caches {
166+ /** cache of subclass security audit results */
167+ static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits =
168+ new ConcurrentHashMap<>();
169+
170+ /** queue for WeakReferences to audited subclasses */
171+ static final ReferenceQueue<Class<?>> subclassAuditsQueue =
172+ new ReferenceQueue<>();
173+ }
174+
175+ /** filter stream for handling block data conversion */
176+ private final BlockDataOutputStream bout;
177+ /** obj -> wire handle map */
178+ private final HandleTable handles;
179+ /** obj -> replacement obj map */
180+ private final ReplaceTable subs;
181+ /** stream protocol version */
182+ private int protocol = PROTOCOL_VERSION_2;
183+ /** recursion depth */
184+ private int depth;
185+
186+ /** buffer for writing primitive field values */
187+ private byte[] primVals;
188+
189+ /** if true, invoke writeObjectOverride() instead of writeObject() */
190+ private final boolean enableOverride;
191+ /** if true, invoke replaceObject() */
192+ private boolean enableReplace;
193+
194+ // values below valid only during upcalls to writeObject()/writeExternal()
195+ /**
196+ * Context during upcalls to class-defined writeObject methods; holds
197+ * object currently being serialized and descriptor for current class.
198+ * Null when not during writeObject upcall.
199+ */
200+ private SerialCallbackContext curContext;
201+ /** current PutField object */
202+ private PutFieldImpl curPut;
203+
204+ /** custom storage for debug trace info */
205+ private final DebugTraceInfoStack debugInfoStack;
206+
207+ /**
208+ * value of "sun.io.serialization.extendedDebugInfo" property,
209+ * as true or false for extended information about exception's place
210+ */
211+ private static final boolean extendedDebugInfo =
212+ java.security.AccessController.doPrivileged(
213+ new sun.security.action.GetBooleanAction(
214+ "sun.io.serialization.extendedDebugInfo")).booleanValue();
215+
216+ /**
217+ * Creates an ObjectOutputStream that writes to the specified OutputStream.
218+ * This constructor writes the serialization stream header to the
219+ * underlying stream; callers may wish to flush the stream immediately to
220+ * ensure that constructors for receiving ObjectInputStreams will not block
221+ * when reading the header.
222+ *
223+ * <p>If a security manager is installed, this constructor will check for
224+ * the "enableSubclassImplementation" SerializablePermission when invoked
225+ * directly or indirectly by the constructor of a subclass which overrides
226+ * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared
227+ * methods.
228+ *
229+ * @param out output stream to write to
230+ * @throws IOException if an I/O error occurs while writing stream header
231+ * @throws SecurityException if untrusted subclass illegally overrides
232+ * security-sensitive methods
233+ * @throws NullPointerException if <code>out</code> is <code>null</code>
234+ * @since 1.4
235+ * @see ObjectOutputStream#ObjectOutputStream()
236+ * @see ObjectOutputStream#putFields()
237+ * @see ObjectInputStream#ObjectInputStream(InputStream)
238+ */
239+ public ObjectOutputStream(OutputStream out) throws IOException {
240+ verifySubclass();
241+ bout = new BlockDataOutputStream(out);
242+ handles = new HandleTable(10, (float) 3.00);
243+ subs = new ReplaceTable(10, (float) 3.00);
244+ enableOverride = false;
245+ writeStreamHeader();
246+ bout.setBlockDataMode(true);
247+ if (extendedDebugInfo) {
248+ debugInfoStack = new DebugTraceInfoStack();
249+ } else {
250+ debugInfoStack = null;
251+ }
252+ }
253+
254+ /**
255+ * Provide a way for subclasses that are completely reimplementing
256+ * ObjectOutputStream to not have to allocate private data just used by
257+ * this implementation of ObjectOutputStream.
258+ *
259+ * <p>If there is a security manager installed, this method first calls the
260+ * security manager's <code>checkPermission</code> method with a
261+ * <code>SerializablePermission("enableSubclassImplementation")</code>
262+ * permission to ensure it's ok to enable subclassing.
263+ *
264+ * @throws SecurityException if a security manager exists and its
265+ * <code>checkPermission</code> method denies enabling
266+ * subclassing.
267+ * @see SecurityManager#checkPermission
268+ * @see java.io.SerializablePermission
269+ */
270+ protected ObjectOutputStream() throws IOException, SecurityException {
271+ SecurityManager sm = System.getSecurityManager();
272+ if (sm != null) {
273+ sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
274+ }
275+ bout = null;
276+ handles = null;
277+ subs = null;
278+ enableOverride = true;
279+ debugInfoStack = null;
280+ }
281+
282+ /**
283+ * Specify stream protocol version to use when writing the stream.
284+ *
285+ * <p>This routine provides a hook to enable the current version of
286+ * Serialization to write in a format that is backwards compatible to a
287+ * previous version of the stream format.
288+ *
289+ * <p>Every effort will be made to avoid introducing additional
290+ * backwards incompatibilities; however, sometimes there is no
291+ * other alternative.
292+ *
293+ * @param version use ProtocolVersion from java.io.ObjectStreamConstants.
294+ * @throws IllegalStateException if called after any objects
295+ * have been serialized.
296+ * @throws IllegalArgumentException if invalid version is passed in.
297+ * @throws IOException if I/O errors occur
298+ * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
299+ * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2
300+ * @since 1.2
301+ */
302+ public void useProtocolVersion(int version) throws IOException {
303+ if (handles.size() != 0) {
304+ // REMIND: implement better check for pristine stream?
305+ throw new IllegalStateException("stream non-empty");
306+ }
307+ switch (version) {
308+ case PROTOCOL_VERSION_1:
309+ case PROTOCOL_VERSION_2:
310+ protocol = version;
311+ break;
312+
313+ default:
314+ throw new IllegalArgumentException(
315+ "unknown version: " + version);
316+ }
317+ }
318+
319+ /**
320+ * Write the specified object to the ObjectOutputStream. The class of the
321+ * object, the signature of the class, and the values of the non-transient
322+ * and non-static fields of the class and all of its supertypes are
323+ * written. Default serialization for a class can be overridden using the
324+ * writeObject and the readObject methods. Objects referenced by this
325+ * object are written transitively so that a complete equivalent graph of
326+ * objects can be reconstructed by an ObjectInputStream.
327+ *
328+ * <p>Exceptions are thrown for problems with the OutputStream and for
329+ * classes that should not be serialized. All exceptions are fatal to the
330+ * OutputStream, which is left in an indeterminate state, and it is up to
331+ * the caller to ignore or recover the stream state.
332+ *
333+ * @throws InvalidClassException Something is wrong with a class used by
334+ * serialization.
335+ * @throws NotSerializableException Some object to be serialized does not
336+ * implement the java.io.Serializable interface.
337+ * @throws IOException Any exception thrown by the underlying
338+ * OutputStream.
339+ */
340+ public final void writeObject(Object obj) throws IOException {
341+ if (enableOverride) {
342+ writeObjectOverride(obj);
343+ return;
344+ }
345+ try {
346+ writeObject0(obj, false);
347+ } catch (IOException ex) {
348+ if (depth == 0) {
349+ writeFatalException(ex);
350+ }
351+ throw ex;
352+ }
353+ }
354+
355+ /**
356+ * Method used by subclasses to override the default writeObject method.
357+ * This method is called by trusted subclasses of ObjectInputStream that
358+ * constructed ObjectInputStream using the protected no-arg constructor.
359+ * The subclass is expected to provide an override method with the modifier
360+ * "final".
361+ *
362+ * @param obj object to be written to the underlying stream
363+ * @throws IOException if there are I/O errors while writing to the
364+ * underlying stream
365+ * @see #ObjectOutputStream()
366+ * @see #writeObject(Object)
367+ * @since 1.2
368+ */
369+ protected void writeObjectOverride(Object obj) throws IOException {
370+ }
371+
372+ /**
373+ * Writes an "unshared" object to the ObjectOutputStream. This method is
374+ * identical to writeObject, except that it always writes the given object
375+ * as a new, unique object in the stream (as opposed to a back-reference
376+ * pointing to a previously serialized instance). Specifically:
377+ * <ul>
378+ * <li>An object written via writeUnshared is always serialized in the
379+ * same manner as a newly appearing object (an object that has not
380+ * been written to the stream yet), regardless of whether or not the
381+ * object has been written previously.
382+ *
383+ * <li>If writeObject is used to write an object that has been previously
384+ * written with writeUnshared, the previous writeUnshared operation
385+ * is treated as if it were a write of a separate object. In other
386+ * words, ObjectOutputStream will never generate back-references to
387+ * object data written by calls to writeUnshared.
388+ * </ul>
389+ * While writing an object via writeUnshared does not in itself guarantee a
390+ * unique reference to the object when it is deserialized, it allows a
391+ * single object to be defined multiple times in a stream, so that multiple
392+ * calls to readUnshared by the receiver will not conflict. Note that the
393+ * rules described above only apply to the base-level object written with
394+ * writeUnshared, and not to any transitively referenced sub-objects in the
395+ * object graph to be serialized.
396+ *
397+ * <p>ObjectOutputStream subclasses which override this method can only be
398+ * constructed in security contexts possessing the
399+ * "enableSubclassImplementation" SerializablePermission; any attempt to
400+ * instantiate such a subclass without this permission will cause a
401+ * SecurityException to be thrown.
402+ *
403+ * @param obj object to write to stream
404+ * @throws NotSerializableException if an object in the graph to be
405+ * serialized does not implement the Serializable interface
406+ * @throws InvalidClassException if a problem exists with the class of an
407+ * object to be serialized
408+ * @throws IOException if an I/O error occurs during serialization
409+ * @since 1.4
410+ */
411+ public void writeUnshared(Object obj) throws IOException {
412+ try {
413+ writeObject0(obj, true);
414+ } catch (IOException ex) {
415+ if (depth == 0) {
416+ writeFatalException(ex);
417+ }
418+ throw ex;
419+ }
420+ }
421+
422+ /**
423+ * Write the non-static and non-transient fields of the current class to
424+ * this stream. This may only be called from the writeObject method of the
425+ * class being serialized. It will throw the NotActiveException if it is
426+ * called otherwise.
427+ *
428+ * @throws IOException if I/O errors occur while writing to the underlying
429+ * <code>OutputStream</code>
430+ */
431+ public void defaultWriteObject() throws IOException {
432+ if ( curContext == null ) {
433+ throw new NotActiveException("not in call to writeObject");
434+ }
435+ Object curObj = curContext.getObj();
436+ ObjectStreamClass curDesc = curContext.getDesc();
437+ bout.setBlockDataMode(false);
438+ defaultWriteFields(curObj, curDesc);
439+ bout.setBlockDataMode(true);
440+ }
441+
442+ /**
443+ * Retrieve the object used to buffer persistent fields to be written to
444+ * the stream. The fields will be written to the stream when writeFields
445+ * method is called.
446+ *
447+ * @return an instance of the class Putfield that holds the serializable
448+ * fields
449+ * @throws IOException if I/O errors occur
450+ * @since 1.2
451+ */
452+ public ObjectOutputStream.PutField putFields() throws IOException {
453+ if (curPut == null) {
454+ if (curContext == null) {
455+ throw new NotActiveException("not in call to writeObject");
456+ }
457+ Object curObj = curContext.getObj();
458+ ObjectStreamClass curDesc = curContext.getDesc();
459+ curPut = new PutFieldImpl(curDesc);
460+ }
461+ return curPut;
462+ }
463+
464+ /**
465+ * Write the buffered fields to the stream.
466+ *
467+ * @throws IOException if I/O errors occur while writing to the underlying
468+ * stream
469+ * @throws NotActiveException Called when a classes writeObject method was
470+ * not called to write the state of the object.
471+ * @since 1.2
472+ */
473+ public void writeFields() throws IOException {
474+ if (curPut == null) {
475+ throw new NotActiveException("no current PutField object");
476+ }
477+ bout.setBlockDataMode(false);
478+ curPut.writeFields();
479+ bout.setBlockDataMode(true);
480+ }
481+
482+ /**
483+ * Reset will disregard the state of any objects already written to the
484+ * stream. The state is reset to be the same as a new ObjectOutputStream.
485+ * The current point in the stream is marked as reset so the corresponding
486+ * ObjectInputStream will be reset at the same point. Objects previously
487+ * written to the stream will not be refered to as already being in the
488+ * stream. They will be written to the stream again.
489+ *
490+ * @throws IOException if reset() is invoked while serializing an object.
491+ */
492+ public void reset() throws IOException {
493+ if (depth != 0) {
494+ throw new IOException("stream active");
495+ }
496+ bout.setBlockDataMode(false);
497+ bout.writeByte(TC_RESET);
498+ clear();
499+ bout.setBlockDataMode(true);
500+ }
501+
502+ /**
503+ * Subclasses may implement this method to allow class data to be stored in
504+ * the stream. By default this method does nothing. The corresponding
505+ * method in ObjectInputStream is resolveClass. This method is called
506+ * exactly once for each unique class in the stream. The class name and
507+ * signature will have already been written to the stream. This method may
508+ * make free use of the ObjectOutputStream to save any representation of
509+ * the class it deems suitable (for example, the bytes of the class file).
510+ * The resolveClass method in the corresponding subclass of
511+ * ObjectInputStream must read and use any data or objects written by
512+ * annotateClass.
513+ *
514+ * @param cl the class to annotate custom data for
515+ * @throws IOException Any exception thrown by the underlying
516+ * OutputStream.
517+ */
518+ protected void annotateClass(Class<?> cl) throws IOException {
519+ }
520+
521+ /**
522+ * Subclasses may implement this method to store custom data in the stream
523+ * along with descriptors for dynamic proxy classes.
524+ *
525+ * <p>This method is called exactly once for each unique proxy class
526+ * descriptor in the stream. The default implementation of this method in
527+ * <code>ObjectOutputStream</code> does nothing.
528+ *
529+ * <p>The corresponding method in <code>ObjectInputStream</code> is
530+ * <code>resolveProxyClass</code>. For a given subclass of
531+ * <code>ObjectOutputStream</code> that overrides this method, the
532+ * <code>resolveProxyClass</code> method in the corresponding subclass of
533+ * <code>ObjectInputStream</code> must read any data or objects written by
534+ * <code>annotateProxyClass</code>.
535+ *
536+ * @param cl the proxy class to annotate custom data for
537+ * @throws IOException any exception thrown by the underlying
538+ * <code>OutputStream</code>
539+ * @see ObjectInputStream#resolveProxyClass(String[])
540+ * @since 1.3
541+ */
542+ protected void annotateProxyClass(Class<?> cl) throws IOException {
543+ }
544+
545+ /**
546+ * This method will allow trusted subclasses of ObjectOutputStream to
547+ * substitute one object for another during serialization. Replacing
548+ * objects is disabled until enableReplaceObject is called. The
549+ * enableReplaceObject method checks that the stream requesting to do
550+ * replacement can be trusted. The first occurrence of each object written
551+ * into the serialization stream is passed to replaceObject. Subsequent
552+ * references to the object are replaced by the object returned by the
553+ * original call to replaceObject. To ensure that the private state of
554+ * objects is not unintentionally exposed, only trusted streams may use
555+ * replaceObject.
556+ *
557+ * <p>The ObjectOutputStream.writeObject method takes a parameter of type
558+ * Object (as opposed to type Serializable) to allow for cases where
559+ * non-serializable objects are replaced by serializable ones.
560+ *
561+ * <p>When a subclass is replacing objects it must insure that either a
562+ * complementary substitution must be made during deserialization or that
563+ * the substituted object is compatible with every field where the
564+ * reference will be stored. Objects whose type is not a subclass of the
565+ * type of the field or array element abort the serialization by raising an
566+ * exception and the object is not be stored.
567+ *
568+ * <p>This method is called only once when each object is first
569+ * encountered. All subsequent references to the object will be redirected
570+ * to the new object. This method should return the object to be
571+ * substituted or the original object.
572+ *
573+ * <p>Null can be returned as the object to be substituted, but may cause
574+ * NullReferenceException in classes that contain references to the
575+ * original object since they may be expecting an object instead of
576+ * null.
577+ *
578+ * @param obj the object to be replaced
579+ * @return the alternate object that replaced the specified one
580+ * @throws IOException Any exception thrown by the underlying
581+ * OutputStream.
582+ */
583+ protected Object replaceObject(Object obj) throws IOException {
584+ return obj;
585+ }
586+
587+ /**
588+ * Enable the stream to do replacement of objects in the stream. When
589+ * enabled, the replaceObject method is called for every object being
590+ * serialized.
591+ *
592+ * <p>If <code>enable</code> is true, and there is a security manager
593+ * installed, this method first calls the security manager's
594+ * <code>checkPermission</code> method with a
595+ * <code>SerializablePermission("enableSubstitution")</code> permission to
596+ * ensure it's ok to enable the stream to do replacement of objects in the
597+ * stream.
598+ *
599+ * @param enable boolean parameter to enable replacement of objects
600+ * @return the previous setting before this method was invoked
601+ * @throws SecurityException if a security manager exists and its
602+ * <code>checkPermission</code> method denies enabling the stream
603+ * to do replacement of objects in the stream.
604+ * @see SecurityManager#checkPermission
605+ * @see java.io.SerializablePermission
606+ */
607+ protected boolean enableReplaceObject(boolean enable)
608+ throws SecurityException
609+ {
610+ if (enable == enableReplace) {
611+ return enable;
612+ }
613+ if (enable) {
614+ SecurityManager sm = System.getSecurityManager();
615+ if (sm != null) {
616+ sm.checkPermission(SUBSTITUTION_PERMISSION);
617+ }
618+ }
619+ enableReplace = enable;
620+ return !enableReplace;
621+ }
622+
623+ /**
624+ * The writeStreamHeader method is provided so subclasses can append or
625+ * prepend their own header to the stream. It writes the magic number and
626+ * version to the stream.
627+ *
628+ * @throws IOException if I/O errors occur while writing to the underlying
629+ * stream
630+ */
631+ protected void writeStreamHeader() throws IOException {
632+ bout.writeShort(STREAM_MAGIC);
633+ bout.writeShort(STREAM_VERSION);
634+ }
635+
636+ /**
637+ * Write the specified class descriptor to the ObjectOutputStream. Class
638+ * descriptors are used to identify the classes of objects written to the
639+ * stream. Subclasses of ObjectOutputStream may override this method to
640+ * customize the way in which class descriptors are written to the
641+ * serialization stream. The corresponding method in ObjectInputStream,
642+ * <code>readClassDescriptor</code>, should then be overridden to
643+ * reconstitute the class descriptor from its custom stream representation.
644+ * By default, this method writes class descriptors according to the format
645+ * defined in the Object Serialization specification.
646+ *
647+ * <p>Note that this method will only be called if the ObjectOutputStream
648+ * is not using the old serialization stream format (set by calling
649+ * ObjectOutputStream's <code>useProtocolVersion</code> method). If this
650+ * serialization stream is using the old format
651+ * (<code>PROTOCOL_VERSION_1</code>), the class descriptor will be written
652+ * internally in a manner that cannot be overridden or customized.
653+ *
654+ * @param desc class descriptor to write to the stream
655+ * @throws IOException If an I/O error has occurred.
656+ * @see java.io.ObjectInputStream#readClassDescriptor()
657+ * @see #useProtocolVersion(int)
658+ * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
659+ * @since 1.3
660+ */
661+ protected void writeClassDescriptor(ObjectStreamClass desc)
662+ throws IOException
663+ {
664+ desc.writeNonProxy(this);
665+ }
666+
667+ /**
668+ * Writes a byte. This method will block until the byte is actually
669+ * written.
670+ *
671+ * @param val the byte to be written to the stream
672+ * @throws IOException If an I/O error has occurred.
673+ */
674+ public void write(int val) throws IOException {
675+ bout.write(val);
676+ }
677+
678+ /**
679+ * Writes an array of bytes. This method will block until the bytes are
680+ * actually written.
681+ *
682+ * @param buf the data to be written
683+ * @throws IOException If an I/O error has occurred.
684+ */
685+ public void write(byte[] buf) throws IOException {
686+ bout.write(buf, 0, buf.length, false);
687+ }
688+
689+ /**
690+ * Writes a sub array of bytes.
691+ *
692+ * @param buf the data to be written
693+ * @param off the start offset in the data
694+ * @param len the number of bytes that are written
695+ * @throws IOException If an I/O error has occurred.
696+ */
697+ public void write(byte[] buf, int off, int len) throws IOException {
698+ if (buf == null) {
699+ throw new NullPointerException();
700+ }
701+ int endoff = off + len;
702+ if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
703+ throw new IndexOutOfBoundsException();
704+ }
705+ bout.write(buf, off, len, false);
706+ }
707+
708+ /**
709+ * Flushes the stream. This will write any buffered output bytes and flush
710+ * through to the underlying stream.
711+ *
712+ * @throws IOException If an I/O error has occurred.
713+ */
714+ public void flush() throws IOException {
715+ bout.flush();
716+ }
717+
718+ /**
719+ * Drain any buffered data in ObjectOutputStream. Similar to flush but
720+ * does not propagate the flush to the underlying stream.
721+ *
722+ * @throws IOException if I/O errors occur while writing to the underlying
723+ * stream
724+ */
725+ protected void drain() throws IOException {
726+ bout.drain();
727+ }
728+
729+ /**
730+ * Closes the stream. This method must be called to release any resources
731+ * associated with the stream.
732+ *
733+ * @throws IOException If an I/O error has occurred.
734+ */
735+ public void close() throws IOException {
736+ flush();
737+ clear();
738+ bout.close();
739+ }
740+
741+ /**
742+ * Writes a boolean.
743+ *
744+ * @param val the boolean to be written
745+ * @throws IOException if I/O errors occur while writing to the underlying
746+ * stream
747+ */
748+ public void writeBoolean(boolean val) throws IOException {
749+ bout.writeBoolean(val);
750+ }
751+
752+ /**
753+ * Writes an 8 bit byte.
754+ *
755+ * @param val the byte value to be written
756+ * @throws IOException if I/O errors occur while writing to the underlying
757+ * stream
758+ */
759+ public void writeByte(int val) throws IOException {
760+ bout.writeByte(val);
761+ }
762+
763+ /**
764+ * Writes a 16 bit short.
765+ *
766+ * @param val the short value to be written
767+ * @throws IOException if I/O errors occur while writing to the underlying
768+ * stream
769+ */
770+ public void writeShort(int val) throws IOException {
771+ bout.writeShort(val);
772+ }
773+
774+ /**
775+ * Writes a 16 bit char.
776+ *
777+ * @param val the char value to be written
778+ * @throws IOException if I/O errors occur while writing to the underlying
779+ * stream
780+ */
781+ public void writeChar(int val) throws IOException {
782+ bout.writeChar(val);
783+ }
784+
785+ /**
786+ * Writes a 32 bit int.
787+ *
788+ * @param val the integer value to be written
789+ * @throws IOException if I/O errors occur while writing to the underlying
790+ * stream
791+ */
792+ public void writeInt(int val) throws IOException {
793+ bout.writeInt(val);
794+ }
795+
796+ /**
797+ * Writes a 64 bit long.
798+ *
799+ * @param val the long value to be written
800+ * @throws IOException if I/O errors occur while writing to the underlying
801+ * stream
802+ */
803+ public void writeLong(long val) throws IOException {
804+ bout.writeLong(val);
805+ }
806+
807+ /**
808+ * Writes a 32 bit float.
809+ *
810+ * @param val the float value to be written
811+ * @throws IOException if I/O errors occur while writing to the underlying
812+ * stream
813+ */
814+ public void writeFloat(float val) throws IOException {
815+ bout.writeFloat(val);
816+ }
817+
818+ /**
819+ * Writes a 64 bit double.
820+ *
821+ * @param val the double value to be written
822+ * @throws IOException if I/O errors occur while writing to the underlying
823+ * stream
824+ */
825+ public void writeDouble(double val) throws IOException {
826+ bout.writeDouble(val);
827+ }
828+
829+ /**
830+ * Writes a String as a sequence of bytes.
831+ *
832+ * @param str the String of bytes to be written
833+ * @throws IOException if I/O errors occur while writing to the underlying
834+ * stream
835+ */
836+ public void writeBytes(String str) throws IOException {
837+ bout.writeBytes(str);
838+ }
839+
840+ /**
841+ * Writes a String as a sequence of chars.
842+ *
843+ * @param str the String of chars to be written
844+ * @throws IOException if I/O errors occur while writing to the underlying
845+ * stream
846+ */
847+ public void writeChars(String str) throws IOException {
848+ bout.writeChars(str);
849+ }
850+
851+ /**
852+ * Primitive data write of this String in
853+ * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
854+ * format. Note that there is a
855+ * significant difference between writing a String into the stream as
856+ * primitive data or as an Object. A String instance written by writeObject
857+ * is written into the stream as a String initially. Future writeObject()
858+ * calls write references to the string into the stream.
859+ *
860+ * @param str the String to be written
861+ * @throws IOException if I/O errors occur while writing to the underlying
862+ * stream
863+ */
864+ public void writeUTF(String str) throws IOException {
865+ bout.writeUTF(str);
866+ }
867+
868+ /**
869+ * Provide programmatic access to the persistent fields to be written
870+ * to ObjectOutput.
871+ *
872+ * @since 1.2
873+ */
874+ public static abstract class PutField {
875+
876+ /**
877+ * Put the value of the named boolean field into the persistent field.
878+ *
879+ * @param name the name of the serializable field
880+ * @param val the value to assign to the field
881+ * @throws IllegalArgumentException if <code>name</code> does not
882+ * match the name of a serializable field for the class whose fields
883+ * are being written, or if the type of the named field is not
884+ * <code>boolean</code>
885+ */
886+ public abstract void put(String name, boolean val);
887+
888+ /**
889+ * Put the value of the named byte field into the persistent field.
890+ *
891+ * @param name the name of the serializable field
892+ * @param val the value to assign to the field
893+ * @throws IllegalArgumentException if <code>name</code> does not
894+ * match the name of a serializable field for the class whose fields
895+ * are being written, or if the type of the named field is not
896+ * <code>byte</code>
897+ */
898+ public abstract void put(String name, byte val);
899+
900+ /**
901+ * Put the value of the named char field into the persistent field.
902+ *
903+ * @param name the name of the serializable field
904+ * @param val the value to assign to the field
905+ * @throws IllegalArgumentException if <code>name</code> does not
906+ * match the name of a serializable field for the class whose fields
907+ * are being written, or if the type of the named field is not
908+ * <code>char</code>
909+ */
910+ public abstract void put(String name, char val);
911+
912+ /**
913+ * Put the value of the named short field into the persistent field.
914+ *
915+ * @param name the name of the serializable field
916+ * @param val the value to assign to the field
917+ * @throws IllegalArgumentException if <code>name</code> does not
918+ * match the name of a serializable field for the class whose fields
919+ * are being written, or if the type of the named field is not
920+ * <code>short</code>
921+ */
922+ public abstract void put(String name, short val);
923+
924+ /**
925+ * Put the value of the named int field into the persistent field.
926+ *
927+ * @param name the name of the serializable field
928+ * @param val the value to assign to the field
929+ * @throws IllegalArgumentException if <code>name</code> does not
930+ * match the name of a serializable field for the class whose fields
931+ * are being written, or if the type of the named field is not
932+ * <code>int</code>
933+ */
934+ public abstract void put(String name, int val);
935+
936+ /**
937+ * Put the value of the named long field into the persistent field.
938+ *
939+ * @param name the name of the serializable field
940+ * @param val the value to assign to the field
941+ * @throws IllegalArgumentException if <code>name</code> does not
942+ * match the name of a serializable field for the class whose fields
943+ * are being written, or if the type of the named field is not
944+ * <code>long</code>
945+ */
946+ public abstract void put(String name, long val);
947+
948+ /**
949+ * Put the value of the named float field into the persistent field.
950+ *
951+ * @param name the name of the serializable field
952+ * @param val the value to assign to the field
953+ * @throws IllegalArgumentException if <code>name</code> does not
954+ * match the name of a serializable field for the class whose fields
955+ * are being written, or if the type of the named field is not
956+ * <code>float</code>
957+ */
958+ public abstract void put(String name, float val);
959+
960+ /**
961+ * Put the value of the named double field into the persistent field.
962+ *
963+ * @param name the name of the serializable field
964+ * @param val the value to assign to the field
965+ * @throws IllegalArgumentException if <code>name</code> does not
966+ * match the name of a serializable field for the class whose fields
967+ * are being written, or if the type of the named field is not
968+ * <code>double</code>
969+ */
970+ public abstract void put(String name, double val);
971+
972+ /**
973+ * Put the value of the named Object field into the persistent field.
974+ *
975+ * @param name the name of the serializable field
976+ * @param val the value to assign to the field
977+ * (which may be <code>null</code>)
978+ * @throws IllegalArgumentException if <code>name</code> does not
979+ * match the name of a serializable field for the class whose fields
980+ * are being written, or if the type of the named field is not a
981+ * reference type
982+ */
983+ public abstract void put(String name, Object val);
984+
985+ /**
986+ * Write the data and fields to the specified ObjectOutput stream,
987+ * which must be the same stream that produced this
988+ * <code>PutField</code> object.
989+ *
990+ * @param out the stream to write the data and fields to
991+ * @throws IOException if I/O errors occur while writing to the
992+ * underlying stream
993+ * @throws IllegalArgumentException if the specified stream is not
994+ * the same stream that produced this <code>PutField</code>
995+ * object
996+ * @deprecated This method does not write the values contained by this
997+ * <code>PutField</code> object in a proper format, and may
998+ * result in corruption of the serialization stream. The
999+ * correct way to write <code>PutField</code> data is by
1000+ * calling the {@link java.io.ObjectOutputStream#writeFields()}
1001+ * method.
1002+ */
1003+ @Deprecated
1004+ public abstract void write(ObjectOutput out) throws IOException;
1005+ }
1006+
1007+
1008+ /**
1009+ * Returns protocol version in use.
1010+ */
1011+ int getProtocolVersion() {
1012+ return protocol;
1013+ }
1014+
1015+ /**
1016+ * Writes string without allowing it to be replaced in stream. Used by
1017+ * ObjectStreamClass to write class descriptor type strings.
1018+ */
1019+ void writeTypeString(String str) throws IOException {
1020+ int handle;
1021+ if (str == null) {
1022+ writeNull();
1023+ } else if ((handle = handles.lookup(str)) != -1) {
1024+ writeHandle(handle);
1025+ } else {
1026+ writeString(str, false);
1027+ }
1028+ }
1029+
1030+ /**
1031+ * Verifies that this (possibly subclass) instance can be constructed
1032+ * without violating security constraints: the subclass must not override
1033+ * security-sensitive non-final methods, or else the
1034+ * "enableSubclassImplementation" SerializablePermission is checked.
1035+ */
1036+ private void verifySubclass() {
1037+ Class cl = getClass();
1038+ if (cl == ObjectOutputStream.class) {
1039+ return;
1040+ }
1041+ SecurityManager sm = System.getSecurityManager();
1042+ if (sm == null) {
1043+ return;
1044+ }
1045+ processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
1046+ WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
1047+ Boolean result = Caches.subclassAudits.get(key);
1048+ if (result == null) {
1049+ result = Boolean.valueOf(auditSubclass(cl));
1050+ Caches.subclassAudits.putIfAbsent(key, result);
1051+ }
1052+ if (result.booleanValue()) {
1053+ return;
1054+ }
1055+ sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
1056+ }
1057+
1058+ /**
1059+ * Performs reflective checks on given subclass to verify that it doesn't
1060+ * override security-sensitive non-final methods. Returns true if subclass
1061+ * is "safe", false otherwise.
1062+ */
1063+ private static boolean auditSubclass(final Class subcl) {
1064+ Boolean result = AccessController.doPrivileged(
1065+ new PrivilegedAction<Boolean>() {
1066+ public Boolean run() {
1067+ for (Class cl = subcl;
1068+ cl != ObjectOutputStream.class;
1069+ cl = cl.getSuperclass())
1070+ {
1071+ try {
1072+ cl.getDeclaredMethod(
1073+ "writeUnshared", new Class[] { Object.class });
1074+ return Boolean.FALSE;
1075+ } catch (NoSuchMethodException ex) {
1076+ }
1077+ try {
1078+ cl.getDeclaredMethod("putFields", (Class[]) null);
1079+ return Boolean.FALSE;
1080+ } catch (NoSuchMethodException ex) {
1081+ }
1082+ }
1083+ return Boolean.TRUE;
1084+ }
1085+ }
1086+ );
1087+ return result.booleanValue();
1088+ }
1089+
1090+ /**
1091+ * Clears internal data structures.
1092+ */
1093+ private void clear() {
1094+ subs.clear();
1095+ handles.clear();
1096+ }
1097+
1098+ /**
1099+ * Underlying writeObject/writeUnshared implementation.
1100+ */
1101+ private void writeObject0(Object obj, boolean unshared)
1102+ throws IOException
1103+ {
1104+ boolean oldMode = bout.setBlockDataMode(false);
1105+ depth++;
1106+ try {
1107+ // handle previously written and non-replaceable objects
1108+ int h;
1109+ if ((obj = subs.lookup(obj)) == null) {
1110+ writeNull();
1111+ return;
1112+ } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1113+ writeHandle(h);
1114+ return;
1115+ } else if (obj instanceof Class) {
1116+ writeClass((Class) obj, unshared);
1117+ return;
1118+ } else if (obj instanceof ObjectStreamClass) {
1119+ writeClassDesc((ObjectStreamClass) obj, unshared);
1120+ return;
1121+ }
1122+
1123+ // check for replacement object
1124+ Object orig = obj;
1125+ Class cl = obj.getClass();
1126+ ObjectStreamClass desc;
1127+ for (;;) {
1128+ // REMIND: skip this check for strings/arrays?
1129+ Class repCl;
1130+ desc = ObjectStreamClass.lookup(cl, true);
1131+ if (!desc.hasWriteReplaceMethod() ||
1132+ (obj = desc.invokeWriteReplace(obj)) == null ||
1133+ (repCl = obj.getClass()) == cl)
1134+ {
1135+ break;
1136+ }
1137+ cl = repCl;
1138+ }
1139+ if (enableReplace) {
1140+ Object rep = replaceObject(obj);
1141+ if (rep != obj && rep != null) {
1142+ cl = rep.getClass();
1143+ desc = ObjectStreamClass.lookup(cl, true);
1144+ }
1145+ obj = rep;
1146+ }
1147+
1148+ // if object replaced, run through original checks a second time
1149+ if (obj != orig) {
1150+ subs.assign(orig, obj);
1151+ if (obj == null) {
1152+ writeNull();
1153+ return;
1154+ } else if (!unshared && (h = handles.lookup(obj)) != -1) {
1155+ writeHandle(h);
1156+ return;
1157+ } else if (obj instanceof Class) {
1158+ writeClass((Class) obj, unshared);
1159+ return;
1160+ } else if (obj instanceof ObjectStreamClass) {
1161+ writeClassDesc((ObjectStreamClass) obj, unshared);
1162+ return;
1163+ }
1164+ }
1165+
1166+ // remaining cases
1167+ if (obj instanceof String) {
1168+ writeString((String) obj, unshared);
1169+ } else if (cl.isArray()) {
1170+ writeArray(obj, desc, unshared);
1171+ } else if (obj instanceof Enum) {
1172+ writeEnum((Enum) obj, desc, unshared);
1173+ } else if (obj instanceof Serializable) {
1174+ writeOrdinaryObject(obj, desc, unshared);
1175+ } else {
1176+ if (extendedDebugInfo) {
1177+ throw new NotSerializableException(
1178+ cl.getName() + "\n" + debugInfoStack.toString());
1179+ } else {
1180+ throw new NotSerializableException(cl.getName());
1181+ }
1182+ }
1183+ } finally {
1184+ depth--;
1185+ bout.setBlockDataMode(oldMode);
1186+ }
1187+ }
1188+
1189+ /**
1190+ * Writes null code to stream.
1191+ */
1192+ private void writeNull() throws IOException {
1193+ bout.writeByte(TC_NULL);
1194+ }
1195+
1196+ /**
1197+ * Writes given object handle to stream.
1198+ */
1199+ private void writeHandle(int handle) throws IOException {
1200+ bout.writeByte(TC_REFERENCE);
1201+ bout.writeInt(baseWireHandle + handle);
1202+ }
1203+
1204+ /**
1205+ * Writes representation of given class to stream.
1206+ */
1207+ private void writeClass(Class cl, boolean unshared) throws IOException {
1208+ bout.writeByte(TC_CLASS);
1209+ writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
1210+ handles.assign(unshared ? null : cl);
1211+ }
1212+
1213+ /**
1214+ * Writes representation of given class descriptor to stream.
1215+ */
1216+ private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
1217+ throws IOException
1218+ {
1219+ int handle;
1220+ if (desc == null) {
1221+ writeNull();
1222+ } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
1223+ writeHandle(handle);
1224+ } else if (desc.isProxy()) {
1225+ writeProxyDesc(desc, unshared);
1226+ } else {
1227+ writeNonProxyDesc(desc, unshared);
1228+ }
1229+ }
1230+
1231+ /**
1232+ * Writes class descriptor representing a dynamic proxy class to stream.
1233+ */
1234+ private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
1235+ throws IOException
1236+ {
1237+ bout.writeByte(TC_PROXYCLASSDESC);
1238+ handles.assign(unshared ? null : desc);
1239+
1240+ Class cl = desc.forClass();
1241+ Class[] ifaces = cl.getInterfaces();
1242+ bout.writeInt(ifaces.length);
1243+ for (int i = 0; i < ifaces.length; i++) {
1244+ bout.writeUTF(ifaces[i].getName());
1245+ }
1246+
1247+ bout.setBlockDataMode(true);
1248+ annotateProxyClass(cl);
1249+ bout.setBlockDataMode(false);
1250+ bout.writeByte(TC_ENDBLOCKDATA);
1251+
1252+ writeClassDesc(desc.getSuperDesc(), false);
1253+ }
1254+
1255+ /**
1256+ * Writes class descriptor representing a standard (i.e., not a dynamic
1257+ * proxy) class to stream.
1258+ */
1259+ private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
1260+ throws IOException
1261+ {
1262+ bout.writeByte(TC_CLASSDESC);
1263+ handles.assign(unshared ? null : desc);
1264+
1265+ if (protocol == PROTOCOL_VERSION_1) {
1266+ // do not invoke class descriptor write hook with old protocol
1267+ desc.writeNonProxy(this);
1268+ } else {
1269+ writeClassDescriptor(desc);
1270+ }
1271+
1272+ Class cl = desc.forClass();
1273+ bout.setBlockDataMode(true);
1274+ annotateClass(cl);
1275+ bout.setBlockDataMode(false);
1276+ bout.writeByte(TC_ENDBLOCKDATA);
1277+
1278+ writeClassDesc(desc.getSuperDesc(), false);
1279+ }
1280+
1281+ /**
1282+ * Writes given string to stream, using standard or long UTF format
1283+ * depending on string length.
1284+ */
1285+ private void writeString(String str, boolean unshared) throws IOException {
1286+ handles.assign(unshared ? null : str);
1287+ long utflen = bout.getUTFLength(str);
1288+ if (utflen <= 0xFFFF) {
1289+ bout.writeByte(TC_STRING);
1290+ bout.writeUTF(str, utflen);
1291+ } else {
1292+ bout.writeByte(TC_LONGSTRING);
1293+ bout.writeLongUTF(str, utflen);
1294+ }
1295+ }
1296+
1297+ /**
1298+ * Writes given array object to stream.
1299+ */
1300+ private void writeArray(Object array,
1301+ ObjectStreamClass desc,
1302+ boolean unshared)
1303+ throws IOException
1304+ {
1305+ bout.writeByte(TC_ARRAY);
1306+ writeClassDesc(desc, false);
1307+ handles.assign(unshared ? null : array);
1308+
1309+ Class ccl = desc.forClass().getComponentType();
1310+ if (ccl.isPrimitive()) {
1311+ if (ccl == Integer.TYPE) {
1312+ int[] ia = (int[]) array;
1313+ bout.writeInt(ia.length);
1314+ bout.writeInts(ia, 0, ia.length);
1315+ } else if (ccl == Byte.TYPE) {
1316+ byte[] ba = (byte[]) array;
1317+ bout.writeInt(ba.length);
1318+ bout.write(ba, 0, ba.length, true);
1319+ } else if (ccl == Long.TYPE) {
1320+ long[] ja = (long[]) array;
1321+ bout.writeInt(ja.length);
1322+ bout.writeLongs(ja, 0, ja.length);
1323+ } else if (ccl == Float.TYPE) {
1324+ float[] fa = (float[]) array;
1325+ bout.writeInt(fa.length);
1326+ bout.writeFloats(fa, 0, fa.length);
1327+ } else if (ccl == Double.TYPE) {
1328+ double[] da = (double[]) array;
1329+ bout.writeInt(da.length);
1330+ bout.writeDoubles(da, 0, da.length);
1331+ } else if (ccl == Short.TYPE) {
1332+ short[] sa = (short[]) array;
1333+ bout.writeInt(sa.length);
1334+ bout.writeShorts(sa, 0, sa.length);
1335+ } else if (ccl == Character.TYPE) {
1336+ char[] ca = (char[]) array;
1337+ bout.writeInt(ca.length);
1338+ bout.writeChars(ca, 0, ca.length);
1339+ } else if (ccl == Boolean.TYPE) {
1340+ boolean[] za = (boolean[]) array;
1341+ bout.writeInt(za.length);
1342+ bout.writeBooleans(za, 0, za.length);
1343+ } else {
1344+ throw new InternalError();
1345+ }
1346+ } else {
1347+ Object[] objs = (Object[]) array;
1348+ int len = objs.length;
1349+ bout.writeInt(len);
1350+ if (extendedDebugInfo) {
1351+ debugInfoStack.push(
1352+ "array (class \"" + array.getClass().getName() +
1353+ "\", size: " + len + ")");
1354+ }
1355+ try {
1356+ for (int i = 0; i < len; i++) {
1357+ if (extendedDebugInfo) {
1358+ debugInfoStack.push(
1359+ "element of array (index: " + i + ")");
1360+ }
1361+ try {
1362+ writeObject0(objs[i], false);
1363+ } finally {
1364+ if (extendedDebugInfo) {
1365+ debugInfoStack.pop();
1366+ }
1367+ }
1368+ }
1369+ } finally {
1370+ if (extendedDebugInfo) {
1371+ debugInfoStack.pop();
1372+ }
1373+ }
1374+ }
1375+ }
1376+
1377+ /**
1378+ * Writes given enum constant to stream.
1379+ */
1380+ private void writeEnum(Enum en,
1381+ ObjectStreamClass desc,
1382+ boolean unshared)
1383+ throws IOException
1384+ {
1385+ bout.writeByte(TC_ENUM);
1386+ ObjectStreamClass sdesc = desc.getSuperDesc();
1387+ writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
1388+ handles.assign(unshared ? null : en);
1389+ writeString(en.name(), false);
1390+ }
1391+
1392+ /**
1393+ * Writes representation of a "ordinary" (i.e., not a String, Class,
1394+ * ObjectStreamClass, array, or enum constant) serializable object to the
1395+ * stream.
1396+ */
1397+ private void writeOrdinaryObject(Object obj,
1398+ ObjectStreamClass desc,
1399+ boolean unshared)
1400+ throws IOException
1401+ {
1402+ if (extendedDebugInfo) {
1403+ debugInfoStack.push(
1404+ (depth == 1 ? "root " : "") + "object (class \"" +
1405+ obj.getClass().getName() + "\", " + obj.toString() + ")");
1406+ }
1407+ try {
1408+ desc.checkSerialize();
1409+
1410+ bout.writeByte(TC_OBJECT);
1411+ writeClassDesc(desc, false);
1412+ handles.assign(unshared ? null : obj);
1413+ if (desc.isExternalizable() && !desc.isProxy()) {
1414+ writeExternalData((Externalizable) obj);
1415+ } else {
1416+ writeSerialData(obj, desc);
1417+ }
1418+ } finally {
1419+ if (extendedDebugInfo) {
1420+ debugInfoStack.pop();
1421+ }
1422+ }
1423+ }
1424+
1425+ /**
1426+ * Writes externalizable data of given object by invoking its
1427+ * writeExternal() method.
1428+ */
1429+ private void writeExternalData(Externalizable obj) throws IOException {
1430+ PutFieldImpl oldPut = curPut;
1431+ curPut = null;
1432+
1433+ if (extendedDebugInfo) {
1434+ debugInfoStack.push("writeExternal data");
1435+ }
1436+ SerialCallbackContext oldContext = curContext;
1437+ try {
1438+ curContext = null;
1439+ if (protocol == PROTOCOL_VERSION_1) {
1440+ obj.writeExternal(this);
1441+ } else {
1442+ bout.setBlockDataMode(true);
1443+ obj.writeExternal(this);
1444+ bout.setBlockDataMode(false);
1445+ bout.writeByte(TC_ENDBLOCKDATA);
1446+ }
1447+ } finally {
1448+ curContext = oldContext;
1449+ if (extendedDebugInfo) {
1450+ debugInfoStack.pop();
1451+ }
1452+ }
1453+
1454+ curPut = oldPut;
1455+ }
1456+
1457+ /**
1458+ * Writes instance data for each serializable class of given object, from
1459+ * superclass to subclass.
1460+ */
1461+ private void writeSerialData(Object obj, ObjectStreamClass desc)
1462+ throws IOException
1463+ {
1464+ ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
1465+ for (int i = 0; i < slots.length; i++) {
1466+ ObjectStreamClass slotDesc = slots[i].desc;
1467+ if (slotDesc.hasWriteObjectMethod()) {
1468+ PutFieldImpl oldPut = curPut;
1469+ curPut = null;
1470+ SerialCallbackContext oldContext = curContext;
1471+
1472+ if (extendedDebugInfo) {
1473+ debugInfoStack.push(
1474+ "custom writeObject data (class \"" +
1475+ slotDesc.getName() + "\")");
1476+ }
1477+ try {
1478+ curContext = new SerialCallbackContext(obj, slotDesc);
1479+ bout.setBlockDataMode(true);
1480+ slotDesc.invokeWriteObject(obj, this);
1481+ bout.setBlockDataMode(false);
1482+ bout.writeByte(TC_ENDBLOCKDATA);
1483+ } finally {
1484+ curContext.setUsed();
1485+ curContext = oldContext;
1486+ if (extendedDebugInfo) {
1487+ debugInfoStack.pop();
1488+ }
1489+ }
1490+
1491+ curPut = oldPut;
1492+ } else {
1493+ defaultWriteFields(obj, slotDesc);
1494+ }
1495+ }
1496+ }
1497+
1498+ /**
1499+ * Fetches and writes values of serializable fields of given object to
1500+ * stream. The given class descriptor specifies which field values to
1501+ * write, and in which order they should be written.
1502+ */
1503+ private void defaultWriteFields(Object obj, ObjectStreamClass desc)
1504+ throws IOException
1505+ {
1506+ // REMIND: perform conservative isInstance check here?
1507+ desc.checkDefaultSerialize();
1508+
1509+ int primDataSize = desc.getPrimDataSize();
1510+ if (primVals == null || primVals.length < primDataSize) {
1511+ primVals = new byte[primDataSize];
1512+ }
1513+ desc.getPrimFieldValues(obj, primVals);
1514+ bout.write(primVals, 0, primDataSize, false);
1515+
1516+ ObjectStreamField[] fields = desc.getFields(false);
1517+ Object[] objVals = new Object[desc.getNumObjFields()];
1518+ int numPrimFields = fields.length - objVals.length;
1519+ desc.getObjFieldValues(obj, objVals);
1520+ for (int i = 0; i < objVals.length; i++) {
1521+ if (extendedDebugInfo) {
1522+ debugInfoStack.push(
1523+ "field (class \"" + desc.getName() + "\", name: \"" +
1524+ fields[numPrimFields + i].getName() + "\", type: \"" +
1525+ fields[numPrimFields + i].getType() + "\")");
1526+ }
1527+ try {
1528+ writeObject0(objVals[i],
1529+ fields[numPrimFields + i].isUnshared());
1530+ } finally {
1531+ if (extendedDebugInfo) {
1532+ debugInfoStack.pop();
1533+ }
1534+ }
1535+ }
1536+ }
1537+
1538+ /**
1539+ * Attempts to write to stream fatal IOException that has caused
1540+ * serialization to abort.
1541+ */
1542+ private void writeFatalException(IOException ex) throws IOException {
1543+ /*
1544+ * Note: the serialization specification states that if a second
1545+ * IOException occurs while attempting to serialize the original fatal
1546+ * exception to the stream, then a StreamCorruptedException should be
1547+ * thrown (section 2.1). However, due to a bug in previous
1548+ * implementations of serialization, StreamCorruptedExceptions were
1549+ * rarely (if ever) actually thrown--the "root" exceptions from
1550+ * underlying streams were thrown instead. This historical behavior is
1551+ * followed here for consistency.
1552+ */
1553+ clear();
1554+ boolean oldMode = bout.setBlockDataMode(false);
1555+ try {
1556+ bout.writeByte(TC_EXCEPTION);
1557+ writeObject0(ex, false);
1558+ clear();
1559+ } finally {
1560+ bout.setBlockDataMode(oldMode);
1561+ }
1562+ }
1563+
1564+ /**
1565+ * Converts specified span of float values into byte values.
1566+ */
1567+ // REMIND: remove once hotspot inlines Float.floatToIntBits
1568+ private static native void floatsToBytes(float[] src, int srcpos,
1569+ byte[] dst, int dstpos,
1570+ int nfloats);
1571+
1572+ /**
1573+ * Converts specified span of double values into byte values.
1574+ */
1575+ // REMIND: remove once hotspot inlines Double.doubleToLongBits
1576+ private static native void doublesToBytes(double[] src, int srcpos,
1577+ byte[] dst, int dstpos,
1578+ int ndoubles);
1579+
1580+ /**
1581+ * Default PutField implementation.
1582+ */
1583+ private class PutFieldImpl extends PutField {
1584+
1585+ /** class descriptor describing serializable fields */
1586+ private final ObjectStreamClass desc;
1587+ /** primitive field values */
1588+ private final byte[] primVals;
1589+ /** object field values */
1590+ private final Object[] objVals;
1591+
1592+ /**
1593+ * Creates PutFieldImpl object for writing fields defined in given
1594+ * class descriptor.
1595+ */
1596+ PutFieldImpl(ObjectStreamClass desc) {
1597+ this.desc = desc;
1598+ primVals = new byte[desc.getPrimDataSize()];
1599+ objVals = new Object[desc.getNumObjFields()];
1600+ }
1601+
1602+ public void put(String name, boolean val) {
1603+ Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val);
1604+ }
1605+
1606+ public void put(String name, byte val) {
1607+ primVals[getFieldOffset(name, Byte.TYPE)] = val;
1608+ }
1609+
1610+ public void put(String name, char val) {
1611+ Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val);
1612+ }
1613+
1614+ public void put(String name, short val) {
1615+ Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val);
1616+ }
1617+
1618+ public void put(String name, int val) {
1619+ Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val);
1620+ }
1621+
1622+ public void put(String name, float val) {
1623+ Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val);
1624+ }
1625+
1626+ public void put(String name, long val) {
1627+ Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
1628+ }
1629+
1630+ public void put(String name, double val) {
1631+ Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val);
1632+ }
1633+
1634+ public void put(String name, Object val) {
1635+ objVals[getFieldOffset(name, Object.class)] = val;
1636+ }
1637+
1638+ // deprecated in ObjectOutputStream.PutField
1639+ public void write(ObjectOutput out) throws IOException {
1640+ /*
1641+ * Applications should *not* use this method to write PutField
1642+ * data, as it will lead to stream corruption if the PutField
1643+ * object writes any primitive data (since block data mode is not
1644+ * unset/set properly, as is done in OOS.writeFields()). This
1645+ * broken implementation is being retained solely for behavioral
1646+ * compatibility, in order to support applications which use
1647+ * OOS.PutField.write() for writing only non-primitive data.
1648+ *
1649+ * Serialization of unshared objects is not implemented here since
1650+ * it is not necessary for backwards compatibility; also, unshared
1651+ * semantics may not be supported by the given ObjectOutput
1652+ * instance. Applications which write unshared objects using the
1653+ * PutField API must use OOS.writeFields().
1654+ */
1655+ if (ObjectOutputStream.this != out) {
1656+ throw new IllegalArgumentException("wrong stream");
1657+ }
1658+ out.write(primVals, 0, primVals.length);
1659+
1660+ ObjectStreamField[] fields = desc.getFields(false);
1661+ int numPrimFields = fields.length - objVals.length;
1662+ // REMIND: warn if numPrimFields > 0?
1663+ for (int i = 0; i < objVals.length; i++) {
1664+ if (fields[numPrimFields + i].isUnshared()) {
1665+ throw new IOException("cannot write unshared object");
1666+ }
1667+ out.writeObject(objVals[i]);
1668+ }
1669+ }
1670+
1671+ /**
1672+ * Writes buffered primitive data and object fields to stream.
1673+ */
1674+ void writeFields() throws IOException {
1675+ bout.write(primVals, 0, primVals.length, false);
1676+
1677+ ObjectStreamField[] fields = desc.getFields(false);
1678+ int numPrimFields = fields.length - objVals.length;
1679+ for (int i = 0; i < objVals.length; i++) {
1680+ if (extendedDebugInfo) {
1681+ debugInfoStack.push(
1682+ "field (class \"" + desc.getName() + "\", name: \"" +
1683+ fields[numPrimFields + i].getName() + "\", type: \"" +
1684+ fields[numPrimFields + i].getType() + "\")");
1685+ }
1686+ try {
1687+ writeObject0(objVals[i],
1688+ fields[numPrimFields + i].isUnshared());
1689+ } finally {
1690+ if (extendedDebugInfo) {
1691+ debugInfoStack.pop();
1692+ }
1693+ }
1694+ }
1695+ }
1696+
1697+ /**
1698+ * Returns offset of field with given name and type. A specified type
1699+ * of null matches all types, Object.class matches all non-primitive
1700+ * types, and any other non-null type matches assignable types only.
1701+ * Throws IllegalArgumentException if no matching field found.
1702+ */
1703+ private int getFieldOffset(String name, Class type) {
1704+ ObjectStreamField field = desc.getField(name, type);
1705+ if (field == null) {
1706+ throw new IllegalArgumentException("no such field " + name +
1707+ " with type " + type);
1708+ }
1709+ return field.getOffset();
1710+ }
1711+ }
1712+
1713+ /**
1714+ * Buffered output stream with two modes: in default mode, outputs data in
1715+ * same format as DataOutputStream; in "block data" mode, outputs data
1716+ * bracketed by block data markers (see object serialization specification
1717+ * for details).
1718+ */
1719+ private static class BlockDataOutputStream
1720+ extends OutputStream implements DataOutput
1721+ {
1722+ /** maximum data block length */
1723+ private static final int MAX_BLOCK_SIZE = 1024;
1724+ /** maximum data block header length */
1725+ private static final int MAX_HEADER_SIZE = 5;
1726+ /** (tunable) length of char buffer (for writing strings) */
1727+ private static final int CHAR_BUF_SIZE = 256;
1728+
1729+ /** buffer for writing general/block data */
1730+ private final byte[] buf = new byte[MAX_BLOCK_SIZE];
1731+ /** buffer for writing block data headers */
1732+ private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
1733+ /** char buffer for fast string writes */
1734+ private final char[] cbuf = new char[CHAR_BUF_SIZE];
1735+
1736+ /** block data mode */
1737+ private boolean blkmode = false;
1738+ /** current offset into buf */
1739+ private int pos = 0;
1740+
1741+ /** underlying output stream */
1742+ private final OutputStream out;
1743+ /** loopback stream (for data writes that span data blocks) */
1744+ private final DataOutputStream dout;
1745+
1746+ /**
1747+ * Creates new BlockDataOutputStream on top of given underlying stream.
1748+ * Block data mode is turned off by default.
1749+ */
1750+ BlockDataOutputStream(OutputStream out) {
1751+ this.out = out;
1752+ dout = new DataOutputStream(this);
1753+ }
1754+
1755+ /**
1756+ * Sets block data mode to the given mode (true == on, false == off)
1757+ * and returns the previous mode value. If the new mode is the same as
1758+ * the old mode, no action is taken. If the new mode differs from the
1759+ * old mode, any buffered data is flushed before switching to the new
1760+ * mode.
1761+ */
1762+ boolean setBlockDataMode(boolean mode) throws IOException {
1763+ if (blkmode == mode) {
1764+ return blkmode;
1765+ }
1766+ drain();
1767+ blkmode = mode;
1768+ return !blkmode;
1769+ }
1770+
1771+ /**
1772+ * Returns true if the stream is currently in block data mode, false
1773+ * otherwise.
1774+ */
1775+ boolean getBlockDataMode() {
1776+ return blkmode;
1777+ }
1778+
1779+ /* ----------------- generic output stream methods ----------------- */
1780+ /*
1781+ * The following methods are equivalent to their counterparts in
1782+ * OutputStream, except that they partition written data into data
1783+ * blocks when in block data mode.
1784+ */
1785+
1786+ public void write(int b) throws IOException {
1787+ if (pos >= MAX_BLOCK_SIZE) {
1788+ drain();
1789+ }
1790+ buf[pos++] = (byte) b;
1791+ }
1792+
1793+ public void write(byte[] b) throws IOException {
1794+ write(b, 0, b.length, false);
1795+ }
1796+
1797+ public void write(byte[] b, int off, int len) throws IOException {
1798+ write(b, off, len, false);
1799+ }
1800+
1801+ public void flush() throws IOException {
1802+ drain();
1803+ out.flush();
1804+ }
1805+
1806+ public void close() throws IOException {
1807+ flush();
1808+ out.close();
1809+ }
1810+
1811+ /**
1812+ * Writes specified span of byte values from given array. If copy is
1813+ * true, copies the values to an intermediate buffer before writing
1814+ * them to underlying stream (to avoid exposing a reference to the
1815+ * original byte array).
1816+ */
1817+ void write(byte[] b, int off, int len, boolean copy)
1818+ throws IOException
1819+ {
1820+ if (!(copy || blkmode)) { // write directly
1821+ drain();
1822+ out.write(b, off, len);
1823+ return;
1824+ }
1825+
1826+ while (len > 0) {
1827+ if (pos >= MAX_BLOCK_SIZE) {
1828+ drain();
1829+ }
1830+ if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) {
1831+ // avoid unnecessary copy
1832+ writeBlockHeader(MAX_BLOCK_SIZE);
1833+ out.write(b, off, MAX_BLOCK_SIZE);
1834+ off += MAX_BLOCK_SIZE;
1835+ len -= MAX_BLOCK_SIZE;
1836+ } else {
1837+ int wlen = Math.min(len, MAX_BLOCK_SIZE - pos);
1838+ System.arraycopy(b, off, buf, pos, wlen);
1839+ pos += wlen;
1840+ off += wlen;
1841+ len -= wlen;
1842+ }
1843+ }
1844+ }
1845+
1846+ /**
1847+ * Writes all buffered data from this stream to the underlying stream,
1848+ * but does not flush underlying stream.
1849+ */
1850+ void drain() throws IOException {
1851+ if (pos == 0) {
1852+ return;
1853+ }
1854+ if (blkmode) {
1855+ writeBlockHeader(pos);
1856+ }
1857+ out.write(buf, 0, pos);
1858+ pos = 0;
1859+ }
1860+
1861+ /**
1862+ * Writes block data header. Data blocks shorter than 256 bytes are
1863+ * prefixed with a 2-byte header; all others start with a 5-byte
1864+ * header.
1865+ */
1866+ private void writeBlockHeader(int len) throws IOException {
1867+ if (len <= 0xFF) {
1868+ hbuf[0] = TC_BLOCKDATA;
1869+ hbuf[1] = (byte) len;
1870+ out.write(hbuf, 0, 2);
1871+ } else {
1872+ hbuf[0] = TC_BLOCKDATALONG;
1873+ Bits.putInt(hbuf, 1, len);
1874+ out.write(hbuf, 0, 5);
1875+ }
1876+ }
1877+
1878+
1879+ /* ----------------- primitive data output methods ----------------- */
1880+ /*
1881+ * The following methods are equivalent to their counterparts in
1882+ * DataOutputStream, except that they partition written data into data
1883+ * blocks when in block data mode.
1884+ */
1885+
1886+ public void writeBoolean(boolean v) throws IOException {
1887+ if (pos >= MAX_BLOCK_SIZE) {
1888+ drain();
1889+ }
1890+ Bits.putBoolean(buf, pos++, v);
1891+ }
1892+
1893+ public void writeByte(int v) throws IOException {
1894+ if (pos >= MAX_BLOCK_SIZE) {
1895+ drain();
1896+ }
1897+ buf[pos++] = (byte) v;
1898+ }
1899+
1900+ public void writeChar(int v) throws IOException {
1901+ if (pos + 2 <= MAX_BLOCK_SIZE) {
1902+ Bits.putChar(buf, pos, (char) v);
1903+ pos += 2;
1904+ } else {
1905+ dout.writeChar(v);
1906+ }
1907+ }
1908+
1909+ public void writeShort(int v) throws IOException {
1910+ if (pos + 2 <= MAX_BLOCK_SIZE) {
1911+ Bits.putShort(buf, pos, (short) v);
1912+ pos += 2;
1913+ } else {
1914+ dout.writeShort(v);
1915+ }
1916+ }
1917+
1918+ public void writeInt(int v) throws IOException {
1919+ if (pos + 4 <= MAX_BLOCK_SIZE) {
1920+ Bits.putInt(buf, pos, v);
1921+ pos += 4;
1922+ } else {
1923+ dout.writeInt(v);
1924+ }
1925+ }
1926+
1927+ public void writeFloat(float v) throws IOException {
1928+ if (pos + 4 <= MAX_BLOCK_SIZE) {
1929+ Bits.putFloat(buf, pos, v);
1930+ pos += 4;
1931+ } else {
1932+ dout.writeFloat(v);
1933+ }
1934+ }
1935+
1936+ public void writeLong(long v) throws IOException {
1937+ if (pos + 8 <= MAX_BLOCK_SIZE) {
1938+ Bits.putLong(buf, pos, v);
1939+ pos += 8;
1940+ } else {
1941+ dout.writeLong(v);
1942+ }
1943+ }
1944+
1945+ public void writeDouble(double v) throws IOException {
1946+ if (pos + 8 <= MAX_BLOCK_SIZE) {
1947+ Bits.putDouble(buf, pos, v);
1948+ pos += 8;
1949+ } else {
1950+ dout.writeDouble(v);
1951+ }
1952+ }
1953+
1954+ public void writeBytes(String s) throws IOException {
1955+ int endoff = s.length();
1956+ int cpos = 0;
1957+ int csize = 0;
1958+ for (int off = 0; off < endoff; ) {
1959+ if (cpos >= csize) {
1960+ cpos = 0;
1961+ csize = Math.min(endoff - off, CHAR_BUF_SIZE);
1962+ s.getChars(off, off + csize, cbuf, 0);
1963+ }
1964+ if (pos >= MAX_BLOCK_SIZE) {
1965+ drain();
1966+ }
1967+ int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
1968+ int stop = pos + n;
1969+ while (pos < stop) {
1970+ buf[pos++] = (byte) cbuf[cpos++];
1971+ }
1972+ off += n;
1973+ }
1974+ }
1975+
1976+ public void writeChars(String s) throws IOException {
1977+ int endoff = s.length();
1978+ for (int off = 0; off < endoff; ) {
1979+ int csize = Math.min(endoff - off, CHAR_BUF_SIZE);
1980+ s.getChars(off, off + csize, cbuf, 0);
1981+ writeChars(cbuf, 0, csize);
1982+ off += csize;
1983+ }
1984+ }
1985+
1986+ public void writeUTF(String s) throws IOException {
1987+ writeUTF(s, getUTFLength(s));
1988+ }
1989+
1990+
1991+ /* -------------- primitive data array output methods -------------- */
1992+ /*
1993+ * The following methods write out spans of primitive data values.
1994+ * Though equivalent to calling the corresponding primitive write
1995+ * methods repeatedly, these methods are optimized for writing groups
1996+ * of primitive data values more efficiently.
1997+ */
1998+
1999+ void writeBooleans(boolean[] v, int off, int len) throws IOException {
2000+ int endoff = off + len;
2001+ while (off < endoff) {
2002+ if (pos >= MAX_BLOCK_SIZE) {
2003+ drain();
2004+ }
2005+ int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos));
2006+ while (off < stop) {
2007+ Bits.putBoolean(buf, pos++, v[off++]);
2008+ }
2009+ }
2010+ }
2011+
2012+ void writeChars(char[] v, int off, int len) throws IOException {
2013+ int limit = MAX_BLOCK_SIZE - 2;
2014+ int endoff = off + len;
2015+ while (off < endoff) {
2016+ if (pos <= limit) {
2017+ int avail = (MAX_BLOCK_SIZE - pos) >> 1;
2018+ int stop = Math.min(endoff, off + avail);
2019+ while (off < stop) {
2020+ Bits.putChar(buf, pos, v[off++]);
2021+ pos += 2;
2022+ }
2023+ } else {
2024+ dout.writeChar(v[off++]);
2025+ }
2026+ }
2027+ }
2028+
2029+ void writeShorts(short[] v, int off, int len) throws IOException {
2030+ int limit = MAX_BLOCK_SIZE - 2;
2031+ int endoff = off + len;
2032+ while (off < endoff) {
2033+ if (pos <= limit) {
2034+ int avail = (MAX_BLOCK_SIZE - pos) >> 1;
2035+ int stop = Math.min(endoff, off + avail);
2036+ while (off < stop) {
2037+ Bits.putShort(buf, pos, v[off++]);
2038+ pos += 2;
2039+ }
2040+ } else {
2041+ dout.writeShort(v[off++]);
2042+ }
2043+ }
2044+ }
2045+
2046+ void writeInts(int[] v, int off, int len) throws IOException {
2047+ int limit = MAX_BLOCK_SIZE - 4;
2048+ int endoff = off + len;
2049+ while (off < endoff) {
2050+ if (pos <= limit) {
2051+ int avail = (MAX_BLOCK_SIZE - pos) >> 2;
2052+ int stop = Math.min(endoff, off + avail);
2053+ while (off < stop) {
2054+ Bits.putInt(buf, pos, v[off++]);
2055+ pos += 4;
2056+ }
2057+ } else {
2058+ dout.writeInt(v[off++]);
2059+ }
2060+ }
2061+ }
2062+
2063+ void writeFloats(float[] v, int off, int len) throws IOException {
2064+ int limit = MAX_BLOCK_SIZE - 4;
2065+ int endoff = off + len;
2066+ while (off < endoff) {
2067+ if (pos <= limit) {
2068+ int avail = (MAX_BLOCK_SIZE - pos) >> 2;
2069+ int chunklen = Math.min(endoff - off, avail);
2070+ floatsToBytes(v, off, buf, pos, chunklen);
2071+ off += chunklen;
2072+ pos += chunklen << 2;
2073+ } else {
2074+ dout.writeFloat(v[off++]);
2075+ }
2076+ }
2077+ }
2078+
2079+ void writeLongs(long[] v, int off, int len) throws IOException {
2080+ int limit = MAX_BLOCK_SIZE - 8;
2081+ int endoff = off + len;
2082+ while (off < endoff) {
2083+ if (pos <= limit) {
2084+ int avail = (MAX_BLOCK_SIZE - pos) >> 3;
2085+ int stop = Math.min(endoff, off + avail);
2086+ while (off < stop) {
2087+ Bits.putLong(buf, pos, v[off++]);
2088+ pos += 8;
2089+ }
2090+ } else {
2091+ dout.writeLong(v[off++]);
2092+ }
2093+ }
2094+ }
2095+
2096+ void writeDoubles(double[] v, int off, int len) throws IOException {
2097+ int limit = MAX_BLOCK_SIZE - 8;
2098+ int endoff = off + len;
2099+ while (off < endoff) {
2100+ if (pos <= limit) {
2101+ int avail = (MAX_BLOCK_SIZE - pos) >> 3;
2102+ int chunklen = Math.min(endoff - off, avail);
2103+ doublesToBytes(v, off, buf, pos, chunklen);
2104+ off += chunklen;
2105+ pos += chunklen << 3;
2106+ } else {
2107+ dout.writeDouble(v[off++]);
2108+ }
2109+ }
2110+ }
2111+
2112+ /**
2113+ * Returns the length in bytes of the UTF encoding of the given string.
2114+ */
2115+ long getUTFLength(String s) {
2116+ int len = s.length();
2117+ long utflen = 0;
2118+ for (int off = 0; off < len; ) {
2119+ int csize = Math.min(len - off, CHAR_BUF_SIZE);
2120+ s.getChars(off, off + csize, cbuf, 0);
2121+ for (int cpos = 0; cpos < csize; cpos++) {
2122+ char c = cbuf[cpos];
2123+ if (c >= 0x0001 && c <= 0x007F) {
2124+ utflen++;
2125+ } else if (c > 0x07FF) {
2126+ utflen += 3;
2127+ } else {
2128+ utflen += 2;
2129+ }
2130+ }
2131+ off += csize;
2132+ }
2133+ return utflen;
2134+ }
2135+
2136+ /**
2137+ * Writes the given string in UTF format. This method is used in
2138+ * situations where the UTF encoding length of the string is already
2139+ * known; specifying it explicitly avoids a prescan of the string to
2140+ * determine its UTF length.
2141+ */
2142+ void writeUTF(String s, long utflen) throws IOException {
2143+ if (utflen > 0xFFFFL) {
2144+ throw new UTFDataFormatException();
2145+ }
2146+ writeShort((int) utflen);
2147+ if (utflen == (long) s.length()) {
2148+ writeBytes(s);
2149+ } else {
2150+ writeUTFBody(s);
2151+ }
2152+ }
2153+
2154+ /**
2155+ * Writes given string in "long" UTF format. "Long" UTF format is
2156+ * identical to standard UTF, except that it uses an 8 byte header
2157+ * (instead of the standard 2 bytes) to convey the UTF encoding length.
2158+ */
2159+ void writeLongUTF(String s) throws IOException {
2160+ writeLongUTF(s, getUTFLength(s));
2161+ }
2162+
2163+ /**
2164+ * Writes given string in "long" UTF format, where the UTF encoding
2165+ * length of the string is already known.
2166+ */
2167+ void writeLongUTF(String s, long utflen) throws IOException {
2168+ writeLong(utflen);
2169+ if (utflen == (long) s.length()) {
2170+ writeBytes(s);
2171+ } else {
2172+ writeUTFBody(s);
2173+ }
2174+ }
2175+
2176+ /**
2177+ * Writes the "body" (i.e., the UTF representation minus the 2-byte or
2178+ * 8-byte length header) of the UTF encoding for the given string.
2179+ */
2180+ private void writeUTFBody(String s) throws IOException {
2181+ int limit = MAX_BLOCK_SIZE - 3;
2182+ int len = s.length();
2183+ for (int off = 0; off < len; ) {
2184+ int csize = Math.min(len - off, CHAR_BUF_SIZE);
2185+ s.getChars(off, off + csize, cbuf, 0);
2186+ for (int cpos = 0; cpos < csize; cpos++) {
2187+ char c = cbuf[cpos];
2188+ if (pos <= limit) {
2189+ if (c <= 0x007F && c != 0) {
2190+ buf[pos++] = (byte) c;
2191+ } else if (c > 0x07FF) {
2192+ buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));
2193+ buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
2194+ buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));
2195+ pos += 3;
2196+ } else {
2197+ buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));
2198+ buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));
2199+ pos += 2;
2200+ }
2201+ } else { // write one byte at a time to normalize block
2202+ if (c <= 0x007F && c != 0) {
2203+ write(c);
2204+ } else if (c > 0x07FF) {
2205+ write(0xE0 | ((c >> 12) & 0x0F));
2206+ write(0x80 | ((c >> 6) & 0x3F));
2207+ write(0x80 | ((c >> 0) & 0x3F));
2208+ } else {
2209+ write(0xC0 | ((c >> 6) & 0x1F));
2210+ write(0x80 | ((c >> 0) & 0x3F));
2211+ }
2212+ }
2213+ }
2214+ off += csize;
2215+ }
2216+ }
2217+ }
2218+
2219+ /**
2220+ * Lightweight identity hash table which maps objects to integer handles,
2221+ * assigned in ascending order.
2222+ */
2223+ private static class HandleTable {
2224+
2225+ /* number of mappings in table/next available handle */
2226+ private int size;
2227+ /* size threshold determining when to expand hash spine */
2228+ private int threshold;
2229+ /* factor for computing size threshold */
2230+ private final float loadFactor;
2231+ /* maps hash value -> candidate handle value */
2232+ private int[] spine;
2233+ /* maps handle value -> next candidate handle value */
2234+ private int[] next;
2235+ /* maps handle value -> associated object */
2236+ private Object[] objs;
2237+
2238+ /**
2239+ * Creates new HandleTable with given capacity and load factor.
2240+ */
2241+ HandleTable(int initialCapacity, float loadFactor) {
2242+ this.loadFactor = loadFactor;
2243+ spine = new int[initialCapacity];
2244+ next = new int[initialCapacity];
2245+ objs = new Object[initialCapacity];
2246+ threshold = (int) (initialCapacity * loadFactor);
2247+ clear();
2248+ }
2249+
2250+ /**
2251+ * Assigns next available handle to given object, and returns handle
2252+ * value. Handles are assigned in ascending order starting at 0.
2253+ */
2254+ int assign(Object obj) {
2255+ if (size >= next.length) {
2256+ growEntries();
2257+ }
2258+ if (size >= threshold) {
2259+ growSpine();
2260+ }
2261+ insert(obj, size);
2262+ return size++;
2263+ }
2264+
2265+ /**
2266+ * Looks up and returns handle associated with given object, or -1 if
2267+ * no mapping found.
2268+ */
2269+ int lookup(Object obj) {
2270+ if (size == 0) {
2271+ return -1;
2272+ }
2273+ int index = hash(obj) % spine.length;
2274+ for (int i = spine[index]; i >= 0; i = next[i]) {
2275+ if (objs[i] == obj) {
2276+ return i;
2277+ }
2278+ }
2279+ return -1;
2280+ }
2281+
2282+ /**
2283+ * Resets table to its initial (empty) state.
2284+ */
2285+ void clear() {
2286+ Arrays.fill(spine, -1);
2287+ Arrays.fill(objs, 0, size, null);
2288+ size = 0;
2289+ }
2290+
2291+ /**
2292+ * Returns the number of mappings currently in table.
2293+ */
2294+ int size() {
2295+ return size;
2296+ }
2297+
2298+ /**
2299+ * Inserts mapping object -> handle mapping into table. Assumes table
2300+ * is large enough to accommodate new mapping.
2301+ */
2302+ private void insert(Object obj, int handle) {
2303+ int index = hash(obj) % spine.length;
2304+ objs[handle] = obj;
2305+ next[handle] = spine[index];
2306+ spine[index] = handle;
2307+ }
2308+
2309+ /**
2310+ * Expands the hash "spine" -- equivalent to increasing the number of
2311+ * buckets in a conventional hash table.
2312+ */
2313+ private void growSpine() {
2314+ spine = new int[(spine.length << 1) + 1];
2315+ threshold = (int) (spine.length * loadFactor);
2316+ Arrays.fill(spine, -1);
2317+ for (int i = 0; i < size; i++) {
2318+ insert(objs[i], i);
2319+ }
2320+ }
2321+
2322+ /**
2323+ * Increases hash table capacity by lengthening entry arrays.
2324+ */
2325+ private void growEntries() {
2326+ int newLength = (next.length << 1) + 1;
2327+ int[] newNext = new int[newLength];
2328+ System.arraycopy(next, 0, newNext, 0, size);
2329+ next = newNext;
2330+
2331+ Object[] newObjs = new Object[newLength];
2332+ System.arraycopy(objs, 0, newObjs, 0, size);
2333+ objs = newObjs;
2334+ }
2335+
2336+ /**
2337+ * Returns hash value for given object.
2338+ */
2339+ private int hash(Object obj) {
2340+ return System.identityHashCode(obj) & 0x7FFFFFFF;
2341+ }
2342+ }
2343+
2344+ /**
2345+ * Lightweight identity hash table which maps objects to replacement
2346+ * objects.
2347+ */
2348+ private static class ReplaceTable {
2349+
2350+ /* maps object -> index */
2351+ private final HandleTable htab;
2352+ /* maps index -> replacement object */
2353+ private Object[] reps;
2354+
2355+ /**
2356+ * Creates new ReplaceTable with given capacity and load factor.
2357+ */
2358+ ReplaceTable(int initialCapacity, float loadFactor) {
2359+ htab = new HandleTable(initialCapacity, loadFactor);
2360+ reps = new Object[initialCapacity];
2361+ }
2362+
2363+ /**
2364+ * Enters mapping from object to replacement object.
2365+ */
2366+ void assign(Object obj, Object rep) {
2367+ int index = htab.assign(obj);
2368+ while (index >= reps.length) {
2369+ grow();
2370+ }
2371+ reps[index] = rep;
2372+ }
2373+
2374+ /**
2375+ * Looks up and returns replacement for given object. If no
2376+ * replacement is found, returns the lookup object itself.
2377+ */
2378+ Object lookup(Object obj) {
2379+ int index = htab.lookup(obj);
2380+ return (index >= 0) ? reps[index] : obj;
2381+ }
2382+
2383+ /**
2384+ * Resets table to its initial (empty) state.
2385+ */
2386+ void clear() {
2387+ Arrays.fill(reps, 0, htab.size(), null);
2388+ htab.clear();
2389+ }
2390+
2391+ /**
2392+ * Returns the number of mappings currently in table.
2393+ */
2394+ int size() {
2395+ return htab.size();
2396+ }
2397+
2398+ /**
2399+ * Increases table capacity.
2400+ */
2401+ private void grow() {
2402+ Object[] newReps = new Object[(reps.length << 1) + 1];
2403+ System.arraycopy(reps, 0, newReps, 0, reps.length);
2404+ reps = newReps;
2405+ }
2406+ }
2407+
2408+ /**
2409+ * Stack to keep debug information about the state of the
2410+ * serialization process, for embedding in exception messages.
2411+ */
2412+ private static class DebugTraceInfoStack {
2413+ private final List<String> stack;
2414+
2415+ DebugTraceInfoStack() {
2416+ stack = new ArrayList<>();
2417+ }
2418+
2419+ /**
2420+ * Removes all of the elements from enclosed list.
2421+ */
2422+ void clear() {
2423+ stack.clear();
2424+ }
2425+
2426+ /**
2427+ * Removes the object at the top of enclosed list.
2428+ */
2429+ void pop() {
2430+ stack.remove(stack.size()-1);
2431+ }
2432+
2433+ /**
2434+ * Pushes a String onto the top of enclosed list.
2435+ */
2436+ void push(String entry) {
2437+ stack.add("\t- " + entry);
2438+ }
2439+
2440+ /**
2441+ * Returns a string representation of this object
2442+ */
2443+ public String toString() {
2444+ StringBuilder buffer = new StringBuilder();
2445+ if (!stack.isEmpty()) {
2446+ for(int i = stack.size(); i > 0; i-- ) {
2447+ buffer.append(stack.get(i-1) + ((i != 1) ? "\n" : ""));
2448+ }
2449+ }
2450+ return buffer.toString();
2451+ }
2452+ }
2453+
2454+}