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