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