--- old/src/java.base/share/classes/java/lang/AbstractStringBuilder.java 2015-05-28 12:50:43.767664507 +0200 +++ new/src/java.base/share/classes/java/lang/AbstractStringBuilder.java 2015-05-28 12:50:43.709664406 +0200 @@ -25,6 +25,7 @@ package java.lang; +import sun.misc.ConstantLengthCharSequence; import sun.misc.FloatingDecimal; import java.util.Arrays; import java.util.Spliterator; @@ -460,10 +461,24 @@ return this.append((String)s); if (s instanceof AbstractStringBuilder) return this.append((AbstractStringBuilder)s); + if (s instanceof ConstantLengthCharSequence) + return this.append((ConstantLengthCharSequence)s); return this.append(s, 0, s.length()); } + /** + * @since 1.9 + */ + AbstractStringBuilder append(ConstantLengthCharSequence ccs) { + // assert ccs != null; + int len = ccs.length(); + ensureCapacityInternal(count + len); + ccs.getChars(0, len, value, count); + count += len; + return this; + } + private AbstractStringBuilder appendNull() { int c = count; ensureCapacityInternal(c + 4); @@ -515,8 +530,12 @@ + s.length()); int len = end - start; ensureCapacityInternal(count + len); - for (int i = start, j = count; i < end; i++, j++) - value[j] = s.charAt(i); + if (s instanceof ConstantLengthCharSequence) { + ((ConstantLengthCharSequence)s).getChars(start, end, value, count); + } else { + for (int i = start, j = count; i < end; i++, j++) + value[j] = s.charAt(i); + } count += len; return this; } @@ -1077,10 +1096,28 @@ s = "null"; if (s instanceof String) return this.insert(dstOffset, (String)s); + if (s instanceof ConstantLengthCharSequence) + return this.insert(dstOffset, (ConstantLengthCharSequence)s); return this.insert(dstOffset, s, 0, s.length()); } /** + * @since 1.9 + */ + AbstractStringBuilder insert(int offset, ConstantLengthCharSequence ccs) { + if ((offset < 0) || (offset > length())) + throw new StringIndexOutOfBoundsException(offset); + if (ccs == null) + ccs = "null"; + int len = ccs.length(); + ensureCapacityInternal(count + len); + System.arraycopy(value, offset, value, offset + len, count - offset); + ccs.getChars(0, len, value, offset); + count += len; + return this; + } + + /** * Inserts a subsequence of the specified {@code CharSequence} into * this sequence. *

@@ -1124,8 +1161,8 @@ * {@code start} is greater than {@code end} or * {@code end} is greater than {@code s.length()} */ - public AbstractStringBuilder insert(int dstOffset, CharSequence s, - int start, int end) { + public AbstractStringBuilder insert(int dstOffset, CharSequence s, + int start, int end) { if (s == null) s = "null"; if ((dstOffset < 0) || (dstOffset > this.length())) @@ -1138,8 +1175,12 @@ ensureCapacityInternal(count + len); System.arraycopy(value, dstOffset, value, dstOffset + len, count - dstOffset); - for (int i=start; i, CharSequence { + implements java.io.Serializable, Comparable, CharSequence, + sun.misc.ConstantLengthCharSequence { /** The value is used for character storage. */ private final char value[]; @@ -789,6 +790,14 @@ } /** + * Returns a character array representing characters of this String. + * It may be shared with the String, so it should not be modified. + */ + char[] getCharsShared() { + return value; + } + + /** * Copies characters from this string into the destination character * array. *

--- old/src/java.base/share/classes/java/lang/StringBuffer.java 2015-05-28 12:50:44.193665251 +0200 +++ new/src/java.base/share/classes/java/lang/StringBuffer.java 2015-05-28 12:50:44.137665153 +0200 @@ -25,6 +25,8 @@ package java.lang; +import sun.misc.ConstantLengthCharSequence; + import java.util.Arrays; /** @@ -346,6 +348,16 @@ } /** + * @since 1.9 + */ + @Override + synchronized StringBuffer append(ConstantLengthCharSequence ccs) { + toStringCache = null; + super.append(ccs); + return this; + } + + /** * @throws IndexOutOfBoundsException {@inheritDoc} * @since 1.5 */ @@ -542,6 +554,16 @@ return this; } + /** + * @since 1.9 + */ + @Override + synchronized StringBuffer insert(int offset, ConstantLengthCharSequence ccs) { + toStringCache = null; + super.insert(offset, ccs); + return this; + } + /** * @throws IndexOutOfBoundsException {@inheritDoc} * @since 1.5 --- old/src/java.base/share/classes/java/util/StringJoiner.java 2015-05-28 12:50:44.394665602 +0200 +++ new/src/java.base/share/classes/java/util/StringJoiner.java 2015-05-28 12:50:44.338665505 +0200 @@ -24,6 +24,7 @@ */ package java.util; +import sun.misc.ConstantLengthCharSequence; import sun.misc.JavaLangAccess; import sun.misc.SharedSecrets; @@ -70,10 +71,10 @@ private final String delimiter; private final String suffix; - /** Contains all the string components added so far. */ - private String[] elts; + /** Contains all the components added so far. */ + private ConstantLengthCharSequence[] elts; - /** The number of string components added so far. */ + /** The number of components added so far. */ private int size; /** Total length in chars so far, excluding prefix and suffix. */ @@ -152,9 +153,10 @@ return this; } - private static int getChars(String s, char[] chars, int start) { - int len = s.length(); - s.getChars(0, len, chars, start); + private static int getChars(ConstantLengthCharSequence ccs, + char[] chars, int start) { + int len = ccs.length(); + ccs.getChars(0, len, chars, start); return len; } @@ -168,7 +170,7 @@ */ @Override public String toString() { - final String[] elts = this.elts; + final ConstantLengthCharSequence[] elts = this.elts; if (elts == null && emptyValue != null) { return emptyValue; } @@ -176,7 +178,7 @@ final int addLen = prefix.length() + suffix.length(); if (addLen == 0) { compactElts(); - return size == 0 ? "" : elts[0]; + return size == 0 ? "" : elts[0].toString(); } final String delimiter = this.delimiter; final char[] chars = new char[len + addLen]; @@ -201,9 +203,10 @@ * @return a reference to this {@code StringJoiner} */ public StringJoiner add(CharSequence newElement) { - final String elt = String.valueOf(newElement); + final ConstantLengthCharSequence elt = + ConstantLengthCharSequence.valueFor(newElement); if (elts == null) { - elts = new String[8]; + elts = new ConstantLengthCharSequence[8]; } else { if (size == elts.length) elts = Arrays.copyOf(elts, 2 * size); --- /dev/null 2015-04-10 13:19:24.023596515 +0200 +++ new/src/java.base/share/classes/java/lang/ArrayCharSequence.java 2015-05-28 12:50:44.533665845 +0200 @@ -0,0 +1,217 @@ +package java.lang; + +import sun.misc.ConstantLengthCharSequence; + +/** + * ArrayCharSequence is a simple {@link CharSequence} implementation based on the + * primitive character array. It represents a view of the underlying sub-array + * bounded by {@code offset} and {@code length}. + * The underlying array can not be mutated through the ArrayCharSequence, + * but direct changes to the underlying array are visible through the ArrayCharSequence. + * ArrayCharSequence is not {@link java.io.Serializable}. + * + * @since 1.9 + */ +public final class ArrayCharSequence implements CharSequence, ConstantLengthCharSequence { + private final char[] chars; + private final int offset, length; + + /** + * Constructs an {@code ArrayCharSequence} representing the view of the + * characters of the given {@code chars} array. Any changes to the + * array are visible through the sequence, but the array can not be modified + * through the sequence. + * + * @param chars the character array to construct the sequence with + * @throws NullPointerException if given {@code chars} array is {@code null} + */ + public ArrayCharSequence(char[] chars) { + this(chars, 0, chars.length, true); + } + + /** + * Constructs an {@code ArrayCharSequence} representing the sub-array view of + * the given {@code chars} array. The sequence starts with the {@code char} + * value at the specified index and ends with the {@code char} value at index + * {@code end - 1}. The length (in {@code char}s) of the constructed sequence + * is {@code end - start}, so if {@code start == end} then constructed sequence + * is empty. Any changes to the given {@code chars} array that pertain to the + * represented range are visible through the sequence, but the array can not + * be modified through the sequence. + * + * @param chars the character array to construct the sequence with + * @param start the start index, inclusive + * @param end the end index, exclusive + * @throws NullPointerException if given {@code chars} array is {@code null} + * @throws IndexOutOfBoundsException if {@code start} or {@code end} are negative, + * if {@code end} is greater than + * {@code chars.length}, or if {@code start} + * is greater than {@code end} + */ + public ArrayCharSequence(char[] chars, int start, int end) { + this(chars, + checkSubSequenceIndexReturnStart(start, end, chars.length), + end - start, true); + } + + /** + * Constructs an {@code ArrayCharSequence} representing the + * characters of the given {@code string}. The constructed sequence may + * share the character storage with the given {@code string}. + * + * @param string the {@code String} to construct the sequence from + * @throws NullPointerException if given {@code string} is {@code null} + */ + public ArrayCharSequence(String string) { + this(string.getCharsShared(), 0, string.length(), true); + } + + /** + * Constructs an {@code ArrayCharSequence} representing the sub-sequence of + * the given {@code string}. The sequence starts with the {@code char} + * value at the specified index and ends with the {@code char} value at index + * {@code end - 1}. The length (in {@code char}s) of the constructed sequence + * is {@code end - start}, so if {@code start == end} then constructed sequence + * is empty. The constructed sequence may share the character storage with + * the given {@code string}. + * + * @param string the {@code String} to construct the sequence from + * @param start the start index, inclusive + * @param end the end index, exclusive + * @throws NullPointerException if given {@code chars} array is {@code null} + * @throws IndexOutOfBoundsException if {@code start} or {@code end} are negative, + * if {@code end} is greater than + * {@code string.length()}, or if {@code start} + * is greater than {@code end} + */ + public ArrayCharSequence(String string, int start, int end) { + this(string.getCharsShared(), + checkSubSequenceIndexReturnStart(start, end, string.length()), + end - start, true); + } + + // designated unchecked constructor + private ArrayCharSequence(char[] chars, int offset, int length, boolean unchecked) { + this.chars = chars; + this.offset = offset; + this.length = length; + } + + /** + * @inheritDoc + */ + @Override + public int length() { + return length; + } + + /** + * @inheritDoc + */ + @Override + public char charAt(int index) { + if (index < 0 || index >= length) { + throw new IndexOutOfBoundsException("index: " + index); + } + return chars[offset + index]; + } + + /** + * Returns an {@code ArrayCharSequence} that is a subsequence of this sequence. + * The subsequence starts with the {@code char} value at the specified index and + * ends with the {@code char} value at index {@code end - 1}. The length + * (in {@code char}s) of the + * returned sequence is {@code end - start}, so if {@code start == end} + * then an empty sequence is returned. + *

+ * The returned {@code ArrayCharSequence} + * represents a subsequence view of this sequence, so any changes to the + * underlying array of this sequence that pertain to the range of the returned + * sub-sequence are also visible in the returned sub-sequence. + * + * @param start the start index, inclusive + * @param end the end index, exclusive + * @return the specified subsequence + * @throws IndexOutOfBoundsException if {@code start} or {@code end} are negative, + * if {@code end} is greater than {@code length()}, + * or if {@code start} is greater than {@code end} + * @implSpec The returned {@code ArrayCharSequence} shares the underlying character storage + * with this sequence and is obtainable in constant time. It is therefore advisable + * to use it only as intermediate result of some computation that is not + * retained for extended period of time, to allow the underlying shared character + * storage to be reclaimed. + */ + @Override + public ArrayCharSequence subSequence(int start, int end) { + checkSubSequenceIndexReturnStart(start, end, length); + if (start == 0 && end == length) { + return this; + } else { + return new ArrayCharSequence(chars, offset + start, end - start); + } + } + + /** + * Copies characters from this {@code ArrayCharSequence} into the + * destination character array. + *

+ * The first character to be copied is at index {@code srcBegin}; + * the last character to be copied is at index {@code srcEnd-1} + * (thus the total number of characters to be copied is + * {@code srcEnd-srcBegin}). The characters are copied into the + * subarray of {@code dst} starting at index {@code dstBegin} + * and ending at index: + *

+     *     dstBegin + (srcEnd-srcBegin) - 1
+     * 
+ * + * @param srcBegin index of the first character in the sequence + * to copy. + * @param srcEnd index after the last character in the sequence + * to copy. + * @param dst the destination array. + * @param dstBegin the start offset in the destination array. + * @throws IndexOutOfBoundsException If any of the following + * is true: + * + */ + public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) { + if (srcBegin < 0) { + throw new IndexOutOfBoundsException("srcBegin: " + srcBegin); + } + if (srcEnd > length) { + throw new IndexOutOfBoundsException("srcEnd: " + srcEnd); + } + if (srcBegin > srcEnd) { + throw new IndexOutOfBoundsException("srcBegin: " + srcBegin + + " > srcEnd: " + srcEnd); + } + System.arraycopy(chars, offset + srcBegin, dst, dstBegin, srcEnd - srcBegin); + } + + /** + * Returns a string containing the characters in this sequence in the same + * order as this sequence. The length of the string will be the length of + * this sequence. + * + * @return a string consisting of exactly this sequence of characters + */ + @Override + public String toString() { + return new String(chars, offset, length); + } + + // common subSequence arguments checking method + private static int checkSubSequenceIndexReturnStart(int start, int end, int length) { + if (start < 0 || end < 0 || start > end || end > length) { + throw new IndexOutOfBoundsException("start: " + start + ", end: " + end); + } + return start; + } +} --- /dev/null 2015-04-10 13:19:24.023596515 +0200 +++ new/src/java.base/share/classes/sun/misc/ConstantLengthCharSequence.java 2015-05-28 12:50:44.752666228 +0200 @@ -0,0 +1,59 @@ +package sun.misc; + +/** + * An internal extension of CharSequence that guarantees it's + * {@link #length()} to be constant. The underlying characters can change + * concurrently only as a result of improper use of objects this object + * was derived from and were not intended for multi-thread use without + * proper synchronization. + */ +public interface ConstantLengthCharSequence extends CharSequence { + + /** + * Returns a {@link ConstantLengthCharSequence} for given {@code charSequence}. + * If given {@code charSequence} is a {@link ConstantLengthCharSequence} then it + * returns the given instance itself. + * + * @return a {@link ConstantLengthCharSequence} for given {@code charSequence} + */ + static ConstantLengthCharSequence valueFor(CharSequence charSequence) { + if (charSequence instanceof ConstantLengthCharSequence) { + return (ConstantLengthCharSequence) charSequence; + } else { + return charSequence.toString(); + } + } + + /** + * Copies characters from this {@code ConstantLengthCharSequence} into the + * destination character array. + *

+ * The first character to be copied is at index {@code srcBegin}; + * the last character to be copied is at index {@code srcEnd-1} + * (thus the total number of characters to be copied is + * {@code srcEnd-srcBegin}). The characters are copied into the + * subarray of {@code dst} starting at index {@code dstBegin} + * and ending at index: + *

+     *     dstBegin + (srcEnd-srcBegin) - 1
+     * 
+ * + * @param srcBegin index of the first character in the sequence + * to copy. + * @param srcEnd index after the last character in the sequence + * to copy. + * @param dst the destination array. + * @param dstBegin the start offset in the destination array. + * @throws IndexOutOfBoundsException If any of the following + * is true: + * + */ + void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin); +}