This adds a feature to CPStringBuilder so that it knows
when its array has been shared with a String object. Once
this has happened, any future write operations will allocate
a new array and reset the flag. This fixes the issue in
PR36147.
ChangeLog:
2008-05-11 Andrew John Hughes <[EMAIL PROTECTED]>
PR classpath/36147
* gnu/java/lang/CPStringBuilder.java:
(allocated): New flag to mark whether or
not the array has been allocated to a String object.
(ensureCapacity(int)): Removed.
(ensureCapacity_unsynchronized(int)): Renamed to
ensureCapacity, and creates an array when allocated
is true.
(allocateArray(int)): Added.
(trimToSize()): Use allocateArray method.
(toString()): Set allocated to true;
(substring(int,int)): Likewise.
--
Andrew :)
Support Free Java!
Contribute to GNU Classpath and the OpenJDK
http://www.gnu.org/software/classpath
http://openjdk.java.net
PGP Key: 94EFD9D8 (http://subkeys.pgp.net)
Fingerprint = F8EF F1EA 401E 2E60 15FA 7927 142C 2591 94EF D9D8
Index: gnu/java/lang/CPStringBuilder.java
===================================================================
RCS file: /sources/classpath/classpath/gnu/java/lang/CPStringBuilder.java,v
retrieving revision 1.7
diff -u -r1.7 CPStringBuilder.java
--- gnu/java/lang/CPStringBuilder.java 11 May 2008 15:44:42 -0000 1.7
+++ gnu/java/lang/CPStringBuilder.java 11 May 2008 18:40:56 -0000
@@ -71,6 +71,19 @@
private char[] value;
/**
+ * A flag to denote whether the string being created has been
+ * allocated to a [EMAIL PROTECTED] String} object. On construction,
+ * the character array, [EMAIL PROTECTED] #value} is referenced only
+ * by this class. Once [EMAIL PROTECTED] #toString()},
+ * [EMAIL PROTECTED] #substring(int)} or [EMAIL PROTECTED]
#substring(int,int)}
+ * are called, the array is also referenced by a [EMAIL PROTECTED] String}
+ * object and this flag is set. Subsequent modifications to
+ * this buffer cause a new array to be allocated and the flag
+ * to be reset.
+ */
+ private boolean allocated = false;
+
+ /**
* The default capacity of a buffer.
* This can be configured using gnu.classpath.cpstringbuilder.capacity
*/
@@ -172,21 +185,6 @@
}
/**
- * Increase the capacity of this <code>CPStringBuilder</code>. This will
- * ensure that an expensive growing operation will not occur until
- * <code>minimumCapacity</code> is reached. The buffer is grown to the
- * larger of <code>minimumCapacity</code> and
- * <code>capacity() * 2 + 2</code>, if it is not already large enough.
- *
- * @param minimumCapacity the new capacity
- * @see #capacity()
- */
- public void ensureCapacity(int minimumCapacity)
- {
- ensureCapacity_unsynchronized(minimumCapacity);
- }
-
- /**
* Set the length of this StringBuffer. If the new length is greater than
* the current length, all the new characters are set to '\0'. If the new
* length is less than the current length, the first <code>newLength</code>
@@ -205,9 +203,9 @@
int valueLength = value.length;
- /* Always call ensureCapacity_unsynchronized in order to preserve
+ /* Always call ensureCapacity in order to preserve
copy-on-write semantics. */
- ensureCapacity_unsynchronized(newLength);
+ ensureCapacity(newLength);
if (newLength < valueLength)
{
@@ -309,7 +307,7 @@
if (index < 0 || index >= count)
throw new StringIndexOutOfBoundsException(index);
// Call ensureCapacity to enforce copy-on-write.
- ensureCapacity_unsynchronized(count);
+ ensureCapacity(count);
value[index] = ch;
}
@@ -340,7 +338,7 @@
if (str == null)
str = "null";
int len = str.length();
- ensureCapacity_unsynchronized(count + len);
+ ensureCapacity(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
@@ -402,7 +400,7 @@
{
if (offset < 0 || count < 0 || offset > data.length - count)
throw new StringIndexOutOfBoundsException();
- ensureCapacity_unsynchronized(this.count + count);
+ ensureCapacity(this.count + count);
System.arraycopy(data, offset, value, this.count, count);
this.count += count;
return this;
@@ -430,7 +428,7 @@
*/
public CPStringBuilder append(char ch)
{
- ensureCapacity_unsynchronized(count + 1);
+ ensureCapacity(count + 1);
value[count++] = ch;
return this;
}
@@ -465,7 +463,7 @@
return append("null");
if (end - start > 0)
{
- ensureCapacity_unsynchronized(count + end - start);
+ ensureCapacity(count + end - start);
for (; start < end; ++start)
value[count++] = seq.charAt(start);
}
@@ -542,7 +540,7 @@
public CPStringBuilder appendCodePoint(int code)
{
int len = Character.charCount(code);
- ensureCapacity_unsynchronized(count + len);
+ ensureCapacity(count + len);
Character.toChars(code, value, count);
count += len;
return this;
@@ -565,7 +563,7 @@
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count;
- ensureCapacity_unsynchronized(count);
+ ensureCapacity(count);
if (count - end != 0)
System.arraycopy(value, end, value, start, count - end);
count -= end - start;
@@ -607,7 +605,7 @@
int len = str.length();
// Calculate the difference in 'count' after the replace.
int delta = len - (end > count ? count : end) + start;
- ensureCapacity_unsynchronized(count + delta);
+ ensureCapacity(count + delta);
if (delta != 0 && end < count)
System.arraycopy(value, end, value, end + delta, count - end);
@@ -635,7 +633,7 @@
if (offset < 0 || offset > count || len < 0
|| str_offset < 0 || str_offset > str.length - len)
throw new StringIndexOutOfBoundsException();
- ensureCapacity_unsynchronized(count + len);
+ ensureCapacity(count + len);
System.arraycopy(value, offset, value, offset + len, count - offset);
System.arraycopy(str, str_offset, value, offset, len);
count += len;
@@ -675,7 +673,7 @@
if (str == null)
str = "null";
int len = str.length();
- ensureCapacity_unsynchronized(count + len);
+ ensureCapacity(count + len);
System.arraycopy(value, offset, value, offset + len, count - offset);
str.getChars(0, len, value, offset);
count += len;
@@ -721,7 +719,7 @@
if (start < 0 || end < 0 || start > end || end > sequence.length())
throw new IndexOutOfBoundsException();
int len = end - start;
- ensureCapacity_unsynchronized(count + len);
+ ensureCapacity(count + len);
System.arraycopy(value, offset, value, offset + len, count - offset);
for (int i = start; i < end; ++i)
value[offset++] = sequence.charAt(i);
@@ -773,7 +771,7 @@
{
if (offset < 0 || offset > count)
throw new StringIndexOutOfBoundsException(offset);
- ensureCapacity_unsynchronized(count + 1);
+ ensureCapacity(count + 1);
System.arraycopy(value, offset, value, offset + 1, count - offset);
value[offset] = ch;
count++;
@@ -928,7 +926,7 @@
public CPStringBuilder reverse()
{
// Call ensureCapacity to enforce copy-on-write.
- ensureCapacity_unsynchronized(count);
+ ensureCapacity(count);
for (int i = count >> 1, j = count - i; --i >= 0; ++j)
{
char c = value[i];
@@ -955,11 +953,7 @@
// If we save more than 200 characters, shrink.
// If we save more than 1/4 of the buffer, shrink.
if (wouldSave > 200 || wouldSave * 4 > value.length)
- {
- char[] newValue = new char[count];
- System.arraycopy(value, 0, newValue, 0, count);
- value = newValue;
- }
+ allocateArray(count);
}
/**
@@ -1040,27 +1034,41 @@
/**
* Increase the capacity of this <code>StringBuilder</code>. This will
- * ensure that an expensive growing operation will not occur until
- * <code>minimumCapacity</code> is reached. The buffer is grown to the
- * larger of <code>minimumCapacity</code> and
+ * ensure that an expensive growing operation will not occur until either
+ * <code>minimumCapacity</code> is reached or the array has been allocated.
+ * The buffer is grown to the larger of <code>minimumCapacity</code> and
* <code>capacity() * 2 + 2</code>, if it is not already large enough.
*
* @param minimumCapacity the new capacity
* @see #capacity()
*/
- protected void ensureCapacity_unsynchronized(int minimumCapacity)
+ public void ensureCapacity(int minimumCapacity)
{
- if (minimumCapacity > value.length)
+ if (allocated || minimumCapacity > value.length)
{
int max = value.length * 2 + 2;
minimumCapacity = (minimumCapacity < max ? max : minimumCapacity);
- char[] nb = new char[minimumCapacity];
- System.arraycopy(value, 0, nb, 0, count);
- value = nb;
+ allocateArray(minimumCapacity);
}
}
/**
+ * Allocates a new character array. This method is triggered when
+ * a write is attempted after the array has been passed to a
+ * [EMAIL PROTECTED] String} object, so that the builder does not modify
+ * the immutable [EMAIL PROTECTED] String}.
+ *
+ * @param capacity the size of the new array.
+ */
+ private void allocateArray(int capacity)
+ {
+ char[] nb = new char[capacity];
+ System.arraycopy(value, 0, nb, 0, count);
+ value = nb;
+ allocated = false;
+ }
+
+ /**
* Get the length of the <code>String</code> this <code>StringBuilder</code>
* would create. Not to be confused with the <em>capacity</em> of the
* <code>StringBuilder</code>.
@@ -1123,6 +1131,7 @@
int len = endIndex - beginIndex;
if (len == 0)
return "";
+ allocated = true;
return VMCPStringBuilder.toString(value, beginIndex, len);
}
@@ -1136,6 +1145,7 @@
*/
public String toString()
{
+ allocated = true;
return VMCPStringBuilder.toString(value, 0, count);
}