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