src/java.base/share/classes/java/lang/AbstractStringBuilder.java

Print this page

        

*** 29,38 **** --- 29,44 ---- import java.util.Arrays; import java.util.Spliterator; import java.util.stream.IntStream; import java.util.stream.StreamSupport; + import static java.lang.String.COMPACT_STRINGS; + import static java.lang.String.UTF16; + import static java.lang.String.LATIN1; + import static java.lang.String.checkIndex; + import static java.lang.String.checkOffset; + /** * A mutable sequence of characters. * <p> * Implements a modifiable string. At any point in time it contains some * particular sequence of characters, but the length and content of the
*** 49,59 **** */ abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ ! char[] value; /** * The count is the number of characters used. */ int count; --- 55,70 ---- */ abstract class AbstractStringBuilder implements Appendable, CharSequence { /** * The value is used for character storage. */ ! byte[] value; ! ! /** ! * The id of the encoding used to encode the bytes in {@code value}. ! */ ! byte coder; /** * The count is the number of characters used. */ int count;
*** 66,76 **** /** * Creates an AbstractStringBuilder of the specified capacity. */ AbstractStringBuilder(int capacity) { ! value = new char[capacity]; } /** * Returns the length (character count). * --- 77,93 ---- /** * Creates an AbstractStringBuilder of the specified capacity. */ AbstractStringBuilder(int capacity) { ! if (COMPACT_STRINGS) { ! value = new byte[capacity]; ! coder = LATIN1; ! } else { ! value = StringUTF16.newBytesFor(capacity); ! coder = UTF16; ! } } /** * Returns the length (character count). *
*** 88,98 **** * will occur. * * @return the current capacity */ public int capacity() { ! return value.length; } /** * Ensures that the capacity is at least equal to the specified minimum. * If the current capacity is less than the argument, then a new internal --- 105,115 ---- * will occur. * * @return the current capacity */ public int capacity() { ! return value.length >> coder; } /** * Ensures that the capacity is at least equal to the specified minimum. * If the current capacity is less than the argument, then a new internal
*** 108,157 **** * actual capacity below that requested here. * * @param minimumCapacity the minimum desired capacity. */ public void ensureCapacity(int minimumCapacity) { ! if (minimumCapacity > 0) ensureCapacityInternal(minimumCapacity); } /** * This method has the same contract as ensureCapacity, but is * never synchronized. */ private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code ! if (minimumCapacity - value.length > 0) expandCapacity(minimumCapacity); } /** * This implements the expansion semantics of ensureCapacity with no * size check or synchronization. */ ! void expandCapacity(int minimumCapacity) { ! int newCapacity = value.length * 2 + 2; ! if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity; if (newCapacity < 0) { ! if (minimumCapacity < 0) // overflow throw new OutOfMemoryError(); newCapacity = Integer.MAX_VALUE; } ! value = Arrays.copyOf(value, newCapacity); } /** * Attempts to reduce storage used for the character sequence. * If the buffer is larger than necessary to hold its current sequence of * characters, then it may be resized to become more space efficient. * Calling this method may, but is not required to, affect the value * returned by a subsequent call to the {@link #capacity()} method. */ public void trimToSize() { ! if (count < value.length) { ! value = Arrays.copyOf(value, count); } } /** * Sets the length of the character sequence. --- 125,200 ---- * actual capacity below that requested here. * * @param minimumCapacity the minimum desired capacity. */ public void ensureCapacity(int minimumCapacity) { ! if (minimumCapacity > 0) { ensureCapacityInternal(minimumCapacity); } + } /** * This method has the same contract as ensureCapacity, but is * never synchronized. */ private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code ! int capacity = value.length >> coder; ! if (minimumCapacity - capacity > 0) { expandCapacity(minimumCapacity); } + } /** * This implements the expansion semantics of ensureCapacity with no * size check or synchronization. */ ! private void expandCapacity(int minimumCapacity) { ! int newCapacity = (value.length >> coder) * 2 + 2; ! if (newCapacity - minimumCapacity < 0) { newCapacity = minimumCapacity; + } if (newCapacity < 0) { ! if (minimumCapacity < 0) {// overflow throw new OutOfMemoryError(); + } newCapacity = Integer.MAX_VALUE; } ! if (coder != LATIN1 && newCapacity > StringUTF16.MAX_LENGTH) { ! if (minimumCapacity >= StringUTF16.MAX_LENGTH) { ! throw new OutOfMemoryError(); ! } ! newCapacity = StringUTF16.MAX_LENGTH; ! } ! this.value = Arrays.copyOf(value, newCapacity << coder); ! } ! ! /** ! * If the coder is "isLatin1", this inflates the internal 8-bit storage ! * to 16-bit <hi=0, low> pair storage. ! */ ! private void inflate() { ! if (!isLatin1()) { ! return; ! } ! byte[] buf = StringUTF16.newBytesFor(value.length); ! StringLatin1.inflateSB(value, buf, 0, count); ! this.value = buf; ! this.coder = UTF16; } /** * Attempts to reduce storage used for the character sequence. * If the buffer is larger than necessary to hold its current sequence of * characters, then it may be resized to become more space efficient. * Calling this method may, but is not required to, affect the value * returned by a subsequent call to the {@link #capacity()} method. */ public void trimToSize() { ! int length = count << coder; ! if (length < value.length) { ! value = Arrays.copyOf(value, length); } } /** * Sets the length of the character sequence.
*** 177,194 **** * @param newLength the new length * @throws IndexOutOfBoundsException if the * {@code newLength} argument is negative. */ public void setLength(int newLength) { ! if (newLength < 0) throw new StringIndexOutOfBoundsException(newLength); ensureCapacityInternal(newLength); - if (count < newLength) { ! Arrays.fill(value, count, newLength, '\0'); } - count = newLength; } /** * Returns the {@code char} value in this sequence at the specified index. --- 220,240 ---- * @param newLength the new length * @throws IndexOutOfBoundsException if the * {@code newLength} argument is negative. */ public void setLength(int newLength) { ! if (newLength < 0) { throw new StringIndexOutOfBoundsException(newLength); + } ensureCapacityInternal(newLength); if (count < newLength) { ! if (isLatin1()) { ! StringLatin1.fillNull(value, count, newLength); ! } else { ! StringUTF16.fillNull(value, count, newLength); ! } } count = newLength; } /** * Returns the {@code char} value in this sequence at the specified index.
*** 207,219 **** * @throws IndexOutOfBoundsException if {@code index} is * negative or greater than or equal to {@code length()}. */ @Override public char charAt(int index) { ! if ((index < 0) || (index >= count)) ! throw new StringIndexOutOfBoundsException(index); ! return value[index]; } /** * Returns the character (Unicode code point) at the specified * index. The index refers to {@code char} values --- 253,267 ---- * @throws IndexOutOfBoundsException if {@code index} is * negative or greater than or equal to {@code length()}. */ @Override public char charAt(int index) { ! checkIndex(index, count); ! if (isLatin1()) { ! return (char)(value[index] & 0xff); ! } ! return StringUTF16.charAt(value, index); } /** * Returns the character (Unicode code point) at the specified * index. The index refers to {@code char} values
*** 234,247 **** * @exception IndexOutOfBoundsException if the {@code index} * argument is negative or not less than the length of this * sequence. */ public int codePointAt(int index) { ! if ((index < 0) || (index >= count)) { ! throw new StringIndexOutOfBoundsException(index); } ! return Character.codePointAtImpl(value, index, count); } /** * Returns the character (Unicode code point) before the specified * index. The index refers to {@code char} values --- 282,296 ---- * @exception IndexOutOfBoundsException if the {@code index} * argument is negative or not less than the length of this * sequence. */ public int codePointAt(int index) { ! checkIndex(index, count); ! if (isLatin1()) { ! return value[index] & 0xff; } ! return StringUTF16.codePointAtSB(value, index, count); } /** * Returns the character (Unicode code point) before the specified * index. The index refers to {@code char} values
*** 263,276 **** * argument is less than 1 or greater than the length * of this sequence. */ public int codePointBefore(int index) { int i = index - 1; ! if ((i < 0) || (i >= count)) { throw new StringIndexOutOfBoundsException(index); } ! return Character.codePointBeforeImpl(value, index, 0); } /** * Returns the number of Unicode code points in the specified text * range of this sequence. The text range begins at the specified --- 312,328 ---- * argument is less than 1 or greater than the length * of this sequence. */ public int codePointBefore(int index) { int i = index - 1; ! if (i < 0 || i >= count) { throw new StringIndexOutOfBoundsException(index); } ! if (isLatin1()) { ! return value[i] & 0xff; ! } ! return StringUTF16.codePointBeforeSB(value, index); } /** * Returns the number of Unicode code points in the specified text * range of this sequence. The text range begins at the specified
*** 293,303 **** */ public int codePointCount(int beginIndex, int endIndex) { if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) { throw new IndexOutOfBoundsException(); } ! return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex); } /** * Returns the index within this sequence that is offset from the * given {@code index} by {@code codePointOffset} code --- 345,358 ---- */ public int codePointCount(int beginIndex, int endIndex) { if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) { throw new IndexOutOfBoundsException(); } ! if (isLatin1()) { ! return endIndex - beginIndex; ! } ! return StringUTF16.codePointCountSB(value, beginIndex, endIndex); } /** * Returns the index within this sequence that is offset from the * given {@code index} by {@code codePointOffset} code
*** 319,329 **** */ public int offsetByCodePoints(int index, int codePointOffset) { if (index < 0 || index > count) { throw new IndexOutOfBoundsException(); } ! return Character.offsetByCodePointsImpl(value, 0, count, index, codePointOffset); } /** * Characters are copied from this sequence into the --- 374,384 ---- */ public int offsetByCodePoints(int index, int codePointOffset) { if (index < 0 || index > count) { throw new IndexOutOfBoundsException(); } ! return Character.offsetByCodePoints(this, index, codePointOffset); } /** * Characters are copied from this sequence into the
*** 353,369 **** * {@code dst.length} * </ul> */ public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) { ! if (srcBegin < 0) ! throw new StringIndexOutOfBoundsException(srcBegin); ! if ((srcEnd < 0) || (srcEnd > count)) ! throw new StringIndexOutOfBoundsException(srcEnd); ! if (srcBegin > srcEnd) ! throw new StringIndexOutOfBoundsException("srcBegin > srcEnd"); ! System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); } /** * The character at the specified index is set to {@code ch}. This * sequence is altered to represent a new character sequence that is --- 408,425 ---- * {@code dst.length} * </ul> */ public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) { ! checkRangeSIOOBE(srcBegin, srcEnd, count); // compatible to old version ! int n = srcEnd - srcBegin; ! checkRange(dstBegin, dstBegin + n, dst.length); ! if (isLatin1()) { ! StringLatin1.getCharsSB(value, srcBegin, srcEnd, dst, dstBegin); ! } else { ! StringUTF16.getCharsSB(value, srcBegin, srcEnd, dst, dstBegin); ! } } /** * The character at the specified index is set to {@code ch}. This * sequence is altered to represent a new character sequence that is
*** 377,389 **** * @param ch the new character. * @throws IndexOutOfBoundsException if {@code index} is * negative or greater than or equal to {@code length()}. */ public void setCharAt(int index, char ch) { ! if ((index < 0) || (index >= count)) ! throw new StringIndexOutOfBoundsException(index); ! value[index] = ch; } /** * Appends the string representation of the {@code Object} argument. * <p> --- 433,451 ---- * @param ch the new character. * @throws IndexOutOfBoundsException if {@code index} is * negative or greater than or equal to {@code length()}. */ public void setCharAt(int index, char ch) { ! checkIndex(index, count); ! if (isLatin1() && StringLatin1.canEncode(ch)) { ! value[index] = (byte)ch; ! } else { ! if (isLatin1()) { ! inflate(); ! } ! StringUTF16.putCharSB(value, index, ch); ! } } /** * Appends the string representation of the {@code Object} argument. * <p>
*** 416,480 **** * * @param str a string. * @return a reference to this object. */ public AbstractStringBuilder append(String str) { ! if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); ! str.getChars(0, len, value, count); count += len; return this; } // Documentation in subclasses because of synchro difference public AbstractStringBuilder append(StringBuffer sb) { ! if (sb == null) ! return appendNull(); ! int len = sb.length(); ! ensureCapacityInternal(count + len); ! sb.getChars(0, len, value, count); ! count += len; ! return this; } /** * @since 1.8 */ AbstractStringBuilder append(AbstractStringBuilder asb) { ! if (asb == null) return appendNull(); int len = asb.length(); ensureCapacityInternal(count + len); ! asb.getChars(0, len, value, count); count += len; return this; } // Documentation in subclasses because of synchro difference @Override public AbstractStringBuilder append(CharSequence s) { ! if (s == null) return appendNull(); ! if (s instanceof String) return this.append((String)s); ! if (s instanceof AbstractStringBuilder) return this.append((AbstractStringBuilder)s); ! return this.append(s, 0, s.length()); } private AbstractStringBuilder appendNull() { ! int c = count; ! ensureCapacityInternal(c + 4); ! final char[] value = this.value; ! value[c++] = 'n'; ! value[c++] = 'u'; ! value[c++] = 'l'; ! value[c++] = 'l'; ! count = c; return this; } /** * Appends a subsequence of the specified {@code CharSequence} to this --- 478,551 ---- * * @param str a string. * @return a reference to this object. */ public AbstractStringBuilder append(String str) { ! if (str == null) { return appendNull(); + } int len = str.length(); ensureCapacityInternal(count + len); ! putStringAt(count, str); count += len; return this; } // Documentation in subclasses because of synchro difference public AbstractStringBuilder append(StringBuffer sb) { ! return this.append((AbstractStringBuilder)sb); } /** * @since 1.8 */ AbstractStringBuilder append(AbstractStringBuilder asb) { ! if (asb == null) { return appendNull(); + } int len = asb.length(); ensureCapacityInternal(count + len); ! if (getCoder() != asb.getCoder()) { ! inflate(); ! } ! asb.getBytes(value, count, coder); count += len; return this; } // Documentation in subclasses because of synchro difference @Override public AbstractStringBuilder append(CharSequence s) { ! if (s == null) { return appendNull(); ! } ! if (s instanceof String) { return this.append((String)s); ! } ! if (s instanceof AbstractStringBuilder) { return this.append((AbstractStringBuilder)s); ! } return this.append(s, 0, s.length()); } private AbstractStringBuilder appendNull() { ! ensureCapacityInternal(count + 4); ! int count = this.count; ! byte[] val = this.value; ! if (isLatin1()) { ! val[count++] = 'n'; ! val[count++] = 'u'; ! val[count++] = 'l'; ! val[count++] = 'l'; ! } else { ! checkOffset(count + 4, val.length >> 1); ! StringUTF16.putChar(val, count++, 'n'); ! StringUTF16.putChar(val, count++, 'u'); ! StringUTF16.putChar(val, count++, 'l'); ! StringUTF16.putChar(val, count++, 'l'); ! } ! this.count = count; return this; } /** * Appends a subsequence of the specified {@code CharSequence} to this
*** 505,529 **** * {@code start} is greater than {@code end} or * {@code end} is greater than {@code s.length()} */ @Override public AbstractStringBuilder append(CharSequence s, int start, int end) { ! if (s == null) s = "null"; ! if ((start < 0) || (start > end) || (end > s.length())) ! throw new IndexOutOfBoundsException( ! "start " + start + ", end " + end + ", s.length() " ! + s.length()); int len = end - start; ensureCapacityInternal(count + len); ! if (s instanceof String) { ! ((String)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; } /** * Appends the string representation of the {@code char} array --- 576,592 ---- * {@code start} is greater than {@code end} or * {@code end} is greater than {@code s.length()} */ @Override public AbstractStringBuilder append(CharSequence s, int start, int end) { ! if (s == null) { s = "null"; ! } ! checkRange(start, end, s.length()); int len = end - start; ensureCapacityInternal(count + len); ! appendChars(s, start, end); return this; } /** * Appends the string representation of the {@code char} array
*** 542,553 **** * @return a reference to this object. */ public AbstractStringBuilder append(char[] str) { int len = str.length; ensureCapacityInternal(count + len); ! System.arraycopy(str, 0, value, count, len); ! count += len; return this; } /** * Appends the string representation of a subarray of the --- 605,615 ---- * @return a reference to this object. */ public AbstractStringBuilder append(char[] str) { int len = str.length; ensureCapacityInternal(count + len); ! appendChars(str, 0, len); return this; } /** * Appends the string representation of a subarray of the
*** 570,583 **** * @throws IndexOutOfBoundsException * if {@code offset < 0} or {@code len < 0} * or {@code offset+len > str.length} */ public AbstractStringBuilder append(char str[], int offset, int len) { ! if (len > 0) // let arraycopy report AIOOBE for len < 0 ensureCapacityInternal(count + len); ! System.arraycopy(str, offset, value, count, len); ! count += len; return this; } /** * Appends the string representation of the {@code boolean} --- 632,645 ---- * @throws IndexOutOfBoundsException * if {@code offset < 0} or {@code len < 0} * or {@code offset+len > str.length} */ public AbstractStringBuilder append(char str[], int offset, int len) { ! int end = offset + len; ! checkRange(offset, end, str.length); ensureCapacityInternal(count + len); ! appendChars(str, offset, end); return this; } /** * Appends the string representation of the {@code boolean}
*** 590,613 **** * * @param b a {@code boolean}. * @return a reference to this object. */ public AbstractStringBuilder append(boolean b) { if (b) { ! ensureCapacityInternal(count + 4); ! value[count++] = 't'; ! value[count++] = 'r'; ! value[count++] = 'u'; ! value[count++] = 'e'; ! } else { ! ensureCapacityInternal(count + 5); ! value[count++] = 'f'; ! value[count++] = 'a'; ! value[count++] = 'l'; ! value[count++] = 's'; ! value[count++] = 'e'; } return this; } /** * Appends the string representation of the {@code char} --- 652,694 ---- * * @param b a {@code boolean}. * @return a reference to this object. */ public AbstractStringBuilder append(boolean b) { + ensureCapacityInternal(count + (b ? 4 : 5)); + int count = this.count; + byte[] val = this.value; + if (isLatin1()) { if (b) { ! val[count++] = 't'; ! val[count++] = 'r'; ! val[count++] = 'u'; ! val[count++] = 'e'; ! } else { ! val[count++] = 'f'; ! val[count++] = 'a'; ! val[count++] = 'l'; ! val[count++] = 's'; ! val[count++] = 'e'; ! } ! } else { ! if (b) { ! checkOffset(count + 4, val.length >> 1); ! StringUTF16.putChar(val, count++, 't'); ! StringUTF16.putChar(val, count++, 'r'); ! StringUTF16.putChar(val, count++, 'u'); ! StringUTF16.putChar(val, count++, 'e'); ! } else { ! checkOffset(count + 5, val.length >> 1); ! StringUTF16.putChar(val, count++, 'f'); ! StringUTF16.putChar(val, count++, 'a'); ! StringUTF16.putChar(val, count++, 'l'); ! StringUTF16.putChar(val, count++, 's'); ! StringUTF16.putChar(val, count++, 'e'); } + } + this.count = count; return this; } /** * Appends the string representation of the {@code char}
*** 625,635 **** * @return a reference to this object. */ @Override public AbstractStringBuilder append(char c) { ensureCapacityInternal(count + 1); ! value[count++] = c; return this; } /** * Appends the string representation of the {@code int} --- 706,723 ---- * @return a reference to this object. */ @Override public AbstractStringBuilder append(char c) { ensureCapacityInternal(count + 1); ! if (isLatin1() && StringLatin1.canEncode(c)) { ! value[count++] = (byte)c; ! } else { ! if (isLatin1()) { ! inflate(); ! } ! StringUTF16.putCharSB(value, count++, c); ! } return this; } /** * Appends the string representation of the {@code int}
*** 650,660 **** --- 738,754 ---- } int appendedLength = (i < 0) ? Integer.stringSize(-i) + 1 : Integer.stringSize(i); int spaceNeeded = count + appendedLength; ensureCapacityInternal(spaceNeeded); + if (isLatin1()) { Integer.getChars(i, spaceNeeded, value); + } else { + byte[] val = this.value; + checkOffset(spaceNeeded, val.length >> 1); + Integer.getCharsUTF16(i, spaceNeeded, val); + } count = spaceNeeded; return this; } /**
*** 676,686 **** --- 770,786 ---- } int appendedLength = (l < 0) ? Long.stringSize(-l) + 1 : Long.stringSize(l); int spaceNeeded = count + appendedLength; ensureCapacityInternal(spaceNeeded); + if (isLatin1()) { Long.getChars(l, spaceNeeded, value); + } else { + byte[] val = this.value; + checkOffset(spaceNeeded, val.length >> 1); + Long.getCharsUTF16(l, spaceNeeded, val); + } count = spaceNeeded; return this; } /**
*** 730,748 **** * @throws StringIndexOutOfBoundsException if {@code start} * is negative, greater than {@code length()}, or * greater than {@code end}. */ public AbstractStringBuilder delete(int start, int end) { ! if (start < 0) ! throw new StringIndexOutOfBoundsException(start); ! if (end > count) end = count; ! if (start > end) ! throw new StringIndexOutOfBoundsException(); int len = end - start; if (len > 0) { ! System.arraycopy(value, start+len, value, start, count-end); count -= len; } return this; } --- 830,846 ---- * @throws StringIndexOutOfBoundsException if {@code start} * is negative, greater than {@code length()}, or * greater than {@code end}. */ public AbstractStringBuilder delete(int start, int end) { ! if (end > count) { end = count; ! } ! checkRangeSIOOBE(start, end, count); int len = end - start; if (len > 0) { ! shift(end, -len); count -= len; } return this; }
*** 764,787 **** * @return a reference to this object. * @exception IllegalArgumentException if the specified * {@code codePoint} isn't a valid Unicode code point */ public AbstractStringBuilder appendCodePoint(int codePoint) { - final int count = this.count; - if (Character.isBmpCodePoint(codePoint)) { ! ensureCapacityInternal(count + 1); ! value[count] = (char) codePoint; ! this.count = count + 1; ! } else if (Character.isValidCodePoint(codePoint)) { ! ensureCapacityInternal(count + 2); ! Character.toSurrogates(codePoint, value, count); ! this.count = count + 2; ! } else { ! throw new IllegalArgumentException(); } ! return this; } /** * Removes the {@code char} at the specified position in this * sequence. This sequence is shortened by one {@code char}. --- 862,875 ---- * @return a reference to this object. * @exception IllegalArgumentException if the specified * {@code codePoint} isn't a valid Unicode code point */ public AbstractStringBuilder appendCodePoint(int codePoint) { if (Character.isBmpCodePoint(codePoint)) { ! return append((char)codePoint); } ! return append(Character.toChars(codePoint)); } /** * Removes the {@code char} at the specified position in this * sequence. This sequence is shortened by one {@code char}.
*** 798,810 **** * @throws StringIndexOutOfBoundsException if the {@code index} * is negative or greater than or equal to * {@code length()}. */ public AbstractStringBuilder deleteCharAt(int index) { ! if ((index < 0) || (index >= count)) ! throw new StringIndexOutOfBoundsException(index); ! System.arraycopy(value, index+1, value, index, count-index-1); count--; return this; } /** --- 886,897 ---- * @throws StringIndexOutOfBoundsException if the {@code index} * is negative or greater than or equal to * {@code length()}. */ public AbstractStringBuilder deleteCharAt(int index) { ! checkIndex(index, count); ! shift(index + 1, -1); count--; return this; } /**
*** 825,850 **** * @throws StringIndexOutOfBoundsException if {@code start} * is negative, greater than {@code length()}, or * greater than {@code end}. */ public AbstractStringBuilder replace(int start, int end, String str) { ! if (start < 0) ! throw new StringIndexOutOfBoundsException(start); ! if (start > count) ! throw new StringIndexOutOfBoundsException("start > length()"); ! if (start > end) ! throw new StringIndexOutOfBoundsException("start > end"); ! ! if (end > count) end = count; int len = str.length(); int newCount = count + len - (end - start); ensureCapacityInternal(newCount); ! ! System.arraycopy(value, end, value, start + len, count - end); ! str.getChars(value, start); count = newCount; return this; } /** * Returns a new {@code String} that contains a subsequence of --- 912,931 ---- * @throws StringIndexOutOfBoundsException if {@code start} * is negative, greater than {@code length()}, or * greater than {@code end}. */ public AbstractStringBuilder replace(int start, int end, String str) { ! if (end > count) { end = count; + } + checkRangeSIOOBE(start, end, count); int len = str.length(); int newCount = count + len - (end - start); ensureCapacityInternal(newCount); ! shift(end, newCount - count); count = newCount; + putStringAt(start, str); return this; } /** * Returns a new {@code String} that contains a subsequence of
*** 905,921 **** * or {@code end} are negative or greater than * {@code length()}, or {@code start} is * greater than {@code end}. */ public String substring(int start, int end) { ! if (start < 0) ! throw new StringIndexOutOfBoundsException(start); ! if (end > count) ! throw new StringIndexOutOfBoundsException(end); ! if (start > end) ! throw new StringIndexOutOfBoundsException(end - start); ! return new String(value, start, end - start); } /** * Inserts the string representation of a subarray of the {@code str} * array argument into this sequence. The subarray begins at the --- 986,1005 ---- * or {@code end} are negative or greater than * {@code length()}, or {@code start} is * greater than {@code end}. */ public String substring(int start, int end) { ! checkRangeSIOOBE(start, end, count); ! if (isLatin1()) { ! return StringLatin1.newString(value, start, end - start); ! } ! return StringUTF16.newStringSB(value, start, end - start); ! } ! ! private void shift(int offset, int n) { ! System.arraycopy(value, offset << coder, ! value, (offset + n) << coder, (count - offset) << coder); } /** * Inserts the string representation of a subarray of the {@code str} * array argument into this sequence. The subarray begins at the
*** 938,957 **** * {@code str.length}. */ public AbstractStringBuilder insert(int index, char[] str, int offset, int len) { ! if ((index < 0) || (index > length())) ! throw new StringIndexOutOfBoundsException(index); ! if ((offset < 0) || (len < 0) || (offset > str.length - len)) ! throw new StringIndexOutOfBoundsException( ! "offset " + offset + ", len " + len + ", str.length " ! + str.length); ensureCapacityInternal(count + len); ! System.arraycopy(value, index, value, index + len, count - index); ! System.arraycopy(str, offset, value, index, len); count += len; return this; } /** * Inserts the string representation of the {@code Object} --- 1022,1037 ---- * {@code str.length}. */ public AbstractStringBuilder insert(int index, char[] str, int offset, int len) { ! checkOffset(index, count); ! checkRangeSIOOBE(offset, offset + len, str.length); ensureCapacityInternal(count + len); ! shift(index, len); count += len; + putCharsAt(index, str, offset, offset + len); return this; } /** * Inserts the string representation of the {@code Object}
*** 1006,1024 **** * @param str a string. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ public AbstractStringBuilder insert(int offset, String str) { ! if ((offset < 0) || (offset > length())) ! throw new StringIndexOutOfBoundsException(offset); ! if (str == null) str = "null"; int len = str.length(); ensureCapacityInternal(count + len); ! System.arraycopy(value, offset, value, offset + len, count - offset); ! str.getChars(value, offset); count += len; return this; } /** * Inserts the string representation of the {@code char} array --- 1086,1104 ---- * @param str a string. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ public AbstractStringBuilder insert(int offset, String str) { ! checkOffset(offset, count); ! if (str == null) { str = "null"; + } int len = str.length(); ensureCapacityInternal(count + len); ! shift(offset, len); count += len; + putStringAt(offset, str); return this; } /** * Inserts the string representation of the {@code char} array
*** 1043,1059 **** * @param str a character array. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ public AbstractStringBuilder insert(int offset, char[] str) { ! if ((offset < 0) || (offset > length())) ! throw new StringIndexOutOfBoundsException(offset); int len = str.length; ensureCapacityInternal(count + len); ! System.arraycopy(value, offset, value, offset + len, count - offset); ! System.arraycopy(str, 0, value, offset, len); count += len; return this; } /** * Inserts the specified {@code CharSequence} into this sequence. --- 1123,1138 ---- * @param str a character array. * @return a reference to this object. * @throws StringIndexOutOfBoundsException if the offset is invalid. */ public AbstractStringBuilder insert(int offset, char[] str) { ! checkOffset(offset, count); int len = str.length; ensureCapacityInternal(count + len); ! shift(offset, len); count += len; + putCharsAt(offset, str, 0, len); return this; } /** * Inserts the specified {@code CharSequence} into this sequence.
*** 1075,1088 **** * @param s the sequence to be inserted * @return a reference to this object. * @throws IndexOutOfBoundsException if the offset is invalid. */ public AbstractStringBuilder insert(int dstOffset, CharSequence s) { ! if (s == null) s = "null"; ! if (s instanceof String) return this.insert(dstOffset, (String)s); return this.insert(dstOffset, s, 0, s.length()); } /** * Inserts a subsequence of the specified {@code CharSequence} into --- 1154,1169 ---- * @param s the sequence to be inserted * @return a reference to this object. * @throws IndexOutOfBoundsException if the offset is invalid. */ public AbstractStringBuilder insert(int dstOffset, CharSequence s) { ! if (s == null) { s = "null"; ! } ! if (s instanceof String) { return this.insert(dstOffset, (String)s); + } return this.insert(dstOffset, s, 0, s.length()); } /** * Inserts a subsequence of the specified {@code CharSequence} into
*** 1127,1152 **** * {@code start} or {@code end} are negative, or * {@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) { ! if (s == null) s = "null"; ! if ((dstOffset < 0) || (dstOffset > this.length())) ! throw new IndexOutOfBoundsException("dstOffset "+dstOffset); ! if ((start < 0) || (end < 0) || (start > end) || (end > s.length())) ! throw new IndexOutOfBoundsException( ! "start " + start + ", end " + end + ", s.length() " ! + s.length()); int len = end - start; ensureCapacityInternal(count + len); ! System.arraycopy(value, dstOffset, value, dstOffset + len, ! count - dstOffset); ! for (int i=start; i<end; i++) ! value[dstOffset++] = s.charAt(i); count += len; return this; } /** * Inserts the string representation of the {@code boolean} --- 1208,1229 ---- * {@code start} or {@code end} are negative, or * {@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) ! { ! if (s == null) { s = "null"; ! } ! checkOffset(dstOffset, count); ! checkRange(start, end, s.length()); int len = end - start; ensureCapacityInternal(count + len); ! shift(dstOffset, len); count += len; + putCharsAt(dstOffset, s, start, end); return this; } /** * Inserts the string representation of the {@code boolean}
*** 1189,1202 **** * @param c a {@code char}. * @return a reference to this object. * @throws IndexOutOfBoundsException if the offset is invalid. */ public AbstractStringBuilder insert(int offset, char c) { ensureCapacityInternal(count + 1); ! System.arraycopy(value, offset, value, offset + 1, count - offset); ! value[offset] = c; count += 1; return this; } /** * Inserts the string representation of the second {@code int} --- 1266,1287 ---- * @param c a {@code char}. * @return a reference to this object. * @throws IndexOutOfBoundsException if the offset is invalid. */ public AbstractStringBuilder insert(int offset, char c) { + checkOffset(offset, count); ensureCapacityInternal(count + 1); ! shift(offset, 1); count += 1; + if (isLatin1() && StringLatin1.canEncode(c)) { + value[offset] = (byte)c; + } else { + if (isLatin1()) { + inflate(); + } + StringUTF16.putCharSB(value, offset, c); + } return this; } /** * Inserts the string representation of the second {@code int}
*** 1324,1334 **** * @return the index of the first occurrence of the specified substring, * starting at the specified index, * or {@code -1} if there is no such occurrence. */ public int indexOf(String str, int fromIndex) { ! return String.indexOf(value, 0, count, str, fromIndex); } /** * Returns the index within this string of the last occurrence of the * specified substring. The last occurrence of the empty string "" is --- 1409,1419 ---- * @return the index of the first occurrence of the specified substring, * starting at the specified index, * or {@code -1} if there is no such occurrence. */ public int indexOf(String str, int fromIndex) { ! return String.indexOf(value, coder, count, str, fromIndex); } /** * Returns the index within this string of the last occurrence of the * specified substring. The last occurrence of the empty string "" is
*** 1364,1374 **** * @return the index of the last occurrence of the specified substring, * searching backward from the specified index, * or {@code -1} if there is no such occurrence. */ public int lastIndexOf(String str, int fromIndex) { ! return String.lastIndexOf(value, 0, count, str, fromIndex); } /** * Causes this character sequence to be replaced by the reverse of * the sequence. If there are any surrogate pairs included in the --- 1449,1459 ---- * @return the index of the last occurrence of the specified substring, * searching backward from the specified index, * or {@code -1} if there is no such occurrence. */ public int lastIndexOf(String str, int fromIndex) { ! return String.lastIndexOf(value, coder, count, str, fromIndex); } /** * Causes this character sequence to be replaced by the reverse of * the sequence. If there are any surrogate pairs included in the
*** 1390,1427 **** * a valid surrogate pair. * * @return a reference to this object. */ public AbstractStringBuilder reverse() { ! boolean hasSurrogates = false; int n = count - 1; for (int j = (n-1) >> 1; j >= 0; j--) { int k = n - j; ! char cj = value[j]; ! char ck = value[k]; ! value[j] = ck; ! value[k] = cj; if (Character.isSurrogate(cj) || Character.isSurrogate(ck)) { hasSurrogates = true; } } if (hasSurrogates) { ! reverseAllValidSurrogatePairs(); } return this; } /** Outlined helper method for reverse() */ ! private void reverseAllValidSurrogatePairs() { for (int i = 0; i < count - 1; i++) { ! char c2 = value[i]; if (Character.isLowSurrogate(c2)) { ! char c1 = value[i + 1]; if (Character.isHighSurrogate(c1)) { ! value[i++] = c1; ! value[i] = c2; } } } } --- 1475,1525 ---- * a valid surrogate pair. * * @return a reference to this object. */ public AbstractStringBuilder reverse() { ! byte[] val = this.value; ! int count = this.count; ! int coder = this.coder; int n = count - 1; + if (COMPACT_STRINGS && coder == LATIN1) { for (int j = (n-1) >> 1; j >= 0; j--) { int k = n - j; ! byte cj = val[j]; ! val[j] = val[k]; ! val[k] = cj; ! } ! } else { ! checkOffset(count, val.length >> 1); ! boolean hasSurrogates = false; ! for (int j = (n-1) >> 1; j >= 0; j--) { ! int k = n - j; ! char cj = StringUTF16.getChar(val, j); ! char ck = StringUTF16.getChar(val, k); ! StringUTF16.putChar(val, j, ck); ! StringUTF16.putChar(val, k, cj); if (Character.isSurrogate(cj) || Character.isSurrogate(ck)) { hasSurrogates = true; } } if (hasSurrogates) { ! reverseAllValidSurrogatePairs(val, count); ! } } return this; } /** Outlined helper method for reverse() */ ! private void reverseAllValidSurrogatePairs(byte[] val, int count) { for (int i = 0; i < count - 1; i++) { ! char c2 = StringUTF16.getChar(val, i); if (Character.isLowSurrogate(c2)) { ! char c1 = StringUTF16.getChar(val, i + 1); if (Character.isHighSurrogate(c1)) { ! StringUTF16.putChar(val, i++, c1); ! StringUTF16.putChar(val, i, c2); } } } }
*** 1442,1476 **** * {@inheritDoc} * @since 1.9 */ @Override public IntStream chars() { // Reuse String-based spliterator. This requires a supplier to // capture the value and count when the terminal operation is executed return StreamSupport.intStream( ! () -> new String.IntCharArraySpliterator(value, 0, count, 0), Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED, false); } /** * {@inheritDoc} * @since 1.9 */ @Override public IntStream codePoints() { // Reuse String-based spliterator. This requires a supplier to // capture the value and count when the terminal operation is executed return StreamSupport.intStream( ! () -> new String.CodePointsSpliterator(value, 0, count, 0), Spliterator.ORDERED, false); } /** * Needed by {@code String} for the contentEquals method. */ ! final char[] getValue() { return value; } } --- 1540,1719 ---- * {@inheritDoc} * @since 1.9 */ @Override public IntStream chars() { + byte[] val = this.value; int count = this.count; byte coder = this.coder; + checkOffset(count, val.length >> coder); // Reuse String-based spliterator. This requires a supplier to // capture the value and count when the terminal operation is executed return StreamSupport.intStream( ! () -> coder == LATIN1 ? new StringLatin1.CharsSpliterator(val, 0, count, 0) ! : new StringUTF16.CharsSpliterator(val, 0, count, 0), Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED, false); } /** * {@inheritDoc} * @since 1.9 */ @Override public IntStream codePoints() { + byte[] val = this.value; int count = this.count; byte coder = this.coder; + checkOffset(count, val.length >> coder); // Reuse String-based spliterator. This requires a supplier to // capture the value and count when the terminal operation is executed return StreamSupport.intStream( ! () -> coder == LATIN1 ? new StringLatin1.CharsSpliterator(val, 0, count, 0) ! : new StringUTF16.CodePointsSpliterator(val, 0, count, 0), Spliterator.ORDERED, false); } /** * Needed by {@code String} for the contentEquals method. */ ! final byte[] getValue() { return value; } + /* + * Invoker guarantees it is in UTF16 (inflate itself for asb), if two + * coders are different and the dstBegin has enough space + * + * @param dstBegin the char index, not offset of byte[] + * @param coder the coder of dst[] + */ + protected 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 + StringLatin1.inflateSB(value, dst, dstBegin, count); + } + } + + /* for readObject() */ + protected void initBytes(char[] value, int off, int len) { + if (String.COMPACT_STRINGS) { + this.value = StringUTF16.compress(value, off, len); + if (this.value != null) { + this.coder = LATIN1; + return; + } + } + this.coder = UTF16; + this.value = StringUTF16.toBytes(value, off, len); + } + + final byte getCoder() { + return COMPACT_STRINGS ? coder : UTF16; + } + + final boolean isLatin1() { + return COMPACT_STRINGS && coder == LATIN1; + } + + private final void putCharsAt(int index, char[] s, int off, int end) { + if (isLatin1()) { + byte[] val = this.value; + for (int i = off, j = index; i < end; i++) { + char c = s[i]; + if (StringLatin1.canEncode(c)) { + val[j++] = (byte)c; + } else { + inflate(); + StringUTF16.putCharsSB(this.value, j, s, i, end); + return; + } + } + } else { + StringUTF16.putCharsSB(this.value, index, s, off, end); + } + } + + private final void putCharsAt(int index, CharSequence s, int off, int end) { + if (isLatin1()) { + byte[] val = this.value; + for (int i = off, j = index; i < end; i++) { + char c = s.charAt(i); + if (StringLatin1.canEncode(c)) { + val[j++] = (byte)c; + } else { + inflate(); + StringUTF16.putCharsSB(this.value, j, s, i, end); + return; + } + } + } else { + StringUTF16.putCharsSB(this.value, index, s, off, end); + } + } + + private final void putStringAt(int index, String str) { + if (getCoder() != str.coder()) { + inflate(); + } + byte[] val = this.value; + byte coder = this.coder; + checkOffset(index + str.length(), val.length >> coder); + str.getBytes(val, index, coder); + } + + private final void appendChars(char[] s, int off, int end) { + if (isLatin1()) { + byte[] val = this.value; + for (int i = off, j = count; i < end; i++) { + char c = s[i]; + if (StringLatin1.canEncode(c)) { + val[j++] = (byte)c; + } else { + count = j; + inflate(); + StringUTF16.putCharsSB(this.value, j, s, i, end); + count += end - i; + return; + } + } + } else { + StringUTF16.putCharsSB(this.value, count, s, off, end); + } + count += end - off; + } + + private final void appendChars(CharSequence s, int off, int end) { + if (isLatin1()) { + byte[] val = this.value; + for (int i = off, j = count; i < end; i++) { + char c = s.charAt(i); + if (StringLatin1.canEncode(c)) { + val[j++] = (byte)c; + } else { + count = j; + inflate(); + StringUTF16.putCharsSB(this.value, j, s, i, end); + count += end - i; + return; + } + } + } else { + StringUTF16.putCharsSB(this.value, count, s, off, end); + } + count += end - off; + } + + /* IndexOutOfBoundsException, if out of bounds */ + private static void checkRange(int start, int end, int len) { + if (start < 0 || start > end || end > len) { + throw new IndexOutOfBoundsException( + "start " + start + ", end " + end + ", length " + len); + } + } + + /* StringIndexOutOfBoundsException, if out of bounds */ + private static void checkRangeSIOOBE(int start, int end, int len) { + if (start < 0 || start > end || end > len) { + throw new StringIndexOutOfBoundsException( + "start " + start + ", end " + end + ", length " + len); + } + } }