diff --git a/src/main/java/com/amazon/ion/BufferConfiguration.java b/src/main/java/com/amazon/ion/BufferConfiguration.java index e31c657d12..69c6f795a8 100644 --- a/src/main/java/com/amazon/ion/BufferConfiguration.java +++ b/src/main/java/com/amazon/ion/BufferConfiguration.java @@ -3,6 +3,8 @@ package com.amazon.ion; +import com.amazon.ion.impl._Private_IonConstants; + /** * Provides logic common to all BufferConfiguration implementations. * @param the type of the concrete subclass of this BufferConfiguration. @@ -60,7 +62,7 @@ public static abstract class Builder< /** * The maximum number of bytes that will be buffered. */ - private int maximumBufferSize = Integer.MAX_VALUE; + private int maximumBufferSize = _Private_IonConstants.ARRAY_MAXIMUM_SIZE; /** * The handler that will be notified when oversized values are encountered. @@ -133,7 +135,7 @@ public final DataHandler getDataHandler() { * Set the maximum size of the buffer. For binary Ion, the minimum value is 5 because all valid binary Ion data * begins with a 4-byte Ion version marker and the smallest value is 1 byte. For text Ion, the minimum value is * 2 because the smallest text Ion value is 1 byte and the smallest delimiter is 1 byte. - * Default: Integer.MAX_VALUE. + * Default: Near to the maximum size of an array. * * @param maximumBufferSizeInBytes the value. * @return this builder. @@ -214,7 +216,7 @@ protected BufferConfiguration(Builder builder) { )); } if (builder.getOversizedValueHandler() == null) { - requireUnlimitedBufferSize(); + requireMaximumBufferSize(); oversizedValueHandler = builder.getThrowingOversizedValueHandler(); } else { oversizedValueHandler = builder.getOversizedValueHandler(); @@ -229,10 +231,10 @@ protected BufferConfiguration(Builder builder) { /** * Requires that the maximum buffer size not be limited. */ - protected void requireUnlimitedBufferSize() { - if (maximumBufferSize < Integer.MAX_VALUE) { + protected void requireMaximumBufferSize() { + if (maximumBufferSize < _Private_IonConstants.ARRAY_MAXIMUM_SIZE) { throw new IllegalArgumentException( - "Must specify an OversizedValueHandler when a maximum buffer size is specified." + "Must specify an OversizedValueHandler when a custom maximum buffer size is specified." ); } } diff --git a/src/main/java/com/amazon/ion/IonBufferConfiguration.java b/src/main/java/com/amazon/ion/IonBufferConfiguration.java index 48190854b1..12256bcfa8 100644 --- a/src/main/java/com/amazon/ion/IonBufferConfiguration.java +++ b/src/main/java/com/amazon/ion/IonBufferConfiguration.java @@ -178,7 +178,7 @@ public IonBufferConfiguration build() { private IonBufferConfiguration(Builder builder) { super(builder); if (builder.getOversizedSymbolTableHandler() == null) { - requireUnlimitedBufferSize(); + requireMaximumBufferSize(); oversizedSymbolTableHandler = builder.getThrowingOversizedSymbolTableHandler(); } else { oversizedSymbolTableHandler = builder.getOversizedSymbolTableHandler(); diff --git a/src/main/java/com/amazon/ion/apps/BaseApp.java b/src/main/java/com/amazon/ion/apps/BaseApp.java index d5890bb5ae..96ca1a5ea8 100644 --- a/src/main/java/com/amazon/ion/apps/BaseApp.java +++ b/src/main/java/com/amazon/ion/apps/BaseApp.java @@ -20,6 +20,7 @@ import com.amazon.ion.IonReader; import com.amazon.ion.IonSystem; import com.amazon.ion.SymbolTable; +import com.amazon.ion.impl._Private_IonConstants; import com.amazon.ion.system.IonSystemBuilder; import com.amazon.ion.system.SimpleCatalog; import java.io.BufferedInputStream; @@ -65,7 +66,7 @@ protected static byte[] loadAsByteArray(File file) throws FileNotFoundException, IOException { long len = file.length(); - if (len < 0 || len > Integer.MAX_VALUE) + if (len < 0 || len > Integer.MAX_VALUE - 8) { throw new IllegalArgumentException("File too long: " + file); } diff --git a/src/main/java/com/amazon/ion/impl/BlockedBuffer.java b/src/main/java/com/amazon/ion/impl/BlockedBuffer.java index 06bd00f4c3..4faa6c53aa 100644 --- a/src/main/java/com/amazon/ion/impl/BlockedBuffer.java +++ b/src/main/java/com/amazon/ion/impl/BlockedBuffer.java @@ -1282,7 +1282,7 @@ private final void fail_on_version_change() throws IOException @Override public final long skip(long n) throws IOException { - if (n < 0 || n > Integer.MAX_VALUE) throw new IllegalArgumentException("we only handle buffer less than "+Integer.MAX_VALUE+" bytes in length"); + if (n < 0 || n > _Private_IonConstants.ARRAY_MAXIMUM_SIZE) throw new IllegalArgumentException("we only handle buffer less than "+_Private_IonConstants.ARRAY_MAXIMUM_SIZE+" bytes in length"); if (_buf == null) throw new IOException("stream is closed"); fail_on_version_change(); if (_pos >= _buf.size()) return -1; diff --git a/src/main/java/com/amazon/ion/impl/IonReaderTextSystemX.java b/src/main/java/com/amazon/ion/impl/IonReaderTextSystemX.java index 713422d44b..c187682d53 100644 --- a/src/main/java/com/amazon/ion/impl/IonReaderTextSystemX.java +++ b/src/main/java/com/amazon/ion/impl/IonReaderTextSystemX.java @@ -744,7 +744,7 @@ private int load_lob_contents() throws IOException } if (_lob_loaded == LOB_STATE.READ) { long raw_size = _current_value_save_point.length(); - if (raw_size < 0 || raw_size > Integer.MAX_VALUE) { + if (raw_size < 0 || raw_size > _Private_IonConstants.ARRAY_MAXIMUM_SIZE) { load_lob_length_overflow_error(raw_size); } _lob_bytes = new byte[(int)raw_size]; diff --git a/src/main/java/com/amazon/ion/impl/ResizingPipedInputStream.java b/src/main/java/com/amazon/ion/impl/ResizingPipedInputStream.java index 0c7f8ba26d..dd09fd1715 100644 --- a/src/main/java/com/amazon/ion/impl/ResizingPipedInputStream.java +++ b/src/main/java/com/amazon/ion/impl/ResizingPipedInputStream.java @@ -129,7 +129,7 @@ public void bytesConsolidatedToStartOfBuffer(int leftShiftAmount) { * such that growth is expected to occur rarely, if ever. */ public ResizingPipedInputStream(final int initialBufferSize) { - this(initialBufferSize, Integer.MAX_VALUE, false); + this(initialBufferSize, _Private_IonConstants.ARRAY_MAXIMUM_SIZE, false); } /** @@ -152,6 +152,9 @@ public ResizingPipedInputStream(final int initialBufferSize) { if (initialBufferSize < 1) { throw new IllegalArgumentException("Initial buffer size must be at least 1."); } + if (initialBufferSize > _Private_IonConstants.ARRAY_MAXIMUM_SIZE) { + throw new IllegalArgumentException("Initial buffer size must be at most " + _Private_IonConstants.ARRAY_MAXIMUM_SIZE + "."); + } if (maximumBufferSize < initialBufferSize) { throw new IllegalArgumentException("Maximum buffer size cannot be less than the initial buffer size."); } diff --git a/src/main/java/com/amazon/ion/impl/UnifiedInputBufferX.java b/src/main/java/com/amazon/ion/impl/UnifiedInputBufferX.java index c8444883cb..021cda0324 100644 --- a/src/main/java/com/amazon/ion/impl/UnifiedInputBufferX.java +++ b/src/main/java/com/amazon/ion/impl/UnifiedInputBufferX.java @@ -67,6 +67,8 @@ protected static final char[] chars_make_char_array(CharSequence chars, private UnifiedInputBufferX(int initialPageSize) { if (initialPageSize < 0) { throw new IllegalArgumentException("page size must be > 0"); + } else if (initialPageSize > _Private_IonConstants.ARRAY_MAXIMUM_SIZE) { + throw new IllegalArgumentException("page size must be < " + _Private_IonConstants.ARRAY_MAXIMUM_SIZE); } _page_size = initialPageSize; _buffers = new UnifiedDataPageX[10]; diff --git a/src/main/java/com/amazon/ion/impl/UnifiedInputStreamX.java b/src/main/java/com/amazon/ion/impl/UnifiedInputStreamX.java index f962946191..71967f7332 100644 --- a/src/main/java/com/amazon/ion/impl/UnifiedInputStreamX.java +++ b/src/main/java/com/amazon/ion/impl/UnifiedInputStreamX.java @@ -564,6 +564,7 @@ private static class FromCharStream extends UnifiedInputStreamX _is_byte_data = false; _is_stream = true; _reader = reader; + // If this page size ever becomes configurable watch out for _Private_IonConstants.ARRAY_MAXIMUM_SIZE _buffer = UnifiedInputBufferX.makePageBuffer(UnifiedInputBufferX.BufferType.CHARS, DEFAULT_PAGE_SIZE); super.init(); _limit = refill(); @@ -601,6 +602,7 @@ private static class FromByteStream extends UnifiedInputStreamX _is_byte_data = true; _is_stream = true; _stream = stream; + // If this page size ever becomes configurable watch out for _Private_IonConstants.ARRAY_MAXIMUM_SIZE _buffer = UnifiedInputBufferX.makePageBuffer(UnifiedInputBufferX.BufferType.BYTES, DEFAULT_PAGE_SIZE); super.init(); _limit = refill(); diff --git a/src/main/java/com/amazon/ion/impl/_Private_IonConstants.java b/src/main/java/com/amazon/ion/impl/_Private_IonConstants.java index dc25adbb1f..e0fe8cc0c3 100644 --- a/src/main/java/com/amazon/ion/impl/_Private_IonConstants.java +++ b/src/main/java/com/amazon/ion/impl/_Private_IonConstants.java @@ -22,6 +22,13 @@ */ public final class _Private_IonConstants { + // Arrays in Java must be indexed by integers (JLS 10.4), but the true maximum array size may be less than + // Integer.MAX_VALUE. True maximum array size is a JVM implementation detail, and varies by type and by JVM. + // We have this pinned at Integer.MAX_VALUE - 8 because that's a fairly common value in the JDK itself, in + // classes such as ConcurrentHashMap, InputStream, Hashtable, ByteArrayChannel, etc. + // In testing against a variety of JVMs and types the smallest maximum size I've seen is Integer.MAX_VALUE - 6 + public static final int ARRAY_MAXIMUM_SIZE = Integer.MAX_VALUE - 8; + private _Private_IonConstants() { } diff --git a/src/main/java/com/amazon/ion/impl/_Private_Utils.java b/src/main/java/com/amazon/ion/impl/_Private_Utils.java index ce68c35a19..751fb46489 100644 --- a/src/main/java/com/amazon/ion/impl/_Private_Utils.java +++ b/src/main/java/com/amazon/ion/impl/_Private_Utils.java @@ -578,7 +578,7 @@ public static byte[] loadFileBytes(File file) throws IOException { long len = file.length(); - if (len < 0 || len > Integer.MAX_VALUE) { + if (len < 0 || len > _Private_IonConstants.ARRAY_MAXIMUM_SIZE) { throw new IllegalArgumentException("File too long: " + file); } diff --git a/src/test/java/com/amazon/ion/impl/ResizingPipedInputStreamTest.java b/src/test/java/com/amazon/ion/impl/ResizingPipedInputStreamTest.java index 126476ac8b..d3d12d4ebb 100644 --- a/src/test/java/com/amazon/ion/impl/ResizingPipedInputStreamTest.java +++ b/src/test/java/com/amazon/ion/impl/ResizingPipedInputStreamTest.java @@ -94,7 +94,7 @@ public static Iterable bufferSizes() { @Before public void setup() { - input = new ResizingPipedInputStream(bufferSize, Integer.MAX_VALUE, useBoundary); + input = new ResizingPipedInputStream(bufferSize, _Private_IonConstants.ARRAY_MAXIMUM_SIZE, useBoundary); knownCapacity = input.capacity(); }