001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.openwire; 018 019import java.io.DataInput; 020import java.io.DataOutput; 021import java.io.IOException; 022import java.lang.reflect.Method; 023import java.util.HashMap; 024import java.util.Map; 025 026import org.apache.activemq.command.CommandTypes; 027import org.apache.activemq.command.DataStructure; 028import org.apache.activemq.command.WireFormatInfo; 029import org.apache.activemq.util.ByteSequence; 030import org.apache.activemq.util.ByteSequenceData; 031import org.apache.activemq.util.DataByteArrayInputStream; 032import org.apache.activemq.util.DataByteArrayOutputStream; 033import org.apache.activemq.util.IOExceptionSupport; 034import org.apache.activemq.wireformat.WireFormat; 035 036/** 037 * 038 * 039 */ 040public final class OpenWireFormat implements WireFormat { 041 042 public static final int DEFAULT_STORE_VERSION = CommandTypes.PROTOCOL_STORE_VERSION; 043 public static final int DEFAULT_WIRE_VERSION = CommandTypes.PROTOCOL_VERSION; 044 public static final int DEFAULT_LEGACY_VERSION = CommandTypes.PROTOCOL_LEGACY_STORE_VERSION; 045 public static final long DEFAULT_MAX_FRAME_SIZE = Long.MAX_VALUE; 046 047 static final byte NULL_TYPE = CommandTypes.NULL; 048 private static final int MARSHAL_CACHE_SIZE = Short.MAX_VALUE / 2; 049 private static final int MARSHAL_CACHE_FREE_SPACE = 100; 050 051 private DataStreamMarshaller[] dataMarshallers; 052 private int version; 053 private boolean stackTraceEnabled; 054 private boolean tcpNoDelayEnabled; 055 private boolean cacheEnabled; 056 private boolean tightEncodingEnabled; 057 private boolean sizePrefixDisabled; 058 private boolean maxFrameSizeEnabled = true; 059 private long maxFrameSize = DEFAULT_MAX_FRAME_SIZE; 060 061 // The following fields are used for value caching 062 private short nextMarshallCacheIndex; 063 private short nextMarshallCacheEvictionIndex; 064 private Map<DataStructure, Short> marshallCacheMap = new HashMap<>(); 065 private DataStructure marshallCache[] = null; 066 private DataStructure unmarshallCache[] = null; 067 private final DataByteArrayOutputStream bytesOut = new DataByteArrayOutputStream(); 068 private final DataByteArrayInputStream bytesIn = new DataByteArrayInputStream(); 069 private WireFormatInfo preferedWireFormatInfo; 070 071 // Used to track the currentFrameSize for validation during unmarshalling 072 // Ideally we would pass the MarshallingContext directly to the marshalling methods, 073 // however this would require modifying the DataStreamMarshaller interface which would result 074 // in hundreds of existing methods having to be updated so this allows avoiding that and 075 // tracking the state without breaking the existing API. 076 // Note that while this is currently only used during unmarshalling, but if necessary could 077 // be extended in the future to be used during marshalling as well. 078 private final ThreadLocal<MarshallingContext> marshallingContext = new ThreadLocal<>(); 079 080 public OpenWireFormat() { 081 this(DEFAULT_STORE_VERSION); 082 } 083 084 public OpenWireFormat(int i) { 085 setVersion(i); 086 } 087 088 @Override 089 public int hashCode() { 090 return version ^ (cacheEnabled ? 0x10000000 : 0x20000000) 091 ^ (stackTraceEnabled ? 0x01000000 : 0x02000000) 092 ^ (tightEncodingEnabled ? 0x00100000 : 0x00200000) 093 ^ (sizePrefixDisabled ? 0x00010000 : 0x00020000) 094 ^ (maxFrameSizeEnabled ? 0x00010000 : 0x00020000); 095 } 096 097 public OpenWireFormat copy() { 098 OpenWireFormat answer = new OpenWireFormat(version); 099 answer.stackTraceEnabled = stackTraceEnabled; 100 answer.tcpNoDelayEnabled = tcpNoDelayEnabled; 101 answer.cacheEnabled = cacheEnabled; 102 answer.tightEncodingEnabled = tightEncodingEnabled; 103 answer.sizePrefixDisabled = sizePrefixDisabled; 104 answer.preferedWireFormatInfo = preferedWireFormatInfo; 105 answer.maxFrameSizeEnabled = maxFrameSizeEnabled; 106 return answer; 107 } 108 109 @Override 110 public boolean equals(Object object) { 111 if (object == null) { 112 return false; 113 } 114 OpenWireFormat o = (OpenWireFormat)object; 115 return o.stackTraceEnabled == stackTraceEnabled && o.cacheEnabled == cacheEnabled 116 && o.version == version && o.tightEncodingEnabled == tightEncodingEnabled 117 && o.sizePrefixDisabled == sizePrefixDisabled 118 && o.maxFrameSizeEnabled == maxFrameSizeEnabled; 119 } 120 121 122 @Override 123 public String toString() { 124 return "OpenWireFormat{version=" + version + ", cacheEnabled=" + cacheEnabled + ", stackTraceEnabled=" + stackTraceEnabled + ", tightEncodingEnabled=" 125 + tightEncodingEnabled + ", sizePrefixDisabled=" + sizePrefixDisabled + ", maxFrameSize=" + maxFrameSize + ", maxFrameSizeEnabled=" + maxFrameSizeEnabled + "}"; 126 // return "OpenWireFormat{id="+id+", 127 // tightEncodingEnabled="+tightEncodingEnabled+"}"; 128 } 129 130 @Override 131 public int getVersion() { 132 return version; 133 } 134 135 @Override 136 public synchronized ByteSequence marshal(Object command) throws IOException { 137 138 if (cacheEnabled) { 139 runMarshallCacheEvictionSweep(); 140 } 141 142 ByteSequence sequence = null; 143 int size = 1; 144 if (command != null) { 145 146 DataStructure c = (DataStructure)command; 147 byte type = c.getDataStructureType(); 148 DataStreamMarshaller dsm = dataMarshallers[type & 0xFF]; 149 if (dsm == null) { 150 throw new IOException("Unknown data type: " + type); 151 } 152 if (tightEncodingEnabled) { 153 154 BooleanStream bs = new BooleanStream(); 155 size += dsm.tightMarshal1(this, c, bs); 156 size += bs.marshalledSize(); 157 158 if(maxFrameSizeEnabled && size > maxFrameSize) { 159 throw IOExceptionSupport.createFrameSizeException(size, maxFrameSize); 160 } 161 162 bytesOut.restart(size); 163 if (!sizePrefixDisabled) { 164 bytesOut.writeInt(size); 165 } 166 bytesOut.writeByte(type); 167 bs.marshal(bytesOut); 168 dsm.tightMarshal2(this, c, bytesOut, bs); 169 sequence = bytesOut.toByteSequence(); 170 171 } else { 172 bytesOut.restart(); 173 if (!sizePrefixDisabled) { 174 bytesOut.writeInt(0); // we don't know the final size 175 // yet but write this here for 176 // now. 177 } 178 bytesOut.writeByte(type); 179 dsm.looseMarshal(this, c, bytesOut); 180 sequence = bytesOut.toByteSequence(); 181 182 if (!sizePrefixDisabled) { 183 size = sequence.getLength() - 4; 184 int pos = sequence.offset; 185 ByteSequenceData.writeIntBig(sequence, size); 186 sequence.offset = pos; 187 } 188 } 189 190 } else { 191 bytesOut.restart(5); 192 bytesOut.writeInt(size); 193 bytesOut.writeByte(NULL_TYPE); 194 sequence = bytesOut.toByteSequence(); 195 } 196 197 return sequence; 198 } 199 200 @Override 201 public synchronized Object unmarshal(ByteSequence sequence) throws IOException { 202 bytesIn.restart(sequence); 203 204 try { 205 final MarshallingContext context = new MarshallingContext(); 206 marshallingContext.set(context); 207 208 if (!sizePrefixDisabled) { 209 int size = bytesIn.readInt(); 210 if (maxFrameSizeEnabled && size > maxFrameSize) { 211 throw IOExceptionSupport.createFrameSizeException(size, maxFrameSize); 212 } 213 context.setFrameSize(size); 214 } 215 return doUnmarshal(bytesIn); 216 } finally { 217 // After we unmarshal we can clear the context 218 marshallingContext.remove(); 219 } 220 } 221 222 @Override 223 public synchronized void marshal(Object o, DataOutput dataOut) throws IOException { 224 225 if (cacheEnabled) { 226 runMarshallCacheEvictionSweep(); 227 } 228 229 int size = 1; 230 if (o != null) { 231 232 DataStructure c = (DataStructure)o; 233 byte type = c.getDataStructureType(); 234 DataStreamMarshaller dsm = dataMarshallers[type & 0xFF]; 235 if (dsm == null) { 236 throw new IOException("Unknown data type: " + type); 237 } 238 if (tightEncodingEnabled) { 239 BooleanStream bs = new BooleanStream(); 240 size += dsm.tightMarshal1(this, c, bs); 241 size += bs.marshalledSize(); 242 243 if(maxFrameSizeEnabled && size > maxFrameSize) { 244 throw IOExceptionSupport.createFrameSizeException(size, maxFrameSize); 245 } 246 247 if (!sizePrefixDisabled) { 248 dataOut.writeInt(size); 249 } 250 251 dataOut.writeByte(type); 252 bs.marshal(dataOut); 253 dsm.tightMarshal2(this, c, dataOut, bs); 254 255 } else { 256 DataOutput looseOut = dataOut; 257 258 if (!sizePrefixDisabled) { 259 bytesOut.restart(); 260 looseOut = bytesOut; 261 } 262 263 looseOut.writeByte(type); 264 dsm.looseMarshal(this, c, looseOut); 265 266 if (!sizePrefixDisabled) { 267 ByteSequence sequence = bytesOut.toByteSequence(); 268 dataOut.writeInt(sequence.getLength()); 269 dataOut.write(sequence.getData(), sequence.getOffset(), sequence.getLength()); 270 } 271 272 } 273 274 } else { 275 if (!sizePrefixDisabled) { 276 dataOut.writeInt(size); 277 } 278 dataOut.writeByte(NULL_TYPE); 279 } 280 } 281 282 @Override 283 public Object unmarshal(DataInput dis) throws IOException { 284 try { 285 final MarshallingContext context = new MarshallingContext(); 286 marshallingContext.set(context); 287 288 if (!sizePrefixDisabled) { 289 int size = dis.readInt(); 290 if (maxFrameSizeEnabled && size > maxFrameSize) { 291 throw IOExceptionSupport.createFrameSizeException(size, maxFrameSize); 292 } 293 context.setFrameSize(size); 294 } 295 return doUnmarshal(dis); 296 } finally { 297 // After we unmarshal we can clear 298 marshallingContext.remove(); 299 } 300 } 301 302 /** 303 * Used by NIO or AIO transports 304 */ 305 public int tightMarshal1(Object o, BooleanStream bs) throws IOException { 306 int size = 1; 307 if (o != null) { 308 DataStructure c = (DataStructure)o; 309 byte type = c.getDataStructureType(); 310 DataStreamMarshaller dsm = dataMarshallers[type & 0xFF]; 311 if (dsm == null) { 312 throw new IOException("Unknown data type: " + type); 313 } 314 315 size += dsm.tightMarshal1(this, c, bs); 316 size += bs.marshalledSize(); 317 } 318 return size; 319 } 320 321 /** 322 * Used by NIO or AIO transports; note that the size is not written as part 323 * of this method. 324 */ 325 public void tightMarshal2(Object o, DataOutput ds, BooleanStream bs) throws IOException { 326 if (cacheEnabled) { 327 runMarshallCacheEvictionSweep(); 328 } 329 330 if (o != null) { 331 DataStructure c = (DataStructure)o; 332 byte type = c.getDataStructureType(); 333 DataStreamMarshaller dsm = dataMarshallers[type & 0xFF]; 334 if (dsm == null) { 335 throw new IOException("Unknown data type: " + type); 336 } 337 ds.writeByte(type); 338 bs.marshal(ds); 339 dsm.tightMarshal2(this, c, ds, bs); 340 } 341 } 342 343 /** 344 * Allows you to dynamically switch the version of the openwire protocol 345 * being used. 346 * 347 * @param version 348 */ 349 @Override 350 public void setVersion(int version) { 351 String mfName = "org.apache.activemq.openwire.v" + version + ".MarshallerFactory"; 352 Class mfClass; 353 try { 354 mfClass = Class.forName(mfName, false, getClass().getClassLoader()); 355 } catch (ClassNotFoundException e) { 356 throw (IllegalArgumentException)new IllegalArgumentException("Invalid version: " + version 357 + ", could not load " + mfName) 358 .initCause(e); 359 } 360 try { 361 Method method = mfClass.getMethod("createMarshallerMap", new Class[] {OpenWireFormat.class}); 362 dataMarshallers = (DataStreamMarshaller[])method.invoke(null, new Object[] {this}); 363 } catch (Throwable e) { 364 throw (IllegalArgumentException)new IllegalArgumentException( 365 "Invalid version: " 366 + version 367 + ", " 368 + mfName 369 + " does not properly implement the createMarshallerMap method.") 370 .initCause(e); 371 } 372 this.version = version; 373 } 374 375 private Object doUnmarshal(DataInput dis) throws IOException { 376 byte dataType = dis.readByte(); 377 if (dataType != NULL_TYPE) { 378 DataStreamMarshaller dsm = dataMarshallers[dataType & 0xFF]; 379 if (dsm == null) { 380 throw new IOException("Unknown data type: " + dataType); 381 } 382 Object data = dsm.createObject(); 383 if (this.tightEncodingEnabled) { 384 BooleanStream bs = new BooleanStream(); 385 bs.unmarshal(dis); 386 dsm.tightUnmarshal(this, data, dis, bs); 387 } else { 388 dsm.looseUnmarshal(this, data, dis); 389 } 390 return data; 391 } else { 392 return null; 393 } 394 } 395 396 // public void debug(String msg) { 397 // String t = (Thread.currentThread().getName()+" ").substring(0, 40); 398 // System.out.println(t+": "+msg); 399 // } 400 public int tightMarshalNestedObject1(DataStructure o, BooleanStream bs) throws IOException { 401 bs.writeBoolean(o != null); 402 if (o == null) { 403 return 0; 404 } 405 406 if (o.isMarshallAware()) { 407 // MarshallAware ma = (MarshallAware)o; 408 ByteSequence sequence = null; 409 // sequence=ma.getCachedMarshalledForm(this); 410 bs.writeBoolean(sequence != null); 411 if (sequence != null) { 412 return 1 + sequence.getLength(); 413 } 414 } 415 416 byte type = o.getDataStructureType(); 417 DataStreamMarshaller dsm = dataMarshallers[type & 0xFF]; 418 if (dsm == null) { 419 throw new IOException("Unknown data type: " + type); 420 } 421 return 1 + dsm.tightMarshal1(this, o, bs); 422 } 423 424 public void tightMarshalNestedObject2(DataStructure o, DataOutput ds, BooleanStream bs) 425 throws IOException { 426 if (!bs.readBoolean()) { 427 return; 428 } 429 430 byte type = o.getDataStructureType(); 431 ds.writeByte(type); 432 433 if (o.isMarshallAware() && bs.readBoolean()) { 434 435 // We should not be doing any caching 436 throw new IOException("Corrupted stream"); 437 // MarshallAware ma = (MarshallAware) o; 438 // ByteSequence sequence=ma.getCachedMarshalledForm(this); 439 // ds.write(sequence.getData(), sequence.getOffset(), 440 // sequence.getLength()); 441 442 } else { 443 444 DataStreamMarshaller dsm = dataMarshallers[type & 0xFF]; 445 if (dsm == null) { 446 throw new IOException("Unknown data type: " + type); 447 } 448 dsm.tightMarshal2(this, o, ds, bs); 449 450 } 451 } 452 453 public DataStructure tightUnmarshalNestedObject(DataInput dis, BooleanStream bs) throws IOException { 454 if (bs.readBoolean()) { 455 456 byte dataType = dis.readByte(); 457 DataStreamMarshaller dsm = dataMarshallers[dataType & 0xFF]; 458 if (dsm == null) { 459 throw new IOException("Unknown data type: " + dataType); 460 } 461 DataStructure data = dsm.createObject(); 462 463 if (data.isMarshallAware() && bs.readBoolean()) { 464 465 dis.readInt(); 466 dis.readByte(); 467 468 BooleanStream bs2 = new BooleanStream(); 469 bs2.unmarshal(dis); 470 dsm.tightUnmarshal(this, data, dis, bs2); 471 472 // TODO: extract the sequence from the dis and associate it. 473 // MarshallAware ma = (MarshallAware)data 474 // ma.setCachedMarshalledForm(this, sequence); 475 476 } else { 477 dsm.tightUnmarshal(this, data, dis, bs); 478 } 479 480 return data; 481 } else { 482 return null; 483 } 484 } 485 486 public DataStructure looseUnmarshalNestedObject(DataInput dis) throws IOException { 487 if (dis.readBoolean()) { 488 489 byte dataType = dis.readByte(); 490 DataStreamMarshaller dsm = dataMarshallers[dataType & 0xFF]; 491 if (dsm == null) { 492 throw new IOException("Unknown data type: " + dataType); 493 } 494 DataStructure data = dsm.createObject(); 495 dsm.looseUnmarshal(this, data, dis); 496 return data; 497 498 } else { 499 return null; 500 } 501 } 502 503 public void looseMarshalNestedObject(DataStructure o, DataOutput dataOut) throws IOException { 504 dataOut.writeBoolean(o != null); 505 if (o != null) { 506 byte type = o.getDataStructureType(); 507 dataOut.writeByte(type); 508 DataStreamMarshaller dsm = dataMarshallers[type & 0xFF]; 509 if (dsm == null) { 510 throw new IOException("Unknown data type: " + type); 511 } 512 dsm.looseMarshal(this, o, dataOut); 513 } 514 } 515 516 public void runMarshallCacheEvictionSweep() { 517 // Do we need to start evicting?? 518 while (marshallCacheMap.size() > marshallCache.length - MARSHAL_CACHE_FREE_SPACE) { 519 520 marshallCacheMap.remove(marshallCache[nextMarshallCacheEvictionIndex]); 521 marshallCache[nextMarshallCacheEvictionIndex] = null; 522 523 nextMarshallCacheEvictionIndex++; 524 if (nextMarshallCacheEvictionIndex >= marshallCache.length) { 525 nextMarshallCacheEvictionIndex = 0; 526 } 527 528 } 529 } 530 531 public Short getMarshallCacheIndex(DataStructure o) { 532 return marshallCacheMap.get(o); 533 } 534 535 public Short addToMarshallCache(DataStructure o) { 536 short i = nextMarshallCacheIndex++; 537 if (nextMarshallCacheIndex >= marshallCache.length) { 538 nextMarshallCacheIndex = 0; 539 } 540 541 // We can only cache that item if there is space left. 542 if (marshallCacheMap.size() < marshallCache.length) { 543 marshallCache[i] = o; 544 Short index = new Short(i); 545 marshallCacheMap.put(o, index); 546 return index; 547 } else { 548 // Use -1 to indicate that the value was not cached due to cache 549 // being full. 550 return new Short((short)-1); 551 } 552 } 553 554 public void setInUnmarshallCache(short index, DataStructure o) { 555 556 // There was no space left in the cache, so we can't 557 // put this in the cache. 558 if (index == -1) { 559 return; 560 } 561 562 unmarshallCache[index] = o; 563 } 564 565 public DataStructure getFromUnmarshallCache(short index) { 566 return unmarshallCache[index]; 567 } 568 569 public void setStackTraceEnabled(boolean b) { 570 stackTraceEnabled = b; 571 } 572 573 public boolean isStackTraceEnabled() { 574 return stackTraceEnabled; 575 } 576 577 public boolean isTcpNoDelayEnabled() { 578 return tcpNoDelayEnabled; 579 } 580 581 public void setTcpNoDelayEnabled(boolean tcpNoDelayEnabled) { 582 this.tcpNoDelayEnabled = tcpNoDelayEnabled; 583 } 584 585 public boolean isCacheEnabled() { 586 return cacheEnabled; 587 } 588 589 public void setCacheEnabled(boolean cacheEnabled) { 590 if(cacheEnabled){ 591 marshallCache = new DataStructure[MARSHAL_CACHE_SIZE]; 592 unmarshallCache = new DataStructure[MARSHAL_CACHE_SIZE]; 593 } 594 this.cacheEnabled = cacheEnabled; 595 } 596 597 public boolean isTightEncodingEnabled() { 598 return tightEncodingEnabled; 599 } 600 601 public void setTightEncodingEnabled(boolean tightEncodingEnabled) { 602 this.tightEncodingEnabled = tightEncodingEnabled; 603 } 604 605 public boolean isSizePrefixDisabled() { 606 return sizePrefixDisabled; 607 } 608 609 public void setSizePrefixDisabled(boolean prefixPacketSize) { 610 this.sizePrefixDisabled = prefixPacketSize; 611 } 612 613 public void setPreferedWireFormatInfo(WireFormatInfo info) { 614 this.preferedWireFormatInfo = info; 615 } 616 617 public WireFormatInfo getPreferedWireFormatInfo() { 618 return preferedWireFormatInfo; 619 } 620 621 public long getMaxFrameSize() { 622 return maxFrameSize; 623 } 624 625 public void setMaxFrameSize(long maxFrameSize) { 626 this.maxFrameSize = maxFrameSize; 627 } 628 629 public boolean isMaxFrameSizeEnabled() { 630 return maxFrameSizeEnabled; 631 } 632 633 /** 634 * Set whether the maxFrameSize check will be enabled. Note this is only applied to this format 635 * and will NOT be negotiated 636 * 637 * @param maxFrameSizeEnabled 638 */ 639 public void setMaxFrameSizeEnabled(boolean maxFrameSizeEnabled) { 640 this.maxFrameSizeEnabled = maxFrameSizeEnabled; 641 } 642 643 public void renegotiateWireFormat(WireFormatInfo info) throws IOException { 644 645 if (preferedWireFormatInfo == null) { 646 throw new IllegalStateException("Wireformat cannot not be renegotiated."); 647 } 648 649 this.setVersion(min(preferedWireFormatInfo.getVersion(), info.getVersion())); 650 info.setVersion(this.getVersion()); 651 652 this.setMaxFrameSize(min(preferedWireFormatInfo.getMaxFrameSize(), info.getMaxFrameSize())); 653 info.setMaxFrameSize(this.getMaxFrameSize()); 654 //Note: Don't negotiate maxFrameSizeEnabled so the client and server can set independently 655 656 this.stackTraceEnabled = info.isStackTraceEnabled() && preferedWireFormatInfo.isStackTraceEnabled(); 657 info.setStackTraceEnabled(this.stackTraceEnabled); 658 659 this.tcpNoDelayEnabled = info.isTcpNoDelayEnabled() && preferedWireFormatInfo.isTcpNoDelayEnabled(); 660 info.setTcpNoDelayEnabled(this.tcpNoDelayEnabled); 661 662 this.cacheEnabled = info.isCacheEnabled() && preferedWireFormatInfo.isCacheEnabled(); 663 info.setCacheEnabled(this.cacheEnabled); 664 665 this.tightEncodingEnabled = info.isTightEncodingEnabled() 666 && preferedWireFormatInfo.isTightEncodingEnabled(); 667 info.setTightEncodingEnabled(this.tightEncodingEnabled); 668 669 this.sizePrefixDisabled = info.isSizePrefixDisabled() 670 && preferedWireFormatInfo.isSizePrefixDisabled(); 671 info.setSizePrefixDisabled(this.sizePrefixDisabled); 672 673 if (cacheEnabled) { 674 675 int size = Math.min(preferedWireFormatInfo.getCacheSize(), info.getCacheSize()); 676 info.setCacheSize(size); 677 678 if (size == 0) { 679 size = MARSHAL_CACHE_SIZE; 680 } 681 682 marshallCache = new DataStructure[size]; 683 unmarshallCache = new DataStructure[size]; 684 nextMarshallCacheIndex = 0; 685 nextMarshallCacheEvictionIndex = 0; 686 marshallCacheMap = new HashMap<DataStructure, Short>(); 687 } else { 688 marshallCache = null; 689 unmarshallCache = null; 690 nextMarshallCacheIndex = 0; 691 nextMarshallCacheEvictionIndex = 0; 692 marshallCacheMap = null; 693 } 694 695 } 696 697 protected int min(int version1, int version2) { 698 if (version1 < version2 && version1 > 0 || version2 <= 0) { 699 return version1; 700 } 701 return version2; 702 } 703 704 protected long min(long version1, long version2) { 705 if (version1 < version2 && version1 > 0 || version2 <= 0) { 706 return version1; 707 } 708 return version2; 709 } 710 711 MarshallingContext getMarshallingContext() { 712 return marshallingContext.get(); 713 } 714 715 // Used to track the estimated allocated buffer sizes to validate 716 // against the current frame being processed 717 static class MarshallingContext { 718 // Use primitives to minimize memory footprint 719 private int frameSize = -1; 720 private int estimatedAllocated = 0; 721 722 void setFrameSize(int frameSize) throws IOException { 723 this.frameSize = frameSize; 724 if (frameSize < 0) { 725 throw error("Frame size " + frameSize + " can't be negative."); 726 } 727 } 728 729 void increment(int size) throws IOException { 730 if (size < 0) { 731 throw error("Size " + size + " can't be negative."); 732 } 733 try { 734 estimatedAllocated = Math.addExact(estimatedAllocated, size); 735 } catch (ArithmeticException e) { 736 throw error("Buffer overflow when incrementing size value: " + size); 737 } 738 } 739 740 public int getFrameSize() { 741 return frameSize; 742 } 743 744 public int getEstimatedAllocated() { 745 return estimatedAllocated; 746 } 747 748 private static IOException error(String errorMessage) { 749 return new IOException(new IllegalArgumentException(errorMessage)); 750 } 751 } 752 753}