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