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