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);
   }
 

Reply via email to