8223593: Refactor code for reallocating storage
Reviewed-by: prappo, plevart, rriggs, smarks
--- a/src/java.base/share/classes/java/io/BufferedInputStream.java Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/io/BufferedInputStream.java Tue May 21 18:40:29 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
package java.io;
import jdk.internal.misc.Unsafe;
+import jdk.internal.util.ArraysSupport;
/**
* A <code>BufferedInputStream</code> adds
@@ -54,14 +55,6 @@
private static int DEFAULT_BUFFER_SIZE = 8192;
/**
- * The maximum size of array to allocate.
- * Some VMs reserve some header words in an array.
- * Attempts to allocate larger arrays may result in
- * OutOfMemoryError: Requested array size exceeds VM limit
- */
- private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
-
- /**
* As this class is used early during bootstrap, it's motivated to use
* Unsafe.compareAndSetObject instead of AtomicReferenceFieldUpdater
* (or VarHandles) to reduce dependencies and improve startup time.
@@ -220,7 +213,7 @@
byte[] buffer = getBufIfOpen();
if (markpos < 0)
pos = 0; /* no mark: throw away the buffer */
- else if (pos >= buffer.length) /* no room left in buffer */
+ else if (pos >= buffer.length) { /* no room left in buffer */
if (markpos > 0) { /* can throw away early part of the buffer */
int sz = pos - markpos;
System.arraycopy(buffer, markpos, buffer, 0, sz);
@@ -229,11 +222,10 @@
} else if (buffer.length >= marklimit) {
markpos = -1; /* buffer got too big, invalidate mark */
pos = 0; /* drop buffer contents */
- } else if (buffer.length >= MAX_BUFFER_SIZE) {
- throw new OutOfMemoryError("Required array size too large");
} else { /* grow buffer */
- int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
- pos * 2 : MAX_BUFFER_SIZE;
+ int nsz = ArraysSupport.newLength(pos,
+ 1, /* minimum growth */
+ pos /* preferred growth */);
if (nsz > marklimit)
nsz = marklimit;
byte[] nbuf = new byte[nsz];
@@ -248,6 +240,7 @@
}
buffer = nbuf;
}
+ }
count = pos;
int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
if (n > 0)
--- a/src/java.base/share/classes/java/io/ByteArrayOutputStream.java Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/io/ByteArrayOutputStream.java Tue May 21 18:40:29 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,8 @@
import java.util.Arrays;
import java.util.Objects;
+import jdk.internal.util.ArraysSupport;
+
/**
* This class implements an output stream in which the data is
* written into a byte array. The buffer automatically grows as data
@@ -84,48 +86,20 @@
* at least the number of elements specified by the minimum
* capacity argument.
*
- * @param minCapacity the desired minimum capacity
- * @throws OutOfMemoryError if {@code minCapacity < 0}. This is
- * interpreted as a request for the unsatisfiably large capacity
+ * @param minCapacity the desired minimum capacity.
+ * @throws OutOfMemoryError if {@code minCapacity < 0} and
+ * {@code minCapacity - buf.length > 0}. This is interpreted as a
+ * request for the unsatisfiably large capacity.
* {@code (long) Integer.MAX_VALUE + (minCapacity - Integer.MAX_VALUE)}.
*/
private void ensureCapacity(int minCapacity) {
// overflow-conscious code
- if (minCapacity - buf.length > 0)
- grow(minCapacity);
- }
-
- /**
- * The maximum size of array to allocate.
- * Some VMs reserve some header words in an array.
- * Attempts to allocate larger arrays may result in
- * OutOfMemoryError: Requested array size exceeds VM limit
- */
- private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
- /**
- * Increases the capacity to ensure that it can hold at least the
- * number of elements specified by the minimum capacity argument.
- *
- * @param minCapacity the desired minimum capacity
- */
- private void grow(int minCapacity) {
- // overflow-conscious code
int oldCapacity = buf.length;
- int newCapacity = oldCapacity << 1;
- if (newCapacity - minCapacity < 0)
- newCapacity = minCapacity;
- if (newCapacity - MAX_ARRAY_SIZE > 0)
- newCapacity = hugeCapacity(minCapacity);
- buf = Arrays.copyOf(buf, newCapacity);
- }
-
- private static int hugeCapacity(int minCapacity) {
- if (minCapacity < 0) // overflow
- throw new OutOfMemoryError();
- return (minCapacity > MAX_ARRAY_SIZE) ?
- Integer.MAX_VALUE :
- MAX_ARRAY_SIZE;
+ int minGrowth = minCapacity - oldCapacity;
+ if (minGrowth > 0) {
+ buf = Arrays.copyOf(buf, ArraysSupport.newLength(oldCapacity,
+ minGrowth, oldCapacity /* preferred growth */));
+ }
}
/**
--- a/src/java.base/share/classes/java/lang/StringLatin1.java Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/lang/StringLatin1.java Tue May 21 18:40:29 2019 -0700
@@ -35,6 +35,7 @@
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import jdk.internal.HotSpotIntrinsicCandidate;
+import jdk.internal.util.ArraysSupport;
import static java.lang.String.LATIN1;
import static java.lang.String.UTF16;
@@ -42,14 +43,6 @@
final class StringLatin1 {
- /**
- * The maximum size of array to allocate (unless necessary).
- * Some VMs reserve some header words in an array.
- * Attempts to allocate larger arrays may result in
- * OutOfMemoryError: Requested array size exceeds VM limit
- */
- private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
public static char charAt(byte[] value, int index) {
if (index < 0 || index >= value.length) {
throw new StringIndexOutOfBoundsException(index);
@@ -353,15 +346,7 @@
i += targLen;
while ((j = indexOf(value, valLen, targ, targLen, i)) > 0) {
if (++p == pos.length) {
- int cap = p + (p >> 1);
- // overflow-conscious code
- if (cap - MAX_ARRAY_SIZE > 0) {
- if (p == MAX_ARRAY_SIZE) {
- throw new OutOfMemoryError();
- }
- cap = MAX_ARRAY_SIZE;
- }
- pos = Arrays.copyOf(pos, cap);
+ pos = Arrays.copyOf(pos, ArraysSupport.newLength(p, 1, p >> 1));
}
pos[p] = j;
i = j + targLen;
--- a/src/java.base/share/classes/java/lang/StringUTF16.java Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/lang/StringUTF16.java Tue May 21 18:40:29 2019 -0700
@@ -33,6 +33,7 @@
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import jdk.internal.HotSpotIntrinsicCandidate;
+import jdk.internal.util.ArraysSupport;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.DontInline;
@@ -649,15 +650,7 @@
: indexOf(value, valLen, targ, targLen, i))) > 0)
{
if (++p == pos.length) {
- int cap = p + (p >> 1);
- // overflow-conscious code
- if (cap - MAX_ARRAY_SIZE > 0) {
- if (p == MAX_ARRAY_SIZE) {
- throw new OutOfMemoryError();
- }
- cap = MAX_ARRAY_SIZE;
- }
- pos = Arrays.copyOf(pos, cap);
+ pos = Arrays.copyOf(pos, ArraysSupport.newLength(p, 1, p >> 1));
}
pos[p] = j;
i = j + targLen;
@@ -1554,15 +1547,6 @@
static final int MAX_LENGTH = Integer.MAX_VALUE >> 1;
-
- /**
- * The maximum size of array to allocate (unless necessary).
- * Some VMs reserve some header words in an array.
- * Attempts to allocate larger arrays may result in
- * OutOfMemoryError: Requested array size exceeds VM limit
- */
- private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
// Used by trusted callers. Assumes all necessary bounds checks have
// been done by the caller.
--- a/src/java.base/share/classes/java/nio/file/Files.java Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/nio/file/Files.java Tue May 21 18:40:29 2019 -0700
@@ -77,6 +77,7 @@
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
+import jdk.internal.util.ArraysSupport;
import sun.nio.ch.FileChannelImpl;
import sun.nio.fs.AbstractFileSystemProvider;
@@ -3196,14 +3197,6 @@
}
}
- /**
- * The maximum size of array to allocate.
- * Some VMs reserve some header words in an array.
- * Attempts to allocate larger arrays may result in
- * OutOfMemoryError: Requested array size exceeds VM limit
- */
- private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
-
private static final jdk.internal.access.JavaLangAccess JLA =
jdk.internal.access.SharedSecrets.getJavaLangAccess();
@@ -3240,13 +3233,10 @@
break;
// one more byte was read; need to allocate a larger buffer
- if (capacity <= MAX_BUFFER_SIZE - capacity) {
- capacity = Math.max(capacity << 1, BUFFER_SIZE);
- } else {
- if (capacity == MAX_BUFFER_SIZE)
- throw new OutOfMemoryError("Required array size too large");
- capacity = MAX_BUFFER_SIZE;
- }
+ capacity = Math.max(ArraysSupport.newLength(capacity,
+ 1, /* minimum growth */
+ capacity /* preferred growth */),
+ BUFFER_SIZE);
buf = Arrays.copyOf(buf, capacity);
buf[nread++] = (byte)n;
}
@@ -3283,7 +3273,7 @@
if (sbc instanceof FileChannelImpl)
((FileChannelImpl) sbc).setUninterruptible();
long size = sbc.size();
- if (size > (long) MAX_BUFFER_SIZE)
+ if (size > (long) Integer.MAX_VALUE)
throw new OutOfMemoryError("Required array size too large");
return read(in, (int)size);
}
--- a/src/java.base/share/classes/java/util/AbstractCollection.java Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/util/AbstractCollection.java Tue May 21 18:40:29 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,8 @@
package java.util;
+import jdk.internal.util.ArraysSupport;
+
/**
* This class provides a skeletal implementation of the {@code Collection}
* interface, to minimize the effort required to implement this interface. <p>
@@ -204,14 +206,6 @@
}
/**
- * The maximum size of array to allocate.
- * Some VMs reserve some header words in an array.
- * Attempts to allocate larger arrays may result in
- * OutOfMemoryError: Requested array size exceeds VM limit
- */
- private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
- /**
* Reallocates the array being used within toArray when the iterator
* returned more elements than expected, and finishes filling it from
* the iterator.
@@ -223,29 +217,19 @@
*/
@SuppressWarnings("unchecked")
private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
- int i = r.length;
+ int len = r.length;
+ int i = len;
while (it.hasNext()) {
- int cap = r.length;
- if (i == cap) {
- int newCap = cap + (cap >> 1) + 1;
- // overflow-conscious code
- if (newCap - MAX_ARRAY_SIZE > 0)
- newCap = hugeCapacity(cap + 1);
- r = Arrays.copyOf(r, newCap);
+ if (i == len) {
+ len = ArraysSupport.newLength(len,
+ 1, /* minimum growth */
+ (len >> 1) + 1 /* preferred growth */);
+ r = Arrays.copyOf(r, len);
}
r[i++] = (T)it.next();
}
// trim if overallocated
- return (i == r.length) ? r : Arrays.copyOf(r, i);
- }
-
- private static int hugeCapacity(int minCapacity) {
- if (minCapacity < 0) // overflow
- throw new OutOfMemoryError
- ("Required array size too large");
- return (minCapacity > MAX_ARRAY_SIZE) ?
- Integer.MAX_VALUE :
- MAX_ARRAY_SIZE;
+ return (i == len) ? r : Arrays.copyOf(r, i);
}
// Modification Operations
--- a/src/java.base/share/classes/java/util/ArrayList.java Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/util/ArrayList.java Tue May 21 18:40:29 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -29,6 +29,7 @@
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import jdk.internal.access.SharedSecrets;
+import jdk.internal.util.ArraysSupport;
/**
* Resizable-array implementation of the {@code List} interface. Implements
@@ -219,14 +220,6 @@
}
/**
- * The maximum size of array to allocate (unless necessary).
- * Some VMs reserve some header words in an array.
- * Attempts to allocate larger arrays may result in
- * OutOfMemoryError: Requested array size exceeds VM limit
- */
- private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
- /**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
@@ -234,8 +227,15 @@
* @throws OutOfMemoryError if minCapacity is less than zero
*/
private Object[] grow(int minCapacity) {
- return elementData = Arrays.copyOf(elementData,
- newCapacity(minCapacity));
+ int oldCapacity = elementData.length;
+ if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+ int newCapacity = ArraysSupport.newLength(oldCapacity,
+ minCapacity - oldCapacity, /* minimum growth */
+ oldCapacity >> 1 /* preferred growth */);
+ return elementData = Arrays.copyOf(elementData, newCapacity);
+ } else {
+ return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
+ }
}
private Object[] grow() {
@@ -243,39 +243,6 @@
}
/**
- * Returns a capacity at least as large as the given minimum capacity.
- * Returns the current capacity increased by 50% if that suffices.
- * Will not return a capacity greater than MAX_ARRAY_SIZE unless
- * the given minimum capacity is greater than MAX_ARRAY_SIZE.
- *
- * @param minCapacity the desired minimum capacity
- * @throws OutOfMemoryError if minCapacity is less than zero
- */
- private int newCapacity(int minCapacity) {
- // overflow-conscious code
- int oldCapacity = elementData.length;
- int newCapacity = oldCapacity + (oldCapacity >> 1);
- if (newCapacity - minCapacity <= 0) {
- if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
- return Math.max(DEFAULT_CAPACITY, minCapacity);
- if (minCapacity < 0) // overflow
- throw new OutOfMemoryError();
- return minCapacity;
- }
- return (newCapacity - MAX_ARRAY_SIZE <= 0)
- ? newCapacity
- : hugeCapacity(minCapacity);
- }
-
- private static int hugeCapacity(int minCapacity) {
- if (minCapacity < 0) // overflow
- throw new OutOfMemoryError();
- return (minCapacity > MAX_ARRAY_SIZE)
- ? Integer.MAX_VALUE
- : MAX_ARRAY_SIZE;
- }
-
- /**
* Returns the number of elements in this list.
*
* @return the number of elements in this list
--- a/src/java.base/share/classes/java/util/PriorityQueue.java Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/util/PriorityQueue.java Tue May 21 18:40:29 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,7 @@
import java.util.function.Consumer;
import java.util.function.Predicate;
import jdk.internal.access.SharedSecrets;
+import jdk.internal.util.ArraysSupport;
/**
* An unbounded priority {@linkplain Queue queue} based on a priority heap.
@@ -282,14 +283,6 @@
}
/**
- * The maximum size of array to allocate.
- * Some VMs reserve some header words in an array.
- * Attempts to allocate larger arrays may result in
- * OutOfMemoryError: Requested array size exceeds VM limit
- */
- private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
- /**
* Increases the capacity of the array.
*
* @param minCapacity the desired minimum capacity
@@ -297,23 +290,13 @@
private void grow(int minCapacity) {
int oldCapacity = queue.length;
// Double size if small; else grow by 50%
- int newCapacity = oldCapacity + ((oldCapacity < 64) ?
- (oldCapacity + 2) :
- (oldCapacity >> 1));
- // overflow-conscious code
- if (newCapacity - MAX_ARRAY_SIZE > 0)
- newCapacity = hugeCapacity(minCapacity);
+ int newCapacity = ArraysSupport.newLength(oldCapacity,
+ minCapacity - oldCapacity, /* minimum growth */
+ oldCapacity < 64 ? oldCapacity + 2 : oldCapacity >> 1
+ /* preferred growth */);
queue = Arrays.copyOf(queue, newCapacity);
}
- private static int hugeCapacity(int minCapacity) {
- if (minCapacity < 0) // overflow
- throw new OutOfMemoryError();
- return (minCapacity > MAX_ARRAY_SIZE) ?
- Integer.MAX_VALUE :
- MAX_ARRAY_SIZE;
- }
-
/**
* Inserts the specified element into this priority queue.
*
--- a/src/java.base/share/classes/java/util/Vector.java Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/util/Vector.java Tue May 21 18:40:29 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,8 @@
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
+import jdk.internal.util.ArraysSupport;
+
/**
* The {@code Vector} class implements a growable array of
* objects. Like an array, it contains components that can be
@@ -242,14 +244,6 @@
}
/**
- * The maximum size of array to allocate (unless necessary).
- * Some VMs reserve some header words in an array.
- * Attempts to allocate larger arrays may result in
- * OutOfMemoryError: Requested array size exceeds VM limit
- */
- private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
- /**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
@@ -257,8 +251,12 @@
* @throws OutOfMemoryError if minCapacity is less than zero
*/
private Object[] grow(int minCapacity) {
- return elementData = Arrays.copyOf(elementData,
- newCapacity(minCapacity));
+ int oldCapacity = elementData.length;
+ int newCapacity = ArraysSupport.newLength(oldCapacity,
+ minCapacity - oldCapacity, /* minimum growth */
+ capacityIncrement > 0 ? capacityIncrement : oldCapacity
+ /* preferred growth */);
+ return elementData = Arrays.copyOf(elementData, newCapacity);
}
private Object[] grow() {
@@ -266,37 +264,6 @@
}
/**
- * Returns a capacity at least as large as the given minimum capacity.
- * Will not return a capacity greater than MAX_ARRAY_SIZE unless
- * the given minimum capacity is greater than MAX_ARRAY_SIZE.
- *
- * @param minCapacity the desired minimum capacity
- * @throws OutOfMemoryError if minCapacity is less than zero
- */
- private int newCapacity(int minCapacity) {
- // overflow-conscious code
- int oldCapacity = elementData.length;
- int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
- capacityIncrement : oldCapacity);
- if (newCapacity - minCapacity <= 0) {
- if (minCapacity < 0) // overflow
- throw new OutOfMemoryError();
- return minCapacity;
- }
- return (newCapacity - MAX_ARRAY_SIZE <= 0)
- ? newCapacity
- : hugeCapacity(minCapacity);
- }
-
- private static int hugeCapacity(int minCapacity) {
- if (minCapacity < 0) // overflow
- throw new OutOfMemoryError();
- return (minCapacity > MAX_ARRAY_SIZE) ?
- Integer.MAX_VALUE :
- MAX_ARRAY_SIZE;
- }
-
- /**
* Sets the size of this vector. If the new size is greater than the
* current size, new {@code null} items are added to the end of
* the vector. If the new size is less than the current size, all
--- a/src/java.base/share/classes/java/util/regex/Pattern.java Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/util/regex/Pattern.java Tue May 21 18:40:29 2019 -0700
@@ -43,6 +43,7 @@
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
+import jdk.internal.util.ArraysSupport;
/**
* A compiled representation of a regular expression.
@@ -2315,13 +2316,15 @@
}
}
- private void append(int ch, int len) {
- if (len >= buffer.length) {
- int[] tmp = new int[len+len];
- System.arraycopy(buffer, 0, tmp, 0, len);
- buffer = tmp;
- }
- buffer[len] = ch;
+ private void append(int ch, int index) {
+ int len = buffer.length;
+ if (index - len >= 0) {
+ len = ArraysSupport.newLength(len,
+ 1 + index - len, /* minimum growth */
+ len /* preferred growth */);
+ buffer = Arrays.copyOf(buffer, len);
+ }
+ buffer[index] = ch;
}
/**
--- a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java Tue May 21 18:40:29 2019 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -28,7 +28,9 @@
import jdk.internal.misc.Unsafe;
/**
- * Utility methods to find a mismatch between two primitive arrays.
+ * Utility methods to work with arrays. This includes a set of methods
+ * to find a mismatch between two primitive arrays. Also included is
+ * a method to calculate the new length of an array to be reallocated.
*
* <p>Array equality and lexicographical comparison can be built on top of
* array mismatch functionality.
@@ -571,4 +573,54 @@
return -1;
}
+
+ /**
+ * The maximum length of array to allocate (unless necessary).
+ * Some VMs reserve some header words in an array.
+ * Attempts to allocate larger arrays may result in
+ * {@code OutOfMemoryError: Requested array size exceeds VM limit}
+ */
+ public static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
+
+ /**
+ * Calculates a new array length given an array's current length, a preferred
+ * growth value, and a minimum growth value. If the preferred growth value
+ * is less than the minimum growth value, the minimum growth value is used in
+ * its place. If the sum of the current length and the preferred growth
+ * value does not exceed {@link #MAX_ARRAY_LENGTH}, that sum is returned.
+ * If the sum of the current length and the minimum growth value does not
+ * exceed {@code MAX_ARRAY_LENGTH}, then {@code MAX_ARRAY_LENGTH} is returned.
+ * If the sum does not overflow an int, then {@code Integer.MAX_VALUE} is
+ * returned. Otherwise, {@code OutOfMemoryError} is thrown.
+ *
+ * @param oldLength current length of the array (must be non negative)
+ * @param minGrowth minimum required growth of the array length (must be
+ * positive)
+ * @param prefGrowth preferred growth of the array length (ignored, if less
+ * then {@code minGrowth})
+ * @return the new length of the array
+ * @throws OutOfMemoryError if increasing {@code oldLength} by
+ * {@code minGrowth} overflows.
+ */
+ public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
+ // assert oldLength >= 0
+ // assert minGrowth > 0
+
+ int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
+ if (newLength - MAX_ARRAY_LENGTH <= 0) {
+ return newLength;
+ }
+ return hugeLength(oldLength, minGrowth);
+ }
+
+ private static int hugeLength(int oldLength, int minGrowth) {
+ int minLength = oldLength + minGrowth;
+ if (minLength < 0) { // overflow
+ throw new OutOfMemoryError("Required array length too large");
+ }
+ if (minLength <= MAX_ARRAY_LENGTH) {
+ return MAX_ARRAY_LENGTH;
+ }
+ return Integer.MAX_VALUE;
+ }
}