1 /* 2 * Copyright (c) 1996, 2025, 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.util.ArrayList; 30 import java.util.Arrays; 31 import java.util.List; 32 import java.util.Objects; 33 import java.util.StringJoiner; 34 35 import jdk.internal.util.ByteArray; 36 import jdk.internal.access.JavaLangAccess; 37 import jdk.internal.access.SharedSecrets; 38 39 import static java.io.ObjectInputStream.TRACE; 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 int utflen = utfLen(str, countNonZeroAscii); 1944 if (utflen <= 0xFFFF) { 1945 if(writeHeader) { 1946 writeByte(TC_STRING); 1947 } 1948 writeShort(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 }