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