1 /* 2 * Copyright (c) 1996, 2023, 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 36 import jdk.internal.util.ByteArray; 37 import sun.reflect.misc.ReflectUtil; 38 39 /** 40 * An ObjectOutputStream writes primitive data types and graphs of Java objects 41 * to an OutputStream. The objects can be read (reconstituted) using an 42 * ObjectInputStream. Persistent storage of objects can be accomplished by 43 * using a file for the stream. If the stream is a network socket stream, the 44 * objects can be reconstituted on another host or in another process. 45 * 46 * <p>Only objects that support the java.io.Serializable interface can be 47 * written to streams. The class of each serializable object is encoded 48 * including the class name and signature of the class, the values of the 49 * object's fields and arrays, and the closure of any other objects referenced 50 * from the initial objects. 51 * 52 * <p>The method writeObject is used to write an object to the stream. Any 53 * object, including Strings and arrays, is written with writeObject. Multiple 54 * objects or primitives can be written to the stream. The objects must be 55 * read back from the corresponding ObjectInputstream with the same types and 56 * in the same order as they were written. 57 * 58 * <p>Primitive data types can also be written to the stream using the 59 * appropriate methods from DataOutput. Strings can also be written using the 60 * writeUTF method. 61 * 62 * <p>The default serialization mechanism for an object writes the class of the 63 * object, the class signature, and the values of all non-transient and 64 * non-static fields. References to other objects (except in transient or 65 * static fields) cause those objects to be written also. Multiple references 66 * to a single object are encoded using a reference sharing mechanism so that 67 * graphs of objects can be restored to the same shape as when the original was 68 * written. 69 * 70 * <p>For example to write an object that can be read by the example in 71 * {@link ObjectInputStream}: 72 * {@snippet lang="java": 73 * try (FileOutputStream fos = new FileOutputStream("t.tmp"); 74 * ObjectOutputStream oos = new ObjectOutputStream(fos)) { 75 * oos.writeObject("Today"); 76 * oos.writeObject(LocalDateTime.now()); 77 * } catch (Exception ex) { 78 * // handle exception 79 * } 80 * } 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 * {@snippet lang="java": 87 * private void readObject(java.io.ObjectInputStream stream) 88 * throws IOException, ClassNotFoundException; 89 * private void writeObject(java.io.ObjectOutputStream stream) 90 * throws IOException; 91 * private void readObjectNoData() 92 * throws ObjectStreamException; 93 * } 94 * 95 * <p>The method name, modifiers, return type, and number and type of 96 * parameters must match exactly for the method to be used by 97 * serialization or deserialization. The methods should only be 98 * declared to throw checked exceptions consistent with these 99 * signatures. 100 * 101 * <p>The writeObject method is responsible for writing the state of the object 102 * for its particular class so that the corresponding readObject method can 103 * restore it. The method does not need to concern itself with the state 104 * belonging to the object's superclasses or subclasses. State is saved by 105 * writing the individual fields to the ObjectOutputStream using the 106 * writeObject method or by using the methods for primitive data types 107 * supported by DataOutput. 108 * 109 * <p>Serialization does not write out the fields of any object that does not 110 * implement the java.io.Serializable interface. Subclasses of Objects that 111 * are not serializable can be serializable. In this case the non-serializable 112 * class must have a no-arg constructor to allow its fields to be initialized. 113 * In this case it is the responsibility of the subclass to save and restore 114 * the state of the non-serializable class. It is frequently the case that the 115 * fields of that class are accessible (public, package, or protected) or that 116 * there are get and set methods that can be used to restore the state. 117 * 118 * <p>Serialization of an object can be prevented by implementing writeObject 119 * and readObject methods that throw the NotSerializableException. The 120 * exception will be caught by the ObjectOutputStream and abort the 121 * serialization process. 122 * 123 * <p>Implementing the Externalizable interface allows the object to assume 124 * complete control over the contents and format of the object's serialized 125 * form. The methods of the Externalizable interface, writeExternal and 126 * readExternal, are called to save and restore the objects state. When 127 * implemented by a class they can write and read their own state using all of 128 * the methods of ObjectOutput and ObjectInput. It is the responsibility of 129 * the objects to handle any versioning that occurs. 130 * 131 * <p>Enum constants are serialized differently than ordinary serializable or 132 * externalizable objects. The serialized form of an enum constant consists 133 * solely of its name; field values of the constant are not transmitted. To 134 * serialize an enum constant, ObjectOutputStream writes the string returned by 135 * the constant's name method. Like other serializable or externalizable 136 * objects, enum constants can function as the targets of back references 137 * appearing subsequently in the serialization stream. The process by which 138 * enum constants are serialized cannot be customized; any class-specific 139 * writeObject and writeReplace methods defined by enum types are ignored 140 * during serialization. Similarly, any serialPersistentFields or 141 * serialVersionUID field declarations are also ignored--all enum types have a 142 * fixed serialVersionUID of 0L. 143 * 144 * <p>Primitive data, excluding serializable fields and externalizable data, is 145 * written to the ObjectOutputStream in block-data records. A block data record 146 * is composed of a header and data. The block data header consists of a marker 147 * and the number of bytes to follow the header. Consecutive primitive data 148 * writes are merged into one block-data record. The blocking factor used for 149 * a block-data record will be 1024 bytes. Each block-data record will be 150 * filled up to 1024 bytes, or be written whenever there is a termination of 151 * block-data mode. Calls to the ObjectOutputStream methods writeObject, 152 * defaultWriteObject and writeFields initially terminate any existing 153 * block-data record. 154 * 155 * <p>Records are serialized differently than ordinary serializable or externalizable 156 * objects, see <a href="ObjectInputStream.html#record-serialization">record serialization</a>. 157 * 158 * @spec serialization/index.html Java Object Serialization Specification 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 * {@link NullPointerException} 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 * @spec serialization/index.html Java Object Serialization Specification 675 * @see java.io.ObjectInputStream#readClassDescriptor() 676 * @see #useProtocolVersion(int) 677 * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1 678 * @since 1.3 679 */ 680 protected void writeClassDescriptor(ObjectStreamClass desc) 681 throws IOException 682 { 683 desc.writeNonProxy(this); 684 } 685 686 /** 687 * Writes a byte. This method will block until the byte is actually 688 * written. 689 * 690 * @param val the byte to be written to the stream 691 * @throws IOException If an I/O error has occurred. 692 */ 693 @Override 694 public void write(int val) throws IOException { 695 bout.write(val); 696 } 697 698 /** 699 * Writes an array of bytes. This method will block until the bytes are 700 * actually written. 701 * 702 * @param buf the data to be written 703 * @throws IOException If an I/O error has occurred. 704 */ 705 @Override 706 public void write(byte[] buf) throws IOException { 707 bout.write(buf, 0, buf.length, false); 708 } 709 710 /** 711 * Writes a sub array of bytes. 712 * 713 * @param buf the data to be written 714 * @param off the start offset in the data 715 * @param len the number of bytes that are written 716 * @throws IOException {@inheritDoc} 717 * @throws IndexOutOfBoundsException {@inheritDoc} 718 */ 719 @Override 720 public void write(byte[] buf, int off, int len) throws IOException { 721 if (buf == null) { 722 throw new NullPointerException(); 723 } 724 Objects.checkFromIndexSize(off, len, buf.length); 725 bout.write(buf, off, len, false); 726 } 727 728 /** 729 * Flushes the stream. This will write any buffered output bytes and flush 730 * through to the underlying stream. 731 * 732 * @throws IOException {@inheritDoc} 733 */ 734 @Override 735 public void flush() throws IOException { 736 bout.flush(); 737 } 738 739 /** 740 * Drain any buffered data in ObjectOutputStream. Similar to flush but 741 * does not propagate the flush to the underlying stream. 742 * 743 * @throws IOException if I/O errors occur while writing to the underlying 744 * stream 745 */ 746 protected void drain() throws IOException { 747 bout.drain(); 748 } 749 750 /** 751 * Closes the stream. This method must be called to release any resources 752 * associated with the stream. 753 * 754 * @throws IOException If an I/O error has occurred. 755 */ 756 @Override 757 public void close() throws IOException { 758 flush(); 759 clear(); 760 bout.close(); 761 } 762 763 /** 764 * Writes a boolean. 765 * 766 * @param val the boolean to be written 767 * @throws IOException if I/O errors occur while writing to the underlying 768 * stream 769 */ 770 public void writeBoolean(boolean val) throws IOException { 771 bout.writeBoolean(val); 772 } 773 774 /** 775 * Writes an 8-bit byte. 776 * 777 * @param val the byte value to be written 778 * @throws IOException if I/O errors occur while writing to the underlying 779 * stream 780 */ 781 public void writeByte(int val) throws IOException { 782 bout.writeByte(val); 783 } 784 785 /** 786 * Writes a 16-bit short. 787 * 788 * @param val the short value to be written 789 * @throws IOException if I/O errors occur while writing to the underlying 790 * stream 791 */ 792 public void writeShort(int val) throws IOException { 793 bout.writeShort(val); 794 } 795 796 /** 797 * Writes a 16-bit char. 798 * 799 * @param val the char value to be written 800 * @throws IOException if I/O errors occur while writing to the underlying 801 * stream 802 */ 803 public void writeChar(int val) throws IOException { 804 bout.writeChar(val); 805 } 806 807 /** 808 * Writes a 32-bit int. 809 * 810 * @param val the integer value to be written 811 * @throws IOException if I/O errors occur while writing to the underlying 812 * stream 813 */ 814 public void writeInt(int val) throws IOException { 815 bout.writeInt(val); 816 } 817 818 /** 819 * Writes a 64-bit long. 820 * 821 * @param val the long value to be written 822 * @throws IOException if I/O errors occur while writing to the underlying 823 * stream 824 */ 825 public void writeLong(long val) throws IOException { 826 bout.writeLong(val); 827 } 828 829 /** 830 * Writes a 32-bit float. 831 * 832 * @param val the float value to be written 833 * @throws IOException if I/O errors occur while writing to the underlying 834 * stream 835 */ 836 public void writeFloat(float val) throws IOException { 837 bout.writeFloat(val); 838 } 839 840 /** 841 * Writes a 64-bit double. 842 * 843 * @param val the double value to be written 844 * @throws IOException if I/O errors occur while writing to the underlying 845 * stream 846 */ 847 public void writeDouble(double val) throws IOException { 848 bout.writeDouble(val); 849 } 850 851 /** 852 * Writes a String as a sequence of bytes. 853 * 854 * @param str the String of bytes to be written 855 * @throws IOException if I/O errors occur while writing to the underlying 856 * stream 857 */ 858 public void writeBytes(String str) throws IOException { 859 bout.writeBytes(str); 860 } 861 862 /** 863 * Writes a String as a sequence of chars. 864 * 865 * @param str the String of chars to be written 866 * @throws IOException if I/O errors occur while writing to the underlying 867 * stream 868 */ 869 public void writeChars(String str) throws IOException { 870 bout.writeChars(str); 871 } 872 873 /** 874 * Primitive data write of this String in 875 * <a href="DataInput.html#modified-utf-8">modified UTF-8</a> 876 * format. Note that there is a 877 * significant difference between writing a String into the stream as 878 * primitive data or as an Object. A String instance written by writeObject 879 * is written into the stream as a String initially. Future writeObject() 880 * calls write references to the string into the stream. 881 * 882 * @param str the String to be written 883 * @throws IOException if I/O errors occur while writing to the underlying 884 * stream 885 */ 886 public void writeUTF(String str) throws IOException { 887 bout.writeUTF(str); 888 } 889 890 /** 891 * Provide programmatic access to the persistent fields to be written 892 * to ObjectOutput. 893 * 894 * @since 1.2 895 */ 896 public abstract static class PutField { 897 /** 898 * Constructor for subclasses to call. 899 */ 900 public PutField() {} 901 902 /** 903 * Put the value of the named boolean field into the persistent field. 904 * 905 * @param name the name of the serializable field 906 * @param val the value to assign to the field 907 * @throws IllegalArgumentException if {@code name} does not 908 * match the name of a serializable field for the class whose fields 909 * are being written, or if the type of the named field is not 910 * {@code boolean} 911 */ 912 public abstract void put(String name, boolean val); 913 914 /** 915 * Put the value of the named byte field into the persistent field. 916 * 917 * @param name the name of the serializable field 918 * @param val the value to assign to the field 919 * @throws IllegalArgumentException if {@code name} does not 920 * match the name of a serializable field for the class whose fields 921 * are being written, or if the type of the named field is not 922 * {@code byte} 923 */ 924 public abstract void put(String name, byte val); 925 926 /** 927 * Put the value of the named char field into the persistent field. 928 * 929 * @param name the name of the serializable field 930 * @param val the value to assign to the field 931 * @throws IllegalArgumentException if {@code name} does not 932 * match the name of a serializable field for the class whose fields 933 * are being written, or if the type of the named field is not 934 * {@code char} 935 */ 936 public abstract void put(String name, char val); 937 938 /** 939 * Put the value of the named short field into the persistent field. 940 * 941 * @param name the name of the serializable field 942 * @param val the value to assign to the field 943 * @throws IllegalArgumentException if {@code name} does not 944 * match the name of a serializable field for the class whose fields 945 * are being written, or if the type of the named field is not 946 * {@code short} 947 */ 948 public abstract void put(String name, short val); 949 950 /** 951 * Put the value of the named int field into the persistent field. 952 * 953 * @param name the name of the serializable field 954 * @param val the value to assign to the field 955 * @throws IllegalArgumentException if {@code name} does not 956 * match the name of a serializable field for the class whose fields 957 * are being written, or if the type of the named field is not 958 * {@code int} 959 */ 960 public abstract void put(String name, int val); 961 962 /** 963 * Put the value of the named long field into the persistent field. 964 * 965 * @param name the name of the serializable field 966 * @param val the value to assign to the field 967 * @throws IllegalArgumentException if {@code name} does not 968 * match the name of a serializable field for the class whose fields 969 * are being written, or if the type of the named field is not 970 * {@code long} 971 */ 972 public abstract void put(String name, long val); 973 974 /** 975 * Put the value of the named float field into the persistent field. 976 * 977 * @param name the name of the serializable field 978 * @param val the value to assign to the field 979 * @throws IllegalArgumentException if {@code name} does not 980 * match the name of a serializable field for the class whose fields 981 * are being written, or if the type of the named field is not 982 * {@code float} 983 */ 984 public abstract void put(String name, float val); 985 986 /** 987 * Put the value of the named double field into the persistent field. 988 * 989 * @param name the name of the serializable field 990 * @param val the value to assign to the field 991 * @throws IllegalArgumentException if {@code name} does not 992 * match the name of a serializable field for the class whose fields 993 * are being written, or if the type of the named field is not 994 * {@code double} 995 */ 996 public abstract void put(String name, double val); 997 998 /** 999 * Put the value of the named Object field into the persistent field. 1000 * 1001 * @param name the name of the serializable field 1002 * @param val the value to assign to the field 1003 * (which may be {@code null}) 1004 * @throws IllegalArgumentException if {@code name} does not 1005 * match the name of a serializable field for the class whose fields 1006 * are being written, or if the type of the named field is not a 1007 * reference type 1008 */ 1009 public abstract void put(String name, Object val); 1010 1011 /** 1012 * Write the data and fields to the specified ObjectOutput stream, 1013 * which must be the same stream that produced this 1014 * {@code PutField} object. 1015 * 1016 * @param out the stream to write the data and fields to 1017 * @throws IOException if I/O errors occur while writing to the 1018 * underlying stream 1019 * @throws IllegalArgumentException if the specified stream is not 1020 * the same stream that produced this {@code PutField} 1021 * object 1022 * @deprecated This method does not write the values contained by this 1023 * {@code PutField} object in a proper format, and may 1024 * result in corruption of the serialization stream. The 1025 * correct way to write {@code PutField} data is by 1026 * calling the {@link java.io.ObjectOutputStream#writeFields()} 1027 * method. 1028 */ 1029 @Deprecated 1030 public abstract void write(ObjectOutput out) throws IOException; 1031 } 1032 1033 1034 /** 1035 * Returns protocol version in use. 1036 */ 1037 int getProtocolVersion() { 1038 return protocol; 1039 } 1040 1041 /** 1042 * Writes string without allowing it to be replaced in stream. Used by 1043 * ObjectStreamClass to write class descriptor type strings. 1044 */ 1045 void writeTypeString(String str) throws IOException { 1046 int handle; 1047 if (str == null) { 1048 writeNull(); 1049 } else if ((handle = handles.lookup(str)) != -1) { 1050 writeHandle(handle); 1051 } else { 1052 writeString(str, false); 1053 } 1054 } 1055 1056 /** 1057 * Verifies that this (possibly subclass) instance can be constructed 1058 * without violating security constraints: the subclass must not override 1059 * security-sensitive non-final methods, or else the 1060 * "enableSubclassImplementation" SerializablePermission is checked. 1061 */ 1062 private void verifySubclass() { 1063 Class<?> cl = getClass(); 1064 if (cl == ObjectOutputStream.class) { 1065 return; 1066 } 1067 @SuppressWarnings("removal") 1068 SecurityManager sm = System.getSecurityManager(); 1069 if (sm == null) { 1070 return; 1071 } 1072 boolean result = Caches.subclassAudits.get(cl); 1073 if (!result) { 1074 sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); 1075 } 1076 } 1077 1078 /** 1079 * Performs reflective checks on given subclass to verify that it doesn't 1080 * override security-sensitive non-final methods. Returns TRUE if subclass 1081 * is "safe", FALSE otherwise. 1082 */ 1083 @SuppressWarnings("removal") 1084 private static Boolean auditSubclass(Class<?> subcl) { 1085 return AccessController.doPrivileged( 1086 new PrivilegedAction<>() { 1087 public Boolean run() { 1088 for (Class<?> cl = subcl; 1089 cl != ObjectOutputStream.class; 1090 cl = cl.getSuperclass()) 1091 { 1092 try { 1093 cl.getDeclaredMethod( 1094 "writeUnshared", new Class<?>[] { Object.class }); 1095 return Boolean.FALSE; 1096 } catch (NoSuchMethodException ex) { 1097 } 1098 try { 1099 cl.getDeclaredMethod("putFields", (Class<?>[]) null); 1100 return Boolean.FALSE; 1101 } catch (NoSuchMethodException ex) { 1102 } 1103 } 1104 return Boolean.TRUE; 1105 } 1106 } 1107 ); 1108 } 1109 1110 /** 1111 * Clears internal data structures. 1112 */ 1113 private void clear() { 1114 subs.clear(); 1115 handles.clear(); 1116 } 1117 1118 /** 1119 * Underlying writeObject/writeUnshared implementation. 1120 */ 1121 private void writeObject0(Object obj, boolean unshared) 1122 throws IOException 1123 { 1124 boolean oldMode = bout.setBlockDataMode(false); 1125 depth++; 1126 try { 1127 // handle previously written and non-replaceable objects 1128 int h; 1129 if ((obj = subs.lookup(obj)) == null) { 1130 writeNull(); 1131 return; 1132 } else if (!unshared && (h = handles.lookup(obj)) != -1) { 1133 writeHandle(h); 1134 return; 1135 } else if (obj instanceof Class) { 1136 writeClass((Class) obj, unshared); 1137 return; 1138 } else if (obj instanceof ObjectStreamClass) { 1139 writeClassDesc((ObjectStreamClass) obj, unshared); 1140 return; 1141 } 1142 1143 // check for replacement object 1144 Object orig = obj; 1145 Class<?> cl = obj.getClass(); 1146 ObjectStreamClass desc; 1147 for (;;) { 1148 // REMIND: skip this check for strings/arrays? 1149 Class<?> repCl; 1150 desc = ObjectStreamClass.lookup(cl, true); 1151 if (!desc.hasWriteReplaceMethod() || 1152 (obj = desc.invokeWriteReplace(obj)) == null || 1153 (repCl = obj.getClass()) == cl) 1154 { 1155 break; 1156 } 1157 cl = repCl; 1158 } 1159 if (enableReplace) { 1160 Object rep = replaceObject(obj); 1161 if (rep != obj && rep != null) { 1162 cl = rep.getClass(); 1163 desc = ObjectStreamClass.lookup(cl, true); 1164 } 1165 obj = rep; 1166 } 1167 1168 // if object replaced, run through original checks a second time 1169 if (obj != orig) { 1170 subs.assign(orig, obj); 1171 if (obj == null) { 1172 writeNull(); 1173 return; 1174 } else if (!unshared && (h = handles.lookup(obj)) != -1) { 1175 writeHandle(h); 1176 return; 1177 } else if (obj instanceof Class) { 1178 writeClass((Class) obj, unshared); 1179 return; 1180 } else if (obj instanceof ObjectStreamClass) { 1181 writeClassDesc((ObjectStreamClass) obj, unshared); 1182 return; 1183 } 1184 } 1185 1186 // remaining cases 1187 if (obj instanceof String) { 1188 writeString((String) obj, unshared); 1189 } else if (cl.isArray()) { 1190 writeArray(obj, desc, unshared); 1191 } else if (obj instanceof Enum) { 1192 writeEnum((Enum<?>) obj, desc, unshared); 1193 } else if (obj instanceof Serializable) { 1194 writeOrdinaryObject(obj, desc, unshared); 1195 } else { 1196 if (extendedDebugInfo) { 1197 throw new NotSerializableException( 1198 cl.getName() + "\n" + debugInfoStack.toString()); 1199 } else { 1200 throw new NotSerializableException(cl.getName()); 1201 } 1202 } 1203 } finally { 1204 depth--; 1205 bout.setBlockDataMode(oldMode); 1206 } 1207 } 1208 1209 /** 1210 * Writes null code to stream. 1211 */ 1212 private void writeNull() throws IOException { 1213 bout.writeByte(TC_NULL); 1214 } 1215 1216 /** 1217 * Writes given object handle to stream. 1218 */ 1219 private void writeHandle(int handle) throws IOException { 1220 bout.writeByte(TC_REFERENCE); 1221 bout.writeInt(baseWireHandle + handle); 1222 } 1223 1224 /** 1225 * Writes representation of given class to stream. 1226 */ 1227 private void writeClass(Class<?> cl, boolean unshared) throws IOException { 1228 bout.writeByte(TC_CLASS); 1229 writeClassDesc(ObjectStreamClass.lookup(cl, true), false); 1230 handles.assign(unshared ? null : cl); 1231 } 1232 1233 /** 1234 * Writes representation of given class descriptor to stream. 1235 */ 1236 private void writeClassDesc(ObjectStreamClass desc, boolean unshared) 1237 throws IOException 1238 { 1239 int handle; 1240 if (desc == null) { 1241 writeNull(); 1242 } else if (!unshared && (handle = handles.lookup(desc)) != -1) { 1243 writeHandle(handle); 1244 } else if (desc.isProxy()) { 1245 writeProxyDesc(desc, unshared); 1246 } else { 1247 writeNonProxyDesc(desc, unshared); 1248 } 1249 } 1250 1251 private boolean isCustomSubclass() { 1252 // Return true if this class is a custom subclass of ObjectOutputStream 1253 return getClass().getClassLoader() 1254 != ObjectOutputStream.class.getClassLoader(); 1255 } 1256 1257 /** 1258 * Writes class descriptor representing a dynamic proxy class to stream. 1259 */ 1260 private void writeProxyDesc(ObjectStreamClass desc, boolean unshared) 1261 throws IOException 1262 { 1263 bout.writeByte(TC_PROXYCLASSDESC); 1264 handles.assign(unshared ? null : desc); 1265 1266 Class<?> cl = desc.forClass(); 1267 Class<?>[] ifaces = cl.getInterfaces(); 1268 bout.writeInt(ifaces.length); 1269 for (int i = 0; i < ifaces.length; i++) { 1270 bout.writeUTF(ifaces[i].getName()); 1271 } 1272 1273 bout.setBlockDataMode(true); 1274 if (cl != null && isCustomSubclass()) { 1275 ReflectUtil.checkPackageAccess(cl); 1276 } 1277 annotateProxyClass(cl); 1278 bout.setBlockDataMode(false); 1279 bout.writeByte(TC_ENDBLOCKDATA); 1280 1281 writeClassDesc(desc.getSuperDesc(), false); 1282 } 1283 1284 /** 1285 * Writes class descriptor representing a standard (i.e., not a dynamic 1286 * proxy) class to stream. 1287 */ 1288 private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared) 1289 throws IOException 1290 { 1291 bout.writeByte(TC_CLASSDESC); 1292 handles.assign(unshared ? null : desc); 1293 1294 if (protocol == PROTOCOL_VERSION_1) { 1295 // do not invoke class descriptor write hook with old protocol 1296 desc.writeNonProxy(this); 1297 } else { 1298 writeClassDescriptor(desc); 1299 } 1300 1301 Class<?> cl = desc.forClass(); 1302 bout.setBlockDataMode(true); 1303 if (cl != null && isCustomSubclass()) { 1304 ReflectUtil.checkPackageAccess(cl); 1305 } 1306 annotateClass(cl); 1307 bout.setBlockDataMode(false); 1308 bout.writeByte(TC_ENDBLOCKDATA); 1309 1310 writeClassDesc(desc.getSuperDesc(), false); 1311 } 1312 1313 /** 1314 * Writes given string to stream, using standard or long UTF format 1315 * depending on string length. 1316 */ 1317 private void writeString(String str, boolean unshared) throws IOException { 1318 handles.assign(unshared ? null : str); 1319 long utflen = bout.getUTFLength(str); 1320 if (utflen <= 0xFFFF) { 1321 bout.writeByte(TC_STRING); 1322 bout.writeUTF(str, utflen); 1323 } else { 1324 bout.writeByte(TC_LONGSTRING); 1325 bout.writeLongUTF(str, utflen); 1326 } 1327 } 1328 1329 /** 1330 * Writes given array object to stream. 1331 */ 1332 private void writeArray(Object array, 1333 ObjectStreamClass desc, 1334 boolean unshared) 1335 throws IOException 1336 { 1337 bout.writeByte(TC_ARRAY); 1338 writeClassDesc(desc, false); 1339 handles.assign(unshared ? null : array); 1340 1341 Class<?> ccl = desc.forClass().getComponentType(); 1342 if (ccl.isPrimitive()) { 1343 if (ccl == Integer.TYPE) { 1344 int[] ia = (int[]) array; 1345 bout.writeInt(ia.length); 1346 bout.writeInts(ia, 0, ia.length); 1347 } else if (ccl == Byte.TYPE) { 1348 byte[] ba = (byte[]) array; 1349 bout.writeInt(ba.length); 1350 bout.write(ba, 0, ba.length, true); 1351 } else if (ccl == Long.TYPE) { 1352 long[] ja = (long[]) array; 1353 bout.writeInt(ja.length); 1354 bout.writeLongs(ja, 0, ja.length); 1355 } else if (ccl == Float.TYPE) { 1356 float[] fa = (float[]) array; 1357 bout.writeInt(fa.length); 1358 bout.writeFloats(fa, 0, fa.length); 1359 } else if (ccl == Double.TYPE) { 1360 double[] da = (double[]) array; 1361 bout.writeInt(da.length); 1362 bout.writeDoubles(da, 0, da.length); 1363 } else if (ccl == Short.TYPE) { 1364 short[] sa = (short[]) array; 1365 bout.writeInt(sa.length); 1366 bout.writeShorts(sa, 0, sa.length); 1367 } else if (ccl == Character.TYPE) { 1368 char[] ca = (char[]) array; 1369 bout.writeInt(ca.length); 1370 bout.writeChars(ca, 0, ca.length); 1371 } else if (ccl == Boolean.TYPE) { 1372 boolean[] za = (boolean[]) array; 1373 bout.writeInt(za.length); 1374 bout.writeBooleans(za, 0, za.length); 1375 } else { 1376 throw new InternalError(); 1377 } 1378 } else { 1379 Object[] objs = (Object[]) array; 1380 int len = objs.length; 1381 bout.writeInt(len); 1382 if (extendedDebugInfo) { 1383 debugInfoStack.push( 1384 "array (class \"" + array.getClass().getName() + 1385 "\", size: " + len + ")"); 1386 } 1387 try { 1388 for (int i = 0; i < len; i++) { 1389 if (extendedDebugInfo) { 1390 debugInfoStack.push( 1391 "element of array (index: " + i + ")"); 1392 } 1393 try { 1394 writeObject0(objs[i], false); 1395 } finally { 1396 if (extendedDebugInfo) { 1397 debugInfoStack.pop(); 1398 } 1399 } 1400 } 1401 } finally { 1402 if (extendedDebugInfo) { 1403 debugInfoStack.pop(); 1404 } 1405 } 1406 } 1407 } 1408 1409 /** 1410 * Writes given enum constant to stream. 1411 */ 1412 private void writeEnum(Enum<?> en, 1413 ObjectStreamClass desc, 1414 boolean unshared) 1415 throws IOException 1416 { 1417 bout.writeByte(TC_ENUM); 1418 ObjectStreamClass sdesc = desc.getSuperDesc(); 1419 writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false); 1420 handles.assign(unshared ? null : en); 1421 writeString(en.name(), false); 1422 } 1423 1424 /** 1425 * Writes representation of an "ordinary" (i.e., not a String, Class, 1426 * ObjectStreamClass, array, or enum constant) serializable object to the 1427 * stream. 1428 */ 1429 private void writeOrdinaryObject(Object obj, 1430 ObjectStreamClass desc, 1431 boolean unshared) 1432 throws IOException 1433 { 1434 if (extendedDebugInfo) { 1435 debugInfoStack.push( 1436 (depth == 1 ? "root " : "") + "object (class \"" + 1437 obj.getClass().getName() + "\", " + obj.toString() + ")"); 1438 } 1439 try { 1440 desc.checkSerialize(); 1441 1442 bout.writeByte(TC_OBJECT); 1443 writeClassDesc(desc, false); 1444 handles.assign(unshared ? null : obj); 1445 1446 if (desc.isRecord()) { 1447 writeRecordData(obj, desc); 1448 } else if (desc.isExternalizable() && !desc.isProxy()) { 1449 writeExternalData((Externalizable) obj); 1450 } else { 1451 writeSerialData(obj, desc); 1452 } 1453 } finally { 1454 if (extendedDebugInfo) { 1455 debugInfoStack.pop(); 1456 } 1457 } 1458 } 1459 1460 /** 1461 * Writes externalizable data of given object by invoking its 1462 * writeExternal() method. 1463 */ 1464 private void writeExternalData(Externalizable obj) throws IOException { 1465 PutFieldImpl oldPut = curPut; 1466 curPut = null; 1467 1468 if (extendedDebugInfo) { 1469 debugInfoStack.push("writeExternal data"); 1470 } 1471 SerialCallbackContext oldContext = curContext; 1472 try { 1473 curContext = null; 1474 if (protocol == PROTOCOL_VERSION_1) { 1475 obj.writeExternal(this); 1476 } else { 1477 bout.setBlockDataMode(true); 1478 obj.writeExternal(this); 1479 bout.setBlockDataMode(false); 1480 bout.writeByte(TC_ENDBLOCKDATA); 1481 } 1482 } finally { 1483 curContext = oldContext; 1484 if (extendedDebugInfo) { 1485 debugInfoStack.pop(); 1486 } 1487 } 1488 1489 curPut = oldPut; 1490 } 1491 1492 /** Writes the record component values for the given record object. */ 1493 private void writeRecordData(Object obj, ObjectStreamClass desc) 1494 throws IOException 1495 { 1496 assert obj.getClass().isRecord(); 1497 ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout(); 1498 if (slots.length != 1) { 1499 throw new InvalidClassException( 1500 "expected a single record slot length, but found: " + slots.length); 1501 } 1502 1503 defaultWriteFields(obj, desc); // #### seems unnecessary to use the accessors 1504 } 1505 1506 /** 1507 * Writes instance data for each serializable class of given object, from 1508 * superclass to subclass. 1509 */ 1510 private void writeSerialData(Object obj, ObjectStreamClass desc) 1511 throws IOException 1512 { 1513 ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout(); 1514 for (int i = 0; i < slots.length; i++) { 1515 ObjectStreamClass slotDesc = slots[i].desc; 1516 if (slotDesc.hasWriteObjectMethod()) { 1517 PutFieldImpl oldPut = curPut; 1518 curPut = null; 1519 SerialCallbackContext oldContext = curContext; 1520 1521 if (extendedDebugInfo) { 1522 debugInfoStack.push( 1523 "custom writeObject data (class \"" + 1524 slotDesc.getName() + "\")"); 1525 } 1526 try { 1527 curContext = new SerialCallbackContext(obj, slotDesc); 1528 bout.setBlockDataMode(true); 1529 slotDesc.invokeWriteObject(obj, this); 1530 bout.setBlockDataMode(false); 1531 bout.writeByte(TC_ENDBLOCKDATA); 1532 } finally { 1533 curContext.setUsed(); 1534 curContext = oldContext; 1535 if (extendedDebugInfo) { 1536 debugInfoStack.pop(); 1537 } 1538 } 1539 1540 curPut = oldPut; 1541 } else { 1542 defaultWriteFields(obj, slotDesc); 1543 } 1544 } 1545 } 1546 1547 /** 1548 * Fetches and writes values of serializable fields of given object to 1549 * stream. The given class descriptor specifies which field values to 1550 * write, and in which order they should be written. 1551 */ 1552 private void defaultWriteFields(Object obj, ObjectStreamClass desc) 1553 throws IOException 1554 { 1555 Class<?> cl = desc.forClass(); 1556 if (cl != null && obj != null && !cl.isInstance(obj)) { 1557 throw new ClassCastException(); 1558 } 1559 1560 desc.checkDefaultSerialize(); 1561 1562 int primDataSize = desc.getPrimDataSize(); 1563 if (primDataSize > 0) { 1564 if (primVals == null || primVals.length < primDataSize) { 1565 primVals = new byte[primDataSize]; 1566 } 1567 desc.getPrimFieldValues(obj, primVals); 1568 bout.write(primVals, 0, primDataSize, false); 1569 } 1570 1571 int numObjFields = desc.getNumObjFields(); 1572 if (numObjFields > 0) { 1573 ObjectStreamField[] fields = desc.getFields(false); 1574 Object[] objVals = new Object[numObjFields]; 1575 int numPrimFields = fields.length - objVals.length; 1576 desc.getObjFieldValues(obj, objVals); 1577 for (int i = 0; i < objVals.length; i++) { 1578 if (extendedDebugInfo) { 1579 debugInfoStack.push( 1580 "field (class \"" + desc.getName() + "\", name: \"" + 1581 fields[numPrimFields + i].getName() + "\", type: \"" + 1582 fields[numPrimFields + i].getType() + "\")"); 1583 } 1584 try { 1585 writeObject0(objVals[i], 1586 fields[numPrimFields + i].isUnshared()); 1587 } finally { 1588 if (extendedDebugInfo) { 1589 debugInfoStack.pop(); 1590 } 1591 } 1592 } 1593 } 1594 } 1595 1596 /** 1597 * Attempts to write to stream fatal IOException that has caused 1598 * serialization to abort. 1599 */ 1600 private void writeFatalException(IOException ex) throws IOException { 1601 /* 1602 * Note: the serialization specification states that if a second 1603 * IOException occurs while attempting to serialize the original fatal 1604 * exception to the stream, then a StreamCorruptedException should be 1605 * thrown (section 2.1). However, due to a bug in previous 1606 * implementations of serialization, StreamCorruptedExceptions were 1607 * rarely (if ever) actually thrown--the "root" exceptions from 1608 * underlying streams were thrown instead. This historical behavior is 1609 * followed here for consistency. 1610 */ 1611 clear(); 1612 boolean oldMode = bout.setBlockDataMode(false); 1613 try { 1614 bout.writeByte(TC_EXCEPTION); 1615 writeObject0(ex, false); 1616 clear(); 1617 } finally { 1618 bout.setBlockDataMode(oldMode); 1619 } 1620 } 1621 1622 /** 1623 * Default PutField implementation. 1624 */ 1625 private class PutFieldImpl extends PutField { 1626 1627 /** class descriptor describing serializable fields */ 1628 private final ObjectStreamClass desc; 1629 /** primitive field values */ 1630 private final byte[] primVals; 1631 /** object field values */ 1632 private final Object[] objVals; 1633 1634 /** 1635 * Creates PutFieldImpl object for writing fields defined in given 1636 * class descriptor. 1637 */ 1638 PutFieldImpl(ObjectStreamClass desc) { 1639 this.desc = desc; 1640 primVals = new byte[desc.getPrimDataSize()]; 1641 objVals = new Object[desc.getNumObjFields()]; 1642 } 1643 1644 public void put(String name, boolean val) { 1645 ByteArray.setBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val); 1646 } 1647 1648 public void put(String name, byte val) { 1649 primVals[getFieldOffset(name, Byte.TYPE)] = val; 1650 } 1651 1652 public void put(String name, char val) { 1653 ByteArray.setChar(primVals, getFieldOffset(name, Character.TYPE), val); 1654 } 1655 1656 public void put(String name, short val) { 1657 ByteArray.setShort(primVals, getFieldOffset(name, Short.TYPE), val); 1658 } 1659 1660 public void put(String name, int val) { 1661 ByteArray.setInt(primVals, getFieldOffset(name, Integer.TYPE), val); 1662 } 1663 1664 public void put(String name, float val) { 1665 ByteArray.setFloat(primVals, getFieldOffset(name, Float.TYPE), val); 1666 } 1667 1668 public void put(String name, long val) { 1669 ByteArray.setLong(primVals, getFieldOffset(name, Long.TYPE), val); 1670 } 1671 1672 public void put(String name, double val) { 1673 ByteArray.setDouble(primVals, getFieldOffset(name, Double.TYPE), val); 1674 } 1675 1676 public void put(String name, Object val) { 1677 objVals[getFieldOffset(name, Object.class)] = val; 1678 } 1679 1680 // deprecated in ObjectOutputStream.PutField 1681 public void write(ObjectOutput out) throws IOException { 1682 /* 1683 * Applications should *not* use this method to write PutField 1684 * data, as it will lead to stream corruption if the PutField 1685 * object writes any primitive data (since block data mode is not 1686 * unset/set properly, as is done in OOS.writeFields()). This 1687 * broken implementation is being retained solely for behavioral 1688 * compatibility, in order to support applications which use 1689 * OOS.PutField.write() for writing only non-primitive data. 1690 * 1691 * Serialization of unshared objects is not implemented here since 1692 * it is not necessary for backwards compatibility; also, unshared 1693 * semantics may not be supported by the given ObjectOutput 1694 * instance. Applications which write unshared objects using the 1695 * PutField API must use OOS.writeFields(). 1696 */ 1697 if (ObjectOutputStream.this != out) { 1698 throw new IllegalArgumentException("wrong stream"); 1699 } 1700 out.write(primVals, 0, primVals.length); 1701 1702 ObjectStreamField[] fields = desc.getFields(false); 1703 int numPrimFields = fields.length - objVals.length; 1704 // REMIND: warn if numPrimFields > 0? 1705 for (int i = 0; i < objVals.length; i++) { 1706 if (fields[numPrimFields + i].isUnshared()) { 1707 throw new IOException("cannot write unshared object"); 1708 } 1709 out.writeObject(objVals[i]); 1710 } 1711 } 1712 1713 /** 1714 * Writes buffered primitive data and object fields to stream. 1715 */ 1716 void writeFields() throws IOException { 1717 bout.write(primVals, 0, primVals.length, false); 1718 1719 ObjectStreamField[] fields = desc.getFields(false); 1720 int numPrimFields = fields.length - objVals.length; 1721 for (int i = 0; i < objVals.length; i++) { 1722 if (extendedDebugInfo) { 1723 debugInfoStack.push( 1724 "field (class \"" + desc.getName() + "\", name: \"" + 1725 fields[numPrimFields + i].getName() + "\", type: \"" + 1726 fields[numPrimFields + i].getType() + "\")"); 1727 } 1728 try { 1729 writeObject0(objVals[i], 1730 fields[numPrimFields + i].isUnshared()); 1731 } finally { 1732 if (extendedDebugInfo) { 1733 debugInfoStack.pop(); 1734 } 1735 } 1736 } 1737 } 1738 1739 /** 1740 * Returns offset of field with given name and type. A specified type 1741 * of null matches all types, Object.class matches all non-primitive 1742 * types, and any other non-null type matches assignable types only. 1743 * Throws IllegalArgumentException if no matching field found. 1744 */ 1745 private int getFieldOffset(String name, Class<?> type) { 1746 ObjectStreamField field = desc.getField(name, type); 1747 if (field == null) { 1748 throw new IllegalArgumentException("no such field " + name + 1749 " with type " + type); 1750 } 1751 return field.getOffset(); 1752 } 1753 } 1754 1755 /** 1756 * Buffered output stream with two modes: in default mode, outputs data in 1757 * same format as DataOutputStream; in "block data" mode, outputs data 1758 * bracketed by block data markers (see object serialization specification 1759 * for details). 1760 */ 1761 private static final class BlockDataOutputStream 1762 extends OutputStream implements DataOutput 1763 { 1764 /** maximum data block length */ 1765 private static final int MAX_BLOCK_SIZE = 1024; 1766 /** maximum data block header length */ 1767 private static final int MAX_HEADER_SIZE = 5; 1768 /** (tunable) length of char buffer (for writing strings) */ 1769 private static final int CHAR_BUF_SIZE = 256; 1770 1771 /** buffer for writing general/block data */ 1772 private final byte[] buf = new byte[MAX_BLOCK_SIZE]; 1773 /** buffer for writing block data headers */ 1774 private final byte[] hbuf = new byte[MAX_HEADER_SIZE]; 1775 /** char buffer for fast string writes */ 1776 private final char[] cbuf = new char[CHAR_BUF_SIZE]; 1777 1778 /** block data mode */ 1779 private boolean blkmode = false; 1780 /** current offset into buf */ 1781 private int pos = 0; 1782 1783 /** underlying output stream */ 1784 private final OutputStream out; 1785 /** loopback stream (for data writes that span data blocks) */ 1786 private final DataOutputStream dout; 1787 1788 /** 1789 * Creates new BlockDataOutputStream on top of given underlying stream. 1790 * Block data mode is turned off by default. 1791 */ 1792 BlockDataOutputStream(OutputStream out) { 1793 this.out = out; 1794 dout = new DataOutputStream(this); 1795 } 1796 1797 /** 1798 * Sets block data mode to the given mode (true == on, false == off) 1799 * and returns the previous mode value. If the new mode is the same as 1800 * the old mode, no action is taken. If the new mode differs from the 1801 * old mode, any buffered data is flushed before switching to the new 1802 * mode. 1803 */ 1804 boolean setBlockDataMode(boolean mode) throws IOException { 1805 if (blkmode == mode) { 1806 return blkmode; 1807 } 1808 drain(); 1809 blkmode = mode; 1810 return !blkmode; 1811 } 1812 1813 /** 1814 * Returns true if the stream is currently in block data mode, false 1815 * otherwise. 1816 */ 1817 boolean getBlockDataMode() { 1818 return blkmode; 1819 } 1820 1821 /* ----------------- generic output stream methods ----------------- */ 1822 /* 1823 * The following methods are equivalent to their counterparts in 1824 * OutputStream, except that they partition written data into data 1825 * blocks when in block data mode. 1826 */ 1827 1828 public void write(int b) throws IOException { 1829 if (pos >= MAX_BLOCK_SIZE) { 1830 drain(); 1831 } 1832 buf[pos++] = (byte) b; 1833 } 1834 1835 public void write(byte[] b) throws IOException { 1836 write(b, 0, b.length, false); 1837 } 1838 1839 public void write(byte[] b, int off, int len) throws IOException { 1840 write(b, off, len, false); 1841 } 1842 1843 public void flush() throws IOException { 1844 drain(); 1845 out.flush(); 1846 } 1847 1848 public void close() throws IOException { 1849 flush(); 1850 out.close(); 1851 } 1852 1853 /** 1854 * Writes specified span of byte values from given array. If copy is 1855 * true, copies the values to an intermediate buffer before writing 1856 * them to underlying stream (to avoid exposing a reference to the 1857 * original byte array). 1858 */ 1859 void write(byte[] b, int off, int len, boolean copy) 1860 throws IOException 1861 { 1862 if (!(copy || blkmode)) { // write directly 1863 drain(); 1864 out.write(b, off, len); 1865 return; 1866 } 1867 1868 while (len > 0) { 1869 if (pos >= MAX_BLOCK_SIZE) { 1870 drain(); 1871 } 1872 if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) { 1873 // avoid unnecessary copy 1874 writeBlockHeader(MAX_BLOCK_SIZE); 1875 out.write(b, off, MAX_BLOCK_SIZE); 1876 off += MAX_BLOCK_SIZE; 1877 len -= MAX_BLOCK_SIZE; 1878 } else { 1879 int wlen = Math.min(len, MAX_BLOCK_SIZE - pos); 1880 System.arraycopy(b, off, buf, pos, wlen); 1881 pos += wlen; 1882 off += wlen; 1883 len -= wlen; 1884 } 1885 } 1886 } 1887 1888 /** 1889 * Writes all buffered data from this stream to the underlying stream, 1890 * but does not flush underlying stream. 1891 */ 1892 void drain() throws IOException { 1893 if (pos == 0) { 1894 return; 1895 } 1896 if (blkmode) { 1897 writeBlockHeader(pos); 1898 } 1899 out.write(buf, 0, pos); 1900 pos = 0; 1901 } 1902 1903 /** 1904 * Writes block data header. Data blocks shorter than 256 bytes are 1905 * prefixed with a 2-byte header; all others start with a 5-byte 1906 * header. 1907 */ 1908 private void writeBlockHeader(int len) throws IOException { 1909 if (len <= 0xFF) { 1910 hbuf[0] = TC_BLOCKDATA; 1911 hbuf[1] = (byte) len; 1912 out.write(hbuf, 0, 2); 1913 } else { 1914 hbuf[0] = TC_BLOCKDATALONG; 1915 ByteArray.setInt(hbuf, 1, len); 1916 out.write(hbuf, 0, 5); 1917 } 1918 } 1919 1920 1921 /* ----------------- primitive data output methods ----------------- */ 1922 /* 1923 * The following methods are equivalent to their counterparts in 1924 * DataOutputStream, except that they partition written data into data 1925 * blocks when in block data mode. 1926 */ 1927 1928 public void writeBoolean(boolean v) throws IOException { 1929 if (pos >= MAX_BLOCK_SIZE) { 1930 drain(); 1931 } 1932 ByteArray.setBoolean(buf, pos++, v); 1933 } 1934 1935 public void writeByte(int v) throws IOException { 1936 if (pos >= MAX_BLOCK_SIZE) { 1937 drain(); 1938 } 1939 buf[pos++] = (byte) v; 1940 } 1941 1942 public void writeChar(int v) throws IOException { 1943 if (pos + 2 <= MAX_BLOCK_SIZE) { 1944 ByteArray.setChar(buf, pos, (char) v); 1945 pos += 2; 1946 } else { 1947 dout.writeChar(v); 1948 } 1949 } 1950 1951 public void writeShort(int v) throws IOException { 1952 if (pos + 2 <= MAX_BLOCK_SIZE) { 1953 ByteArray.setShort(buf, pos, (short) v); 1954 pos += 2; 1955 } else { 1956 dout.writeShort(v); 1957 } 1958 } 1959 1960 public void writeInt(int v) throws IOException { 1961 if (pos + 4 <= MAX_BLOCK_SIZE) { 1962 ByteArray.setInt(buf, pos, v); 1963 pos += 4; 1964 } else { 1965 dout.writeInt(v); 1966 } 1967 } 1968 1969 public void writeFloat(float v) throws IOException { 1970 if (pos + 4 <= MAX_BLOCK_SIZE) { 1971 ByteArray.setFloat(buf, pos, v); 1972 pos += 4; 1973 } else { 1974 dout.writeFloat(v); 1975 } 1976 } 1977 1978 public void writeLong(long v) throws IOException { 1979 if (pos + 8 <= MAX_BLOCK_SIZE) { 1980 ByteArray.setLong(buf, pos, v); 1981 pos += 8; 1982 } else { 1983 dout.writeLong(v); 1984 } 1985 } 1986 1987 public void writeDouble(double v) throws IOException { 1988 if (pos + 8 <= MAX_BLOCK_SIZE) { 1989 ByteArray.setDouble(buf, pos, v); 1990 pos += 8; 1991 } else { 1992 dout.writeDouble(v); 1993 } 1994 } 1995 1996 public void writeBytes(String s) throws IOException { 1997 int endoff = s.length(); 1998 int cpos = 0; 1999 int csize = 0; 2000 for (int off = 0; off < endoff; ) { 2001 if (cpos >= csize) { 2002 cpos = 0; 2003 csize = Math.min(endoff - off, CHAR_BUF_SIZE); 2004 s.getChars(off, off + csize, cbuf, 0); 2005 } 2006 if (pos >= MAX_BLOCK_SIZE) { 2007 drain(); 2008 } 2009 int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos); 2010 int stop = pos + n; 2011 while (pos < stop) { 2012 buf[pos++] = (byte) cbuf[cpos++]; 2013 } 2014 off += n; 2015 } 2016 } 2017 2018 public void writeChars(String s) throws IOException { 2019 int endoff = s.length(); 2020 for (int off = 0; off < endoff; ) { 2021 int csize = Math.min(endoff - off, CHAR_BUF_SIZE); 2022 s.getChars(off, off + csize, cbuf, 0); 2023 writeChars(cbuf, 0, csize); 2024 off += csize; 2025 } 2026 } 2027 2028 public void writeUTF(String s) throws IOException { 2029 writeUTF(s, getUTFLength(s)); 2030 } 2031 2032 2033 /* -------------- primitive data array output methods -------------- */ 2034 /* 2035 * The following methods write out spans of primitive data values. 2036 * Though equivalent to calling the corresponding primitive write 2037 * methods repeatedly, these methods are optimized for writing groups 2038 * of primitive data values more efficiently. 2039 */ 2040 2041 void writeBooleans(boolean[] v, int off, int len) throws IOException { 2042 int endoff = off + len; 2043 while (off < endoff) { 2044 if (pos >= MAX_BLOCK_SIZE) { 2045 drain(); 2046 } 2047 int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos)); 2048 while (off < stop) { 2049 ByteArray.setBoolean(buf, pos++, v[off++]); 2050 } 2051 } 2052 } 2053 2054 void writeChars(char[] v, int off, int len) throws IOException { 2055 int limit = MAX_BLOCK_SIZE - 2; 2056 int endoff = off + len; 2057 while (off < endoff) { 2058 if (pos <= limit) { 2059 int avail = (MAX_BLOCK_SIZE - pos) >> 1; 2060 int stop = Math.min(endoff, off + avail); 2061 while (off < stop) { 2062 ByteArray.setChar(buf, pos, v[off++]); 2063 pos += 2; 2064 } 2065 } else { 2066 dout.writeChar(v[off++]); 2067 } 2068 } 2069 } 2070 2071 void writeShorts(short[] v, int off, int len) throws IOException { 2072 int limit = MAX_BLOCK_SIZE - 2; 2073 int endoff = off + len; 2074 while (off < endoff) { 2075 if (pos <= limit) { 2076 int avail = (MAX_BLOCK_SIZE - pos) >> 1; 2077 int stop = Math.min(endoff, off + avail); 2078 while (off < stop) { 2079 ByteArray.setShort(buf, pos, v[off++]); 2080 pos += 2; 2081 } 2082 } else { 2083 dout.writeShort(v[off++]); 2084 } 2085 } 2086 } 2087 2088 void writeInts(int[] v, int off, int len) throws IOException { 2089 int limit = MAX_BLOCK_SIZE - 4; 2090 int endoff = off + len; 2091 while (off < endoff) { 2092 if (pos <= limit) { 2093 int avail = (MAX_BLOCK_SIZE - pos) >> 2; 2094 int stop = Math.min(endoff, off + avail); 2095 while (off < stop) { 2096 ByteArray.setInt(buf, pos, v[off++]); 2097 pos += 4; 2098 } 2099 } else { 2100 dout.writeInt(v[off++]); 2101 } 2102 } 2103 } 2104 2105 void writeFloats(float[] v, int off, int len) throws IOException { 2106 int limit = MAX_BLOCK_SIZE - 4; 2107 int endoff = off + len; 2108 while (off < endoff) { 2109 if (pos <= limit) { 2110 int avail = (MAX_BLOCK_SIZE - pos) >> 2; 2111 int stop = Math.min(endoff, off + avail); 2112 while (off < stop) { 2113 ByteArray.setFloat(buf, pos, v[off++]); 2114 pos += 4; 2115 } 2116 } else { 2117 dout.writeFloat(v[off++]); 2118 } 2119 } 2120 } 2121 2122 void writeLongs(long[] v, int off, int len) throws IOException { 2123 int limit = MAX_BLOCK_SIZE - 8; 2124 int endoff = off + len; 2125 while (off < endoff) { 2126 if (pos <= limit) { 2127 int avail = (MAX_BLOCK_SIZE - pos) >> 3; 2128 int stop = Math.min(endoff, off + avail); 2129 while (off < stop) { 2130 ByteArray.setLong(buf, pos, v[off++]); 2131 pos += 8; 2132 } 2133 } else { 2134 dout.writeLong(v[off++]); 2135 } 2136 } 2137 } 2138 2139 void writeDoubles(double[] v, int off, int len) throws IOException { 2140 int limit = MAX_BLOCK_SIZE - 8; 2141 int endoff = off + len; 2142 while (off < endoff) { 2143 if (pos <= limit) { 2144 int avail = (MAX_BLOCK_SIZE - pos) >> 3; 2145 int stop = Math.min(endoff, off + avail); 2146 while (off < stop) { 2147 ByteArray.setDouble(buf, pos, v[off++]); 2148 pos += 8; 2149 } 2150 } else { 2151 dout.writeDouble(v[off++]); 2152 } 2153 } 2154 } 2155 2156 /** 2157 * Returns the length in bytes of the UTF encoding of the given string. 2158 */ 2159 long getUTFLength(String s) { 2160 int len = s.length(); 2161 long utflen = 0; 2162 for (int off = 0; off < len; ) { 2163 int csize = Math.min(len - off, CHAR_BUF_SIZE); 2164 s.getChars(off, off + csize, cbuf, 0); 2165 for (int cpos = 0; cpos < csize; cpos++) { 2166 char c = cbuf[cpos]; 2167 if (c >= 0x0001 && c <= 0x007F) { 2168 utflen++; 2169 } else if (c > 0x07FF) { 2170 utflen += 3; 2171 } else { 2172 utflen += 2; 2173 } 2174 } 2175 off += csize; 2176 } 2177 return utflen; 2178 } 2179 2180 /** 2181 * Writes the given string in UTF format. This method is used in 2182 * situations where the UTF encoding length of the string is already 2183 * known; specifying it explicitly avoids a prescan of the string to 2184 * determine its UTF length. 2185 */ 2186 void writeUTF(String s, long utflen) throws IOException { 2187 if (utflen > 0xFFFFL) { 2188 throw new UTFDataFormatException(); 2189 } 2190 writeShort((int) utflen); 2191 if (utflen == (long) s.length()) { 2192 writeBytes(s); 2193 } else { 2194 writeUTFBody(s); 2195 } 2196 } 2197 2198 /** 2199 * Writes given string in "long" UTF format. "Long" UTF format is 2200 * identical to standard UTF, except that it uses an 8 byte header 2201 * (instead of the standard 2 bytes) to convey the UTF encoding length. 2202 */ 2203 void writeLongUTF(String s) throws IOException { 2204 writeLongUTF(s, getUTFLength(s)); 2205 } 2206 2207 /** 2208 * Writes given string in "long" UTF format, where the UTF encoding 2209 * length of the string is already known. 2210 */ 2211 void writeLongUTF(String s, long utflen) throws IOException { 2212 writeLong(utflen); 2213 if (utflen == (long) s.length()) { 2214 writeBytes(s); 2215 } else { 2216 writeUTFBody(s); 2217 } 2218 } 2219 2220 /** 2221 * Writes the "body" (i.e., the UTF representation minus the 2-byte or 2222 * 8-byte length header) of the UTF encoding for the given string. 2223 */ 2224 private void writeUTFBody(String s) throws IOException { 2225 int limit = MAX_BLOCK_SIZE - 3; 2226 int len = s.length(); 2227 for (int off = 0; off < len; ) { 2228 int csize = Math.min(len - off, CHAR_BUF_SIZE); 2229 s.getChars(off, off + csize, cbuf, 0); 2230 for (int cpos = 0; cpos < csize; cpos++) { 2231 char c = cbuf[cpos]; 2232 if (pos <= limit) { 2233 if (c <= 0x007F && c != 0) { 2234 buf[pos++] = (byte) c; 2235 } else if (c > 0x07FF) { 2236 buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F)); 2237 buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F)); 2238 buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F)); 2239 pos += 3; 2240 } else { 2241 buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F)); 2242 buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F)); 2243 pos += 2; 2244 } 2245 } else { // write one byte at a time to normalize block 2246 if (c <= 0x007F && c != 0) { 2247 write(c); 2248 } else if (c > 0x07FF) { 2249 write(0xE0 | ((c >> 12) & 0x0F)); 2250 write(0x80 | ((c >> 6) & 0x3F)); 2251 write(0x80 | ((c >> 0) & 0x3F)); 2252 } else { 2253 write(0xC0 | ((c >> 6) & 0x1F)); 2254 write(0x80 | ((c >> 0) & 0x3F)); 2255 } 2256 } 2257 } 2258 off += csize; 2259 } 2260 } 2261 } 2262 2263 /** 2264 * Lightweight identity hash table which maps objects to integer handles, 2265 * assigned in ascending order. 2266 */ 2267 private static final class HandleTable { 2268 2269 /* number of mappings in table/next available handle */ 2270 private int size; 2271 /* size threshold determining when to expand hash spine */ 2272 private int threshold; 2273 /* factor for computing size threshold */ 2274 private final float loadFactor; 2275 /* maps hash value -> candidate handle value */ 2276 private int[] spine; 2277 /* maps handle value -> next candidate handle value */ 2278 private int[] next; 2279 /* maps handle value -> associated object */ 2280 private Object[] objs; 2281 2282 /** 2283 * Creates new HandleTable with given capacity and load factor. 2284 */ 2285 HandleTable(int initialCapacity, float loadFactor) { 2286 this.loadFactor = loadFactor; 2287 spine = new int[initialCapacity]; 2288 next = new int[initialCapacity]; 2289 objs = new Object[initialCapacity]; 2290 threshold = (int) (initialCapacity * loadFactor); 2291 clear(); 2292 } 2293 2294 /** 2295 * Assigns next available handle to given object, and returns handle 2296 * value. Handles are assigned in ascending order starting at 0. 2297 */ 2298 int assign(Object obj) { 2299 if (size >= next.length) { 2300 growEntries(); 2301 } 2302 if (size >= threshold) { 2303 growSpine(); 2304 } 2305 insert(obj, size); 2306 return size++; 2307 } 2308 2309 /** 2310 * Looks up and returns handle associated with given object, or -1 if 2311 * no mapping found. 2312 */ 2313 int lookup(Object obj) { 2314 if (size == 0) { 2315 return -1; 2316 } 2317 int index = hash(obj) % spine.length; 2318 for (int i = spine[index]; i >= 0; i = next[i]) { 2319 if (objs[i] == obj) { 2320 return i; 2321 } 2322 } 2323 return -1; 2324 } 2325 2326 /** 2327 * Resets table to its initial (empty) state. 2328 */ 2329 void clear() { 2330 Arrays.fill(spine, -1); 2331 Arrays.fill(objs, 0, size, null); 2332 size = 0; 2333 } 2334 2335 /** 2336 * Returns the number of mappings currently in table. 2337 */ 2338 int size() { 2339 return size; 2340 } 2341 2342 /** 2343 * Inserts mapping object -> handle mapping into table. Assumes table 2344 * is large enough to accommodate new mapping. 2345 */ 2346 private void insert(Object obj, int handle) { 2347 int index = hash(obj) % spine.length; 2348 objs[handle] = obj; 2349 next[handle] = spine[index]; 2350 spine[index] = handle; 2351 } 2352 2353 /** 2354 * Expands the hash "spine" -- equivalent to increasing the number of 2355 * buckets in a conventional hash table. 2356 */ 2357 private void growSpine() { 2358 spine = new int[(spine.length << 1) + 1]; 2359 threshold = (int) (spine.length * loadFactor); 2360 Arrays.fill(spine, -1); 2361 for (int i = 0; i < size; i++) { 2362 insert(objs[i], i); 2363 } 2364 } 2365 2366 /** 2367 * Increases hash table capacity by lengthening entry arrays. 2368 */ 2369 private void growEntries() { 2370 int newLength = (next.length << 1) + 1; 2371 int[] newNext = new int[newLength]; 2372 System.arraycopy(next, 0, newNext, 0, size); 2373 next = newNext; 2374 2375 Object[] newObjs = new Object[newLength]; 2376 System.arraycopy(objs, 0, newObjs, 0, size); 2377 objs = newObjs; 2378 } 2379 2380 /** 2381 * Returns hash value for given object. 2382 */ 2383 private int hash(Object obj) { 2384 return System.identityHashCode(obj) & 0x7FFFFFFF; 2385 } 2386 } 2387 2388 /** 2389 * Lightweight identity hash table which maps objects to replacement 2390 * objects. 2391 */ 2392 private static final class ReplaceTable { 2393 2394 /* maps object -> index */ 2395 private final HandleTable htab; 2396 /* maps index -> replacement object */ 2397 private Object[] reps; 2398 2399 /** 2400 * Creates new ReplaceTable with given capacity and load factor. 2401 */ 2402 ReplaceTable(int initialCapacity, float loadFactor) { 2403 htab = new HandleTable(initialCapacity, loadFactor); 2404 reps = new Object[initialCapacity]; 2405 } 2406 2407 /** 2408 * Enters mapping from object to replacement object. 2409 */ 2410 void assign(Object obj, Object rep) { 2411 int index = htab.assign(obj); 2412 while (index >= reps.length) { 2413 grow(); 2414 } 2415 reps[index] = rep; 2416 } 2417 2418 /** 2419 * Looks up and returns replacement for given object. If no 2420 * replacement is found, returns the lookup object itself. 2421 */ 2422 Object lookup(Object obj) { 2423 int index = htab.lookup(obj); 2424 return (index >= 0) ? reps[index] : obj; 2425 } 2426 2427 /** 2428 * Resets table to its initial (empty) state. 2429 */ 2430 void clear() { 2431 Arrays.fill(reps, 0, htab.size(), null); 2432 htab.clear(); 2433 } 2434 2435 /** 2436 * Returns the number of mappings currently in table. 2437 */ 2438 int size() { 2439 return htab.size(); 2440 } 2441 2442 /** 2443 * Increases table capacity. 2444 */ 2445 private void grow() { 2446 Object[] newReps = new Object[(reps.length << 1) + 1]; 2447 System.arraycopy(reps, 0, newReps, 0, reps.length); 2448 reps = newReps; 2449 } 2450 } 2451 2452 /** 2453 * Stack to keep debug information about the state of the 2454 * serialization process, for embedding in exception messages. 2455 */ 2456 private static final class DebugTraceInfoStack { 2457 private final List<String> stack; 2458 2459 DebugTraceInfoStack() { 2460 stack = new ArrayList<>(); 2461 } 2462 2463 /** 2464 * Removes all of the elements from enclosed list. 2465 */ 2466 void clear() { 2467 stack.clear(); 2468 } 2469 2470 /** 2471 * Removes the object at the top of enclosed list. 2472 */ 2473 void pop() { 2474 stack.remove(stack.size()-1); 2475 } 2476 2477 /** 2478 * Pushes a String onto the top of enclosed list. 2479 */ 2480 void push(String entry) { 2481 stack.add("\t- " + entry); 2482 } 2483 2484 /** 2485 * Returns a string representation of this object 2486 */ 2487 public String toString() { 2488 StringJoiner sj = new StringJoiner("\n"); 2489 for (int i = stack.size() - 1; i >= 0; i--) { 2490 sj.add(stack.get(i)); 2491 } 2492 return sj.toString(); 2493 } 2494 } 2495 2496 }