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