--- 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
--- 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:
+ *
+ * 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:
+ *
+ *
+ * @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:
+ *
+ * dstBegin + (srcEnd-srcBegin) - 1
+ *
+ */
+ 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.
+ *
+ *
+ * @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:
+ *
+ * dstBegin + (srcEnd-srcBegin) - 1
+ *
+ */
+ void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin);
+}