# HG changeset patch # User igerasim # Date 1448468068 -10800 # Wed Nov 25 19:14:28 2015 +0300 # Node ID 71ad39df4f44b5f88262aa1729911dde9e852379 # Parent fc3266c221b721a195d9ca5a0adae438884812e1 [mq]: XXXXXXX-ASB-shared-value diff --git a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java --- a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java +++ b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java @@ -70,6 +70,11 @@ int count; /** + * Whether value array is shared with a String. + */ + transient boolean shared; + + /** * This no-arg constructor is necessary for serialization of subclasses. */ AbstractStringBuilder() { @@ -88,6 +93,15 @@ } } + byte[] unshareValue() { + if (shared) { + // assert (count << coder) == value.length + value = Arrays.copyOf(value, value.length); + shared = false; + } + return value; + } + /** * Returns the length (character count). * @@ -166,6 +180,7 @@ newCapacity = StringUTF16.MAX_LENGTH; } this.value = Arrays.copyOf(value, newCapacity << coder); + this.shared = false; } /** @@ -180,6 +195,7 @@ StringLatin1.inflateSB(value, buf, 0, count); this.value = buf; this.coder = UTF16; + this.shared = false; } /** @@ -225,13 +241,15 @@ if (newLength < 0) { throw new StringIndexOutOfBoundsException(newLength); } - ensureCapacityInternal(newLength); if (count < newLength) { + ensureCapacityInternal(newLength); if (isLatin1()) { StringLatin1.fillNull(value, count, newLength); } else { StringUTF16.fillNull(value, count, newLength); } + } else if (newLength < count) { + unshareValue(); } count = newLength; } @@ -437,10 +455,13 @@ public void setCharAt(int index, char ch) { checkIndex(index, count); if (isLatin1() && StringLatin1.canEncode(ch)) { + final byte[] value = unshareValue(); value[index] = (byte)ch; } else { if (isLatin1()) { inflate(); + } else { + unshareValue(); } StringUTF16.putCharSB(value, index, ch); } @@ -826,6 +847,7 @@ checkRangeSIOOBE(start, end, count); int len = end - start; if (len > 0) { + unshareValue(); shift(end, -len); count -= len; } @@ -876,10 +898,7 @@ * {@code length()}. */ public AbstractStringBuilder deleteCharAt(int index) { - checkIndex(index, count); - shift(index + 1, -1); - count--; - return this; + return delete(index, index + 1); } /** @@ -909,6 +928,7 @@ int len = str.length(); int newCount = count + len - (end - start); ensureCapacityInternal(newCount); + unshareValue(); shift(end, newCount - count); count = newCount; putStringAt(start, str); @@ -1465,7 +1485,7 @@ * @return a reference to this object. */ public AbstractStringBuilder reverse() { - byte[] val = this.value; + byte[] val = unshareValue(); int count = this.count; int coder = this.coder; int n = count - 1; @@ -1477,7 +1497,7 @@ val[k] = cj; } } else { - checkOffset(count, val.length >> 1); + checkOffset(count, value.length >> 1); boolean hasSurrogates = false; for (int j = (n-1) >> 1; j >= 0; j--) { int k = n - j; @@ -1575,7 +1595,7 @@ void getBytes(byte dst[], int dstBegin, byte coder) { if (this.coder == coder) { System.arraycopy(value, 0, dst, dstBegin << coder, count << coder); - } else { // this.coder == LATIN && coder == UTF16 + } else { // this.coder == LATIN1 && coder == UTF16 StringLatin1.inflateSB(value, dst, dstBegin, count); } } diff --git a/src/java.base/share/classes/java/lang/StringBuffer.java b/src/java.base/share/classes/java/lang/StringBuffer.java --- a/src/java.base/share/classes/java/lang/StringBuffer.java +++ b/src/java.base/share/classes/java/lang/StringBuffer.java @@ -100,12 +100,6 @@ implements java.io.Serializable, CharSequence { - /** - * A cache of the last value returned by toString. Cleared - * whenever the StringBuffer is modified. - */ - private transient String toStringCache; - /** use serialVersionUID from JDK 1.0.2 for interoperability */ static final long serialVersionUID = 3388685877147921107L; @@ -192,7 +186,6 @@ */ @Override public synchronized void setLength(int newLength) { - toStringCache = null; super.setLength(newLength); } @@ -257,13 +250,11 @@ */ @Override public synchronized void setCharAt(int index, char ch) { - toStringCache = null; super.setCharAt(index, ch); } @Override public synchronized StringBuffer append(Object obj) { - toStringCache = null; super.append(String.valueOf(obj)); return this; } @@ -271,7 +262,6 @@ @Override @HotSpotIntrinsicCandidate public synchronized StringBuffer append(String str) { - toStringCache = null; super.append(str); return this; } @@ -301,7 +291,6 @@ * @since 1.4 */ public synchronized StringBuffer append(StringBuffer sb) { - toStringCache = null; super.append(sb); return this; } @@ -311,7 +300,6 @@ */ @Override synchronized StringBuffer append(AbstractStringBuilder asb) { - toStringCache = null; super.append(asb); return this; } @@ -339,7 +327,6 @@ */ @Override public synchronized StringBuffer append(CharSequence s) { - toStringCache = null; super.append(s); return this; } @@ -351,14 +338,12 @@ @Override public synchronized StringBuffer append(CharSequence s, int start, int end) { - toStringCache = null; super.append(s, start, end); return this; } @Override public synchronized StringBuffer append(char[] str) { - toStringCache = null; super.append(str); return this; } @@ -368,14 +353,12 @@ */ @Override public synchronized StringBuffer append(char[] str, int offset, int len) { - toStringCache = null; super.append(str, offset, len); return this; } @Override public synchronized StringBuffer append(boolean b) { - toStringCache = null; super.append(b); return this; } @@ -383,7 +366,6 @@ @Override @HotSpotIntrinsicCandidate public synchronized StringBuffer append(char c) { - toStringCache = null; super.append(c); return this; } @@ -391,7 +373,6 @@ @Override @HotSpotIntrinsicCandidate public synchronized StringBuffer append(int i) { - toStringCache = null; super.append(i); return this; } @@ -401,28 +382,24 @@ */ @Override public synchronized StringBuffer appendCodePoint(int codePoint) { - toStringCache = null; super.appendCodePoint(codePoint); return this; } @Override public synchronized StringBuffer append(long lng) { - toStringCache = null; super.append(lng); return this; } @Override public synchronized StringBuffer append(float f) { - toStringCache = null; super.append(f); return this; } @Override public synchronized StringBuffer append(double d) { - toStringCache = null; super.append(d); return this; } @@ -433,7 +410,6 @@ */ @Override public synchronized StringBuffer delete(int start, int end) { - toStringCache = null; super.delete(start, end); return this; } @@ -444,7 +420,6 @@ */ @Override public synchronized StringBuffer deleteCharAt(int index) { - toStringCache = null; super.deleteCharAt(index); return this; } @@ -455,7 +430,6 @@ */ @Override public synchronized StringBuffer replace(int start, int end, String str) { - toStringCache = null; super.replace(start, end, str); return this; } @@ -495,7 +469,6 @@ public synchronized StringBuffer insert(int index, char[] str, int offset, int len) { - toStringCache = null; super.insert(index, str, offset, len); return this; } @@ -505,7 +478,6 @@ */ @Override public synchronized StringBuffer insert(int offset, Object obj) { - toStringCache = null; super.insert(offset, String.valueOf(obj)); return this; } @@ -515,7 +487,6 @@ */ @Override public synchronized StringBuffer insert(int offset, String str) { - toStringCache = null; super.insert(offset, str); return this; } @@ -525,7 +496,6 @@ */ @Override public synchronized StringBuffer insert(int offset, char[] str) { - toStringCache = null; super.insert(offset, str); return this; } @@ -538,7 +508,6 @@ public StringBuffer insert(int dstOffset, CharSequence s) { // Note, synchronization achieved via invocations of other StringBuffer methods // after narrowing of s to specific type - // Ditto for toStringCache clearing super.insert(dstOffset, s); return this; } @@ -551,7 +520,6 @@ public synchronized StringBuffer insert(int dstOffset, CharSequence s, int start, int end) { - toStringCache = null; super.insert(dstOffset, s, start, end); return this; } @@ -563,7 +531,6 @@ public StringBuffer insert(int offset, boolean b) { // Note, synchronization achieved via invocation of StringBuffer insert(int, String) // after conversion of b to String by super class method - // Ditto for toStringCache clearing super.insert(offset, b); return this; } @@ -573,7 +540,6 @@ */ @Override public synchronized StringBuffer insert(int offset, char c) { - toStringCache = null; super.insert(offset, c); return this; } @@ -585,7 +551,6 @@ public StringBuffer insert(int offset, int i) { // Note, synchronization achieved via invocation of StringBuffer insert(int, String) // after conversion of i to String by super class method - // Ditto for toStringCache clearing super.insert(offset, i); return this; } @@ -597,7 +562,6 @@ public StringBuffer insert(int offset, long l) { // Note, synchronization achieved via invocation of StringBuffer insert(int, String) // after conversion of l to String by super class method - // Ditto for toStringCache clearing super.insert(offset, l); return this; } @@ -609,7 +573,6 @@ public StringBuffer insert(int offset, float f) { // Note, synchronization achieved via invocation of StringBuffer insert(int, String) // after conversion of f to String by super class method - // Ditto for toStringCache clearing super.insert(offset, f); return this; } @@ -621,7 +584,6 @@ public StringBuffer insert(int offset, double d) { // Note, synchronization achieved via invocation of StringBuffer insert(int, String) // after conversion of d to String by super class method - // Ditto for toStringCache clearing super.insert(offset, d); return this; } @@ -665,7 +627,6 @@ */ @Override public synchronized StringBuffer reverse() { - toStringCache = null; super.reverse(); return this; } @@ -673,12 +634,15 @@ @Override @HotSpotIntrinsicCandidate public synchronized String toString() { - if (toStringCache == null) { - return toStringCache = - isLatin1() ? StringLatin1.newString(value, 0, count) - : StringUTF16.newString(value, 0, count); + final byte[] value = this.value; + if (isLatin1()) { + if ((count << coder) < value.length) { + return StringLatin1.newString(value, 0, count); + } + shared = true; + return new String(value, String.LATIN1); } - return new String(toStringCache); + return StringUTF16.newString(value, 0, count); } /** @@ -728,6 +692,7 @@ char[] val = (char[])fields.get("value", null); initBytes(val, 0, val.length); count = fields.get("count", 0); + shared = false; } synchronized void getBytes(byte dst[], int dstBegin, byte coder) { diff --git a/src/java.base/share/classes/java/lang/StringBuilder.java b/src/java.base/share/classes/java/lang/StringBuilder.java --- a/src/java.base/share/classes/java/lang/StringBuilder.java +++ b/src/java.base/share/classes/java/lang/StringBuilder.java @@ -411,9 +411,15 @@ @Override @HotSpotIntrinsicCandidate public String toString() { - // Create a copy, don't share the array - return isLatin1() ? StringLatin1.newString(value, 0, count) - : StringUTF16.newStringSB(value, 0, count); + final byte[] value = this.value; + if (isLatin1()) { + if ((count << coder) < value.length) { + return StringLatin1.newString(value, 0, count); + } + shared = true; + return new String(value, String.LATIN1); + } + return StringUTF16.newStringSB(value, 0, count); } /** @@ -450,6 +456,7 @@ count = s.readInt(); char[] val = (char[]) s.readObject(); initBytes(val, 0, val.length); + shared = false; } }