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