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