# HG changeset patch # User igerasim # Date 1557546883 25200 # Fri May 10 20:54:43 2019 -0700 # Node ID 27b7098787ba059f98cb693bfd05d460ce56d475 # Parent ad757676262c50fbb74790da778b3891e71b7fc6 [mq]: 8223593-Refactor-code-for-reallocating-storage diff --git a/src/java.base/share/classes/java/io/BufferedInputStream.java b/src/java.base/share/classes/java/io/BufferedInputStream.java --- a/src/java.base/share/classes/java/io/BufferedInputStream.java +++ b/src/java.base/share/classes/java/io/BufferedInputStream.java @@ -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 BufferedInputStream 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,8 @@ } 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.newCapacity(pos, 1, pos); if (nsz > marklimit) nsz = marklimit; byte[] nbuf = new byte[nsz]; @@ -248,6 +238,7 @@ } buffer = nbuf; } + } count = pos; int n = getInIfOpen().read(buffer, pos, buffer.length - pos); if (n > 0) diff --git a/src/java.base/share/classes/java/io/ByteArrayOutputStream.java b/src/java.base/share/classes/java/io/ByteArrayOutputStream.java --- a/src/java.base/share/classes/java/io/ByteArrayOutputStream.java +++ b/src/java.base/share/classes/java/io/ByteArrayOutputStream.java @@ -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 growAtLeastBy = minCapacity - oldCapacity; + if (growAtLeastBy > 0) { + buf = Arrays.copyOf(buf, ArraysSupport.newCapacity(oldCapacity, + growAtLeastBy, oldCapacity)); + } } /** diff --git a/src/java.base/share/classes/java/lang/StringLatin1.java b/src/java.base/share/classes/java/lang/StringLatin1.java --- a/src/java.base/share/classes/java/lang/StringLatin1.java +++ b/src/java.base/share/classes/java/lang/StringLatin1.java @@ -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,8 @@ 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.newCapacity(pos.length, + 1, p >> 1)); } pos[p] = j; i = j + targLen; diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -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,8 @@ : 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.newCapacity(pos.length, + 1, p >> 1)); } pos[p] = j; i = j + targLen; @@ -1554,15 +1548,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. diff --git a/src/java.base/share/classes/java/nio/file/Files.java b/src/java.base/share/classes/java/nio/file/Files.java --- a/src/java.base/share/classes/java/nio/file/Files.java +++ b/src/java.base/share/classes/java/nio/file/Files.java @@ -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,8 @@ 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.newCapacity(capacity, 1, capacity), + BUFFER_SIZE); buf = Arrays.copyOf(buf, capacity); buf[nread++] = (byte)n; } @@ -3283,7 +3271,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); } diff --git a/src/java.base/share/classes/java/util/AbstractCollection.java b/src/java.base/share/classes/java/util/AbstractCollection.java --- a/src/java.base/share/classes/java/util/AbstractCollection.java +++ b/src/java.base/share/classes/java/util/AbstractCollection.java @@ -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.

@@ -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,17 @@ */ @SuppressWarnings("unchecked") private static T[] finishToArray(T[] r, Iterator it) { - int i = r.length; + int cap = r.length; + int i = cap; 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); + cap = ArraysSupport.newCapacity(cap, 1, (cap >> 1) + 1); + r = Arrays.copyOf(r, cap); } 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 == cap) ? r : Arrays.copyOf(r, i); } // Modification Operations diff --git a/src/java.base/share/classes/java/util/ArrayList.java b/src/java.base/share/classes/java/util/ArrayList.java --- a/src/java.base/share/classes/java/util/ArrayList.java +++ b/src/java.base/share/classes/java/util/ArrayList.java @@ -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,14 @@ * @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.newCapacity(oldCapacity, + minCapacity - oldCapacity, oldCapacity >> 1); + return elementData = Arrays.copyOf(elementData, newCapacity); + } else { + return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)]; + } } private Object[] grow() { @@ -243,39 +242,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 diff --git a/src/java.base/share/classes/java/util/PriorityQueue.java b/src/java.base/share/classes/java/util/PriorityQueue.java --- a/src/java.base/share/classes/java/util/PriorityQueue.java +++ b/src/java.base/share/classes/java/util/PriorityQueue.java @@ -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,12 @@ 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.newCapacity(oldCapacity, + minCapacity - oldCapacity, + oldCapacity < 64 ? oldCapacity + 2 : oldCapacity >> 1); 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. * diff --git a/src/java.base/share/classes/java/util/Vector.java b/src/java.base/share/classes/java/util/Vector.java --- a/src/java.base/share/classes/java/util/Vector.java +++ b/src/java.base/share/classes/java/util/Vector.java @@ -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,11 @@ * @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.newCapacity(oldCapacity, + minCapacity - oldCapacity, + capacityIncrement > 0 ? capacityIncrement : oldCapacity); + return elementData = Arrays.copyOf(elementData, newCapacity); } private Object[] grow() { @@ -266,37 +263,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 diff --git a/src/java.base/share/classes/java/util/regex/Pattern.java b/src/java.base/share/classes/java/util/regex/Pattern.java --- a/src/java.base/share/classes/java/util/regex/Pattern.java +++ b/src/java.base/share/classes/java/util/regex/Pattern.java @@ -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,14 @@ } } - 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 oldCapacity = buffer.length; + if (index - oldCapacity >= 0) { + int newCapacity = ArraysSupport.newCapacity(oldCapacity, + 1 + index - oldCapacity, oldCapacity); + buffer = Arrays.copyOf(buffer, newCapacity); + } + buffer[index] = ch; } /** diff --git a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java --- a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java +++ b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java @@ -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 size of an array to be reallocated * *

Array equality and lexicographical comparison can be built on top of * array mismatch functionality. @@ -571,4 +573,49 @@ return -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 + */ + public static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; + + /** + * Calculates new size of an array to be reallocated. + * + * The result will be at least {@code X = oldCapacity + growAtLeastBy}. + * The result can be greater (normally, up to + * {@code oldCapacity + preferredGrowBy}). + * This function will not return values greater than + * {@link #MAX_ARRAY_SIZE} unless the minimum required size + * {@code X} is greater. + * + * @param oldCapacity current size of the array (must be non negative) + * @param growAtLeastBy minimum required growth of the array size (must + be positive) + * @param preferredGrowBy preferred growth of the array size + * @return the new size of the array + * @throws OutOfMemoryError if increasing {@code oldCapacity) by + * {@code growAtLeastBy} overflows. + */ + public static int newCapacity(int oldCapacity, + int growAtLeastBy, + int preferredGrowBy) { + // assert oldCapacity >= 0 + // assert growAtLeastBy > 0 + + int newCapacity = oldCapacity + Math.max(growAtLeastBy, preferredGrowBy); + return (newCapacity - MAX_ARRAY_SIZE > 0) + ? hugeCapacity(oldCapacity + growAtLeastBy) : newCapacity; + } + + 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; + } }