--- old/src/java.base/share/classes/java/lang/AbstractStringBuilder.java 2015-10-30 14:03:43.089336972 -0700 +++ new/src/java.base/share/classes/java/lang/AbstractStringBuilder.java 2015-10-30 14:03:42.851337872 -0700 @@ -31,6 +31,12 @@ 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. *

@@ -51,7 +57,12 @@ /** * The value is used for character storage. */ - char[] value; + 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. @@ -68,7 +79,13 @@ * Creates an AbstractStringBuilder of the specified capacity. */ AbstractStringBuilder(int capacity) { - value = new char[capacity]; + if (COMPACT_STRINGS) { + value = new byte[capacity]; + coder = LATIN1; + } else { + value = StringUTF16.newBytesFor(capacity); + coder = UTF16; + } } /** @@ -90,7 +107,7 @@ * @return the current capacity */ public int capacity() { - return value.length; + return value.length >> coder; } /** @@ -110,8 +127,9 @@ * @param minimumCapacity the minimum desired capacity. */ public void ensureCapacity(int minimumCapacity) { - if (minimumCapacity > 0) + if (minimumCapacity > 0) { ensureCapacityInternal(minimumCapacity); + } } /** @@ -120,24 +138,48 @@ */ private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code - if (minimumCapacity - value.length > 0) + int capacity = value.length >> coder; + if (minimumCapacity - capacity > 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) + 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 + if (minimumCapacity < 0) {// overflow throw new OutOfMemoryError(); + } newCapacity = Integer.MAX_VALUE; } - value = Arrays.copyOf(value, newCapacity); + 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 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; } /** @@ -148,8 +190,9 @@ * returned by a subsequent call to the {@link #capacity()} method. */ public void trimToSize() { - if (count < value.length) { - value = Arrays.copyOf(value, count); + int length = count << coder; + if (length < value.length) { + value = Arrays.copyOf(value, length); } } @@ -179,14 +222,17 @@ * {@code newLength} argument is negative. */ public void setLength(int newLength) { - if (newLength < 0) + if (newLength < 0) { throw new StringIndexOutOfBoundsException(newLength); + } ensureCapacityInternal(newLength); - if (count < newLength) { - Arrays.fill(value, count, newLength, '\0'); + if (isLatin1()) { + StringLatin1.fillNull(value, count, newLength); + } else { + StringUTF16.fillNull(value, count, newLength); + } } - count = newLength; } @@ -209,9 +255,11 @@ */ @Override public char charAt(int index) { - if ((index < 0) || (index >= count)) - throw new StringIndexOutOfBoundsException(index); - return value[index]; + checkIndex(index, count); + if (isLatin1()) { + return (char)(value[index] & 0xff); + } + return StringUTF16.charAt(value, index); } /** @@ -236,10 +284,11 @@ * sequence. */ public int codePointAt(int index) { - if ((index < 0) || (index >= count)) { - throw new StringIndexOutOfBoundsException(index); + checkIndex(index, count); + if (isLatin1()) { + return value[index] & 0xff; } - return Character.codePointAtImpl(value, index, count); + return StringUTF16.codePointAtSB(value, index, count); } /** @@ -265,10 +314,13 @@ */ public int codePointBefore(int index) { int i = index - 1; - if ((i < 0) || (i >= count)) { + if (i < 0 || i >= count) { throw new StringIndexOutOfBoundsException(index); } - return Character.codePointBeforeImpl(value, index, 0); + if (isLatin1()) { + return value[i] & 0xff; + } + return StringUTF16.codePointBeforeSB(value, index); } /** @@ -295,7 +347,10 @@ if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) { throw new IndexOutOfBoundsException(); } - return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex); + if (isLatin1()) { + return endIndex - beginIndex; + } + return StringUTF16.codePointCountSB(value, beginIndex, endIndex); } /** @@ -321,8 +376,8 @@ if (index < 0 || index > count) { throw new IndexOutOfBoundsException(); } - return Character.offsetByCodePointsImpl(value, 0, count, - index, codePointOffset); + return Character.offsetByCodePoints(this, + index, codePointOffset); } /** @@ -355,13 +410,14 @@ */ 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); + 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); + } } /** @@ -379,9 +435,15 @@ * 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; + checkIndex(index, count); + if (isLatin1() && StringLatin1.canEncode(ch)) { + value[index] = (byte)ch; + } else { + if (isLatin1()) { + inflate(); + } + StringUTF16.putCharSB(value, index, ch); + } } /** @@ -418,35 +480,34 @@ * @return a reference to this object. */ public AbstractStringBuilder append(String str) { - if (str == null) + if (str == null) { return appendNull(); + } int len = str.length(); ensureCapacityInternal(count + len); - str.getChars(0, len, value, count); + putStringAt(count, str); 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; + return this.append((AbstractStringBuilder)sb); } /** * @since 1.8 */ AbstractStringBuilder append(AbstractStringBuilder asb) { - if (asb == null) + if (asb == null) { return appendNull(); + } int len = asb.length(); ensureCapacityInternal(count + len); - asb.getChars(0, len, value, count); + if (getCoder() != asb.getCoder()) { + inflate(); + } + asb.getBytes(value, count, coder); count += len; return this; } @@ -454,25 +515,35 @@ // Documentation in subclasses because of synchro difference @Override public AbstractStringBuilder append(CharSequence s) { - if (s == null) + if (s == null) { return appendNull(); - if (s instanceof String) + } + if (s instanceof String) { return this.append((String)s); - if (s instanceof AbstractStringBuilder) + } + 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; + 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; } @@ -507,21 +578,13 @@ */ @Override public AbstractStringBuilder append(CharSequence s, int start, int end) { - if (s == null) + if (s == null) { s = "null"; - if ((start < 0) || (start > end) || (end > s.length())) - throw new IndexOutOfBoundsException( - "start " + start + ", end " + end + ", s.length() " - + s.length()); + } + checkRange(start, end, 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; + appendChars(s, start, end); return this; } @@ -544,8 +607,7 @@ public AbstractStringBuilder append(char[] str) { int len = str.length; ensureCapacityInternal(count + len); - System.arraycopy(str, 0, value, count, len); - count += len; + appendChars(str, 0, len); return this; } @@ -572,10 +634,10 @@ * 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; + int end = offset + len; + checkRange(offset, end, str.length); + ensureCapacityInternal(count + len); + appendChars(str, offset, end); return this; } @@ -592,20 +654,39 @@ * @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'; + 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 { - ensureCapacityInternal(count + 5); - value[count++] = 'f'; - value[count++] = 'a'; - value[count++] = 'l'; - value[count++] = 's'; - value[count++] = 'e'; + 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; } @@ -627,7 +708,14 @@ @Override public AbstractStringBuilder append(char c) { ensureCapacityInternal(count + 1); - value[count++] = c; + if (isLatin1() && StringLatin1.canEncode(c)) { + value[count++] = (byte)c; + } else { + if (isLatin1()) { + inflate(); + } + StringUTF16.putCharSB(value, count++, c); + } return this; } @@ -652,7 +740,13 @@ : Integer.stringSize(i); int spaceNeeded = count + appendedLength; ensureCapacityInternal(spaceNeeded); - Integer.getChars(i, spaceNeeded, value); + 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; } @@ -678,7 +772,13 @@ : Long.stringSize(l); int spaceNeeded = count + appendedLength; ensureCapacityInternal(spaceNeeded); - Long.getChars(l, spaceNeeded, value); + 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; } @@ -732,15 +832,13 @@ * greater than {@code end}. */ public AbstractStringBuilder delete(int start, int end) { - if (start < 0) - throw new StringIndexOutOfBoundsException(start); - if (end > count) + if (end > count) { end = count; - if (start > end) - throw new StringIndexOutOfBoundsException(); + } + checkRangeSIOOBE(start, end, count); int len = end - start; if (len > 0) { - System.arraycopy(value, start+len, value, start, count-end); + shift(end, -len); count -= len; } return this; @@ -766,20 +864,10 @@ * {@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 append((char)codePoint); } - return this; + return append(Character.toChars(codePoint)); } /** @@ -800,9 +888,8 @@ * {@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); + checkIndex(index, count); + shift(index + 1, -1); count--; return this; } @@ -827,22 +914,16 @@ * 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) + if (end > count) { end = count; + } + checkRangeSIOOBE(start, 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); + shift(end, newCount - count); count = newCount; + putStringAt(start, str); return this; } @@ -907,13 +988,16 @@ * 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); + 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); } /** @@ -940,16 +1024,12 @@ 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); + checkOffset(index, count); + checkRangeSIOOBE(offset, offset + len, str.length); ensureCapacityInternal(count + len); - System.arraycopy(value, index, value, index + len, count - index); - System.arraycopy(str, offset, value, index, len); + shift(index, len); count += len; + putCharsAt(index, str, offset, offset + len); return this; } @@ -1008,15 +1088,15 @@ * @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) + checkOffset(offset, count); + 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); + shift(offset, len); count += len; + putStringAt(offset, str); return this; } @@ -1045,13 +1125,12 @@ * @throws StringIndexOutOfBoundsException if the offset is invalid. */ public AbstractStringBuilder insert(int offset, char[] str) { - if ((offset < 0) || (offset > length())) - throw new StringIndexOutOfBoundsException(offset); + checkOffset(offset, count); int len = str.length; ensureCapacityInternal(count + len); - System.arraycopy(value, offset, value, offset + len, count - offset); - System.arraycopy(str, 0, value, offset, len); + shift(offset, len); count += len; + putCharsAt(offset, str, 0, len); return this; } @@ -1077,10 +1156,12 @@ * @throws IndexOutOfBoundsException if the offset is invalid. */ public AbstractStringBuilder insert(int dstOffset, CharSequence s) { - if (s == null) + if (s == null) { s = "null"; - if (s instanceof String) + } + if (s instanceof String) { return this.insert(dstOffset, (String)s); + } return this.insert(dstOffset, s, 0, s.length()); } @@ -1128,23 +1209,19 @@ * {@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) + 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()); + } + checkOffset(dstOffset, count); + checkRange(start, end, s.length()); int len = end - start; ensureCapacityInternal(count + len); - System.arraycopy(value, dstOffset, value, dstOffset + len, - count - dstOffset); - for (int i=start; i> 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 (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); } - } - if (hasSurrogates) { - reverseAllValidSurrogatePairs(); } return this; } /** Outlined helper method for reverse() */ - private void reverseAllValidSurrogatePairs() { + private void reverseAllValidSurrogatePairs(byte[] val, int count) { for (int i = 0; i < count - 1; i++) { - char c2 = value[i]; + char c2 = StringUTF16.getChar(val, i); if (Character.isLowSurrogate(c2)) { - char c1 = value[i + 1]; + char c1 = StringUTF16.getChar(val, i + 1); if (Character.isHighSurrogate(c1)) { - value[i++] = c1; - value[i] = c2; + StringUTF16.putChar(val, i++, c1); + StringUTF16.putChar(val, i, c2); } } } @@ -1444,10 +1542,13 @@ */ @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( - () -> new String.IntCharArraySpliterator(value, 0, count, 0), + () -> coder == LATIN1 ? new StringLatin1.CharsSpliterator(val, 0, count, 0) + : new StringUTF16.CharsSpliterator(val, 0, count, 0), Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED, false); } @@ -1458,10 +1559,13 @@ */ @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( - () -> new String.CodePointsSpliterator(value, 0, count, 0), + () -> coder == LATIN1 ? new StringLatin1.CharsSpliterator(val, 0, count, 0) + : new StringUTF16.CodePointsSpliterator(val, 0, count, 0), Spliterator.ORDERED, false); } @@ -1469,8 +1573,147 @@ /** * Needed by {@code String} for the contentEquals method. */ - final char[] getValue() { + 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); + } + } }