--- old/src/java.base/share/classes/java/lang/String.java 2015-10-30 14:03:39.877349116 -0700 +++ new/src/java.base/share/classes/java/lang/String.java 2015-10-30 14:03:39.623350076 -0700 @@ -36,7 +36,6 @@ import java.util.Objects; import java.util.Spliterator; import java.util.StringJoiner; -import java.util.function.IntConsumer; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -119,8 +118,19 @@ public final class String implements java.io.Serializable, Comparable, CharSequence { + /** The value is used for character storage. */ - private final char value[]; + private final byte[] value; + + /** + * The identifier of the encoding used to encode the bytes in + * {@code value}. The supported values in this implementation are + * + * LATIN1 + * UTF16 + * + */ + private final byte coder; /** Cache the hash code for the string */ private int hash; // Default to 0 @@ -129,6 +139,49 @@ private static final long serialVersionUID = -6849794470754667710L; /** + * If String compaction is disabled, the bytes in {@code value} are + * always encoded in UTF16. + * + * For methods with several possible implementation paths, when String + * compaction is disabled, only one code path is taken. + * + * The instance field value is generally opaque to optimizing JIT + * compilers. Therefore, in performance-sensitive place, an explicit + * check of the static boolean {@code COMPACT_STRINGS} is done first + * before checking the {@code coder} field since the static boolean + * {@code COMPACT_STRINGS} would be constant folded away by an + * optimizing JIT compiler. The idioms for these cases are as follows. + * + * For code such as: + * + * if (coder == LATIN1) { ... } + * + * can be written more optimally as + * + * if (coder() == LATIN1) { ... } + * + * or: + * + * if (COMPACT_STRINGS && coder == LATIN1) { ... } + * + * An optimizing JIT compiler can fold the above conditional as: + * + * COMPACT_STRINGS == true => if (coder == LATIN1) { ... } + * COMPACT_STRINGS == false => if (false) { ... } + * + * @implNote + * The actual value for this field is injected by JVM. The static + * initialization block is used to set the value here to communicate + * that this static final field is not statically foldable, and to + * avoid any possible circular dependency during vm initialization. + */ + static final boolean COMPACT_STRINGS; + + static { + COMPACT_STRINGS = true; + } + + /** * Class String is special cased within the Serialization Stream Protocol. * * A String instance is written into an ObjectOutputStream according to @@ -145,6 +198,7 @@ */ public String() { this.value = "".value; + this.coder = "".coder; } /** @@ -160,6 +214,7 @@ @HotSpotIntrinsicCandidate public String(String original) { this.value = original.value; + this.coder = original.coder; this.hash = original.hash; } @@ -173,7 +228,7 @@ * The initial value of the string */ public String(char value[]) { - this.value = Arrays.copyOf(value, value.length); + this(value, 0, value.length, null); } /** @@ -198,23 +253,12 @@ * {@code offset} is greater than {@code value.length - count} */ public String(char value[], int offset, int count) { - if (offset < 0) { - throw new StringIndexOutOfBoundsException(offset); - } - if (count <= 0) { - if (count < 0) { - throw new StringIndexOutOfBoundsException(count); - } - if (offset <= value.length) { - this.value = "".value; - return; - } - } - // Note: offset or count might be near -1>>>1. - if (offset > value.length - count) { - throw new StringIndexOutOfBoundsException(offset + count); - } - this.value = Arrays.copyOfRange(value, offset, offset + count); + this(value, offset, count, rangeCheck(value, offset, count)); + } + + private static Void rangeCheck(char[] value, int offset, int count) { + checkBoundsOffCount(offset, count, value.length); + return null; } /** @@ -246,48 +290,22 @@ * @since 1.5 */ public String(int[] codePoints, int offset, int count) { - if (offset < 0) { - throw new StringIndexOutOfBoundsException(offset); - } - if (count <= 0) { - if (count < 0) { - throw new StringIndexOutOfBoundsException(count); - } - if (offset <= codePoints.length) { - this.value = "".value; + checkBoundsOffCount(offset, count, codePoints.length); + if (count == 0) { + this.value = "".value; + this.coder = "".coder; + return; + } + if (COMPACT_STRINGS) { + byte[] val = StringLatin1.toBytes(codePoints, offset, count); + if (val != null) { + this.coder = LATIN1; + this.value = val; return; } } - // Note: offset or count might be near -1>>>1. - if (offset > codePoints.length - count) { - throw new StringIndexOutOfBoundsException(offset + count); - } - - final int end = offset + count; - - // Pass 1: Compute precise size of char[] - int n = count; - for (int i = offset; i < end; i++) { - int c = codePoints[i]; - if (Character.isBmpCodePoint(c)) - continue; - else if (Character.isValidCodePoint(c)) - n++; - else throw new IllegalArgumentException(Integer.toString(c)); - } - - // Pass 2: Allocate and fill in char[] - final char[] v = new char[n]; - - for (int i = offset, j = 0; i < end; i++, j++) { - int c = codePoints[i]; - if (Character.isBmpCodePoint(c)) - v[j] = (char)c; - else - Character.toSurrogates(c, v, j++); - } - - this.value = v; + this.coder = UTF16; + this.value = StringUTF16.toBytes(codePoints, offset, count); } /** @@ -332,20 +350,24 @@ */ @Deprecated public String(byte ascii[], int hibyte, int offset, int count) { - checkBounds(ascii, offset, count); - char[] value = new char[count]; - - if (hibyte == 0) { - for (int i = count; i-- > 0;) { - value[i] = (char)(ascii[i + offset] & 0xff); - } + checkBoundsOffCount(offset, count, ascii.length); + if (count == 0) { + this.value = "".value; + this.coder = "".coder; + return; + } + if (COMPACT_STRINGS && (byte)hibyte == 0) { + this.value = Arrays.copyOfRange(ascii, offset, offset + count); + this.coder = LATIN1; } else { hibyte <<= 8; - for (int i = count; i-- > 0;) { - value[i] = (char)(hibyte | (ascii[i + offset] & 0xff)); + byte[] val = StringUTF16.newBytesFor(count); + for (int i = 0; i < count; i++) { + StringUTF16.putChar(val, i, hibyte | (ascii[offset++] & 0xff)); } + this.value = val; + this.coder = UTF16; } - this.value = value; } /** @@ -383,19 +405,6 @@ this(ascii, hibyte, 0, ascii.length); } - /* Common private utility method used to bounds check the byte array - * and requested offset & length values used by the String(byte[],..) - * constructors. - */ - private static void checkBounds(byte[] bytes, int offset, int length) { - if (length < 0) - throw new StringIndexOutOfBoundsException(length); - if (offset < 0) - throw new StringIndexOutOfBoundsException(offset); - if (offset > bytes.length - length) - throw new StringIndexOutOfBoundsException(offset + length); - } - /** * Constructs a new {@code String} by decoding the specified subarray of * bytes using the specified charset. The length of the new {@code String} @@ -433,8 +442,11 @@ throws UnsupportedEncodingException { if (charsetName == null) throw new NullPointerException("charsetName"); - checkBounds(bytes, offset, length); - this.value = StringCoding.decode(charsetName, bytes, offset, length); + checkBoundsOffCount(offset, length, bytes.length); + StringCoding.Result ret = + StringCoding.decode(charsetName, bytes, offset, length); + this.value = ret.value; + this.coder = ret.coder; } /** @@ -470,8 +482,11 @@ public String(byte bytes[], int offset, int length, Charset charset) { if (charset == null) throw new NullPointerException("charset"); - checkBounds(bytes, offset, length); - this.value = StringCoding.decode(charset, bytes, offset, length); + checkBoundsOffCount(offset, length, bytes.length); + StringCoding.Result ret = + StringCoding.decode(charset, bytes, offset, length); + this.value = ret.value; + this.coder = ret.coder; } /** @@ -553,8 +568,10 @@ * @since 1.1 */ public String(byte bytes[], int offset, int length) { - checkBounds(bytes, offset, length); - this.value = StringCoding.decode(bytes, offset, length); + checkBoundsOffCount(offset, length, bytes.length); + StringCoding.Result ret = StringCoding.decode(bytes, offset, length); + this.value = ret.value; + this.coder = ret.coder; } /** @@ -587,9 +604,7 @@ * A {@code StringBuffer} */ public String(StringBuffer buffer) { - synchronized(buffer) { - this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); - } + this(buffer.toString()); } /** @@ -608,18 +623,20 @@ * @since 1.5 */ public String(StringBuilder builder) { - this.value = Arrays.copyOf(builder.getValue(), builder.length()); + this(builder, null); } - /* + /* * Package private constructor which shares value array for speed. * this constructor is always expected to be called with share==true. * a separate constructor is needed because we already have a public * String(char[]) constructor that makes a copy of the given char[]. */ - String(char[] value, boolean share) { + // TBD: this is kept for package internal use (Thread/System), + // should be removed if they all have a byte[] version + String(char[] val, boolean share) { // assert share : "unshared not supported"; - this.value = value; + this(val, 0, val.length, null); } /** @@ -631,7 +648,7 @@ * object. */ public int length() { - return value.length; + return value.length >> coder(); } /** @@ -665,10 +682,11 @@ * string. */ public char charAt(int index) { - if ((index < 0) || (index >= value.length)) { - throw new StringIndexOutOfBoundsException(index); + if (isLatin1()) { + return StringLatin1.charAt(value, index); + } else { + return StringUTF16.charAt(value, index); } - return value[index]; } /** @@ -694,10 +712,13 @@ * @since 1.5 */ public int codePointAt(int index) { - if ((index < 0) || (index >= value.length)) { - throw new StringIndexOutOfBoundsException(index); - } - return Character.codePointAtImpl(value, index, value.length); + if (isLatin1()) { + checkIndex(index, value.length); + return value[index] & 0xff; + } + int length = value.length >> 1; + checkIndex(index, length); + return StringUTF16.codePointAt(value, index, length); } /** @@ -724,10 +745,13 @@ */ public int codePointBefore(int index) { int i = index - 1; - if ((i < 0) || (i >= value.length)) { + if (i < 0 || i >= length()) { throw new StringIndexOutOfBoundsException(index); } - return Character.codePointBeforeImpl(value, index, 0); + if (isLatin1()) { + return (value[i] & 0xff); + } + return StringUTF16.codePointBefore(value, index); } /** @@ -752,10 +776,14 @@ * @since 1.5 */ public int codePointCount(int beginIndex, int endIndex) { - if (beginIndex < 0 || endIndex > value.length || beginIndex > endIndex) { + if (beginIndex < 0 || beginIndex > endIndex || + endIndex > length()) { throw new IndexOutOfBoundsException(); } - return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex); + if (isLatin1()) { + return endIndex - beginIndex; + } + return StringUTF16.codePointCount(value, beginIndex, endIndex); } /** @@ -779,19 +807,10 @@ * @since 1.5 */ public int offsetByCodePoints(int index, int codePointOffset) { - if (index < 0 || index > value.length) { + if (index < 0 || index > length()) { throw new IndexOutOfBoundsException(); } - return Character.offsetByCodePointsImpl(value, 0, value.length, - index, codePointOffset); - } - - /** - * Copy characters from this string into dst starting at dstBegin. - * This method doesn't perform any range checking. - */ - void getChars(char dst[], int dstBegin) { - System.arraycopy(value, 0, dst, dstBegin, value.length); + return Character.offsetByCodePoints(this, index, codePointOffset); } /** @@ -825,16 +844,13 @@ * {@code dst.length} */ public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) { - if (srcBegin < 0) { - throw new StringIndexOutOfBoundsException(srcBegin); - } - if (srcEnd > value.length) { - throw new StringIndexOutOfBoundsException(srcEnd); - } - if (srcBegin > srcEnd) { - throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); + checkBoundsBeginEnd(srcBegin, srcEnd, length()); + checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length); + if (isLatin1()) { + StringLatin1.getChars(value, srcBegin, srcEnd, dst, dstBegin); + } else { + StringUTF16.getChars(value, srcBegin, srcEnd, dst, dstBegin); } - System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); } /** @@ -882,24 +898,13 @@ */ @Deprecated public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin) { - if (srcBegin < 0) { - throw new StringIndexOutOfBoundsException(srcBegin); - } - if (srcEnd > value.length) { - throw new StringIndexOutOfBoundsException(srcEnd); - } - if (srcBegin > srcEnd) { - throw new StringIndexOutOfBoundsException(srcEnd - srcBegin); - } + checkBoundsBeginEnd(srcBegin, srcEnd, length()); Objects.requireNonNull(dst); - - int j = dstBegin; - int n = srcEnd; - int i = srcBegin; - char[] val = value; /* avoid getfield opcode */ - - while (i < n) { - dst[j++] = (byte)val[i++]; + checkBoundsOffCount(dstBegin, srcEnd - srcBegin, dst.length); + if (isLatin1()) { + StringLatin1.getBytes(value, srcBegin, srcEnd, dst, dstBegin); + } else { + StringUTF16.getBytes(value, srcBegin, srcEnd, dst, dstBegin); } } @@ -926,7 +931,7 @@ public byte[] getBytes(String charsetName) throws UnsupportedEncodingException { if (charsetName == null) throw new NullPointerException(); - return StringCoding.encode(charsetName, value, 0, value.length); + return StringCoding.encode(charsetName, coder(), value); } /** @@ -949,8 +954,8 @@ */ public byte[] getBytes(Charset charset) { if (charset == null) throw new NullPointerException(); - return StringCoding.encode(charset, value, 0, value.length); - } + return StringCoding.encode(charset, coder(), value); + } /** * Encodes this {@code String} into a sequence of bytes using the @@ -966,7 +971,7 @@ * @since 1.1 */ public byte[] getBytes() { - return StringCoding.encode(value, 0, value.length); + return StringCoding.encode(coder(), value); } /** @@ -987,23 +992,15 @@ * @see #compareTo(String) * @see #equalsIgnoreCase(String) */ - @HotSpotIntrinsicCandidate public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { - char[] v1 = value; - char[] v2 = ((String)anObject).value; - int n = v1.length; - if (n == v2.length) { - int i = 0; - while (n-- != 0) { - if (v1[i] != v2[i]) - return false; - i++; - } - return true; + String aString = (String)anObject; + if (coder() == aString.coder()) { + return isLatin1() ? StringLatin1.equals(value, aString.value) + : StringUTF16.equals(value, aString.value); } } return false; @@ -1032,16 +1029,28 @@ } private boolean nonSyncContentEquals(AbstractStringBuilder sb) { - char[] v1 = value; - char[] v2 = sb.getValue(); - int n = v1.length; - if (n != sb.length()) { + int len = length(); + if (len != sb.length()) { return false; } - for (int i = 0; i < n; i++) { - if (v1[i] != v2[i]) { + byte v1[] = value; + byte v2[] = sb.getValue(); + if (coder() == sb.getCoder()) { + int n = v1.length; + for (int i = 0; i < n; i++) { + if (v1[i] != v2[i]) { + return false; + } + } + } else { + if (!isLatin1()) { // utf16 str and latin1 abs can never be "equal" return false; } + for (int i = 0; i < len; i++) { + if ((char)(v1[i] & 0xff) != StringUTF16.getChar(v2, i)) { + return false; + } + } } return true; } @@ -1081,14 +1090,22 @@ return equals(cs); } // Argument is a generic CharSequence - char[] v1 = value; - int n = v1.length; - if (n != cs.length()) { + int n = cs.length(); + if (n != length()) { return false; } - for (int i = 0; i < n; i++) { - if (v1[i] != cs.charAt(i)) { - return false; + byte[] val = this.value; + if (isLatin1()) { + for (int i = 0; i < n; i++) { + if ((val[i] & 0xff) != cs.charAt(i)) { + return false; + } + } + } else { + for (int i = 0; i < n; i++) { + if (StringUTF16.getChar(val, i) != cs.charAt(i)) { + return false; + } } } return true; @@ -1125,8 +1142,8 @@ public boolean equalsIgnoreCase(String anotherString) { return (this == anotherString) ? true : (anotherString != null) - && (anotherString.value.length == value.length) - && regionMatches(true, 0, anotherString, 0, value.length); + && (anotherString.length() == length()) + && regionMatches(true, 0, anotherString, 0, length()); } /** @@ -1173,23 +1190,16 @@ * value greater than {@code 0} if this string is * lexicographically greater than the string argument. */ - @HotSpotIntrinsicCandidate public int compareTo(String anotherString) { - char[] v1 = value; - char[] v2 = anotherString.value; - int len1 = v1.length; - int len2 = v2.length; - int lim = Math.min(len1, len2); - - for (int k = 0; k < lim; k++) { - char c1 = v1[k]; - char c2 = v2[k]; - if (c1 != c2) { - return c1 - c2; - } - } - return len1 - len2; - } + byte v1[] = value; + byte v2[] = anotherString.value; + if (coder() == anotherString.coder()) { + return isLatin1() ? StringLatin1.compareTo(v1, v2) + : StringUTF16.compareTo(v1, v2); + } + return isLatin1() ? StringLatin1.compareToUTF16(v1, v2) + : StringUTF16.compareToLatin1(v1, v2); + } /** * A Comparator that orders {@code String} objects as by @@ -1210,12 +1220,18 @@ private static final long serialVersionUID = 8575799808933029326L; public int compare(String s1, String s2) { + byte v1[] = s1.value; + byte v2[] = s2.value; int n1 = s1.length(); int n2 = s2.length(); + boolean s1IsLatin1 = s1.isLatin1(); + boolean s2IsLatin1 = s2.isLatin1(); int min = Math.min(n1, n2); for (int i = 0; i < min; i++) { - char c1 = s1.charAt(i); - char c2 = s2.charAt(i); + char c1 = s1IsLatin1 ? StringLatin1.getChar(v1, i) + : StringUTF16.getChar(v1, i); + char c2 = s2IsLatin1 ? StringLatin1.getChar(v2, i) + : StringUTF16.getChar(v2, i); if (c1 != c2) { c1 = Character.toUpperCase(c1); c2 = Character.toUpperCase(c2); @@ -1294,21 +1310,41 @@ * exactly matches the specified subregion of the string argument; * {@code false} otherwise. */ - public boolean regionMatches(int toffset, String other, int ooffset, - int len) { - char[] ta = value; - int to = toffset; - char[] pa = other.value; - int po = ooffset; + public boolean regionMatches(int toffset, String other, int ooffset, int len) { + byte tv[] = value; + byte ov[] = other.value; // Note: toffset, ooffset, or len might be near -1>>>1. - if ((ooffset < 0) || (toffset < 0) - || (toffset > (long)ta.length - len) - || (ooffset > (long)pa.length - len)) { + if ((ooffset < 0) || (toffset < 0) || + (toffset > (long)length() - len) || + (ooffset > (long)other.length() - len)) { return false; } - while (len-- > 0) { - if (ta[to++] != pa[po++]) { - return false; + if (coder() == other.coder()) { + if (!isLatin1() && (len > 0)) { + toffset = toffset << 1; + ooffset = ooffset << 1; + len = len << 1; + } + while (len-- > 0) { + if (tv[toffset++] != ov[ooffset++]) { + return false; + } + } + } else { + if (coder() == LATIN1) { + while (len-- > 0) { + if (StringLatin1.getChar(tv, toffset++) != + StringUTF16.getChar(ov, ooffset++)) { + return false; + } + } + } else { + while (len-- > 0) { + if (StringUTF16.getChar(tv, toffset++) != + StringLatin1.getChar(ov, ooffset++)) { + return false; + } + } } } return true; @@ -1366,43 +1402,25 @@ */ public boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) { - char[] ta = value; - int to = toffset; - char[] pa = other.value; - int po = ooffset; + if (!ignoreCase) { + return regionMatches(toffset, other, ooffset, len); + } // Note: toffset, ooffset, or len might be near -1>>>1. if ((ooffset < 0) || (toffset < 0) - || (toffset > (long)ta.length - len) - || (ooffset > (long)pa.length - len)) { - return false; - } - while (len-- > 0) { - char c1 = ta[to++]; - char c2 = pa[po++]; - if (c1 == c2) { - continue; - } - if (ignoreCase) { - // If characters don't match but case may be ignored, - // try converting both characters to uppercase. - // If the results match, then the comparison scan should - // continue. - char u1 = Character.toUpperCase(c1); - char u2 = Character.toUpperCase(c2); - if (u1 == u2) { - continue; - } - // Unfortunately, conversion to uppercase does not work properly - // for the Georgian alphabet, which has strange rules about case - // conversion. So we need to make one last check before - // exiting. - if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { - continue; - } - } + || (toffset > (long)length() - len) + || (ooffset > (long)other.length() - len)) { return false; } - return true; + byte tv[] = value; + byte ov[] = other.value; + if (coder() == other.coder()) { + return isLatin1() + ? StringLatin1.regionMatchesCI(tv, toffset, ov, ooffset, len) + : StringUTF16.regionMatchesCI(tv, toffset, ov, ooffset, len); + } + return isLatin1() + ? StringLatin1.regionMatchesCI_UTF16(tv, toffset, ov, ooffset, len) + : StringUTF16.regionMatchesCI_Latin1(tv, toffset, ov, ooffset, len); } /** @@ -1423,19 +1441,31 @@ * */ public boolean startsWith(String prefix, int toffset) { - char[] ta = value; - int to = toffset; - char[] pa = prefix.value; - int po = 0; - int pc = pa.length; // Note: toffset might be near -1>>>1. - if ((toffset < 0) || (toffset > ta.length - pc)) { + if (toffset < 0 || toffset > length() - prefix.length()) { return false; } - while (--pc >= 0) { - if (ta[to++] != pa[po++]) { + byte ta[] = value; + byte pa[] = prefix.value; + int po = 0; + int pc = pa.length; + if (coder() == prefix.coder()) { + int to = isLatin1() ? toffset : toffset << 1; + while (po < pc) { + if (ta[to++] != pa[po++]) { + return false; + } + } + } else { + if (isLatin1()) { // && pcoder == UTF16 return false; } + // coder == UTF16 && pcoder == LATIN1) + while (po < pc) { + if (StringUTF16.getChar(ta, toffset++) != (pa[po++] & 0xff)) { + return false; + } + } } return true; } @@ -1469,7 +1499,7 @@ * as determined by the {@link #equals(Object)} method. */ public boolean endsWith(String suffix) { - return startsWith(suffix, value.length - suffix.value.length); + return startsWith(suffix, length() - suffix.length()); } /** @@ -1486,16 +1516,11 @@ * @return a hash code value for this object. */ public int hashCode() { - int h = hash; - if (h == 0) { - for (char v : value) { - h = 31 * h + v; - } - if (h != 0) { - hash = h; - } + if (hash == 0 && value.length > 0) { + hash = isLatin1() ? StringLatin1.hashCode(value) + : StringUTF16.hashCode(value); } - return h; + return hash; } /** @@ -1566,45 +1591,8 @@ * if the character does not occur. */ public int indexOf(int ch, int fromIndex) { - final int max = value.length; - if (fromIndex < 0) { - fromIndex = 0; - } else if (fromIndex >= max) { - // Note: fromIndex might be near -1>>>1. - return -1; - } - - if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { - // handle most cases here (ch is a BMP code point or a - // negative value (invalid code point)) - final char[] value = this.value; - for (int i = fromIndex; i < max; i++) { - if (value[i] == ch) { - return i; - } - } - return -1; - } else { - return indexOfSupplementary(ch, fromIndex); - } - } - - /** - * Handles (rare) calls of indexOf with a supplementary character. - */ - private int indexOfSupplementary(int ch, int fromIndex) { - if (Character.isValidCodePoint(ch)) { - final char[] value = this.value; - final char hi = Character.highSurrogate(ch); - final char lo = Character.lowSurrogate(ch); - final int max = value.length - 1; - for (int i = fromIndex; i < max; i++) { - if (value[i] == hi && value[i + 1] == lo) { - return i; - } - } - } - return -1; + return isLatin1() ? StringLatin1.indexOf(value, ch, fromIndex) + : StringUTF16.indexOf(value, ch, fromIndex); } /** @@ -1631,7 +1619,7 @@ * {@code -1} if the character does not occur. */ public int lastIndexOf(int ch) { - return lastIndexOf(ch, value.length - 1); + return lastIndexOf(ch, length() - 1); } /** @@ -1669,38 +1657,8 @@ * if the character does not occur before that point. */ public int lastIndexOf(int ch, int fromIndex) { - if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { - // handle most cases here (ch is a BMP code point or a - // negative value (invalid code point)) - final char[] value = this.value; - int i = Math.min(fromIndex, value.length - 1); - for (; i >= 0; i--) { - if (value[i] == ch) { - return i; - } - } - return -1; - } else { - return lastIndexOfSupplementary(ch, fromIndex); - } - } - - /** - * Handles (rare) calls of lastIndexOf with a supplementary character. - */ - private int lastIndexOfSupplementary(int ch, int fromIndex) { - if (Character.isValidCodePoint(ch)) { - final char[] value = this.value; - char hi = Character.highSurrogate(ch); - char lo = Character.lowSurrogate(ch); - int i = Math.min(fromIndex, value.length - 2); - for (; i >= 0; i--) { - if (value[i] == hi && value[i + 1] == lo) { - return i; - } - } - } - return -1; + return isLatin1() ? StringLatin1.lastIndexOf(value, ch, fromIndex) + : StringUTF16.lastIndexOf(value, ch, fromIndex); } /** @@ -1717,9 +1675,15 @@ * @return the index of the first occurrence of the specified substring, * or {@code -1} if there is no such occurrence. */ - @HotSpotIntrinsicCandidate public int indexOf(String str) { - return indexOf(str, 0); + if (coder() == str.coder()) { + return isLatin1() ? StringLatin1.indexOf(value, str.value) + : StringUTF16.indexOf(value, str.value); + } + if (coder() == LATIN1) { // str.coder == UTF16 + return -1; + } + return StringUTF16.indexOfLatin1(value, str.value); } /** @@ -1740,8 +1704,7 @@ * or {@code -1} if there is no such occurrence. */ public int indexOf(String str, int fromIndex) { - return indexOf(value, 0, value.length, - str.value, 0, str.value.length, fromIndex); + return indexOf(value, coder(), length(), str, fromIndex); } /** @@ -1749,68 +1712,38 @@ * source is the character array being searched, and the target * is the string being searched for. * - * @param source the characters being searched. - * @param sourceOffset offset of the source string. - * @param sourceCount count of the source string. - * @param target the characters being searched for. - * @param fromIndex the index to begin searching from. - */ - static int indexOf(char[] source, int sourceOffset, int sourceCount, - String target, int fromIndex) { - return indexOf(source, sourceOffset, sourceCount, - target.value, 0, target.value.length, - fromIndex); - } + * @param src the characters being searched. + * @param srcCoder the coder of the source string. + * @param srcCount length of the source string. + * @param tgtStr the characters being searched for. + * @param fromIndex the index to begin searching from. + */ + static int indexOf(byte[] src, byte srcCoder, int srcCount, + String tgtStr, int fromIndex) { + + byte[] tgt = tgtStr.value; + byte tgtCoder = tgtStr.coder(); + int tgtCount = tgtStr.length(); - /** - * Code shared by String and StringBuffer to do searches. The - * source is the character array being searched, and the target - * is the string being searched for. - * - * @param source the characters being searched. - * @param sourceOffset offset of the source string. - * @param sourceCount count of the source string. - * @param target the characters being searched for. - * @param targetOffset offset of the target string. - * @param targetCount count of the target string. - * @param fromIndex the index to begin searching from. - */ - static int indexOf(char[] source, int sourceOffset, int sourceCount, - char[] target, int targetOffset, int targetCount, - int fromIndex) { - if (fromIndex >= sourceCount) { - return (targetCount == 0 ? sourceCount : -1); + if (fromIndex >= srcCount) { + return (tgtCount == 0 ? srcCount : -1); } if (fromIndex < 0) { fromIndex = 0; } - if (targetCount == 0) { + if (tgtCount == 0) { return fromIndex; } - - char first = target[targetOffset]; - int max = sourceOffset + (sourceCount - targetCount); - - for (int i = sourceOffset + fromIndex; i <= max; i++) { - /* Look for first character. */ - if (source[i] != first) { - while (++i <= max && source[i] != first); - } - - /* Found first character, now look at the rest of v2 */ - if (i <= max) { - int j = i + 1; - int end = j + targetCount - 1; - for (int k = targetOffset + 1; j < end && source[j] - == target[k]; j++, k++); - - if (j == end) { - /* Found whole string. */ - return i - sourceOffset; - } - } + if (srcCoder == tgtCoder) { + return srcCoder == LATIN1 + ? StringLatin1.indexOf(src, srcCount, tgt, tgtCount, fromIndex) + : StringUTF16.indexOf(src, srcCount, tgt, tgtCount, fromIndex); } - return -1; + if (srcCoder == LATIN1) { // && tgtCoder == UTF16 + return -1; + } + // srcCoder == UTF16 && tgtCoder == LATIN1) { + return StringUTF16.indexOfLatin1(src, srcCount, tgt, tgtCount, fromIndex); } /** @@ -1829,7 +1762,7 @@ * or {@code -1} if there is no such occurrence. */ public int lastIndexOf(String str) { - return lastIndexOf(str, value.length); + return lastIndexOf(str, length()); } /** @@ -1850,8 +1783,7 @@ * or {@code -1} if there is no such occurrence. */ public int lastIndexOf(String str, int fromIndex) { - return lastIndexOf(value, 0, value.length, - str.value, 0, str.value.length, fromIndex); + return lastIndexOf(value, coder(), length(), str, fromIndex); } /** @@ -1859,40 +1791,22 @@ * source is the character array being searched, and the target * is the string being searched for. * - * @param source the characters being searched. - * @param sourceOffset offset of the source string. - * @param sourceCount count of the source string. - * @param target the characters being searched for. - * @param fromIndex the index to begin searching from. - */ - static int lastIndexOf(char[] source, int sourceOffset, int sourceCount, - String target, int fromIndex) { - return lastIndexOf(source, sourceOffset, sourceCount, - target.value, 0, target.value.length, - fromIndex); - } - - /** - * Code shared by String and StringBuffer to do searches. The - * source is the character array being searched, and the target - * is the string being searched for. - * - * @param source the characters being searched. - * @param sourceOffset offset of the source string. - * @param sourceCount count of the source string. - * @param target the characters being searched for. - * @param targetOffset offset of the target string. - * @param targetCount count of the target string. - * @param fromIndex the index to begin searching from. - */ - static int lastIndexOf(char[] source, int sourceOffset, int sourceCount, - char[] target, int targetOffset, int targetCount, - int fromIndex) { + * @param src the characters being searched. + * @param srcCoder coder handles the mapping between bytes/chars + * @param srcCount count of the source string. + * @param tgt the characters being searched for. + * @param fromIndex the index to begin searching from. + */ + static int lastIndexOf(byte[] src, byte srcCoder, int srcCount, + String tgtStr, int fromIndex) { + byte[] tgt = tgtStr.value; + byte tgtCoder = tgtStr.coder(); + int tgtCount = tgtStr.length(); /* * Check arguments; return immediately where possible. For * consistency, don't check for null str. */ - int rightIndex = sourceCount - targetCount; + int rightIndex = srcCount - tgtCount; if (fromIndex < 0) { return -1; } @@ -1900,34 +1814,41 @@ fromIndex = rightIndex; } /* Empty string always matches. */ - if (targetCount == 0) { + if (tgtCount == 0) { return fromIndex; } - - int strLastIndex = targetOffset + targetCount - 1; - char strLastChar = target[strLastIndex]; - int min = sourceOffset + targetCount - 1; + if (srcCoder == tgtCoder) { + return srcCoder == LATIN1 + ? StringLatin1.lastIndexOf(src, srcCount, tgt, tgtCount, fromIndex) + : StringUTF16.lastIndexOf(src, srcCount, tgt, tgtCount, fromIndex); + } + if (srcCoder == LATIN1) { // && tgtCoder == UTF16 + return -1; + } + // srcCoder == UTF16 && tgtCoder == LATIN1 + int min = tgtCount - 1; int i = min + fromIndex; + int strLastIndex = tgtCount - 1; + char strLastChar = (char)(tgt[strLastIndex] & 0xff); startSearchForLastChar: while (true) { - while (i >= min && source[i] != strLastChar) { + while (i >= min && StringUTF16.getChar(src, i) != strLastChar) { i--; } if (i < min) { return -1; } int j = i - 1; - int start = j - (targetCount - 1); + int start = j - strLastIndex; int k = strLastIndex - 1; - while (j > start) { - if (source[j--] != target[k--]) { + if (StringUTF16.getChar(src, j--) != (tgt[k--] & 0xff)) { i--; continue startSearchForLastChar; } } - return start - sourceOffset + 1; + return start + 1; } } @@ -1949,17 +1870,18 @@ * length of this {@code String} object. */ public String substring(int beginIndex) { - if (beginIndex <= 0) { - if (beginIndex < 0) { - throw new StringIndexOutOfBoundsException(beginIndex); - } - return this; + if (beginIndex < 0) { + throw new StringIndexOutOfBoundsException(beginIndex); } - int subLen = value.length - beginIndex; + int subLen = length() - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } - return new String(value, beginIndex, subLen); + if (beginIndex == 0) { + return this; + } + return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen) + : StringUTF16.newString(value, beginIndex, subLen); } /** @@ -1985,22 +1907,14 @@ * {@code endIndex}. */ public String substring(int beginIndex, int endIndex) { - if (beginIndex <= 0) { - if (beginIndex < 0) { - throw new StringIndexOutOfBoundsException(beginIndex); - } - if (endIndex == value.length) { - return this; - } - } - if (endIndex > value.length) { - throw new StringIndexOutOfBoundsException(endIndex); - } + int length = length(); + checkBoundsBeginEnd(beginIndex, endIndex, length); int subLen = endIndex - beginIndex; - if (subLen < 0) { - throw new StringIndexOutOfBoundsException(subLen); + if (beginIndex == 0 && endIndex == length) { + return this; } - return new String(value, beginIndex, subLen); + return isLatin1() ? StringLatin1.newString(value, beginIndex, subLen) + : StringUTF16.newString(value, beginIndex, subLen); } /** @@ -2057,14 +1971,23 @@ * characters followed by the string argument's characters. */ public String concat(String str) { - int otherLen = str.length(); - if (otherLen == 0) { + int olen = str.length(); + if (olen == 0) { return this; } - int len = value.length; - char[] buf = Arrays.copyOf(value, len + otherLen); - str.getChars(buf, len); - return new String(buf, true); + if (coder() == str.coder()) { + byte[] val = this.value; + byte[] oval = str.value; + int len = val.length + oval.length; + byte[] buf = Arrays.copyOf(val, len); + System.arraycopy(oval, 0, buf, val.length, oval.length); + return new String(buf, coder); + } + int len = length(); + byte[] buf = StringUTF16.newBytesFor(len + olen); + getBytes(buf, 0, UTF16); + str.getBytes(buf, len, UTF16); + return new String(buf, UTF16); } /** @@ -2098,26 +2021,10 @@ */ public String replace(char oldChar, char newChar) { if (oldChar != newChar) { - char[] val = value; /* avoid getfield opcode */ - int len = val.length; - int i = -1; - - while (++i < len) { - if (val[i] == oldChar) { - break; - } - } - if (i < len) { - char[] buf = new char[len]; - for (int j = 0; j < i; j++) { - buf[j] = val[j]; - } - while (i < len) { - char c = val[i]; - buf[i] = (c == oldChar) ? newChar : c; - i++; - } - return new String(buf, true); + String ret = isLatin1() ? StringLatin1.replace(value, oldChar, newChar) + : StringUTF16.replace(value, oldChar, newChar); + if (ret != null) { + return ret; } } return this; @@ -2269,29 +2176,27 @@ * @since 1.5 */ public String replace(CharSequence target, CharSequence replacement) { - String starget = target.toString(); - String srepl = replacement.toString(); - int j = indexOf(starget); + String tgtStr = target.toString(); + String replStr = replacement.toString(); + int j = indexOf(tgtStr); if (j < 0) { return this; } - int targLen = starget.length(); - int targLen1 = Math.max(targLen, 1); - final char[] value = this.value; - final char[] replValue = srepl.value; - int newLenHint = value.length - targLen + replValue.length; + int tgtLen = tgtStr.length(); + int tgtLen1 = Math.max(tgtLen, 1); + int thisLen = length(); + + int newLenHint = thisLen - tgtLen + replStr.length(); if (newLenHint < 0) { throw new OutOfMemoryError(); } StringBuilder sb = new StringBuilder(newLenHint); int i = 0; do { - sb.append(value, i, j - i) - .append(replValue); - i = j + targLen; - } while (j < value.length && (j = indexOf(starget, j + targLen1)) > 0); - - return sb.append(value, i, value.length - i).toString(); + sb.append(this, i, j).append(replStr); + i = j + tgtLen; + } while (j < thisLen && (j = indexOf(tgtStr, j + tgtLen1)) > 0); + return sb.append(this, i, thisLen).toString(); } /** @@ -2388,7 +2293,7 @@ the second is not the ascii digit or ascii letter. */ char ch = 0; - if (((regex.value.length == 1 && + if (((regex.length() == 1 && ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) || (regex.length() == 2 && regex.charAt(0) == '\\' && @@ -2408,8 +2313,9 @@ off = next + 1; } else { // last one //assert (list.size() == limit - 1); - list.add(substring(off, value.length)); - off = value.length; + int last = length(); + list.add(substring(off, last)); + off = last; break; } } @@ -2419,7 +2325,7 @@ // Add remaining segment if (!limited || list.size() < limit) - list.add(substring(off, value.length)); + list.add(substring(off, length())); // Construct result int resultSize = list.size(); @@ -2613,95 +2519,8 @@ * @since 1.1 */ public String toLowerCase(Locale locale) { - if (locale == null) { - throw new NullPointerException(); - } - int first; - boolean hasSurr = false; - final int len = value.length; - - // Now check if there are any characters that need to be changed, or are surrogate - for (first = 0 ; first < len; first++) { - int cp = (int)value[first]; - if (Character.isSurrogate((char)cp)) { - hasSurr = true; - break; - } - if (cp != Character.toLowerCase(cp)) { // no need to check Character.ERROR - break; - } - } - if (first == len) - return this; - char[] result = new char[len]; - System.arraycopy(value, 0, result, 0, first); // Just copy the first few - // lowerCase characters. - String lang = locale.getLanguage(); - if (lang == "tr" || lang == "az" || lang == "lt") { - return toLowerCaseEx(result, first, locale, true); - } - if (hasSurr) { - return toLowerCaseEx(result, first, locale, false); - } - for (int i = first; i < len; i++) { - int cp = (int)value[i]; - if (cp == '\u03A3' || // GREEK CAPITAL LETTER SIGMA - Character.isSurrogate((char)cp)) { - return toLowerCaseEx(result, i, locale, false); - } - if (cp == '\u0130') { // LATIN CAPITAL LETTER I WITH DOT ABOVE - return toLowerCaseEx(result, i, locale, true); - } - cp = Character.toLowerCase(cp); - if (!Character.isBmpCodePoint(cp)) { - return toLowerCaseEx(result, i, locale, false); - } - result[i] = (char)cp; - } - return new String(result, true); - } - - private String toLowerCaseEx(char[] result, int first, Locale locale, boolean localeDependent) { - int resultOffset = first; - int srcCount; - for (int i = first; i < value.length; i += srcCount) { - int srcChar = (int)value[i]; - int lowerChar; - char[] lowerCharArray; - srcCount = 1; - if (Character.isSurrogate((char)srcChar)) { - srcChar = codePointAt(i); - srcCount = Character.charCount(srcChar); - } - if (localeDependent || srcChar == '\u03A3') { // GREEK CAPITAL LETTER SIGMA - lowerChar = ConditionalSpecialCasing.toLowerCaseEx(this, i, locale); - } else { - lowerChar = Character.toLowerCase(srcChar); - } - if (Character.isBmpCodePoint(lowerChar)) { // Character.ERROR is not a bmp - result[resultOffset++] = (char)lowerChar; - } else { - if (lowerChar == Character.ERROR) { - lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(this, i, locale); - } else if (srcCount == 2) { - resultOffset += Character.toChars(lowerChar, result, resultOffset); - continue; - } else { - lowerCharArray = Character.toChars(lowerChar); - } - /* Grow result if needed */ - int mapLen = lowerCharArray.length; - if (mapLen > srcCount) { - char[] result2 = new char[result.length + mapLen - srcCount]; - System.arraycopy(result, 0, result2, 0, resultOffset); - result = result2; - } - for (int x = 0; x < mapLen; ++x) { - result[resultOffset++] = lowerCharArray[x]; - } - } - } - return new String(result, 0, resultOffset); + return isLatin1() ? StringLatin1.toLowerCase(this, value, locale) + : StringUTF16.toLowerCase(this, value, locale); } /** @@ -2776,98 +2595,8 @@ * @since 1.1 */ public String toUpperCase(Locale locale) { - if (locale == null) { - throw new NullPointerException(); - } - int first; - boolean hasSurr = false; - final int len = value.length; - - // Now check if there are any characters that need to be changed, or are surrogate - for (first = 0 ; first < len; first++ ) { - int cp = (int)value[first]; - if (Character.isSurrogate((char)cp)) { - hasSurr = true; - break; - } - if (cp != Character.toUpperCaseEx(cp)) { // no need to check Character.ERROR - break; - } - } - if (first == len) { - return this; - } - char[] result = new char[len]; - System.arraycopy(value, 0, result, 0, first); // Just copy the first few - // upperCase characters. - String lang = locale.getLanguage(); - if (lang == "tr" || lang == "az" || lang == "lt") { - return toUpperCaseEx(result, first, locale, true); - } - if (hasSurr) { - return toUpperCaseEx(result, first, locale, false); - } - for (int i = first; i < len; i++) { - int cp = (int)value[i]; - if (Character.isSurrogate((char)cp)) { - return toUpperCaseEx(result, i, locale, false); - } - cp = Character.toUpperCaseEx(cp); - if (!Character.isBmpCodePoint(cp)) { // Character.ERROR is not bmp - return toUpperCaseEx(result, i, locale, false); - } - result[i] = (char)cp; - } - return new String(result, true); - } - - private String toUpperCaseEx(char[] result, int first, Locale locale, - boolean localeDependent) { - int resultOffset = first; - int srcCount; - for (int i = first; i < value.length; i += srcCount) { - int srcChar = (int)value[i]; - int upperChar; - char[] upperCharArray; - srcCount = 1; - if (Character.isSurrogate((char)srcChar)) { - srcChar = codePointAt(i); - srcCount = Character.charCount(srcChar); - } - if (localeDependent) { - upperChar = ConditionalSpecialCasing.toUpperCaseEx(this, i, locale); - } else { - upperChar = Character.toUpperCaseEx(srcChar); - } - if (Character.isBmpCodePoint(upperChar)) { - result[resultOffset++] = (char)upperChar; - } else { - if (upperChar == Character.ERROR) { - if (localeDependent) { - upperCharArray = - ConditionalSpecialCasing.toUpperCaseCharArray(this, i, locale); - } else { - upperCharArray = Character.toUpperCaseCharArray(srcChar); - } - } else if (srcCount == 2) { - resultOffset += Character.toChars(upperChar, result, resultOffset); - continue; - } else { - upperCharArray = Character.toChars(upperChar); - } - /* Grow result if needed */ - int mapLen = upperCharArray.length; - if (mapLen > srcCount) { - char[] result2 = new char[result.length + mapLen - srcCount]; - System.arraycopy(result, 0, result2, 0, resultOffset); - result = result2; - } - for (int x = 0; x < mapLen; ++x) { - result[resultOffset++] = upperCharArray[x]; - } - } - } - return new String(result, 0, resultOffset); + return isLatin1() ? StringLatin1.toUpperCase(this, value, locale) + : StringUTF16.toUpperCase(this, value, locale); } /** @@ -2925,17 +2654,9 @@ * trailing white space. */ public String trim() { - char[] val = value; /* avoid getfield opcode */ - int end = val.length; - int beg = 0; - - while ((beg < end) && (val[beg] <= ' ')) { - beg++; - } - while ((beg < end) && (val[end - 1] <= ' ')) { - end--; - } - return substring(beg, end); + String ret = isLatin1() ? StringLatin1.trim(value) + : StringUTF16.trim(value); + return ret == null ? this : ret; } /** @@ -2947,63 +2668,6 @@ return this; } - static class IntCharArraySpliterator implements Spliterator.OfInt { - private final char[] array; - private int index; // current index, modified on advance/split - private final int fence; // one past last index - private final int cs; - - IntCharArraySpliterator(char[] array, int acs) { - this(array, 0, array.length, acs); - } - - IntCharArraySpliterator(char[] array, int origin, int fence, int acs) { - this.array = array; - this.index = origin; - this.fence = fence; - this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED - | Spliterator.SUBSIZED; - } - - @Override - public OfInt trySplit() { - int lo = index, mid = (lo + fence) >>> 1; - return (lo >= mid) - ? null - : new IntCharArraySpliterator(array, lo, index = mid, cs); - } - - @Override - public void forEachRemaining(IntConsumer action) { - char[] a; int i, hi; // hoist accesses and checks from loop - if (action == null) - throw new NullPointerException(); - if ((a = array).length >= (hi = fence) && - (i = index) >= 0 && i < (index = hi)) { - do { action.accept(a[i]); } while (++i < hi); - } - } - - @Override - public boolean tryAdvance(IntConsumer action) { - if (action == null) - throw new NullPointerException(); - if (index >= 0 && index < fence) { - action.accept(array[index++]); - return true; - } - return false; - } - - @Override - public long estimateSize() { return (long)(fence - index); } - - @Override - public int characteristics() { - return cs; - } - } - /** * Returns a stream of {@code int} zero-extending the {@code char} values * from this sequence. Any char which maps to a >> 1; - if (lo >= mid) - return null; - - int midOneLess; - // If the mid-point intersects a surrogate pair - if (Character.isLowSurrogate(array[mid]) && - Character.isHighSurrogate(array[midOneLess = (mid -1)])) { - // If there is only one pair it cannot be split - if (lo >= midOneLess) - return null; - // Shift the mid-point to align with the surrogate pair - return new CodePointsSpliterator(array, lo, index = midOneLess, cs); - } - return new CodePointsSpliterator(array, lo, index = mid, cs); - } - - @Override - public void forEachRemaining(IntConsumer action) { - char[] a; int i, hi; // hoist accesses and checks from loop - if (action == null) - throw new NullPointerException(); - if ((a = array).length >= (hi = fence) && - (i = index) >= 0 && i < (index = hi)) { - do { - i = advance(a, i, hi, action); - } while (i < hi); - } - } - - @Override - public boolean tryAdvance(IntConsumer action) { - if (action == null) - throw new NullPointerException(); - if (index >= 0 && index < fence) { - index = advance(array, index, fence, action); - return true; - } - return false; - } - - // Advance one code point from the index, i, and return the next - // index to advance from - private static int advance(char[] a, int i, int hi, IntConsumer action) { - char c1 = a[i++]; - int cp = c1; - if (Character.isHighSurrogate(c1) && i < hi) { - char c2 = a[i]; - if (Character.isLowSurrogate(c2)) { - i++; - cp = Character.toCodePoint(c1, c2); - } - } - action.accept(cp); - return i; - } - - @Override - public long estimateSize() { return (long)(fence - index); } - - @Override - public int characteristics() { - return cs; - } - } /** * Returns a stream of code point values from this sequence. Any surrogate @@ -3118,7 +2700,9 @@ @Override public IntStream codePoints() { return StreamSupport.intStream( - new CodePointsSpliterator(value, Spliterator.IMMUTABLE), false); + isLatin1() ? new StringLatin1.CharsSpliterator(value, Spliterator.IMMUTABLE) + : new StringUTF16.CodePointsSpliterator(value, Spliterator.IMMUTABLE), + false); } /** @@ -3129,10 +2713,8 @@ * the character sequence represented by this string. */ public char[] toCharArray() { - // Cannot use Arrays.copyOf because of class initialization order issues - char[] result = new char[value.length]; - System.arraycopy(value, 0, result, 0, value.length); - return result; + return isLatin1() ? StringLatin1.toChars(value) + : StringUTF16.toChars(value); } /** @@ -3315,7 +2897,10 @@ * as its single character the argument {@code c}. */ public static String valueOf(char c) { - return new String(new char[]{c}, true); + if (COMPACT_STRINGS && StringLatin1.canEncode(c)) { + return new String(StringLatin1.toBytes(c), LATIN1); + } + return new String(StringUTF16.toBytes(c), UTF16); } /** @@ -3398,4 +2983,145 @@ * guaranteed to be from a pool of unique strings. */ public native String intern(); + + //////////////////////////////////////////////////////////////// + + /** + * Copy character bytes from this string into dst starting at dstBegin. + * This method doesn't perform any range checking. + * + * Invoker guarantees: dst is in UTF16 (inflate itself for asb), if two + * coders are different, and dst is big enough (range check) + * + * @param dstBegin the char index, not offset of byte[] + * @param coder the coder of dst[] + */ + void getBytes(byte dst[], int dstBegin, byte coder) { + if (coder() == coder) { + System.arraycopy(value, 0, dst, dstBegin << coder, value.length); + } else { // this.coder == LATIN && coder == UTF16 + StringLatin1.inflate(value, 0, dst, dstBegin, value.length); + } + } + + /* + * Package private constructor. Trailing Void argument is there for + * disambiguating it against other (public) constructors. + * + * Stores the char[] value into a byte[] that each byte represents + * the8 low-order bits of the corresponding character, if the char[] + * contains only latin1 character. Or a byte[] that stores all + * characters in their byte sequences defined by the {@code StringUTF16}. + */ + String(char[] value, int off, int len, Void sig) { + if (len == 0) { + this.value = "".value; + this.coder = "".coder; + return; + } + if (COMPACT_STRINGS) { + byte[] val = StringUTF16.compress(value, off, len); + if (val != null) { + this.value = val; + this.coder = LATIN1; + return; + } + } + this.coder = UTF16; + this.value = StringUTF16.toBytes(value, off, len); + } + + /* + * Package private constructor. Trailing Void argument is there for + * disambiguating it against other (public) constructors. + */ + String(AbstractStringBuilder asb, Void sig) { + byte[] val = asb.getValue(); + int length = asb.length(); + if (asb.isLatin1()) { + this.coder = LATIN1; + this.value = Arrays.copyOfRange(val, 0, length); + } else { + if (COMPACT_STRINGS) { + byte[] buf = StringUTF16.compress(val, 0, length); + if (buf != null) { + this.coder = LATIN1; + this.value = buf; + return; + } + } + this.coder = UTF16; + this.value = Arrays.copyOfRange(val, 0, length << 1); + } + } + + /* + * Package private constructor which shares value array for speed. + */ + String(byte[] value, byte coder) { + this.value = value; + this.coder = coder; + } + + byte coder() { + return COMPACT_STRINGS ? coder : UTF16; + } + + private boolean isLatin1() { + return COMPACT_STRINGS && coder == LATIN1; + } + + static final byte LATIN1 = 0; + static final byte UTF16 = 1; + + /* + * StringIndexOutOfBoundsException if {@code index} is + * negative or greater than or equal to {@code length}. + */ + static void checkIndex(int index, int length) { + if (index < 0 || index >= length) { + throw new StringIndexOutOfBoundsException("index " + index); + } + } + + /* + * StringIndexOutOfBoundsException if {@code offset} + * is negative or greater than {@code length}. + */ + static void checkOffset(int offset, int length) { + if (offset < 0 || offset > length) { + throw new StringIndexOutOfBoundsException("offset " + offset + + ",length " + length); + } + } + + /* + * Check {@code offset}, {@code count} against {@code 0} and {@code length} + * bounds. + * + * @throws StringIndexOutOfBoundsException + * If {@code offset} is negative, {@code count} is negative, + * or {@code offset} is greater than {@code length - count} + */ + private static void checkBoundsOffCount(int offset, int count, int length) { + if (offset < 0 || count < 0 || offset > length - count) { + throw new StringIndexOutOfBoundsException( + "offset " + offset + ", count " + count + ", length " + length); + } + } + + /* + * Check {@code begin}, {@code end} against {@code 0} and {@code length} + * bounds. + * + * @throws StringIndexOutOfBoundsException + * If {@code begin} is negative, {@code begin} is greater than + * {@code end}, or {@code end} is greater than {@code length}. + */ + private static void checkBoundsBeginEnd(int begin, int end, int length) { + if (begin < 0 || begin > end || end > length) { + throw new StringIndexOutOfBoundsException( + "begin " + begin + ", end " + end + ", length " + length); + } + } } --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/src/java.base/share/classes/java/lang/StringLatin1.java 2015-10-30 14:03:40.422347055 -0700 @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +import java.util.Arrays; +import java.util.Locale; +import java.util.Objects; +import java.util.Spliterator; +import java.util.function.IntConsumer; +import java.util.stream.IntStream; +import jdk.internal.HotSpotIntrinsicCandidate; + +import static java.lang.String.LATIN1; +import static java.lang.String.UTF16; +import static java.lang.String.checkOffset; + +final class StringLatin1 { + + public static char charAt(byte[] value, int index) { + if (index < 0 || index >= value.length) { + throw new StringIndexOutOfBoundsException(index); + } + return (char)(value[index] & 0xff); + } + + public static boolean canEncode(int cp) { + return cp >>> 8 == 0; + } + + public static int length(byte[] value) { + return value.length; + } + + public static int codePointAt(byte[] value, int index, int end) { + return value[index] & 0xff; + } + + public static int codePointBefore(byte[] value, int index) { + return value[index - 1] & 0xff; + } + + public static int codePointCount(byte[] value, int beginIndex, int endIndex) { + return endIndex - beginIndex; + } + + public static char[] toChars(byte[] value) { + char[] dst = new char[value.length]; + inflate(value, 0, dst, 0, value.length); + return dst; + } + + public static byte[] inflate(byte[] value, int off, int len) { + byte[] ret = StringUTF16.newBytesFor(len); + inflate(value, off, ret, 0, len); + return ret; + } + + public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) { + inflate(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); + } + + public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin) { + System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); + } + + @HotSpotIntrinsicCandidate + public static boolean equals(byte[] value, byte[] other) { + if (value.length == other.length) { + for (int i = 0; i < value.length; i++) { + if (value[i] != other[i]) { + return false; + } + } + return true; + } + return false; + } + + @HotSpotIntrinsicCandidate + public static int compareTo(byte[] value, byte[] other) { + int len1 = value.length; + int len2 = other.length; + int lim = Math.min(len1, len2); + for (int k = 0; k < lim; k++) { + if (value[k] != other[k]) { + return getChar(value, k) - getChar(other, k); + } + } + return len1 - len2; + } + + @HotSpotIntrinsicCandidate + public static int compareToUTF16(byte[] value, byte[] other) { + int len1 = length(value); + int len2 = StringUTF16.length(other); + int lim = Math.min(len1, len2); + for (int k = 0; k < lim; k++) { + char c1 = getChar(value, k); + char c2 = StringUTF16.getChar(other, k); + if (c1 != c2) { + return c1 - c2; + } + } + return len1 - len2; + } + + public static int hashCode(byte[] value) { + int h = 0; + for (byte v : value) { + h = 31 * h + (v & 0xff); + } + return h; + } + + public static int indexOf(byte[] value, int ch, int fromIndex) { + if (!canEncode(ch)) { + return -1; + } + int max = value.length; + if (fromIndex < 0) { + fromIndex = 0; + } else if (fromIndex >= max) { + // Note: fromIndex might be near -1>>>1. + return -1; + } + byte c = (byte)ch; + for (int i = fromIndex; i < max; i++) { + if (value[i] == c) { + return i; + } + } + return -1; + } + + @HotSpotIntrinsicCandidate + public static int indexOf(byte[] value, byte[] str) { + if (str.length == 0) { + return 0; + } + if (value.length == 0) { + return -1; + } + return indexOf(value, value.length, str, str.length, 0); + } + + @HotSpotIntrinsicCandidate + public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) { + byte first = str[0]; + int max = (valueCount - strCount); + for (int i = fromIndex; i <= max; i++) { + // Look for first character. + if (value[i] != first) { + while (++i <= max && value[i] != first); + } + // Found first character, now look at the rest of value + if (i <= max) { + int j = i + 1; + int end = j + strCount - 1; + for (int k = 1; j < end && value[j] == str[k]; j++, k++); + if (j == end) { + // Found whole string. + return i; + } + } + } + return -1; + } + + public static int lastIndexOf(byte[] src, int srcCount, + byte[] tgt, int tgtCount, int fromIndex) { + int min = tgtCount - 1; + int i = min + fromIndex; + int strLastIndex = tgtCount - 1; + char strLastChar = (char)(tgt[strLastIndex] & 0xff); + + startSearchForLastChar: + while (true) { + while (i >= min && (src[i] & 0xff) != strLastChar) { + i--; + } + if (i < min) { + return -1; + } + int j = i - 1; + int start = j - strLastIndex; + int k = strLastIndex - 1; + while (j > start) { + if ((src[j--] & 0xff) != (tgt[k--] & 0xff)) { + i--; + continue startSearchForLastChar; + } + } + return start + 1; + } + } + + public static int lastIndexOf(final byte[] value, int ch, int fromIndex) { + if (!canEncode(ch)) { + return -1; + } + int off = Math.min(fromIndex, value.length - 1); + for (; off >= 0; off--) { + if (value[off] == (byte)ch) { + return off; + } + } + return -1; + } + + public static String replace(byte[] value, char oldChar, char newChar) { + if (canEncode(oldChar)) { + int len = value.length; + int i = -1; + while (++i < len) { + if (value[i] == (byte)oldChar) { + break; + } + } + if (i < len) { + if (canEncode(newChar)) { + byte buf[] = new byte[len]; + for (int j = 0; j < i; j++) { // TBD arraycopy? + buf[j] = value[j]; + } + while (i < len) { + byte c = value[i]; + buf[i] = (c == (byte)oldChar) ? (byte)newChar : c; + i++; + } + return new String(buf, LATIN1); + } else { + byte[] buf = StringUTF16.newBytesFor(len); + // inflate from latin1 to UTF16 + inflate(value, 0, buf, 0, i); + while (i < len) { + char c = (char)(value[i] & 0xff); + StringUTF16.putChar(buf, i, (c == oldChar) ? newChar : c); + i++; + } + return new String(buf, UTF16); + } + } + } + return null; // for string to return this; + } + + // case insensitive + public static boolean regionMatchesCI(byte[] value, int toffset, + byte[] other, int ooffset, int len) { + int last = toffset + len; + while (toffset < last) { + char c1 = (char)(value[toffset++] & 0xff); + char c2 = (char)(other[ooffset++] & 0xff); + if (c1 == c2) { + continue; + } + char u1 = Character.toUpperCase(c1); + char u2 = Character.toUpperCase(c2); + if (u1 == u2) { + continue; + } + if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { + continue; + } + return false; + } + return true; + } + + public static boolean regionMatchesCI_UTF16(byte[] value, int toffset, + byte[] other, int ooffset, int len) { + int last = toffset + len; + while (toffset < last) { + char c1 = (char)(value[toffset++] & 0xff); + char c2 = StringUTF16.getChar(other, ooffset++); + if (c1 == c2) { + continue; + } + char u1 = Character.toUpperCase(c1); + char u2 = Character.toUpperCase(c2); + if (u1 == u2) { + continue; + } + if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { + continue; + } + return false; + } + return true; + } + + public static String toLowerCase(String str, byte[] value, Locale locale) { + if (locale == null) { + throw new NullPointerException(); + } + int first; + final int len = value.length; + // Now check if there are any characters that need to be changed, or are surrogate + for (first = 0 ; first < len; first++) { + int cp = value[first] & 0xff; + if (cp != Character.toLowerCase(cp)) { // no need to check Character.ERROR + break; + } + } + if (first == len) + return str; + String lang = locale.getLanguage(); + if (lang == "tr" || lang == "az" || lang == "lt") { + return toLowerCaseEx(str, value, first, locale, true); + } + byte[] result = new byte[len]; + System.arraycopy(value, 0, result, 0, first); // Just copy the first few + // lowerCase characters. + for (int i = first; i < len; i++) { + int cp = value[i] & 0xff; + cp = Character.toLowerCase(cp); + if (!canEncode(cp)) { // not a latin1 character + return toLowerCaseEx(str, value, first, locale, false); + } + result[i] = (byte)cp; + } + return new String(result, LATIN1); + } + + private static String toLowerCaseEx(String str, byte[] value, + int first, Locale locale, boolean localeDependent) + { + byte[] result = StringUTF16.newBytesFor(value.length); + int resultOffset = 0; + for (int i = 0; i < first; i++) { + StringUTF16.putChar(result, resultOffset++, value[i] & 0xff); + } + for (int i = first; i < value.length; i++) { + int srcChar = value[i] & 0xff; + int lowerChar; + char[] lowerCharArray; + if (localeDependent) { + lowerChar = ConditionalSpecialCasing.toLowerCaseEx(str, i, locale); + } else { + lowerChar = Character.toLowerCase(srcChar); + } + if (Character.isBmpCodePoint(lowerChar)) { // Character.ERROR is not a bmp + StringUTF16.putChar(result, resultOffset++, lowerChar); + } else { + if (lowerChar == Character.ERROR) { + lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(str, i, locale); + } else { + lowerCharArray = Character.toChars(lowerChar); + } + /* Grow result if needed */ + int mapLen = lowerCharArray.length; + if (mapLen > 1) { + byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1); + System.arraycopy(result, 0, result2, 0, resultOffset << 1); + result = result2; + } + for (int x = 0; x < mapLen; ++x) { + StringUTF16.putChar(result, resultOffset++, lowerCharArray[x]); + } + } + } + return StringUTF16.newString(result, 0, resultOffset); + } + + public static String toUpperCase(String str, byte[] value, Locale locale) { + if (locale == null) { + throw new NullPointerException(); + } + int first; + final int len = value.length; + + // Now check if there are any characters that need to be changed, or are surrogate + for (first = 0 ; first < len; first++ ) { + int cp = value[first] & 0xff; + if (cp != Character.toUpperCaseEx(cp)) { // no need to check Character.ERROR + break; + } + } + if (first == len) { + return str; + } + String lang = locale.getLanguage(); + if (lang == "tr" || lang == "az" || lang == "lt") { + return toUpperCaseEx(str, value, first, locale, true); + } + byte[] result = new byte[len]; + System.arraycopy(value, 0, result, 0, first); // Just copy the first few + // upperCase characters. + for (int i = first; i < len; i++) { + int cp = value[i] & 0xff; + cp = Character.toUpperCaseEx(cp); + if (!canEncode(cp)) { // not a latin1 character + return toUpperCaseEx(str, value, first, locale, false); + } + result[i] = (byte)cp; + } + return new String(result, LATIN1); + } + + private static String toUpperCaseEx(String str, byte[] value, + int first, Locale locale, boolean localeDependent) + { + byte[] result = StringUTF16.newBytesFor(value.length); + int resultOffset = 0; + for (int i = 0; i < first; i++) { + StringUTF16.putChar(result, resultOffset++, value[i] & 0xff); + } + for (int i = first; i < value.length; i++) { + int srcChar = value[i] & 0xff; + int upperChar; + char[] upperCharArray; + if (localeDependent) { + upperChar = ConditionalSpecialCasing.toUpperCaseEx(str, i, locale); + } else { + upperChar = Character.toUpperCaseEx(srcChar); + } + if (Character.isBmpCodePoint(upperChar)) { + StringUTF16.putChar(result, resultOffset++, upperChar); + } else { + if (upperChar == Character.ERROR) { + if (localeDependent) { + upperCharArray = + ConditionalSpecialCasing.toUpperCaseCharArray(str, i, locale); + } else { + upperCharArray = Character.toUpperCaseCharArray(srcChar); + } + } else { + upperCharArray = Character.toChars(upperChar); + } + /* Grow result if needed */ + int mapLen = upperCharArray.length; + if (mapLen > 1) { + byte[] result2 = StringUTF16.newBytesFor((result.length >> 1) + mapLen - 1); + System.arraycopy(result, 0, result2, 0, resultOffset << 1); + result = result2; + } + for (int x = 0; x < mapLen; ++x) { + StringUTF16.putChar(result, resultOffset++, upperCharArray[x]); + } + } + } + return StringUTF16.newString(result, 0, resultOffset); + } + + public static String trim(byte[] value) { + int len = value.length; + int st = 0; + while ((st < len) && ((value[st] & 0xff) <= ' ')) { + st++; + } + while ((st < len) && ((value[len - 1] & 0xff) <= ' ')) { + len--; + } + return ((st > 0) || (len < value.length)) ? + newString(value, st, len - st) : null; + } + + public static void putChar(byte[] val, int index, int c) { + //assert (canEncode(c)); + val[index] = (byte)(c); + } + + public static char getChar(byte[] val, int index) { + return (char)(val[index] & 0xff); + } + + public static byte[] toBytes(int[] val, int off, int len) { + byte[] ret = new byte[len]; + for (int i = 0; i < len; i++) { + int cp = val[off++]; + if (!canEncode(cp)) { + return null; + } + ret[i] = (byte)cp; + } + return ret; + } + + public static byte[] toBytes(char c) { + return new byte[] { (byte)c }; + } + + public static String newString(byte[] val, int index, int len) { + return new String(Arrays.copyOfRange(val, index, index + len), + LATIN1); + } + + public static void fillNull(byte[] val, int index, int end) { + Arrays.fill(val, index, end, (byte)0); + } + + // inflatedCopy byte[] -> char[] + @HotSpotIntrinsicCandidate + private static void inflate(byte[] src, int srcOff, char[] dst, int dstOff, int len) { + for (int i = 0; i < len; i++) { + dst[dstOff++] = (char)(src[srcOff++] & 0xff); + } + } + + // inflatedCopy byte[] -> byte[] + @HotSpotIntrinsicCandidate + public static void inflate(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { + for (int i = 0; i < len; i++) { + StringUTF16.putChar(dst, dstOff++, src[srcOff++] & 0xff); + } + } + + static class CharsSpliterator implements Spliterator.OfInt { + private final byte[] array; + private int index; // current index, modified on advance/split + private final int fence; // one past last index + private final int cs; + + CharsSpliterator(byte[] array, int acs) { + this(array, 0, array.length, acs); + } + + CharsSpliterator(byte[] array, int origin, int fence, int acs) { + this.array = array; + this.index = origin; + this.fence = fence; + this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED + | Spliterator.SUBSIZED; + } + + @Override + public OfInt trySplit() { + int lo = index, mid = (lo + fence) >>> 1; + return (lo >= mid) + ? null + : new CharsSpliterator(array, lo, index = mid, cs); + } + + @Override + public void forEachRemaining(IntConsumer action) { + byte[] a; int i, hi; // hoist accesses and checks from loop + if (action == null) + throw new NullPointerException(); + if ((a = array).length >= (hi = fence) && + (i = index) >= 0 && i < (index = hi)) { + do { action.accept(a[i] & 0xff); } while (++i < hi); + } + } + + @Override + public boolean tryAdvance(IntConsumer action) { + if (action == null) + throw new NullPointerException(); + if (index >= 0 && index < fence) { + action.accept(array[index++] & 0xff); + return true; + } + return false; + } + + @Override + public long estimateSize() { return (long)(fence - index); } + + @Override + public int characteristics() { + return cs; + } + } + + //////////////////////////////////////////////////////////////// + + public static void getCharsSB(byte[] val, int srcBegin, int srcEnd, char dst[], int dstBegin) { + checkOffset(srcEnd, val.length); + getChars(val, srcBegin, srcEnd, dst, dstBegin); + } + + public static void inflateSB(byte[] val, byte[] dst, int dstOff, int count) { + checkOffset(count, val.length); + checkOffset(dstOff + count, dst.length >> 1); // dst is utf16 + inflate(val, 0, dst, dstOff, count); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/src/java.base/share/classes/java/lang/StringUTF16.java 2015-10-30 14:03:41.037344730 -0700 @@ -0,0 +1,971 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +import java.util.Arrays; +import java.util.Locale; +import java.util.Spliterator; +import java.util.function.IntConsumer; +import jdk.internal.HotSpotIntrinsicCandidate; + +import static java.lang.String.UTF16; +import static java.lang.String.LATIN1; +import static java.lang.String.checkIndex; +import static java.lang.String.checkOffset; + +final class StringUTF16 { + + public static byte[] newBytesFor(int len) { + if (len < 0) { + throw new NegativeArraySizeException(); + } + if (len > MAX_LENGTH) { + throw new OutOfMemoryError("UTF16 String size is " + len + + ", should be less than " + MAX_LENGTH); + } + return new byte[len << 1]; + } + + @HotSpotIntrinsicCandidate + public static void putChar(byte[] val, int index, int c) { + index <<= 1; + val[index++] = (byte)(c >> HI_BYTE_SHIFT); + val[index] = (byte)(c >> LO_BYTE_SHIFT); + } + + @HotSpotIntrinsicCandidate + public static char getChar(byte[] val, int index) { + index <<= 1; + return (char)(((val[index++] & 0xff) << HI_BYTE_SHIFT) | + ((val[index] & 0xff) << LO_BYTE_SHIFT)); + } + + public static char charAt(byte[] value, int index) { + if (index < 0 || index >= value.length >> 1) { + throw new StringIndexOutOfBoundsException(index); + } + return getChar(value, index); + } + + public static int length(byte[] value) { + return value.length >> 1; + } + + public static int codePointAt(byte[] value, int index, int end) { + char c1 = getChar(value, index); + if (Character.isHighSurrogate(c1) && ++index < end) { + char c2 = getChar(value, index); + if (Character.isLowSurrogate(c2)) { + return Character.toCodePoint(c1, c2); + } + } + return c1; + } + + public static int codePointBefore(byte[] value, int index) { + char c2 = getChar(value, --index); + if (Character.isLowSurrogate(c2) && index > 0) { + char c1 = getChar(value, --index); + if (Character.isHighSurrogate(c1)) { + return Character.toCodePoint(c1, c2); + } + } + return c2; + } + + public static int codePointCount(byte[] value, int beginIndex, int endIndex) { + int count = endIndex - beginIndex; + for (int i = beginIndex; i < endIndex; ) { + if (Character.isHighSurrogate(getChar(value, i++)) && + i < endIndex && + Character.isLowSurrogate(getChar(value, i))) { + count--; + i++; + } + } + return count; + } + + public static char[] toChars(byte[] value) { + char[] dst = new char[value.length >> 1]; + getChars(value, 0, dst.length, dst, 0); + return dst; + } + + @HotSpotIntrinsicCandidate + public static byte[] toBytes(char[] value, int off, int len) { + byte[] val = newBytesFor(len); + for (int i = 0; i < len; i++) { + putChar(val, i, value[off++]); + } + return val; + } + + public static byte[] compress(char[] val, int off, int len) { + byte[] ret = new byte[len]; + if (compress(val, off, ret, 0, len) == len) { + return ret; + } + return null; + } + + public static byte[] compress(byte[] val, int off, int len) { + byte[] ret = new byte[len]; + if (compress(val, off, ret, 0, len) == len) { + return ret; + } + return null; + } + + // compressedCopy char[] -> byte[] + @HotSpotIntrinsicCandidate + private static int compress(char[] src, int srcOff, byte[] dst, int dstOff, int len) { + for (int i = 0; i < len; i++) { + int c = src[srcOff++]; + if (c >>> 8 != 0) { + return 0; + } + dst[dstOff++] = (byte)c; + } + return len; + } + + // compressedCopy byte[] -> byte[] + @HotSpotIntrinsicCandidate + public static int compress(byte[] src, int srcOff, byte[] dst, int dstOff, int len) { + for (int i = 0; i < len; i++) { + int c = getChar(src, srcOff++); + if (c >>> 8 != 0) { + return 0; + } + dst[dstOff++] = (byte)c; + } + return len; + } + + public static byte[] toBytes(int[] val, int index, int len) { + final int end = index + len; + // Pass 1: Compute precise size of char[] + int n = len; + for (int i = index; i < end; i++) { + int cp = val[i]; + if (Character.isBmpCodePoint(cp)) + continue; + else if (Character.isValidCodePoint(cp)) + n++; + else throw new IllegalArgumentException(Integer.toString(cp)); + } + // Pass 2: Allocate and fill in pair + byte[] buf = newBytesFor(n); + for (int i = index, j = 0; i < end; i++, j++) { + int cp = val[i]; + if (Character.isBmpCodePoint(cp)) { + putChar(buf, j, cp); + } else { + putChar(buf, j++, Character.highSurrogate(cp)); + putChar(buf, j, Character.lowSurrogate(cp)); + } + } + return buf; + } + + public static byte[] toBytes(char c) { + byte[] result = new byte[2]; + putChar(result, 0, c); + return result; + } + + @HotSpotIntrinsicCandidate + public static void getChars(byte[] value, int srcBegin, int srcEnd, char dst[], int dstBegin) { + for (int i = srcBegin; i < srcEnd; i++) { + dst[dstBegin++] = getChar(value, i); + } + } + + /* @see java.lang.String.getBytes(int, int, byte[], int) */ + public static void getBytes(byte[] value, int srcBegin, int srcEnd, byte dst[], int dstBegin) { + srcBegin <<= 1; + srcEnd <<= 1; + for (int i = srcBegin + (1 >> LO_BYTE_SHIFT); i < srcEnd; i += 2) { + dst[dstBegin++] = value[i]; + } + } + + @HotSpotIntrinsicCandidate + public static boolean equals(byte[] value, byte[] other) { + if (value.length == other.length) { + int len = value.length >> 1; + for (int i = 0; i < len; i++) { + if (getChar(value, i) != getChar(other, i)) { + return false; + } + } + return true; + } + return false; + } + + @HotSpotIntrinsicCandidate + public static int compareTo(byte[] value, byte[] other) { + int len1 = length(value); + int len2 = length(other); + int lim = Math.min(len1, len2); + for (int k = 0; k < lim; k++) { + char c1 = getChar(value, k); + char c2 = getChar(other, k); + if (c1 != c2) { + return c1 - c2; + } + } + return len1 - len2; + } + + @HotSpotIntrinsicCandidate + public static int compareToLatin1(byte[] value, byte[] other) { + int len1 = length(value); + int len2 = StringLatin1.length(other); + int lim = Math.min(len1, len2); + for (int k = 0; k < lim; k++) { + char c1 = getChar(value, k); + char c2 = StringLatin1.getChar(other, k); + if (c1 != c2) { + return c1 - c2; + } + } + return len1 - len2; + } + + public static int hashCode(byte[] value) { + int h = 0; + int length = value.length >> 1; + for (int i = 0; i < length; i++) { + h = 31 * h + getChar(value, i); + } + return h; + } + + public static int indexOf(byte[] value, int ch, int fromIndex) { + int max = value.length >> 1; + if (fromIndex < 0) { + fromIndex = 0; + } else if (fromIndex >= max) { + // Note: fromIndex might be near -1>>>1. + return -1; + } + if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { + // handle most cases here (ch is a BMP code point or a + // negative value (invalid code point)) + return indexOfChar(value, ch, fromIndex, max); + } else { + return indexOfSupplementary(value, ch, fromIndex, max); + } + } + + @HotSpotIntrinsicCandidate + public static int indexOf(byte[] value, byte[] str) { + if (str.length == 0) { + return 0; + } + if (value.length == 0) { + return -1; + } + return indexOf(value, length(value), str, length(str), 0); + } + + @HotSpotIntrinsicCandidate + public static int indexOf(byte[] value, int valueCount, byte[] str, int strCount, int fromIndex) { + char first = getChar(str, 0); + int max = (valueCount - strCount); + for (int i = fromIndex; i <= max; i++) { + // Look for first character. + if (getChar(value, i) != first) { + while (++i <= max && getChar(value, i) != first); + } + // Found first character, now look at the rest of value + if (i <= max) { + int j = i + 1; + int end = j + strCount - 1; + for (int k = 1; j < end && getChar(value, j) == getChar(str, k); j++, k++); + if (j == end) { + // Found whole string. + return i; + } + } + } + return -1; + } + + /** + * Handles indexOf Latin1 substring in UTF16 string. + */ + @HotSpotIntrinsicCandidate + public static int indexOfLatin1(byte[] value, byte[] str) { + if (str.length == 0) { + return 0; + } + if (value.length == 0) { + return -1; + } + return indexOfLatin1(value, length(value), str, str.length, 0); + } + + @HotSpotIntrinsicCandidate + public static int indexOfLatin1(byte[] src, int srcCount, byte[] tgt, int tgtCount, int fromIndex) { + char first = (char)(tgt[0] & 0xff); + int max = (srcCount - tgtCount); + for (int i = fromIndex; i <= max; i++) { + // Look for first character. + if (getChar(src, i) != first) { + while (++i <= max && getChar(src, i) != first); + } + // Found first character, now look at the rest of v2 + if (i <= max) { + int j = i + 1; + int end = j + tgtCount - 1; + for (int k = 1; + j < end && getChar(src, j) == (tgt[k] & 0xff); + j++, k++); + if (j == end) { + // Found whole string. + return i; + } + } + } + return -1; + } + + @HotSpotIntrinsicCandidate + private static int indexOfChar(byte[] value, int ch, int fromIndex, int max) { + for (int i = fromIndex; i < max; i++) { + if (getChar(value, i) == ch) { + return i; + } + } + return -1; + } + + /** + * Handles (rare) calls of indexOf with a supplementary character. + */ + private static int indexOfSupplementary(byte[] value, int ch, int fromIndex, int max) { + if (Character.isValidCodePoint(ch)) { + final char hi = Character.highSurrogate(ch); + final char lo = Character.lowSurrogate(ch); + for (int i = fromIndex; i < max - 1; i++) { + if (getChar(value, i) == hi && getChar(value, i + 1 ) == lo) { + return i; + } + } + } + return -1; + } + + public static int lastIndexOf(byte[] src, int srcCount, + byte[] tgt, int tgtCount, int fromIndex) { + int min = tgtCount - 1; + int i = min + fromIndex; + int strLastIndex = tgtCount - 1; + char strLastChar = getChar(tgt, strLastIndex); + + startSearchForLastChar: + while (true) { + while (i >= min && getChar(src, i) != strLastChar) { + i--; + } + if (i < min) { + return -1; + } + int j = i - 1; + int start = j - strLastIndex; + int k = strLastIndex - 1; + while (j > start) { + if (getChar(src, j--) != getChar(tgt, k--)) { + i--; + continue startSearchForLastChar; + } + } + return start + 1; + } + } + + public static int lastIndexOf(byte[] value, int ch, int fromIndex) { + if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { + // handle most cases here (ch is a BMP code point or a + // negative value (invalid code point)) + int i = Math.min(fromIndex, (value.length >> 1) - 1); + for (; i >= 0; i--) { + if (getChar(value, i) == ch) { + return i; + } + } + return -1; + } else { + return lastIndexOfSupplementary(value, ch, fromIndex); + } + } + + /** + * Handles (rare) calls of lastIndexOf with a supplementary character. + */ + private static int lastIndexOfSupplementary(final byte[] value, int ch, int fromIndex) { + if (Character.isValidCodePoint(ch)) { + char hi = Character.highSurrogate(ch); + char lo = Character.lowSurrogate(ch); + int i = Math.min(fromIndex, (value.length >> 1) - 2); + for (; i >= 0; i--) { + if (getChar(value, i) == hi && getChar(value, i + 1) == lo) { + return i; + } + } + } + return -1; + } + + public static String replace(byte[] value, char oldChar, char newChar) { + int len = value.length >> 1; + int i = -1; + while (++i < len) { + if (getChar(value, i) == oldChar) { + break; + } + } + if (i < len) { + byte buf[] = new byte[value.length]; + for (int j = 0; j < i; j++) { + putChar(buf, j, getChar(value, j)); // TBD:arraycopy? + } + while (i < len) { + char c = getChar(value, i); + putChar(buf, i, c == oldChar ? newChar : c); + i++; + } + // Check if we should try to compress to latin1 + if (String.COMPACT_STRINGS && + !StringLatin1.canEncode(oldChar) && + StringLatin1.canEncode(newChar)) { + byte[] val = compress(buf, 0, len); + if (val != null) { + return new String(val, LATIN1); + } + } + return new String(buf, UTF16); + } + return null; + } + + public static boolean regionMatchesCI(byte[] value, int toffset, + byte[] other, int ooffset, int len) { + int last = toffset + len; + while (toffset < last) { + char c1 = getChar(value, toffset++); + char c2 = getChar(other, ooffset++); + if (c1 == c2) { + continue; + } + // try converting both characters to uppercase. + // If the results match, then the comparison scan should + // continue. + char u1 = Character.toUpperCase(c1); + char u2 = Character.toUpperCase(c2); + if (u1 == u2) { + continue; + } + // Unfortunately, conversion to uppercase does not work properly + // for the Georgian alphabet, which has strange rules about case + // conversion. So we need to make one last check before + // exiting. + if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { + continue; + } + return false; + } + return true; + } + + public static boolean regionMatchesCI_Latin1(byte[] value, int toffset, + byte[] other, int ooffset, + int len) { + int last = toffset + len; + while (toffset < last) { + char c1 = getChar(value, toffset++); + char c2 = (char)(other[ooffset++] & 0xff); + if (c1 == c2) { + continue; + } + char u1 = Character.toUpperCase(c1); + char u2 = Character.toUpperCase(c2); + if (u1 == u2) { + continue; + } + if (Character.toLowerCase(u1) == Character.toLowerCase(u2)) { + continue; + } + return false; + } + return true; + } + + public static String toLowerCase(String str, byte[] value, Locale locale) { + if (locale == null) { + throw new NullPointerException(); + } + int first; + boolean hasSurr = false; + final int len = value.length >> 1; + + // Now check if there are any characters that need to be changed, or are surrogate + for (first = 0 ; first < len; first++) { + int cp = (int)getChar(value, first); + if (Character.isSurrogate((char)cp)) { + hasSurr = true; + break; + } + if (cp != Character.toLowerCase(cp)) { // no need to check Character.ERROR + break; + } + } + if (first == len) + return str; + byte[] result = new byte[value.length]; + System.arraycopy(value, 0, result, 0, first << 1); // Just copy the first few + // lowerCase characters. + String lang = locale.getLanguage(); + if (lang == "tr" || lang == "az" || lang == "lt") { + return toLowerCaseEx(str, value, result, first, locale, true); + } + if (hasSurr) { + return toLowerCaseEx(str, value, result, first, locale, false); + } + int bits = 0; + for (int i = first; i < len; i++) { + int cp = (int)getChar(value, i); + if (cp == '\u03A3' || // GREEK CAPITAL LETTER SIGMA + Character.isSurrogate((char)cp)) { + return toLowerCaseEx(str, value, result, i, locale, false); + } + if (cp == '\u0130') { // LATIN CAPITAL LETTER I WITH DOT ABOVE + return toLowerCaseEx(str, value, result, i, locale, true); + } + cp = Character.toLowerCase(cp); + if (!Character.isBmpCodePoint(cp)) { + return toLowerCaseEx(str, value, result, i, locale, false); + } + bits |= cp; + putChar(result, i, cp); + } + if (bits >>> 8 != 0) { + return new String(result, UTF16); + } else { + return newString(result, 0, len); + } + } + + private static String toLowerCaseEx(String str, byte[] value, + byte[] result, int first, Locale locale, + boolean localeDependent) { + int resultOffset = first; + int length = value.length >> 1; + int srcCount; + for (int i = first; i < length; i += srcCount) { + int srcChar = getChar(value, i); + int lowerChar; + char[] lowerCharArray; + srcCount = 1; + if (Character.isSurrogate((char)srcChar)) { + srcChar = codePointAt(value, i, length); + srcCount = Character.charCount(srcChar); + } + if (localeDependent || + srcChar == '\u03A3' || // GREEK CAPITAL LETTER SIGMA + srcChar == '\u0130') { // LATIN CAPITAL LETTER I WITH DOT ABOVE + lowerChar = ConditionalSpecialCasing.toLowerCaseEx(str, i, locale); + } else { + lowerChar = Character.toLowerCase(srcChar); + } + if (Character.isBmpCodePoint(lowerChar)) { // Character.ERROR is not a bmp + putChar(result, resultOffset++, lowerChar); + } else { + if (lowerChar == Character.ERROR) { + lowerCharArray = ConditionalSpecialCasing.toLowerCaseCharArray(str, i, locale); + } else { + lowerCharArray = Character.toChars(lowerChar); + } + /* Grow result if needed */ + int mapLen = lowerCharArray.length; + if (mapLen > srcCount) { + byte[] result2 = newBytesFor((result.length >> 1) + mapLen - srcCount); + System.arraycopy(result, 0, result2, 0, resultOffset << 1); + result = result2; + } + for (int x = 0; x < mapLen; ++x) { + putChar(result, resultOffset++, lowerCharArray[x]); + } + } + } + return newString(result, 0, resultOffset); + } + + public static String toUpperCase(String str, byte[] value, Locale locale) { + if (locale == null) { + throw new NullPointerException(); + } + int first; + boolean hasSurr = false; + final int len = value.length >> 1; + + // Now check if there are any characters that need to be changed, or are surrogate + for (first = 0 ; first < len; first++) { + int cp = (int)getChar(value, first); + if (Character.isSurrogate((char)cp)) { + hasSurr = true; + break; + } + if (cp != Character.toUpperCaseEx(cp)) { // no need to check Character.ERROR + break; + } + } + if (first == len) { + return str; + } + byte[] result = new byte[value.length]; + System.arraycopy(value, 0, result, 0, first << 1); // Just copy the first few + // upperCase characters. + String lang = locale.getLanguage(); + if (lang == "tr" || lang == "az" || lang == "lt") { + return toUpperCaseEx(str, value, result, first, locale, true); + } + if (hasSurr) { + return toUpperCaseEx(str, value, result, first, locale, false); + } + int bits = 0; + for (int i = first; i < len; i++) { + int cp = (int)getChar(value, i); + if (Character.isSurrogate((char)cp)) { + return toUpperCaseEx(str, value, result, i, locale, false); + } + cp = Character.toUpperCaseEx(cp); + if (!Character.isBmpCodePoint(cp)) { // Character.ERROR is not bmp + return toUpperCaseEx(str, value, result, i, locale, false); + } + bits |= cp; + putChar(result, i, cp); + } + if (bits >>> 8 != 0) { + return new String(result, UTF16); + } else { + return newString(result, 0, len); + } + } + + private static String toUpperCaseEx(String str, byte[] value, + byte[] result, int first, + Locale locale, boolean localeDependent) + { + int resultOffset = first; + int length = value.length >> 1; + int srcCount; + for (int i = first; i < length; i += srcCount) { + int srcChar = getChar(value, i); + int upperChar; + char[] upperCharArray; + srcCount = 1; + if (Character.isSurrogate((char)srcChar)) { + srcChar = codePointAt(value, i, length); + srcCount = Character.charCount(srcChar); + } + if (localeDependent) { + upperChar = ConditionalSpecialCasing.toUpperCaseEx(str, i, locale); + } else { + upperChar = Character.toUpperCaseEx(srcChar); + } + if (Character.isBmpCodePoint(upperChar)) { + putChar(result, resultOffset++, upperChar); + } else { + if (upperChar == Character.ERROR) { + if (localeDependent) { + upperCharArray = + ConditionalSpecialCasing.toUpperCaseCharArray(str, i, locale); + } else { + upperCharArray = Character.toUpperCaseCharArray(srcChar); + } + } else { + upperCharArray = Character.toChars(upperChar); + } + /* Grow result if needed */ + int mapLen = upperCharArray.length; + if (mapLen > srcCount) { + byte[] result2 = newBytesFor((result.length >> 1) + mapLen - srcCount); + System.arraycopy(result, 0, result2, 0, resultOffset << 1); + result = result2; + } + for (int x = 0; x < mapLen; ++x) { + putChar(result, resultOffset++, upperCharArray[x]); + } + } + } + return newString(result, 0, resultOffset); + } + + public static String trim(byte[] value) { + int length = value.length >> 1; + int len = length; + int st = 0; + while (st < len && getChar(value, st) <= ' ') { + st++; + } + while (st < len && getChar(value, len - 1) <= ' ') { + len--; + } + return ((st > 0) || (len < length )) ? + new String(Arrays.copyOfRange(value, st << 1, len << 1), UTF16) : + null; + } + + public static void putChars(byte[] val, int index, char[] str, int off, int end) { + while (off < end) { + putChar(val, index++, str[off++]); + } + } + + public static String newString(byte[] val, int index, int len) { + if (String.COMPACT_STRINGS) { + byte[] buf = compress(val, index, len); + if (buf != null) { + return new String(buf, LATIN1); + } + } + int last = index + len; + return new String(Arrays.copyOfRange(val, index << 1, last << 1), UTF16); + } + + public static void fillNull(byte[] val, int index, int end) { + Arrays.fill(val, index << 1, end << 1, (byte)0); + } + + static class CharsSpliterator implements Spliterator.OfInt { + private final byte[] array; + private int index; // current index, modified on advance/split + private final int fence; // one past last index + private final int cs; + + CharsSpliterator(byte[] array, int acs) { + this(array, 0, array.length >> 1, acs); + } + + CharsSpliterator(byte[] array, int origin, int fence, int acs) { + this.array = array; + this.index = origin; + this.fence = fence; + this.cs = acs | Spliterator.ORDERED | Spliterator.SIZED + | Spliterator.SUBSIZED; + } + + @Override + public OfInt trySplit() { + int lo = index, mid = (lo + fence) >>> 1; + return (lo >= mid) + ? null + : new CharsSpliterator(array, lo, index = mid, cs); + } + + @Override + public void forEachRemaining(IntConsumer action) { + byte[] a; int i, hi; // hoist accesses and checks from loop + if (action == null) + throw new NullPointerException(); + if (((a = array).length >> 1) >= (hi = fence) && + (i = index) >= 0 && i < (index = hi)) { + do { action.accept(getChar(a, i)); } while (++i < hi); + } + } + + @Override + public boolean tryAdvance(IntConsumer action) { + if (action == null) + throw new NullPointerException(); + if (index >= 0 && index < fence) { + action.accept(getChar(array, index++)); + return true; + } + return false; + } + + @Override + public long estimateSize() { return (long)(fence - index); } + + @Override + public int characteristics() { + return cs; + } + } + + static class CodePointsSpliterator implements Spliterator.OfInt { + private final byte[] array; + private int index; // current index, modified on advance/split + private final int fence; // one past last index + private final int cs; + + CodePointsSpliterator(byte[] array, int acs) { + this(array, 0, array.length >> 1, acs); + } + + CodePointsSpliterator(byte[] array, int origin, int fence, int acs) { + this.array = array; + this.index = origin; + this.fence = fence; + this.cs = acs | Spliterator.ORDERED; + } + + @Override + public OfInt trySplit() { + int lo = index, mid = (lo + fence) >>> 1; + if (lo >= mid) + return null; + + int midOneLess; + // If the mid-point intersects a surrogate pair + if (Character.isLowSurrogate(getChar(array, mid)) && + Character.isHighSurrogate(getChar(array, midOneLess = (mid -1)))) { + // If there is only one pair it cannot be split + if (lo >= midOneLess) + return null; + // Shift the mid-point to align with the surrogate pair + return new CodePointsSpliterator(array, lo, index = midOneLess, cs); + } + return new CodePointsSpliterator(array, lo, index = mid, cs); + } + + @Override + public void forEachRemaining(IntConsumer action) { + byte[] a; int i, hi; // hoist accesses and checks from loop + if (action == null) + throw new NullPointerException(); + if (((a = array).length >> 1) >= (hi = fence) && + (i = index) >= 0 && i < (index = hi)) { + do { + i = advance(a, i, hi, action); + } while (i < hi); + } + } + + @Override + public boolean tryAdvance(IntConsumer action) { + if (action == null) + throw new NullPointerException(); + if (index >= 0 && index < fence) { + index = advance(array, index, fence, action); + return true; + } + return false; + } + + // Advance one code point from the index, i, and return the next + // index to advance from + private static int advance(byte[] a, int i, int hi, IntConsumer action) { + char c1 = getChar(a, i++); + int cp = c1; + if (Character.isHighSurrogate(c1) && i < hi) { + char c2 = getChar(a, i); + if (Character.isLowSurrogate(c2)) { + i++; + cp = Character.toCodePoint(c1, c2); + } + } + action.accept(cp); + return i; + } + + @Override + public long estimateSize() { return (long)(fence - index); } + + @Override + public int characteristics() { + return cs; + } + } + + //////////////////////////////////////////////////////////////// + + public static void getCharsSB(byte[] val, int srcBegin, int srcEnd, char dst[], int dstBegin) { + checkOffset(srcEnd, val.length >> 1); + getChars(val, srcBegin, srcEnd, dst, dstBegin); + } + + public static void putCharSB(byte[] val, int index, int c) { + checkIndex(index, val.length >> 1); + putChar(val, index, c); + } + + public static void putCharsSB(byte[] val, int index, char[] ca, int off, int end) { + checkOffset(index + end - off, val.length >> 1); + putChars(val, index, ca, off, end); + } + + public static void putCharsSB(byte[] val, int index, CharSequence s, int off, int end) { + checkOffset(index + end - off, val.length >> 1); + for (int i = off; i < end; i++) { + putChar(val, index++, s.charAt(i)); + } + } + + public static int codePointAtSB(byte[] val, int index, int end) { + checkOffset(end, val.length >> 1); + return codePointAt(val, index, end); + } + + public static int codePointBeforeSB(byte[] val, int index) { + checkOffset(index, val.length >> 1); + return codePointBefore(val, index); + } + + public static int codePointCountSB(byte[] val, int beginIndex, int endIndex) { + checkOffset(endIndex, val.length >> 1); + return codePointCount(val, beginIndex, endIndex); + } + + public static String newStringSB(byte[] val, int index, int len) { + checkOffset(index + len, val.length >> 1); + return newString(val, index, len); + } + + //////////////////////////////////////////////////////////////// + + private static native boolean isBigEndian(); + + static final int HI_BYTE_SHIFT; + static final int LO_BYTE_SHIFT; + static { + if (isBigEndian()) { + HI_BYTE_SHIFT = 8; + LO_BYTE_SHIFT = 0; + } else { + HI_BYTE_SHIFT = 0; + LO_BYTE_SHIFT = 8; + } + } + + static final int MAX_LENGTH = Integer.MAX_VALUE >> 1; +} --- old/src/java.base/share/classes/java/lang/StringCoding.java 2015-10-30 14:03:41.874341566 -0700 +++ new/src/java.base/share/classes/java/lang/StringCoding.java 2015-10-30 14:03:41.633342476 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,11 +38,19 @@ import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; import java.util.Arrays; +import jdk.internal.HotSpotIntrinsicCandidate; import sun.misc.MessageUtils; import sun.nio.cs.HistoricallyNamedCharset; import sun.nio.cs.ArrayDecoder; import sun.nio.cs.ArrayEncoder; +import static java.lang.String.LATIN1; +import static java.lang.String.UTF16; +import static java.lang.String.COMPACT_STRINGS; +import static java.nio.charset.StandardCharsets.ISO_8859_1; +import static java.nio.charset.StandardCharsets.US_ASCII; +import static java.nio.charset.StandardCharsets.UTF_8; + /** * Utility class for string encoding and decoding. */ @@ -72,23 +80,13 @@ // Trim the given byte array to the given length // - private static byte[] safeTrim(byte[] ba, int len, Charset cs, boolean isTrusted) { + private static byte[] safeTrim(byte[] ba, int len, boolean isTrusted) { if (len == ba.length && (isTrusted || System.getSecurityManager() == null)) return ba; else return Arrays.copyOf(ba, len); } - // Trim the given char array to the given length - // - private static char[] safeTrim(char[] ca, int len, - Charset cs, boolean isTrusted) { - if (len == ca.length && (isTrusted || System.getSecurityManager() == null)) - return ca; - else - return Arrays.copyOf(ca, len); - } - private static int scale(int len, float expansionFactor) { // We need to perform double, not float, arithmetic; otherwise // we lose low order bits when len is larger than 2**24. @@ -117,21 +115,64 @@ } } + static class Result { + byte[] value; + byte coder; + + Result with() { + coder = COMPACT_STRINGS ? LATIN1 : UTF16; + value = new byte[0]; + return this; + } + + Result with(char[] val, int off, int len) { + if (String.COMPACT_STRINGS) { + byte[] bs = StringUTF16.compress(val, off, len); + if (bs != null) { + value = bs; + coder = LATIN1; + return this; + } + } + coder = UTF16; + value = StringUTF16.toBytes(val, off, len); + return this; + } + + Result with(byte[] val, byte coder) { + this.coder = coder; + value = val; + return this; + } + } + + @HotSpotIntrinsicCandidate + private static boolean hasNegatives(byte[] ba, int off, int len) { + for (int i = off; i < off + len; i++) { + if (ba[i] < 0) { + return true; + } + } + return false; + } // -- Decoding -- - private static class StringDecoder { + static class StringDecoder { private final String requestedCharsetName; private final Charset cs; + private final boolean isASCIICompatible; private final CharsetDecoder cd; - private final boolean isTrusted; + protected final Result result; - private StringDecoder(Charset cs, String rcn) { + StringDecoder(Charset cs, String rcn) { this.requestedCharsetName = rcn; this.cs = cs; this.cd = cs.newDecoder() .onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE); - this.isTrusted = (cs.getClass().getClassLoader0() == null); + this.result = new Result(); + this.isASCIICompatible = (cd instanceof ArrayDecoder) && + ((ArrayDecoder)cd).isASCIICompatible(); } String charsetName() { @@ -144,36 +185,58 @@ return requestedCharsetName; } - char[] decode(byte[] ba, int off, int len) { + Result decode(byte[] ba, int off, int len) { + if (len == 0) { + return result.with(); + } + // fastpath for ascii compatible + if (isASCIICompatible && !hasNegatives(ba, off, len)) { + if (COMPACT_STRINGS) { + return result.with(Arrays.copyOfRange(ba, off, off + len), + LATIN1); + } else { + return result.with(StringLatin1.inflate(ba, off, len), UTF16); + } + } int en = scale(len, cd.maxCharsPerByte()); char[] ca = new char[en]; - if (len == 0) - return ca; if (cd instanceof ArrayDecoder) { int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca); - return safeTrim(ca, clen, cs, isTrusted); + return result.with(ca, 0, clen); + } + cd.reset(); + ByteBuffer bb = ByteBuffer.wrap(ba, off, len); + CharBuffer cb = CharBuffer.wrap(ca); + try { + CoderResult cr = cd.decode(bb, cb, true); + if (!cr.isUnderflow()) + cr.throwException(); + cr = cd.flush(cb); + if (!cr.isUnderflow()) + cr.throwException(); + } catch (CharacterCodingException x) { + // Substitution is always enabled, + // so this shouldn't happen + throw new Error(x); + } + return result.with(ca, 0, cb.position()); + } + } + + private static class StringDecoder8859_1 extends StringDecoder { + StringDecoder8859_1(Charset cs, String rcn) { + super(cs, rcn); + } + Result decode(byte[] ba, int off, int len) { + if (COMPACT_STRINGS) { + return result.with(Arrays.copyOfRange(ba, off, off + len), LATIN1); } else { - cd.reset(); - ByteBuffer bb = ByteBuffer.wrap(ba, off, len); - CharBuffer cb = CharBuffer.wrap(ca); - try { - CoderResult cr = cd.decode(bb, cb, true); - if (!cr.isUnderflow()) - cr.throwException(); - cr = cd.flush(cb); - if (!cr.isUnderflow()) - cr.throwException(); - } catch (CharacterCodingException x) { - // Substitution is always enabled, - // so this shouldn't happen - throw new Error(x); - } - return safeTrim(ca, cb.position(), cs, isTrusted); + return result.with(StringLatin1.inflate(ba, off, len), UTF16); } } } - static char[] decode(String charsetName, byte[] ba, int off, int len) + static Result decode(String charsetName, byte[] ba, int off, int len) throws UnsupportedEncodingException { StringDecoder sd = deref(decoder); @@ -183,8 +246,15 @@ sd = null; try { Charset cs = lookupCharset(csn); - if (cs != null) - sd = new StringDecoder(cs, csn); + if (cs != null) { + if (cs == UTF_8) { + sd = new StringDecoderUTF8(cs, csn); + } else if (cs == ISO_8859_1) { + sd = new StringDecoder8859_1(cs, csn); + } else { + sd = new StringDecoder(cs, csn); + } + } } catch (IllegalCharsetNameException x) {} if (sd == null) throw new UnsupportedEncodingException(csn); @@ -193,7 +263,7 @@ return sd.decode(ba, off, len); } - static char[] decode(Charset cs, byte[] ba, int off, int len) { + static Result decode(Charset cs, byte[] ba, int off, int len) { // (1)We never cache the "external" cs, the only benefit of creating // an additional StringDe/Encoder object to wrap it is to share the // de/encode() method. These SD/E objects are short-lived, the young-gen @@ -210,44 +280,57 @@ // check (... && (isTrusted || SM == null || getClassLoader0())) in trim // but it then can be argued that the SM is null when the operation // is started... + if (cs == UTF_8) { + return StringDecoderUTF8.decode(ba, off, len, new Result()); + } CharsetDecoder cd = cs.newDecoder(); + // ascii fastpath + if (cs == ISO_8859_1 || ((cd instanceof ArrayDecoder) && + ((ArrayDecoder)cd).isASCIICompatible() && + !hasNegatives(ba, off, len))) { + if (COMPACT_STRINGS) { + return new Result().with(Arrays.copyOfRange(ba, off, off + len), + LATIN1); + } else { + return new Result().with(StringLatin1.inflate(ba, off, len), UTF16); + } + } int en = scale(len, cd.maxCharsPerByte()); - char[] ca = new char[en]; - if (len == 0) - return ca; - boolean isTrusted = false; - if (System.getSecurityManager() != null) { - if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) { - ba = Arrays.copyOfRange(ba, off, off + len); - off = 0; - } + if (len == 0) { + return new Result().with(); + } + if (System.getSecurityManager() != null && + cs.getClass().getClassLoader0() != null) { + ba = Arrays.copyOfRange(ba, off, off + len); + off = 0; } cd.onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE) .reset(); + + char[] ca = new char[en]; if (cd instanceof ArrayDecoder) { int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca); - return safeTrim(ca, clen, cs, isTrusted); - } else { - ByteBuffer bb = ByteBuffer.wrap(ba, off, len); - CharBuffer cb = CharBuffer.wrap(ca); - try { - CoderResult cr = cd.decode(bb, cb, true); - if (!cr.isUnderflow()) - cr.throwException(); - cr = cd.flush(cb); - if (!cr.isUnderflow()) - cr.throwException(); - } catch (CharacterCodingException x) { - // Substitution is always enabled, - // so this shouldn't happen - throw new Error(x); - } - return safeTrim(ca, cb.position(), cs, isTrusted); + return new Result().with(ca, 0, clen); } + ByteBuffer bb = ByteBuffer.wrap(ba, off, len); + CharBuffer cb = CharBuffer.wrap(ca); + try { + CoderResult cr = cd.decode(bb, cb, true); + if (!cr.isUnderflow()) + cr.throwException(); + cr = cd.flush(cb); + if (!cr.isUnderflow()) + cr.throwException(); + } catch (CharacterCodingException x) { + // Substitution is always enabled, + // so this shouldn't happen + throw new Error(x); + } + return new Result().with(ca, 0, cb.position()); } - static char[] decode(byte[] ba, int off, int len) { + static Result decode(byte[] ba, int off, int len) { String csn = Charset.defaultCharset().name(); try { // use charset name decode() variant which provides caching. @@ -273,6 +356,7 @@ private static class StringEncoder { private Charset cs; private CharsetEncoder ce; + private final boolean isASCIICompatible; private final String requestedCharsetName; private final boolean isTrusted; @@ -283,6 +367,8 @@ .onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE); this.isTrusted = (cs.getClass().getClassLoader0() == null); + this.isASCIICompatible = (ce instanceof ArrayEncoder) && + ((ArrayEncoder)ce).isASCIICompatible(); } String charsetName() { @@ -295,36 +381,186 @@ return requestedCharsetName; } - byte[] encode(char[] ca, int off, int len) { + byte[] encode(byte coder, byte[] val) { + // fastpath for ascii compatible + if (coder == LATIN1 && isASCIICompatible && + !hasNegatives(val, 0, val.length)) { + return Arrays.copyOf(val, val.length); + } + int len = val.length >> coder; // assume LATIN1=0/UTF16=1; int en = scale(len, ce.maxBytesPerChar()); byte[] ba = new byte[en]; - if (len == 0) + if (len == 0) { return ba; + } if (ce instanceof ArrayEncoder) { - int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba); - return safeTrim(ba, blen, cs, isTrusted); - } else { - ce.reset(); - ByteBuffer bb = ByteBuffer.wrap(ba); - CharBuffer cb = CharBuffer.wrap(ca, off, len); - try { - CoderResult cr = ce.encode(cb, bb, true); - if (!cr.isUnderflow()) - cr.throwException(); - cr = ce.flush(bb); - if (!cr.isUnderflow()) - cr.throwException(); - } catch (CharacterCodingException x) { - // Substitution is always enabled, - // so this shouldn't happen - throw new Error(x); + if (!isTrusted) { + val = Arrays.copyOf(val, val.length); + } + int blen = (coder == LATIN1 ) ? ((ArrayEncoder)ce).encodeFromLatin1(val, 0, len, ba) + : ((ArrayEncoder)ce).encodeFromUTF16(val, 0, len, ba); + if (blen != -1) { + return safeTrim(ba, blen, isTrusted); + } + } + char[] ca = (coder == LATIN1 ) ? StringLatin1.toChars(val) + : StringUTF16.toChars(val); + ce.reset(); + ByteBuffer bb = ByteBuffer.wrap(ba); + CharBuffer cb = CharBuffer.wrap(ca, 0, len); + try { + CoderResult cr = ce.encode(cb, bb, true); + if (!cr.isUnderflow()) + cr.throwException(); + cr = ce.flush(bb); + if (!cr.isUnderflow()) + cr.throwException(); + } catch (CharacterCodingException x) { + // Substitution is always enabled, + // so this shouldn't happen + throw new Error(x); + } + return safeTrim(ba, bb.position(), isTrusted); + } + } + + @HotSpotIntrinsicCandidate + private static int implEncodeISOArray(byte[] sa, int sp, + byte[] da, int dp, int len) { + int i = 0; + for (; i < len; i++) { + char c = StringUTF16.getChar(sa, sp++); + if (c > '\u00FF') + break; + da[dp++] = (byte)c; + } + return i; + } + + static byte[] encode8859_1(byte coder, byte[] val) { + if (coder == LATIN1) { + return Arrays.copyOf(val, val.length); + } + int len = val.length >> 1; + byte[] dst = new byte[len]; + int dp = 0; + int sp = 0; + int sl = len; + while (sp < sl) { + int ret = implEncodeISOArray(val, sp, dst, dp, len); + sp = sp + ret; + dp = dp + ret; + if (ret != len) { + char c = StringUTF16.getChar(val, sp++); + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(StringUTF16.getChar(val, sp))) { + sp++; } - return safeTrim(ba, bb.position(), cs, isTrusted); + dst[dp++] = '?'; + len = sl - sp; } } + if (dp == dst.length) { + return dst; + } + return Arrays.copyOf(dst, dp); } - static byte[] encode(String charsetName, char[] ca, int off, int len) + static byte[] encodeASCII(byte coder, byte[] val) { + if (coder == LATIN1) { + byte[] dst = new byte[val.length]; + for (int i = 0; i < val.length; i++) { + if (val[i] < 0) { + dst[i] = '?'; + } else { + dst[i] = val[i]; + } + } + return dst; + } + int len = val.length >> 1; + byte[] dst = new byte[len]; + int dp = 0; + for (int i = 0; i < len; i++) { + char c = StringUTF16.getChar(val, i); + if (c < 0x80) { + dst[dp++] = (byte)c; + continue; + } + if (Character.isHighSurrogate(c) && i + 1 < len && + Character.isLowSurrogate(StringUTF16.getChar(val, i + 1))) { + i++; + } + dst[dp++] = '?'; + } + if (len == dp) { + return dst; + } + return Arrays.copyOf(dst, dp); + } + + static byte[] encodeUTF8(byte coder, byte[] val) { + int dp = 0; + byte[] dst; + if (coder == LATIN1) { + dst = new byte[val.length << 1]; + for (int sp = 0; sp < val.length; sp++) { + byte c = val[sp]; + if (c < 0) { + dst[dp++] = (byte)(0xc0 | ((c & 0xff) >> 6)); + dst[dp++] = (byte)(0x80 | (c & 0x3f)); + } else { + dst[dp++] = c; + } + } + } else { + int sp = 0; + int sl = val.length >> 1; + dst = new byte[sl * 3]; + char c; + while (sp < sl && (c = StringUTF16.getChar(val, sp)) < '\u0080') { + // ascii fast loop; + dst[dp++] = (byte)c; + sp++; + } + while (sp < sl) { + c = StringUTF16.getChar(val, sp++); + if (c < 0x80) { + dst[dp++] = (byte)c; + } else if (c < 0x800) { + dst[dp++] = (byte)(0xc0 | (c >> 6)); + dst[dp++] = (byte)(0x80 | (c & 0x3f)); + } else if (Character.isSurrogate(c)) { + int uc = -1; + char c2; + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(c2 = StringUTF16.getChar(val, sp))) { + uc = Character.toCodePoint(c, c2); + } + if (uc < 0) { + dst[dp++] = '?'; + } else { + dst[dp++] = (byte)(0xf0 | ((uc >> 18))); + dst[dp++] = (byte)(0x80 | ((uc >> 12) & 0x3f)); + dst[dp++] = (byte)(0x80 | ((uc >> 6) & 0x3f)); + dst[dp++] = (byte)(0x80 | (uc & 0x3f)); + sp++; // 2 chars + } + } else { + // 3 bytes, 16 bits + dst[dp++] = (byte)(0xe0 | ((c >> 12))); + dst[dp++] = (byte)(0x80 | ((c >> 6) & 0x3f)); + dst[dp++] = (byte)(0x80 | (c & 0x3f)); + } + } + } + if (dp == dst.length) { + return dst; + } + return Arrays.copyOf(dst, dp); + } + + static byte[] encode(String charsetName, byte coder, byte[] val) throws UnsupportedEncodingException { StringEncoder se = deref(encoder); @@ -334,62 +570,88 @@ se = null; try { Charset cs = lookupCharset(csn); - if (cs != null) + if (cs != null) { + if (cs == UTF_8) { + return encodeUTF8(coder, val); + } else if (cs == ISO_8859_1) { + return encode8859_1(coder, val); + } else if (cs == US_ASCII) { + return encodeASCII(coder, val); + } se = new StringEncoder(cs, csn); + } } catch (IllegalCharsetNameException x) {} - if (se == null) + if (se == null) { throw new UnsupportedEncodingException (csn); + } set(encoder, se); } - return se.encode(ca, off, len); + return se.encode(coder, val); } - static byte[] encode(Charset cs, char[] ca, int off, int len) { + static byte[] encode(Charset cs, byte coder, byte[] val) { + if (cs == UTF_8) { + return encodeUTF8(coder, val); + } else if (cs == ISO_8859_1) { + return encode8859_1(coder, val); + } else if (cs == US_ASCII) { + return encodeASCII(coder, val); + } CharsetEncoder ce = cs.newEncoder(); + // fastpath for ascii compatible + if (coder == LATIN1 && (((ce instanceof ArrayEncoder) && + ((ArrayEncoder)ce).isASCIICompatible() && + !hasNegatives(val, 0, val.length)))) { + return Arrays.copyOf(val, val.length); + } + int len = val.length >> coder; // assume LATIN1=0/UTF16=1; int en = scale(len, ce.maxBytesPerChar()); byte[] ba = new byte[en]; - if (len == 0) + if (len == 0) { return ba; - boolean isTrusted = false; - if (System.getSecurityManager() != null) { - if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) { - ca = Arrays.copyOfRange(ca, off, off + len); - off = 0; - } } + boolean isTrusted = System.getSecurityManager() == null || + cs.getClass().getClassLoader0() == null; ce.onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE) .reset(); if (ce instanceof ArrayEncoder) { - int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba); - return safeTrim(ba, blen, cs, isTrusted); - } else { - ByteBuffer bb = ByteBuffer.wrap(ba); - CharBuffer cb = CharBuffer.wrap(ca, off, len); - try { - CoderResult cr = ce.encode(cb, bb, true); - if (!cr.isUnderflow()) - cr.throwException(); - cr = ce.flush(bb); - if (!cr.isUnderflow()) - cr.throwException(); - } catch (CharacterCodingException x) { - throw new Error(x); + if (!isTrusted) { + val = Arrays.copyOf(val, val.length); } - return safeTrim(ba, bb.position(), cs, isTrusted); + int blen = (coder == LATIN1 ) ? ((ArrayEncoder)ce).encodeFromLatin1(val, 0, len, ba) + : ((ArrayEncoder)ce).encodeFromUTF16(val, 0, len, ba); + if (blen != -1) { + return safeTrim(ba, blen, isTrusted); + } + } + char[] ca = (coder == LATIN1 ) ? StringLatin1.toChars(val) + : StringUTF16.toChars(val); + ByteBuffer bb = ByteBuffer.wrap(ba); + CharBuffer cb = CharBuffer.wrap(ca, 0, len); + try { + CoderResult cr = ce.encode(cb, bb, true); + if (!cr.isUnderflow()) + cr.throwException(); + cr = ce.flush(bb); + if (!cr.isUnderflow()) + cr.throwException(); + } catch (CharacterCodingException x) { + throw new Error(x); } + return safeTrim(ba, bb.position(), isTrusted); } - static byte[] encode(char[] ca, int off, int len) { + static byte[] encode(byte coder, byte[] val) { String csn = Charset.defaultCharset().name(); try { // use charset name encode() variant which provides caching. - return encode(csn, ca, off, len); + return encode(csn, coder, val); } catch (UnsupportedEncodingException x) { warnUnsupportedCharset(csn); } try { - return encode("ISO-8859-1", ca, off, len); + return encode("ISO-8859-1", coder, val); } catch (UnsupportedEncodingException x) { // If this code is hit during VM initialization, MessageUtils is // the only way we will be able to get any kind of error message. --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/src/java.base/share/classes/java/lang/StringDecoderUTF8.java 2015-10-30 14:03:42.269340072 -0700 @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.lang; + +import java.nio.charset.Charset; +import java.util.Arrays; + +import static java.lang.String.LATIN1; +import static java.lang.String.UTF16; +import static java.lang.String.COMPACT_STRINGS; +import static java.lang.Character.isSurrogate; +import static java.lang.Character.highSurrogate; +import static java.lang.Character.lowSurrogate; +import static java.lang.Character.isSupplementaryCodePoint; +import static java.lang.StringUTF16.putChar; + +class StringDecoderUTF8 extends StringCoding.StringDecoder { + + StringDecoderUTF8(Charset cs, String rcn) { + super(cs, rcn); + } + + private static boolean isNotContinuation(int b) { + return (b & 0xc0) != 0x80; + } + + private static boolean isMalformed3(int b1, int b2, int b3) { + return (b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) || + (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80; + } + + private static boolean isMalformed3_2(int b1, int b2) { + return (b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) || + (b2 & 0xc0) != 0x80; + } + + private static boolean isMalformed4(int b2, int b3, int b4) { + return (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || + (b4 & 0xc0) != 0x80; + } + + private static boolean isMalformed4_2(int b1, int b2) { + return (b1 == 0xf0 && (b2 < 0x90 || b2 > 0xbf)) || + (b1 == 0xf4 && (b2 & 0xf0) != 0x80) || + (b2 & 0xc0) != 0x80; + } + + private static boolean isMalformed4_3(int b3) { + return (b3 & 0xc0) != 0x80; + } + + // for nb == 3/4 + private static int malformedN(byte[] src, int sp, int nb) { + if (nb == 3) { + int b1 = src[sp++]; + int b2 = src[sp++]; // no need to lookup b3 + return ((b1 == (byte)0xe0 && (b2 & 0xe0) == 0x80) || + isNotContinuation(b2)) ? 1 : 2; + } else if (nb == 4) { // we don't care the speed here + int b1 = src[sp++] & 0xff; + int b2 = src[sp++] & 0xff; + if (b1 > 0xf4 || + (b1 == 0xf0 && (b2 < 0x90 || b2 > 0xbf)) || + (b1 == 0xf4 && (b2 & 0xf0) != 0x80) || + isNotContinuation(b2)) + return 1; + if (isNotContinuation(src[sp++])) + return 2; + return 3; + } + assert false; + return -1; + } + + private static char repl = '\ufffd'; + + StringCoding.Result decode(byte[] src, int sp, int len) { + return decode(src, sp, len, result); + } + + static StringCoding.Result decode(byte[] src, int sp, int len, + StringCoding.Result ret) { + int sl = sp + len; + byte[] dst = new byte[len]; + int dp = 0; + if (COMPACT_STRINGS) { // Latin1 only loop + while (sp < sl) { + int b1 = src[sp]; + if (b1 >= 0) { + dst[dp++] = (byte)b1; + sp++; + continue; + } + if ((b1 == (byte)0xc2 || b1 == (byte)0xc3) && + sp + 1 < sl) { + int b2 = src[sp + 1]; + if (!isNotContinuation(b2)) { + dst[dp++] = (byte)(((b1 << 6) ^ b2)^ + (((byte) 0xC0 << 6) ^ + ((byte) 0x80 << 0))); + sp += 2; + continue; + } + } + // anything not a latin1, including the repl + // we have to go with the utf16 + break; + } + if (sp == sl) { + if (dp != dst.length) { + dst = Arrays.copyOf(dst, dp); + } + return ret.with(dst, LATIN1); + } + } + if (dp == 0) { + dst = new byte[len << 1]; + } else { + byte[] buf = new byte[len << 1]; + StringLatin1.inflate(dst, 0, buf, 0, dp); + dst = buf; + } + while (sp < sl) { + int b1 = src[sp++]; + if (b1 >= 0) { + putChar(dst, dp++, (char) b1); + } else if ((b1 >> 5) == -2 && (b1 & 0x1e) != 0) { + if (sp < sl) { + int b2 = src[sp++]; + if (isNotContinuation(b2)) { + putChar(dst, dp++, repl); + sp--; + } else { + putChar(dst, dp++, (char)(((b1 << 6) ^ b2)^ + (((byte) 0xC0 << 6) ^ + ((byte) 0x80 << 0)))); + } + continue; + } + putChar(dst, dp++, repl); + break; + } else if ((b1 >> 4) == -2) { + if (sp + 1 < sl) { + int b2 = src[sp++]; + int b3 = src[sp++]; + if (isMalformed3(b1, b2, b3)) { + putChar(dst, dp++, repl); + sp -= 3; + sp += malformedN(src, sp, 3); + } else { + char c = (char)((b1 << 12) ^ + (b2 << 6) ^ + (b3 ^ + (((byte) 0xE0 << 12) ^ + ((byte) 0x80 << 6) ^ + ((byte) 0x80 << 0)))); + putChar(dst, dp++, isSurrogate(c) ? repl : c); + } + continue; + } + if (sp < sl && isMalformed3_2(b1, src[sp])) { + putChar(dst, dp++, repl); + continue; + } + putChar(dst, dp++, repl); + break; + } else if ((b1 >> 3) == -2) { + if (sp + 2 < sl) { + int b2 = src[sp++]; + int b3 = src[sp++]; + int b4 = src[sp++]; + int uc = ((b1 << 18) ^ + (b2 << 12) ^ + (b3 << 6) ^ + (b4 ^ + (((byte) 0xF0 << 18) ^ + ((byte) 0x80 << 12) ^ + ((byte) 0x80 << 6) ^ + ((byte) 0x80 << 0)))); + if (isMalformed4(b2, b3, b4) || + !isSupplementaryCodePoint(uc)) { // shortest form check + putChar(dst, dp++, repl); + sp -= 4; + sp += malformedN(src, sp, 4); + } else { + putChar(dst, dp++, highSurrogate(uc)); + putChar(dst, dp++, lowSurrogate(uc)); + } + continue; + } + b1 &= 0xff; + if (b1 > 0xf4 || + sp < sl && isMalformed4_2(b1, src[sp] & 0xff)) { + putChar(dst, dp++, repl); + continue; + } + sp++; + putChar(dst, dp++, repl); + if (sp < sl && isMalformed4_3(src[sp])) { + continue; + } + break; + } else { + putChar(dst, dp++, repl); + } + } + if (dp != len) { + dst = Arrays.copyOf(dst, dp << 1); + } + return ret.with(dst, UTF16); + } +} --- 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); + } + } } --- old/src/java.base/share/classes/java/lang/StringBuffer.java 2015-10-30 14:03:43.772334389 -0700 +++ new/src/java.base/share/classes/java/lang/StringBuffer.java 2015-10-30 14:03:43.534335289 -0700 @@ -104,7 +104,7 @@ * A cache of the last value returned by toString. Cleared * whenever the StringBuffer is modified. */ - private transient char[] toStringCache; + private transient String toStringCache; /** use serialVersionUID from JDK 1.0.2 for interoperability */ static final long serialVersionUID = 3388685877147921107L; @@ -169,15 +169,13 @@ @Override public synchronized int capacity() { - return value.length; + return super.capacity(); } @Override public synchronized void ensureCapacity(int minimumCapacity) { - if (minimumCapacity > value.length) { - expandCapacity(minimumCapacity); - } + super.ensureCapacity(minimumCapacity); } /** @@ -204,9 +202,7 @@ */ @Override public synchronized char charAt(int index) { - if ((index < 0) || (index >= count)) - throw new StringIndexOutOfBoundsException(index); - return value[index]; + return super.charAt(index); } /** @@ -261,10 +257,8 @@ */ @Override public synchronized void setCharAt(int index, char ch) { - if ((index < 0) || (index >= count)) - throw new StringIndexOutOfBoundsException(index); toStringCache = null; - value[index] = ch; + super.setCharAt(index, ch); } @Override @@ -680,9 +674,11 @@ @HotSpotIntrinsicCandidate public synchronized String toString() { if (toStringCache == null) { - toStringCache = Arrays.copyOfRange(value, 0, count); + return toStringCache = + isLatin1() ? StringLatin1.newString(value, 0, count) + : StringUTF16.newString(value, 0, count); } - return new String(toStringCache, true); + return new String(toStringCache); } /** @@ -710,7 +706,13 @@ private synchronized void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { java.io.ObjectOutputStream.PutField fields = s.putFields(); - fields.put("value", value); + char[] val = new char[capacity()]; + if (isLatin1()) { + StringLatin1.getChars(value, 0, count, val, 0); + } else { + StringUTF16.getChars(value, 0, count, val, 0); + } + fields.put("value", val); fields.put("count", count); fields.put("shared", false); s.writeFields(); @@ -723,7 +725,12 @@ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { java.io.ObjectInputStream.GetField fields = s.readFields(); - value = (char[])fields.get("value", null); + char[] val = (char[])fields.get("value", null); + initBytes(val, 0, val.length); count = fields.get("count", 0); } + + protected synchronized void getBytes(byte dst[], int dstBegin, byte coder) { + super.getBytes(dst, dstBegin, coder); + } } --- old/src/java.base/share/classes/java/lang/StringBuilder.java 2015-10-30 14:03:44.438331870 -0700 +++ new/src/java.base/share/classes/java/lang/StringBuilder.java 2015-10-30 14:03:44.195332790 -0700 @@ -412,7 +412,8 @@ @HotSpotIntrinsicCandidate public String toString() { // Create a copy, don't share the array - return new String(value, 0, count); + return isLatin1() ? StringLatin1.newString(value, 0, count) + : StringUTF16.newStringSB(value, 0, count); } /** @@ -430,7 +431,13 @@ throws java.io.IOException { s.defaultWriteObject(); s.writeInt(count); - s.writeObject(value); + char[] val = new char[capacity()]; + if (isLatin1()) { + StringLatin1.getChars(value, 0, count, val, 0); + } else { + StringUTF16.getChars(value, 0, count, val, 0); + } + s.writeObject(val); } /** @@ -441,7 +448,8 @@ throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); count = s.readInt(); - value = (char[]) s.readObject(); + char[] val = (char[]) s.readObject(); + initBytes(val, 0, val.length); } } --- old/src/java.base/share/classes/java/lang/Integer.java 2015-10-30 14:03:45.091329402 -0700 +++ new/src/java.base/share/classes/java/lang/Integer.java 2015-10-30 14:03:44.842330342 -0700 @@ -29,6 +29,10 @@ import java.util.Objects; import jdk.internal.HotSpotIntrinsicCandidate; +import static java.lang.String.COMPACT_STRINGS; +import static java.lang.String.LATIN1; +import static java.lang.String.UTF16; + /** * The {@code Integer} class wraps a value of the primitive type * {@code int} in an object. An object of type {@code Integer} @@ -138,25 +142,47 @@ return toString(i); } - char buf[] = new char[33]; + if (COMPACT_STRINGS) { + byte[] buf = new byte[33]; + boolean negative = (i < 0); + int charPos = 32; + + if (!negative) { + i = -i; + } + + while (i <= -radix) { + buf[charPos--] = (byte)digits[-(i % radix)]; + i = i / radix; + } + buf[charPos] = (byte)digits[-i]; + + if (negative) { + buf[--charPos] = '-'; + } + + return StringLatin1.newString(buf, charPos, (33 - charPos)); + } + return toStringUTF16(i, radix); + } + + private static String toStringUTF16(int i, int radix) { + byte[] buf = new byte[33 * 2]; boolean negative = (i < 0); int charPos = 32; - if (!negative) { i = -i; } - while (i <= -radix) { - buf[charPos--] = digits[-(i % radix)]; + StringUTF16.putChar(buf, charPos--, digits[-(i % radix)]); i = i / radix; } - buf[charPos] = digits[-i]; + StringUTF16.putChar(buf, charPos, digits[-i]); if (negative) { - buf[--charPos] = '-'; + StringUTF16.putChar(buf, --charPos, '-'); } - - return new String(buf, charPos, (33 - charPos)); + return StringUTF16.newString(buf, charPos, (33 - charPos)); } /** @@ -312,12 +338,16 @@ // assert shift > 0 && shift <=5 : "Illegal shift value"; int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val); int chars = Math.max(((mag + (shift - 1)) / shift), 1); - char[] buf = new char[chars]; - formatUnsignedInt(val, shift, buf, 0, chars); - - // Use special constructor which takes over "buf". - return new String(buf, true); + if (COMPACT_STRINGS) { + byte[] buf = new byte[chars]; + formatUnsignedInt(val, shift, buf, 0, chars); + return new String(buf, LATIN1); + } else { + byte[] buf = new byte[chars * 2]; + formatUnsignedIntUTF16(val, shift, buf, 0, chars); + return new String(buf, UTF16); + } } /** @@ -331,7 +361,7 @@ * @param offset the offset in the destination buffer to start at * @param len the number of characters to write */ - static void formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) { + static void formatUnsignedInt(int val, int shift, char[] buf, int offset, int len) { // assert shift > 0 && shift <=5 : "Illegal shift value"; // assert offset >= 0 && offset < buf.length : "illegal offset"; // assert len > 0 && (offset + len) <= buf.length : "illegal length"; @@ -344,6 +374,28 @@ } while (charPos > offset); } + /** byte[]/LATIN1 version */ + static void formatUnsignedInt(int val, int shift, byte[] buf, int offset, int len) { + int charPos = offset + len; + int radix = 1 << shift; + int mask = radix - 1; + do { + buf[--charPos] = (byte)Integer.digits[val & mask]; + val >>>= shift; + } while (charPos > offset); + } + + /** byte[]/UTF16 version */ + static void formatUnsignedIntUTF16(int val, int shift, byte[] buf, int offset, int len) { + int charPos = offset + len; + int radix = 1 << shift; + int mask = radix - 1; + do { + StringUTF16.putChar(buf, --charPos, Integer.digits[val & mask]); + val >>>= shift; + } while (charPos > offset); + } + static final char [] DigitTens = { '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', @@ -401,9 +453,15 @@ if (i == Integer.MIN_VALUE) return "-2147483648"; int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); - char[] buf = new char[size]; - getChars(i, size, buf); - return new String(buf, true); + if (COMPACT_STRINGS) { + byte[] buf = new byte[size]; + getChars(i, size, buf); + return new String(buf, LATIN1); + } else { + byte[] buf = new byte[size * 2]; + getCharsUTF16(i, size, buf); + return new String(buf, UTF16); + } } /** @@ -433,7 +491,41 @@ * * Will fail if i == Integer.MIN_VALUE */ - static void getChars(int i, int index, char[] buf) { + static void getChars(int i, int index, byte[] buf) { + int q, r; + int charPos = index; + char sign = 0; + + if (i < 0) { + sign = '-'; + i = -i; + } + + // Generate two digits per iteration + while (i >= 65536) { + q = i / 100; + // really: r = i - (q * 100); + r = i - ((q << 6) + (q << 5) + (q << 2)); + i = q; + buf [--charPos] = (byte)DigitOnes[r]; + buf [--charPos] = (byte)DigitTens[r]; + } + + // Fall thru to fast mode for smaller numbers + // assert(i <= 65536, i); + for (;;) { + q = (i * 52429) >>> (16+3); + r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ... + buf [--charPos] = (byte)digits [r]; + i = q; + if (i == 0) break; + } + if (sign != 0) { + buf [--charPos] = (byte)sign; + } + } + + static void getCharsUTF16(int i, int index, byte[] buf) { int q, r; int charPos = index; char sign = 0; @@ -449,8 +541,8 @@ // really: r = i - (q * 100); r = i - ((q << 6) + (q << 5) + (q << 2)); i = q; - buf [--charPos] = DigitOnes[r]; - buf [--charPos] = DigitTens[r]; + StringUTF16.putChar(buf, --charPos, DigitOnes[r]); + StringUTF16.putChar(buf, --charPos, DigitTens[r]); } // Fall thru to fast mode for smaller numbers @@ -458,12 +550,12 @@ for (;;) { q = (i * 52429) >>> (16+3); r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ... - buf [--charPos] = digits [r]; + StringUTF16.putChar(buf, --charPos, Integer.digits[r]); i = q; if (i == 0) break; } if (sign != 0) { - buf [--charPos] = sign; + StringUTF16.putChar(buf, --charPos, sign); } } --- old/src/java.base/share/classes/java/lang/Long.java 2015-10-30 14:03:45.777326808 -0700 +++ new/src/java.base/share/classes/java/lang/Long.java 2015-10-30 14:03:45.532327734 -0700 @@ -30,6 +30,9 @@ import java.util.Objects; import jdk.internal.HotSpotIntrinsicCandidate; +import static java.lang.String.COMPACT_STRINGS; +import static java.lang.String.LATIN1; +import static java.lang.String.UTF16; /** * The {@code Long} class wraps a value of the primitive type {@code @@ -124,25 +127,46 @@ radix = 10; if (radix == 10) return toString(i); - char[] buf = new char[65]; + + if (COMPACT_STRINGS) { + byte[] buf = new byte[65]; + int charPos = 64; + boolean negative = (i < 0); + + if (!negative) { + i = -i; + } + + while (i <= -radix) { + buf[charPos--] = (byte)Integer.digits[(int)(-(i % radix))]; + i = i / radix; + } + buf[charPos] = (byte)Integer.digits[(int)(-i)]; + + if (negative) { + buf[--charPos] = '-'; + } + return StringLatin1.newString(buf, charPos, (65 - charPos)); + } + return toStringUTF16(i, radix); + } + + private static String toStringUTF16(long i, int radix) { + byte[] buf = new byte[65 * 2]; int charPos = 64; boolean negative = (i < 0); - if (!negative) { i = -i; } - while (i <= -radix) { - buf[charPos--] = Integer.digits[(int)(-(i % radix))]; + StringUTF16.putChar(buf, charPos--, Integer.digits[(int)(-(i % radix))]); i = i / radix; } - buf[charPos] = Integer.digits[(int)(-i)]; - + StringUTF16.putChar(buf, charPos, Integer.digits[(int)(-i)]); if (negative) { - buf[--charPos] = '-'; + StringUTF16.putChar(buf, --charPos, '-'); } - - return new String(buf, charPos, (65 - charPos)); + return StringUTF16.newString(buf, charPos, (65 - charPos)); } /** @@ -355,10 +379,16 @@ // assert shift > 0 && shift <=5 : "Illegal shift value"; int mag = Long.SIZE - Long.numberOfLeadingZeros(val); int chars = Math.max(((mag + (shift - 1)) / shift), 1); - char[] buf = new char[chars]; - formatUnsignedLong(val, shift, buf, 0, chars); - return new String(buf, true); + if (COMPACT_STRINGS) { + byte[] buf = new byte[chars]; + formatUnsignedLong0(val, shift, buf, 0, chars); + return new String(buf, LATIN1); + } else { + byte[] buf = new byte[chars * 2]; + formatUnsignedLong0UTF16(val, shift, buf, 0, chars); + return new String(buf, UTF16); + } } /** @@ -385,6 +415,28 @@ } while (charPos > offset); } + /** byte[]/LATIN1 version */ + static void formatUnsignedLong0(long val, int shift, byte[] buf, int offset, int len) { + int charPos = offset + len; + int radix = 1 << shift; + int mask = radix - 1; + do { + buf[--charPos] = (byte)Integer.digits[((int) val) & mask]; + val >>>= shift; + } while (charPos > offset); + } + + /** byte[]/UTF16 version */ + static void formatUnsignedLong0UTF16(long val, int shift, byte[] buf, int offset, int len) { + int charPos = offset + len; + int radix = 1 << shift; + int mask = radix - 1; + do { + StringUTF16.putChar(buf, --charPos, Integer.digits[((int) val) & mask]); + val >>>= shift; + } while (charPos > offset); + } + /** * Returns a {@code String} object representing the specified * {@code long}. The argument is converted to signed decimal @@ -399,9 +451,15 @@ if (i == Long.MIN_VALUE) return "-9223372036854775808"; int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); - char[] buf = new char[size]; - getChars(i, size, buf); - return new String(buf, true); + if (COMPACT_STRINGS) { + byte[] buf = new byte[size]; + getChars(i, size, buf); + return new String(buf, LATIN1); + } else { + byte[] buf = new byte[size * 2]; + getCharsUTF16(i, size, buf); + return new String(buf, UTF16); + } } /** @@ -431,7 +489,54 @@ * * Will fail if i == Long.MIN_VALUE */ - static void getChars(long i, int index, char[] buf) { + static void getChars(long i, int index, byte[] buf) { + long q; + int r; + int charPos = index; + char sign = 0; + + if (i < 0) { + sign = '-'; + i = -i; + } + + // Get 2 digits/iteration using longs until quotient fits into an int + while (i > Integer.MAX_VALUE) { + q = i / 100; + // really: r = i - (q * 100); + r = (int)(i - ((q << 6) + (q << 5) + (q << 2))); + i = q; + buf[--charPos] = (byte)Integer.DigitOnes[r]; + buf[--charPos] = (byte)Integer.DigitTens[r]; + } + + // Get 2 digits/iteration using ints + int q2; + int i2 = (int)i; + while (i2 >= 65536) { + q2 = i2 / 100; + // really: r = i2 - (q * 100); + r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2)); + i2 = q2; + buf[--charPos] = (byte)Integer.DigitOnes[r]; + buf[--charPos] = (byte)Integer.DigitTens[r]; + } + + // Fall thru to fast mode for smaller numbers + // assert(i2 <= 65536, i2); + for (;;) { + q2 = (i2 * 52429) >>> (16+3); + r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ... + buf[--charPos] = (byte)Integer.digits[r]; + i2 = q2; + if (i2 == 0) break; + } + if (sign != 0) { + buf[--charPos] = (byte)sign; + } + } + + static void getCharsUTF16(long i, int index, byte[] buf) { long q; int r; int charPos = index; @@ -448,8 +553,8 @@ // really: r = i - (q * 100); r = (int)(i - ((q << 6) + (q << 5) + (q << 2))); i = q; - buf[--charPos] = Integer.DigitOnes[r]; - buf[--charPos] = Integer.DigitTens[r]; + StringUTF16.putChar(buf, --charPos, Integer.DigitOnes[r]); + StringUTF16.putChar(buf, --charPos, Integer.DigitTens[r]); } // Get 2 digits/iteration using ints @@ -460,8 +565,8 @@ // really: r = i2 - (q * 100); r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2)); i2 = q2; - buf[--charPos] = Integer.DigitOnes[r]; - buf[--charPos] = Integer.DigitTens[r]; + StringUTF16.putChar(buf, --charPos, Integer.DigitOnes[r]); + StringUTF16.putChar(buf, --charPos, Integer.DigitTens[r]); } // Fall thru to fast mode for smaller numbers @@ -469,12 +574,12 @@ for (;;) { q2 = (i2 * 52429) >>> (16+3); r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ... - buf[--charPos] = Integer.digits[r]; + StringUTF16.putChar(buf, --charPos, Integer.digits[r]); i2 = q2; if (i2 == 0) break; } if (sign != 0) { - buf[--charPos] = sign; + StringUTF16.putChar(buf, --charPos, sign); } } --- old/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java 2015-10-30 14:03:46.441324296 -0700 +++ new/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java 2015-10-30 14:03:46.191325242 -0700 @@ -32,4 +32,8 @@ public interface ArrayDecoder { int decode(byte[] src, int off, int len, char[] dst); + + default boolean isASCIICompatible() { + return false; + } } --- old/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java 2015-10-30 14:03:47.081321876 -0700 +++ new/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java 2015-10-30 14:03:46.844322772 -0700 @@ -26,10 +26,24 @@ package sun.nio.cs; /* - * FastPath char[]->byte[] encoder, REPLACE on malformed input or + * FastPath char[]/byte[] -> byte[] encoder, REPLACE on malformed input or * unmappable input. */ public interface ArrayEncoder { + + // is only used by j.u.zip.ZipCoder for utf8 int encode(char[] src, int off, int len, byte[] dst); + + default int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) { + return -1; + } + + default int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { + return -1; + } + + default boolean isASCIICompatible() { + return false; + } } --- old/src/java.base/share/classes/sun/nio/cs/DoubleByte.java 2015-10-30 14:03:47.713319486 -0700 +++ new/src/java.base/share/classes/sun/nio/cs/DoubleByte.java 2015-10-30 14:03:47.469320408 -0700 @@ -115,6 +115,7 @@ final char[] b2cSB; final int b2Min; final int b2Max; + final boolean isASCIICompatible; // for SimpleEUC override protected CoderResult crMalformedOrUnderFlow(int b) { @@ -132,16 +133,23 @@ public Decoder(Charset cs, float avgcpb, float maxcpb, char[][] b2c, char[] b2cSB, - int b2Min, int b2Max) { + int b2Min, int b2Max, + boolean isASCIICompatible) { super(cs, avgcpb, maxcpb); this.b2c = b2c; this.b2cSB = b2cSB; this.b2Min = b2Min; this.b2Max = b2Max; + this.isASCIICompatible = isASCIICompatible; + } + + public Decoder(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max, + boolean isASCIICompatible) { + this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max, isASCIICompatible); } public Decoder(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { - this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max); + this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max, false); } protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) { @@ -215,6 +223,7 @@ return decodeBufferLoop(src, dst); } + @Override public int decode(byte[] src, int sp, int len, char[] dst) { int dp = 0; int sl = sp + len; @@ -230,12 +239,12 @@ if (b2c[b1] == B2C_UNMAPPABLE || // isNotLeadingByte b2c[b2] != B2C_UNMAPPABLE || // isLeadingByte decodeSingle(b2) != UNMAPPABLE_DECODING) { - sp--; + sp--; } } } if (c == UNMAPPABLE_DECODING) { - c = repl; + c = repl; } } dst[dp++] = c; @@ -243,6 +252,11 @@ return dp; } + @Override + public boolean isASCIICompatible() { + return isASCIICompatible; + } + public void implReset() { super.implReset(); } @@ -274,8 +288,14 @@ private int currentState; public Decoder_EBCDIC(Charset cs, - char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { - super(cs, b2c, b2cSB, b2Min, b2Max); + char[][] b2c, char[] b2cSB, int b2Min, int b2Max, + boolean isASCIICompatible) { + super(cs, b2c, b2cSB, b2Min, b2Max, isASCIICompatible); + } + + public Decoder_EBCDIC(Charset cs, + char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { + super(cs, b2c, b2cSB, b2Min, b2Max, false); } public void implReset() { @@ -403,6 +423,7 @@ } } + @Override public int decode(byte[] src, int sp, int len, char[] dst) { int dp = 0; int sl = sp + len; @@ -451,8 +472,13 @@ b2cSB_UNMAPPABLE = new char[0x100]; Arrays.fill(b2cSB_UNMAPPABLE, UNMAPPABLE_DECODING); } + public Decoder_DBCSONLY(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max, + boolean isASCIICompatible) { + super(cs, 0.5f, 1.0f, b2c, b2cSB_UNMAPPABLE, b2Min, b2Max, isASCIICompatible); + } + public Decoder_DBCSONLY(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { - super(cs, 0.5f, 1.0f, b2c, b2cSB_UNMAPPABLE, b2Min, b2Max); + super(cs, 0.5f, 1.0f, b2c, b2cSB_UNMAPPABLE, b2Min, b2Max, false); } } @@ -464,8 +490,9 @@ private final int SS3 = 0x8F; public Decoder_EUC_SIM(Charset cs, - char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { - super(cs, b2c, b2cSB, b2Min, b2Max); + char[][] b2c, char[] b2cSB, int b2Min, int b2Max, + boolean isASCIICompatible) { + super(cs, b2c, b2cSB, b2Min, b2Max, isASCIICompatible); } // No support provided for G2/G3 for SimpleEUC @@ -481,6 +508,7 @@ return CoderResult.unmappableForLength(2); } + @Override public int decode(byte[] src, int sp, int len, char[] dst) { int dp = 0; int sl = sp + len; @@ -515,17 +543,25 @@ private final char[] c2b; private final char[] c2bIndex; protected Surrogate.Parser sgp; + final boolean isASCIICompatible; public Encoder(Charset cs, char[] c2b, char[] c2bIndex) { + this(cs, c2b, c2bIndex, false); + } + + public Encoder(Charset cs, char[] c2b, char[] c2bIndex, boolean isASCIICompatible) { super(cs, 2.0f, 2.0f); this.c2b = c2b; this.c2bIndex = c2bIndex; + this.isASCIICompatible = isASCIICompatible; } - public Encoder(Charset cs, float avg, float max, byte[] repl, char[] c2b, char[] c2bIndex) { + public Encoder(Charset cs, float avg, float max, byte[] repl, char[] c2b, char[] c2bIndex, + boolean isASCIICompatible) { super(cs, avg, max, repl); this.c2b = c2b; this.c2bIndex = c2bIndex; + this.isASCIICompatible = isASCIICompatible; } public boolean canEncode(char c) { @@ -624,6 +660,7 @@ repl = newReplacement; } + @Override public int encode(char[] src, int sp, int len, byte[] dst) { int dp = 0; int sl = sp + len; @@ -647,11 +684,69 @@ } else { // SingleByte dst[dp++] = (byte)bb; } + } + return dp; + } + + @Override + public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + len; + while (sp < sl) { + char c = (char)(src[sp++] & 0xff); + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + // no surrogate pair in latin1 string + dst[dp++] = repl[0]; + if (repl.length > 1) { + dst[dp++] = repl[1]; + } + continue; + } //else + if (bb > MAX_SINGLEBYTE) { // DoubleByte + dst[dp++] = (byte)(bb >> 8); + dst[dp++] = (byte)bb; + } else { // SingleByte + dst[dp++] = (byte)bb; + } } return dp; } + @Override + public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + len; + while (sp < sl) { + char c = StringUTF16.getChar(src, sp++); + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(StringUTF16.getChar(src, sp))) { + sp++; + } + dst[dp++] = repl[0]; + if (repl.length > 1) { + dst[dp++] = repl[1]; + } + continue; + } //else + if (bb > MAX_SINGLEBYTE) { // DoubleByte + dst[dp++] = (byte)(bb >> 8); + dst[dp++] = (byte)bb; + } else { // SingleByte + dst[dp++] = (byte)bb; + } + } + return dp; + } + + @Override + public boolean isASCIICompatible() { + return isASCIICompatible; + } + public int encodeChar(char ch) { return c2b[c2bIndex[ch >> 8] + (ch & 0xff)]; } @@ -741,9 +836,11 @@ } public static class Encoder_DBCSONLY extends Encoder { + public Encoder_DBCSONLY(Charset cs, byte[] repl, - char[] c2b, char[] c2bIndex) { - super(cs, 2.0f, 2.0f, repl, c2b, c2bIndex); + char[] c2b, char[] c2bIndex, + boolean isASCIICompatible) { + super(cs, 2.0f, 2.0f, repl, c2b, c2bIndex, isASCIICompatible); } public int encodeChar(char ch) { @@ -754,8 +851,6 @@ } } - - public static class Encoder_EBCDIC extends Encoder { static final int SBCS = 0; static final int DBCS = 1; @@ -764,8 +859,9 @@ protected int currentState = SBCS; - public Encoder_EBCDIC(Charset cs, char[] c2b, char[] c2bIndex) { - super(cs, 4.0f, 5.0f, new byte[] {(byte)0x6f}, c2b, c2bIndex); + public Encoder_EBCDIC(Charset cs, char[] c2b, char[] c2bIndex, + boolean isASCIICompatible) { + super(cs, 4.0f, 5.0f, new byte[] {(byte)0x6f}, c2b, c2bIndex, isASCIICompatible); } protected void implReset() { @@ -878,6 +974,7 @@ } } + @Override public int encode(char[] src, int sp, int len, byte[] dst) { int dp = 0; int sl = sp + len; @@ -917,12 +1014,88 @@ } return dp; } + + @Override + public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + len; + while (sp < sl) { + char c = (char)(src[sp++] & 0xff); + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + // no surrogate pair in latin1 string + dst[dp++] = repl[0]; + if (repl.length > 1) + dst[dp++] = repl[1]; + continue; + } //else + if (bb > MAX_SINGLEBYTE) { // DoubleByte + if (currentState == SBCS) { + currentState = DBCS; + dst[dp++] = SO; + } + dst[dp++] = (byte)(bb >> 8); + dst[dp++] = (byte)bb; + } else { // SingleByte + if (currentState == DBCS) { + currentState = SBCS; + dst[dp++] = SI; + } + dst[dp++] = (byte)bb; + } + } + if (currentState == DBCS) { + currentState = SBCS; + dst[dp++] = SI; + } + return dp; + } + + @Override + public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + len; + while (sp < sl) { + char c = StringUTF16.getChar(src, sp++); + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(StringUTF16.getChar(src, sp))) { + sp++; + } + dst[dp++] = repl[0]; + if (repl.length > 1) + dst[dp++] = repl[1]; + continue; + } //else + if (bb > MAX_SINGLEBYTE) { // DoubleByte + if (currentState == SBCS) { + currentState = DBCS; + dst[dp++] = SO; + } + dst[dp++] = (byte)(bb >> 8); + dst[dp++] = (byte)bb; + } else { // SingleByte + if (currentState == DBCS) { + currentState = SBCS; + dst[dp++] = SI; + } + dst[dp++] = (byte)bb; + } + } + if (currentState == DBCS) { + currentState = SBCS; + dst[dp++] = SI; + } + return dp; + } } // EUC_SIMPLE public static class Encoder_EUC_SIM extends Encoder { - public Encoder_EUC_SIM(Charset cs, char[] c2b, char[] c2bIndex) { - super(cs, c2b, c2bIndex); + public Encoder_EUC_SIM(Charset cs, char[] c2b, char[] c2bIndex, + boolean isASCIICompatible) { + super(cs, c2b, c2bIndex, isASCIICompatible); } } --- old/src/java.base/share/classes/sun/nio/cs/HKSCS.java 2015-10-30 14:03:48.350317076 -0700 +++ new/src/java.base/share/classes/sun/nio/cs/HKSCS.java 2015-10-30 14:03:48.116317962 -0700 @@ -53,7 +53,7 @@ // super(cs, 0.5f, 1.0f); // need to extends DoubleByte.Decoder so the // sun.io can use it. this implementation - super(cs, 0.5f, 1.0f, null, null, 0, 0); + super(cs, 0.5f, 1.0f, null, null, 0, 0, true); this.big5Dec = big5Dec; this.b2cBmp = b2cBmp; this.b2cSupp = b2cSupp; @@ -239,7 +239,7 @@ char[][] c2bBmp, char[][] c2bSupp) { - super(cs, null, null); + super(cs, null, null, true); this.big5Enc = big5Enc; this.c2bBmp = c2bBmp; this.c2bSupp = c2bSupp; @@ -389,6 +389,33 @@ return dp; } + public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + len; + int dl = dst.length; + while (sp < sl) { + char c = StringUTF16.getChar(src, sp++); + int bb = encodeChar(c); + if (bb == UNMAPPABLE_ENCODING) { + if (!Character.isHighSurrogate(c) || sp == sl || + !Character.isLowSurrogate(StringUTF16.getChar(src,sp)) || + (bb = encodeSupp(Character.toCodePoint(c, StringUTF16.getChar(src, sp++)))) + == UNMAPPABLE_ENCODING) { + dst[dp++] = repl[0]; + if (repl.length > 1) + dst[dp++] = repl[1]; + continue; + } + } + if (bb > MAX_SINGLEBYTE) { // DoubleByte + dst[dp++] = (byte)(bb >> 8); + dst[dp++] = (byte)bb; + } else { // SingleByte + dst[dp++] = (byte)bb; + } + } + return dp; + } static char[] C2B_UNMAPPABLE = new char[0x100]; static { --- old/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java 2015-10-30 14:03:48.982314686 -0700 +++ new/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java 2015-10-30 14:03:48.741315598 -0700 @@ -132,6 +132,10 @@ dst[dp++] = (char)(src[sp++] & 0xff); return dp; } + + public boolean isASCIICompatible() { + return true; + } } private static class Encoder extends CharsetEncoder @@ -297,5 +301,9 @@ } return dp; } + + public boolean isASCIICompatible() { + return true; + } } } --- old/src/java.base/share/classes/sun/nio/cs/SingleByte.java 2015-10-30 14:03:49.629312238 -0700 +++ new/src/java.base/share/classes/sun/nio/cs/SingleByte.java 2015-10-30 14:03:49.377313192 -0700 @@ -49,10 +49,18 @@ public static final class Decoder extends CharsetDecoder implements ArrayDecoder { private final char[] b2c; + private final boolean isASCIICompatible; public Decoder(Charset cs, char[] b2c) { super(cs, 1.0f, 1.0f); this.b2c = b2c; + this.isASCIICompatible = false; + } + + public Decoder(Charset cs, char[] b2c, boolean isASCIICompatible) { + super(cs, 1.0f, 1.0f); + this.b2c = b2c; + this.isASCIICompatible = isASCIICompatible; } private CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) { @@ -116,6 +124,7 @@ repl = newReplacement.charAt(0); } + @Override public int decode(byte[] src, int sp, int len, char[] dst) { if (len > dst.length) len = dst.length; @@ -129,6 +138,11 @@ } return dp; } + + @Override + public boolean isASCIICompatible() { + return isASCIICompatible; + } } public static final class Encoder extends CharsetEncoder @@ -136,11 +150,13 @@ private Surrogate.Parser sgp; private final char[] c2b; private final char[] c2bIndex; + private final boolean isASCIICompatible; - public Encoder(Charset cs, char[] c2b, char[] c2bIndex) { + public Encoder(Charset cs, char[] c2b, char[] c2bIndex, boolean isASCIICompatible) { super(cs, 1.0f, 1.0f); this.c2b = c2b; this.c2bIndex = c2bIndex; + this.isASCIICompatible = isASCIICompatible; } public boolean canEncode(char c) { @@ -252,6 +268,51 @@ } return dp; } + + @Override + public int encodeFromLatin1(byte[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + Math.min(len, dst.length); + while (sp < sl) { + char c = (char)(src[sp++] & 0xff); + int b = encode(c); + if (b == UNMAPPABLE_ENCODING) { + dst[dp++] = repl; + } else { + dst[dp++] = (byte)b; + } + } + return dp; + } + + @Override + public int encodeFromUTF16(byte[] src, int sp, int len, byte[] dst) { + int dp = 0; + int sl = sp + Math.min(len, dst.length); + while (sp < sl) { + char c = StringUTF16.getChar(src, sp++); + int b = encode(c); + if (b != UNMAPPABLE_ENCODING) { + dst[dp++] = (byte)b; + continue; + } + if (Character.isHighSurrogate(c) && sp < sl && + Character.isLowSurrogate(StringUTF16.getChar(src, sp))) { + if (len > dst.length) { + sl++; + len--; + } + sp++; + } + dst[dp++] = repl; + } + return dp; + } + + @Override + public boolean isASCIICompatible() { + return isASCIICompatible; + } } // init the c2b and c2bIndex tables from b2c. --- old/src/java.base/share/classes/sun/nio/cs/US_ASCII.java 2015-10-30 14:03:50.283309765 -0700 +++ new/src/java.base/share/classes/sun/nio/cs/US_ASCII.java 2015-10-30 14:03:50.035310703 -0700 @@ -146,6 +146,10 @@ } return dp; } + + public boolean isASCIICompatible() { + return true; + } } private static class Encoder extends CharsetEncoder @@ -259,6 +263,10 @@ } return dp; } + + public boolean isASCIICompatible() { + return true; + } } } --- old/src/java.base/share/classes/sun/nio/cs/UTF_8.java 2015-10-30 14:03:50.941307276 -0700 +++ new/src/java.base/share/classes/sun/nio/cs/UTF_8.java 2015-10-30 14:03:50.701308184 -0700 @@ -549,6 +549,10 @@ } return dp; } + + public boolean isASCIICompatible() { + return true; + } } private static final class Encoder extends CharsetEncoder @@ -742,5 +746,9 @@ } return dp; } + + public boolean isASCIICompatible() { + return true; + } } } --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/src/java.base/share/classes/sun/nio/cs/StringUTF16.java 2015-10-30 14:03:51.335305786 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.nio.cs; + +import static sun.misc.Unsafe.ARRAY_BYTE_BASE_OFFSET; +import static sun.misc.Unsafe.ARRAY_BYTE_INDEX_SCALE; + +class StringUTF16 { + + public static char getChar(byte[] val, int index) { + return unsafe.getChar(val, + ARRAY_BYTE_BASE_OFFSET + ARRAY_BYTE_INDEX_SCALE * index * 2L); + } + + private static final sun.misc.Unsafe unsafe = sun.misc.Unsafe.getUnsafe(); +} --- old/src/jdk.charsets/share/classes/sun/nio/cs/ext/Big5_Solaris.java.template 2015-10-30 14:03:52.190302552 -0700 +++ new/src/jdk.charsets/share/classes/sun/nio/cs/ext/Big5_Solaris.java.template 2015-10-30 14:03:51.938303504 -0700 @@ -51,12 +51,12 @@ public CharsetDecoder newDecoder() { initb2c(); - return new DoubleByte.Decoder(this, b2c, b2cSB, 0x40, 0xfe); + return new DoubleByte.Decoder(this, b2c, b2cSB, 0x40, 0xfe, true); } public CharsetEncoder newEncoder() { initc2b(); - return new DoubleByte.Encoder(this, c2b, c2bIndex); + return new DoubleByte.Encoder(this, c2b, c2bIndex, true); } static char[][] b2c; --- old/src/jdk.charsets/share/classes/sun/nio/cs/ext/IBM834.java 2015-10-30 14:03:52.842300085 -0700 +++ new/src/jdk.charsets/share/classes/sun/nio/cs/ext/IBM834.java 2015-10-30 14:03:52.580301076 -0700 @@ -62,7 +62,7 @@ protected static class Encoder extends DoubleByte.Encoder_DBCSONLY { public Encoder(Charset cs) { super(cs, new byte[] {(byte)0xfe, (byte)0xfe}, - IBM933.c2b, IBM933.c2bIndex); + IBM933.c2b, IBM933.c2bIndex, false); } public int encodeChar(char ch) { --- old/src/java.base/share/classes/java/util/Arrays.java 2015-10-30 14:03:53.512297550 -0700 +++ new/src/java.base/share/classes/java/util/Arrays.java 2015-10-30 14:03:53.264298488 -0700 @@ -2685,6 +2685,7 @@ * @param a2 the other array to be tested for equality * @return {@code true} if the two arrays are equal */ + @HotSpotIntrinsicCandidate public static boolean equals(byte[] a, byte[] a2) { if (a==a2) return true; --- old/src/java.base/share/native/libjava/String.c 2015-10-30 14:03:54.213294899 -0700 +++ new/src/java.base/share/native/libjava/String.c 2015-10-30 14:03:53.968295825 -0700 @@ -31,3 +31,14 @@ { return JVM_InternString(env, this); } + +JNIEXPORT jboolean JNICALL +Java_java_lang_StringUTF16_isBigEndian(JNIEnv *env, jclass cls) +{ + unsigned int endianTest = 0xff000000; + if (((char*)(&endianTest))[0] != 0) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } +} --- old/make/mapfiles/libjava/mapfile-vers 2015-10-30 14:03:54.846292503 -0700 +++ new/make/mapfiles/libjava/mapfile-vers 2015-10-30 14:03:54.607293407 -0700 @@ -211,6 +211,7 @@ Java_java_lang_SecurityManager_getClassContext; Java_java_lang_Shutdown_halt0; Java_java_lang_String_intern; + Java_java_lang_StringUTF16_isBigEndian; Java_java_lang_System_identityHashCode; Java_java_lang_System_initProperties; Java_java_lang_System_mapLibraryName; --- old/make/mapfiles/libjava/reorder-sparc 2015-10-30 14:03:55.462290172 -0700 +++ new/make/mapfiles/libjava/reorder-sparc 2015-10-30 14:03:55.232291043 -0700 @@ -57,6 +57,7 @@ text: .text%JNU_ClassString; text: .text%JNU_CopyObjectArray; text: .text%Java_java_lang_String_intern; +text: .text%Java_java_lang_StringUTF16_isBigEndian; text: .text%Java_java_lang_ClassLoader_findLoadedClass0; text: .text%Java_java_lang_ClassLoader_findBootstrapClass; text: .text%Java_java_lang_Throwable_fillInStackTrace; --- old/make/mapfiles/libjava/reorder-sparcv9 2015-10-30 14:03:56.090287796 -0700 +++ new/make/mapfiles/libjava/reorder-sparcv9 2015-10-30 14:03:55.841288738 -0700 @@ -29,6 +29,7 @@ text: .text%Java_sun_reflect_Reflection_getCallerClass__I; text: .text%Java_java_lang_Class_forName0; text: .text%Java_java_lang_String_intern; +text: .text%Java_java_lang_StringUTF16_isBigEndian; text: .text%Java_java_lang_Float_floatToRawIntBits; text: .text%Java_java_lang_Double_doubleToRawLongBits; text: .text%Java_java_lang_ClassLoader_findLoadedClass0; --- old/make/mapfiles/libjava/reorder-x86 2015-10-30 14:03:56.694285511 -0700 +++ new/make/mapfiles/libjava/reorder-x86 2015-10-30 14:03:56.457286408 -0700 @@ -31,6 +31,7 @@ text: .text%Java_sun_reflect_Reflection_getCallerClass__I; text: .text%Java_java_lang_Class_forName0; text: .text%Java_java_lang_String_intern; +text: .text%Java_java_lang_StringUTF16_isBigEndian; text: .text%Java_sun_reflect_NativeConstructorAccessorImpl_newInstance0; text: .text%Java_java_lang_Throwable_fillInStackTrace; text: .text%Java_java_lang_System_setOut0; --- old/make/data/charsetmapping/DoubleByte-X.java.template 2015-10-30 14:03:57.354283014 -0700 +++ new/make/data/charsetmapping/DoubleByte-X.java.template 2015-10-30 14:03:57.112283930 -0700 @@ -50,12 +50,12 @@ public CharsetDecoder newDecoder() { initb2c(); - return new DoubleByte.Decoder$DECTYPE$(this, b2c, b2cSB, $B2MIN$, $B2MAX$); + return new DoubleByte.Decoder$DECTYPE$(this, b2c, b2cSB, $B2MIN$, $B2MAX$, $ASCIICOMPATIBLE$); } public CharsetEncoder newEncoder() { initc2b(); - return new DoubleByte.Encoder$ENCTYPE$(this, $ENC_REPLACEMENT$ c2b, c2bIndex); + return new DoubleByte.Encoder$ENCTYPE$(this, $ENC_REPLACEMENT$ c2b, c2bIndex, $ASCIICOMPATIBLE$); } $B2C$ --- old/make/data/charsetmapping/SingleByte-X.java.template 2015-10-30 14:03:57.989280612 -0700 +++ new/make/data/charsetmapping/SingleByte-X.java.template 2015-10-30 14:03:57.758281485 -0700 @@ -48,11 +48,11 @@ } public CharsetDecoder newDecoder() { - return new SingleByte.Decoder(this, b2c); + return new SingleByte.Decoder(this, b2c, $ASCIICOMPATIBLE$); } public CharsetEncoder newEncoder() { - return new SingleByte.Encoder(this, c2b, c2bIndex); + return new SingleByte.Encoder(this, c2b, c2bIndex, $ASCIICOMPATIBLE$); } private final static String b2cTable = $B2CTABLE$ --- old/make/src/classes/build/tools/charsetmapping/DBCS.java 2015-10-30 14:03:58.632278179 -0700 +++ new/make/src/classes/build/tools/charsetmapping/DBCS.java 2015-10-30 14:03:58.397279068 -0700 @@ -197,6 +197,7 @@ .replace("$B1MAX$" , "0x" + Integer.toString(b1Max, 16)) .replace("$B2MIN$" , "0x" + Integer.toString(b2Min, 16)) .replace("$B2MAX$" , "0x" + Integer.toString(b2Max, 16)) + .replace("$ASCIICOMPATIBLE$", isASCII ? "true" : "false") .replace("$B2C$", b2c) .replace("$C2BLENGTH$", "0x" + Integer.toString(c2bOff, 16)) .replace("$NONROUNDTRIP_B2C$", b2cNR) --- old/make/src/classes/build/tools/charsetmapping/SBCS.java 2015-10-30 14:03:59.292275681 -0700 +++ new/make/src/classes/build/tools/charsetmapping/SBCS.java 2015-10-30 14:03:59.057276569 -0700 @@ -175,6 +175,9 @@ else line = " return (cs instanceof " + clzName + ");"; } + if (line.indexOf("$ASCIICOMPATIBLE$") != -1) { + line = line.replace("$ASCIICOMPATIBLE$", isASCII ? "true" : "false"); + } if (line.indexOf("$B2CTABLE$") != -1) { line = line.replace("$B2CTABLE$", b2c); } --- old/test/sun/nio/cs/TestStringCoding.java 2015-10-30 14:03:59.927273279 -0700 +++ new/test/sun/nio/cs/TestStringCoding.java 2015-10-30 14:03:59.668274257 -0700 @@ -22,7 +22,7 @@ */ /* @test - * @bug 6636323 6636319 7040220 7096080 7183053 8080248 + * @bug 6636323 6636319 7040220 7096080 7183053 8080248 8054307 * @summary Test if StringCoding and NIO result have the same de/encoding result * @modules java.base/sun.nio.cs * @run main/othervm/timeout=2000 TestStringCoding @@ -36,41 +36,61 @@ public class TestStringCoding { public static void main(String[] args) throws Throwable { + // full bmp first + char[] bmp = new char[0x10000]; + for (int i = 0; i < 0x10000; i++) { + bmp[i] = (char)i; + } + char[] latin = Arrays.copyOf(bmp, 0x100); + char[] ascii = Arrays.copyOf(bmp, 0x80); + + byte[] latinBA = new byte[0x100]; + for (int i = 0; i < 0x100; i++) { + latinBA[i] = (byte)i; + } + byte[] asciiBA = Arrays.copyOf(latinBA, 0x80); + for (Boolean hasSM: new boolean[] { false, true }) { - if (hasSM) + if (hasSM) { System.setSecurityManager(new PermissiveSecurityManger()); + } for (Charset cs: Charset.availableCharsets().values()) { if ("ISO-2022-CN".equals(cs.name()) || "x-COMPOUND_TEXT".equals(cs.name()) || "x-JISAutoDetect".equals(cs.name())) continue; System.out.printf("Testing(sm=%b) " + cs.name() + "....", hasSM); - // full bmp first - char[] bmpCA = new char[0x10000]; - for (int i = 0; i < 0x10000; i++) { - bmpCA[i] = (char)i; - } - byte[] sbBA = new byte[0x100]; - for (int i = 0; i < 0x100; i++) { - sbBA[i] = (byte)i; - } - test(cs, bmpCA, sbBA); + + testNewString(cs, testGetBytes(cs, new String(bmp))); + testNewString(cs, testGetBytes(cs, new String(latin))); + testNewString(cs, testGetBytes(cs, new String(ascii))); + testGetBytes(cs, testNewString(cs, latinBA)); + testGetBytes(cs, testNewString(cs, asciiBA)); + // "randomed" sizes Random rnd = new Random(); for (int i = 0; i < 10; i++) { - int clen = rnd.nextInt(0x10000); - int blen = rnd.nextInt(0x100); //System.out.printf(" blen=%d, clen=%d%n", blen, clen); - test(cs, Arrays.copyOf(bmpCA, clen), Arrays.copyOf(sbBA, blen)); + char[] bmp0 = Arrays.copyOf(bmp, rnd.nextInt(0x10000)); + testNewString(cs, testGetBytes(cs, new String(bmp0))); //add a pair of surrogates - int pos = clen / 2; - if ((pos + 1) < blen) { - bmpCA[pos] = '\uD800'; - bmpCA[pos+1] = '\uDC00'; + int pos = bmp0.length / 2; + if ((pos + 1) < bmp0.length) { + bmp0[pos] = '\uD800'; + bmp0[pos+1] = '\uDC00'; } - test(cs, Arrays.copyOf(bmpCA, clen), Arrays.copyOf(sbBA, blen)); - } + testNewString(cs, testGetBytes(cs, new String(bmp0))); + char[] latin0 = Arrays.copyOf(latin, rnd.nextInt(0x100)); + char[] ascii0 = Arrays.copyOf(ascii, rnd.nextInt(0x80)); + byte[] latinBA0 = Arrays.copyOf(latinBA, rnd.nextInt(0x100)); + byte[] asciiBA0 = Arrays.copyOf(asciiBA, rnd.nextInt(0x80)); + testNewString(cs, testGetBytes(cs, new String(latin0))); + testNewString(cs, testGetBytes(cs, new String(ascii0))); + testGetBytes(cs, testNewString(cs, latinBA0)); + testGetBytes(cs, testNewString(cs, asciiBA0)); + } + testSurrogates(cs); testMixed(cs); System.out.println("done!"); } @@ -109,8 +129,9 @@ //getBytes(cs); bmpBA = bmpStr.getBytes(cs); - if (!Arrays.equals(bmpBA, baNIO)) + if (!Arrays.equals(bmpBA, baNIO)) { throw new RuntimeException("getBytes(cs) failed -> " + cs.name()); + } //new String(csn); String strSC = new String(bmpBA, cs.name()); @@ -118,49 +139,61 @@ if(!strNIO.equals(strSC)) { throw new RuntimeException("new String(csn) failed -> " + cs.name()); } - //new String(cs); strSC = new String(bmpBA, cs); - if (!strNIO.equals(strSC)) + if (!strNIO.equals(strSC)) { throw new RuntimeException("new String(cs) failed -> " + cs.name()); + } + } + static byte[] getBytes(CharsetEncoder enc, String str) throws Throwable { + ByteBuffer bf = enc.reset().encode(CharBuffer.wrap(str.toCharArray())); + byte[] ba = new byte[bf.limit()]; + bf.get(ba, 0, ba.length); + return ba; } - static void test(Charset cs, char[] bmpCA, byte[] sbBA) throws Throwable { - String bmpStr = new String(bmpCA); - CharsetDecoder dec = cs.newDecoder() - .onMalformedInput(CodingErrorAction.REPLACE) - .onUnmappableCharacter(CodingErrorAction.REPLACE); + static byte[] testGetBytes(Charset cs, String str) throws Throwable { CharsetEncoder enc = cs.newEncoder() .onMalformedInput(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE); - //getBytes(csn); - byte[] baSC = bmpStr.getBytes(cs.name()); - ByteBuffer bf = enc.reset().encode(CharBuffer.wrap(bmpCA)); - byte[] baNIO = new byte[bf.limit()]; - bf.get(baNIO, 0, baNIO.length); - if (!Arrays.equals(baSC, baNIO)) + byte[] baSC = str.getBytes(cs.name()); + byte[] baNIO = getBytes(enc, str); + if (!Arrays.equals(baSC, baNIO)) { throw new RuntimeException("getBytes(csn) failed -> " + cs.name()); - + } //getBytes(cs); - baSC = bmpStr.getBytes(cs); - if (!Arrays.equals(baSC, baNIO)) + baSC = str.getBytes(cs); + if (!Arrays.equals(baSC, baNIO)) { throw new RuntimeException("getBytes(cs) failed -> " + cs.name()); + } + return baSC; + } + static String testNewString(Charset cs, byte[] ba) throws Throwable { + CharsetDecoder dec = cs.newDecoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); //new String(csn); - String strSC = new String(sbBA, cs.name()); - String strNIO = dec.reset().decode(ByteBuffer.wrap(sbBA)).toString(); - - if(!strNIO.equals(strSC)) + String strSC = new String(ba, cs.name()); + String strNIO = dec.reset().decode(ByteBuffer.wrap(ba)).toString(); + if(!strNIO.equals(strSC)) { throw new RuntimeException("new String(csn) failed -> " + cs.name()); - + } //new String(cs); - strSC = new String(sbBA, cs); - if (!strNIO.equals(strSC)) - throw new RuntimeException("new String(cs) failed -> " + cs.name()); + strSC = new String(ba, cs); + if (!strNIO.equals(strSC)) { + throw new RuntimeException("new String(cs)/bmp failed -> " + cs.name()); + } + return strSC; + } + static void testSurrogates(Charset cs) throws Throwable { //encode unmappable surrogates + CharsetEncoder enc = cs.newEncoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); if (enc instanceof sun.nio.cs.ArrayEncoder && cs.contains(Charset.forName("ASCII"))) { if (cs.name().equals("UTF-8") || // utf8 handles surrogates --- old/test/sun/nio/cs/TestStringCodingUTF8.java 2015-10-30 14:04:00.569270847 -0700 +++ new/test/sun/nio/cs/TestStringCodingUTF8.java 2015-10-30 14:04:00.321271786 -0700 @@ -22,7 +22,7 @@ */ /* @test - @bug 7040220 + @bug 7040220 8054307 @summary Test if StringCoding and NIO result have the same de/encoding result for UTF-8 * @run main/othervm/timeout=2000 TestStringCodingUTF8 * @key randomness @@ -50,6 +50,18 @@ } test(cs, bmp, 0, bmp.length); + char[] ascii = new char[0x80]; + for (int i = 0; i < 0x80; i++) { + ascii[i] = (char)i; + } + test(cs, ascii, 0, ascii.length); + + char[] latin1 = new char[0x100]; + for (int i = 0; i < 0x100; i++) { + latin1[i] = (char)i; + } + test(cs, latin1, 0, latin1.length); + ArrayList list = new ArrayList<>(0x20000); for (int i = 0; i < 0x20000; i++) { list.add(i, i); --- old/test/java/lang/String/LiteralReplace.java 2015-10-30 14:04:01.233268336 -0700 +++ new/test/java/lang/String/LiteralReplace.java 2015-10-30 14:04:00.980269292 -0700 @@ -22,7 +22,7 @@ */ /* @test - * @bug 8058779 + * @bug 8058779 8054307 * @library /lib/testlibrary/ * @build jdk.testlibrary.RandomFactory * @run testng LiteralReplace @@ -104,6 +104,109 @@ {"abcdefgh", "[a-h]", "X", "abcdefgh"}, {"aa+", "a+", "", "a"}, {"^abc$", "abc", "x", "^x$"}, + + // more with non-latin1 characters + {"\u4e00\u4e00\u4e00", + "\u4e00\u4e00", + "\u4e01", + "\u4e01\u4e00"}, + + {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08", + "\u4e03\u4e04\u4e05", + "\u4e10\u4e11\u4e12", + "\u4e00\u4e01\u4e02\u4e10\u4e11\u4e12\u4e06\u4e07\u4e08"}, + + {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08", + "ABC", + "\u4e10\u4e11\u4e12", + "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08"}, + + {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e02\u4e03\u4e07\u4e08", + "\u4e02\u4e03", + "\u4e12\u4e13", + "\u4e00\u4e01\u4e12\u4e13\u4e04\u4e12\u4e13\u4e07\u4e08"}, + + {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e02\u4e03\u4e07\u4e08", + "\u4e02\u4e03", + "ab", + "\u4e00\u4e01ab\u4e04ab\u4e07\u4e08"}, + + {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07", + "", + "_", + "_\u4e00_\u4e01_\u4e02_\u4e03_\u4e04_\u4e05_\u4e06_\u4e07_"}, + {"^\u4e00\u4e01\u4e02$", + "\u4e00\u4e01\u4e02", + "\u4e03", + "^\u4e03$"}, + + {"", "\u4e00", "\u4e01", ""}, + {"", "", "\u4e00\u4e01\u4e02", "\u4e00\u4e01\u4e02"}, + + {"^\u4e00\u4e01\u4e02$", + "\u4e00\u4e01\u4e02", + "X", + "^X$"}, + + {"abcdefgh", + "def", + "\u4e01", + "abc\u4e01gh"}, + + {"abcdefgh", + "def", + "\u4e01\u4e02", + "abc\u4e01\u4e02gh"}, + + {"abcdefabcgh", + "abc", + "\u4e01\u4e02", + "\u4e01\u4e02def\u4e01\u4e02gh"}, + + {"abcdefabcghabc", + "abc", + "\u4e01\u4e02", + "\u4e01\u4e02def\u4e01\u4e02gh\u4e01\u4e02"}, + + {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05", + "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05", + "abcd", + "abcd"}, + + {"\u4e00\u4e01", + "\u4e00\u4e01", + "abcdefg", + "abcdefg"}, + + {"\u4e00\u4e01xyz", + "\u4e00\u4e01", + "abcdefg", + "abcdefgxyz"}, + + {"\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00", + "\u4e00\u4e00", + "\u4e00\u4e00\u4e00", + "\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00"}, + + {"\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00", + "\u4e00\u4e00\u4e00", + "\u4e00\u4e00", + "\u4e00\u4e00\u4e00\u4e00"}, + + {"\u4e00.\u4e01.\u4e02.\u4e03.\u4e04.", + ".", + "-", + "\u4e00-\u4e01-\u4e02-\u4e03-\u4e04-"}, + + {"\u4e00\u4e00\u4e00\u4e00\u4e00\u4e00", + "\u4e00", + "", + ""}, + + {"\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05", + "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05", + "", + ""}, }; } --- old/test/java/lang/String/ToLowerCase.java 2015-10-30 14:04:01.879265890 -0700 +++ new/test/java/lang/String/ToLowerCase.java 2015-10-30 14:04:01.623266859 -0700 @@ -23,7 +23,7 @@ /* @test - @bug 4217441 4533872 4900935 8020037 8032012 8041791 8042589 + @bug 4217441 4533872 4900935 8020037 8032012 8041791 8042589 8054307 @summary toLowerCase should lower-case Greek Sigma correctly depending on the context (final/non-final). Also it should handle Locale specific (lt, tr, and az) lowercasings and supplementary @@ -134,14 +134,60 @@ } test(src.toString(), Locale.US, exp.toString()); + // test latin1 + src = new StringBuilder(0x100); + exp = new StringBuilder(0x100); + for (int cp = 0; cp < 0x100; cp++) { + int lowerCase = Character.toLowerCase(cp); + if (lowerCase == -1) { //Character.ERROR + continue; + } + src.appendCodePoint(cp); + exp.appendCodePoint(lowerCase); + } + test(src.toString(), Locale.US, exp.toString()); + + // test non-latin1 -> latin1 + src = new StringBuilder(0x100).append("abc"); + exp = new StringBuilder(0x100).append("abc"); + for (int cp = 0x100; cp < 0x10000; cp++) { + int lowerCase = Character.toLowerCase(cp); + if (lowerCase < 0x100 && cp != '\u0130') { + src.appendCodePoint(cp); + exp.appendCodePoint(lowerCase); + } + } + test(src.toString(), Locale.US, exp.toString()); } static void test(String in, Locale locale, String expected) { + test0(in, locale,expected); + for (String[] ss : new String[][] { + new String[] {"abc", "abc"}, + new String[] {"aBc", "abc"}, + new String[] {"ABC", "abc"}, + new String[] {"ab\u4e00", "ab\u4e00"}, + new String[] {"aB\u4e00", "ab\u4e00"}, + new String[] {"AB\u4e00", "ab\u4e00"}, + new String[] {"ab\uD800\uDC00", "ab\uD800\uDC00"}, + new String[] {"aB\uD800\uDC00", "ab\uD800\uDC00"}, + new String[] {"AB\uD800\uDC00", "ab\uD800\uDC00"}, + new String[] {"ab\uD801\uDC1C", "ab\uD801\uDC44"}, + new String[] {"aB\uD801\uDC1C", "ab\uD801\uDC44"}, + new String[] {"AB\uD801\uDC1C", "ab\uD801\uDC44"}, + + }) { + test0(ss[0] + " " + in, locale, ss[1] + " " + expected); + test0(in + " " + ss[0], locale, expected + " " + ss[1]); + } + } + + static void test0(String in, Locale locale, String expected) { String result = in.toLowerCase(locale); if (!result.equals(expected)) { System.err.println("input: " + in + ", locale: " + locale + ", expected: " + expected + ", actual: " + result); throw new RuntimeException(); } - } + } } --- old/test/java/lang/String/ToUpperCase.java 2015-10-30 14:04:02.540263388 -0700 +++ new/test/java/lang/String/ToUpperCase.java 2015-10-30 14:04:02.284264358 -0700 @@ -23,7 +23,7 @@ /* @test - @bug 4219630 4304573 4533872 4900935 8042589 + @bug 4219630 4304573 4533872 4900935 8042589 8054307 @summary toUpperCase should upper-case German sharp s correctly even if it's the only character in the string. should also uppercase all of the 1:M char mappings correctly. Also it should handle @@ -97,14 +97,66 @@ test("A\uD801\uDC44", Locale.ROOT, "A\uD801\uDC1c"); test("a\uD801\uDC28\uD801\uDC29\uD801\uDC2A", Locale.US, "A\uD801\uDC00\uD801\uDC01\uD801\uDC02"); test("A\uD801\uDC28a\uD801\uDC29b\uD801\uDC2Ac", Locale.US, "A\uD801\uDC00A\uD801\uDC01B\uD801\uDC02C"); + + // test latin1 only case + StringBuilder src = new StringBuilder(0x100); + StringBuilder exp = new StringBuilder(0x100); + for (int cp = 0; cp < 0x100; cp++) { + int upperCase = Character.toUpperCase(cp); + if (upperCase == -1) { //Character.ERROR + continue; + } + src.appendCodePoint(cp); + if (cp == '\u00df') { + exp.append("SS"); // need Character.toUpperCaseEx() + } else { + exp.appendCodePoint(upperCase); + } + } + test(src.toString(), Locale.US, exp.toString()); + + // test non-latin1 -> latin1 + src = new StringBuilder(0x100).append("ABC"); + exp = new StringBuilder(0x100).append("ABC"); + for (int cp = 0x100; cp < 0x10000; cp++) { + int upperCase = Character.toUpperCase(cp); + if (upperCase < 0x100) { + src.appendCodePoint(cp); + exp.appendCodePoint(upperCase); + } + } + test(src.toString(), Locale.US, exp.toString()); + } static void test(String in, Locale locale, String expected) { + test0(in, locale,expected); + // trigger different code paths + for (String[] ss : new String[][] { + new String[] {"abc", "ABC"}, + new String[] {"AbC", "ABC"}, + new String[] {"ABC", "ABC"}, + new String[] {"AB\u4e00", "AB\u4e00"}, + new String[] {"ab\u4e00", "AB\u4e00"}, + new String[] {"aB\u4e00", "AB\u4e00"}, + new String[] {"AB\uD800\uDC00", "AB\uD800\uDC00"}, + new String[] {"Ab\uD800\uDC00", "AB\uD800\uDC00"}, + new String[] {"ab\uD800\uDC00", "AB\uD800\uDC00"}, + new String[] {"AB\uD801\uDC44", "AB\uD801\uDC1C"}, + new String[] {"Ab\uD801\uDC44", "AB\uD801\uDC1C"}, + new String[] {"ab\uD801\uDC44", "AB\uD801\uDC1C"}, + }) { + test0(ss[0] + " " + in, locale, ss[1] + " " + expected); + test0(in + " " + ss[0], locale, expected + " " + ss[1]); + } + } + + static void test0(String in, Locale locale, String expected) { String result = in.toUpperCase(locale); if (!result.equals(expected)) { System.err.println("input: " + in + ", locale: " + locale + ", expected: " + expected + ", actual: " + result); throw new RuntimeException(); } - } + } } --- old/test/java/lang/StringBuffer/Exceptions.java 2015-10-30 14:04:03.188260936 -0700 +++ new/test/java/lang/StringBuffer/Exceptions.java 2015-10-30 14:04:02.939261878 -0700 @@ -94,7 +94,7 @@ System.out.println("StringBuffer.replace(int start, int end, String str)"); tryCatch(" -1, 2, \" \"", - new StringIndexOutOfBoundsException(-1), + new StringIndexOutOfBoundsException("start -1, end 2, length 7"), new Runnable() { public void run() { StringBuffer sb = new StringBuffer("hilbert"); @@ -102,14 +102,14 @@ }}); tryCatch(" 7, 8, \" \"", - new StringIndexOutOfBoundsException("start > length()"), + new StringIndexOutOfBoundsException("start 7, end 6, length 6"), new Runnable() { public void run() { StringBuffer sb = new StringBuffer("banach"); sb.replace(7, 8, " "); }}); tryCatch(" 2, 1, \" \"", - new StringIndexOutOfBoundsException("start > end"), + new StringIndexOutOfBoundsException("start 2, end 1, length 7"), new Runnable() { public void run() { StringBuffer sb = new StringBuffer("riemann"); --- old/test/java/lang/StringBuilder/BuilderForwarding.java 2015-10-30 14:04:03.838258476 -0700 +++ new/test/java/lang/StringBuilder/BuilderForwarding.java 2015-10-30 14:04:03.585259434 -0700 @@ -264,4 +264,3 @@ } } } - --- old/test/java/lang/StringBuilder/Exceptions.java 2015-10-30 14:04:04.513255921 -0700 +++ new/test/java/lang/StringBuilder/Exceptions.java 2015-10-30 14:04:04.256256893 -0700 @@ -94,21 +94,21 @@ System.out.println("StringBuilder.replace(int start, int end, String str)"); tryCatch(" -1, 2, \" \"", - new StringIndexOutOfBoundsException(-1), + new StringIndexOutOfBoundsException("start -1, end 2, length 7"), new Runnable() { public void run() { StringBuilder sb = new StringBuilder("hilbert"); sb.replace(-1, 2, " "); }}); tryCatch(" 7, 8, \" \"", - new StringIndexOutOfBoundsException("start > length()"), + new StringIndexOutOfBoundsException("start 7, end 6, length 6"), new Runnable() { public void run() { StringBuilder sb = new StringBuilder("banach"); sb.replace(7, 8, " "); }}); tryCatch(" 2, 1, \" \"", - new StringIndexOutOfBoundsException("start > end"), + new StringIndexOutOfBoundsException("start 2, end 1, length 7"), new Runnable() { public void run() { StringBuilder sb = new StringBuilder("riemann"); --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/Chars.java 2015-10-30 14:04:04.909254422 -0700 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + @test + @bug 8054307 + @summary test chars() and codePoints() +*/ + +import java.util.Arrays; +import java.util.Random; + +public class Chars { + + public static void main(String[] args) { + Random r = new Random(); + for (int i = 0; i < 10; i++) { + int n = 100 + r.nextInt(100); + char[] cc = new char[n]; + int[] ccExp = new int[n]; + int[] cpExp = new int[n]; + // latin1 + for (int j = 0; j < n; j++) { + cc[j] = (char)(ccExp[j] = cpExp[j] = r.nextInt(0x80)); + } + testChars(cc, ccExp); + testCPs(cc, cpExp); + + // bmp without surrogates + for (int j = 0; j < n; j++) { + cc[j] = (char)(ccExp[j] = cpExp[j] = r.nextInt(0x8000)); + } + testChars(cc, ccExp); + testCPs(cc, cpExp); + + // bmp with surrogates + int k = 0; + for (int j = 0; j < n; j++) { + if (j % 9 == 5 && j + 1 < n) { + int cp = 0x10000 + r.nextInt(2000); + cpExp[k++] = cp; + Character.toChars(cp, cc, j); + ccExp[j] = cc[j]; + ccExp[j + 1] = cc[j + 1]; + j++; + } else { + cc[j] = (char)(ccExp[j] = cpExp[k++] = r.nextInt(0x8000)); + } + } + cpExp = Arrays.copyOf(cpExp, k); + testChars(cc, ccExp); + testCPs(cc, cpExp); + } + } + + static void testChars(char[] cc, int[] expected) { + String str = new String(cc); + if (!Arrays.equals(expected, str.chars().toArray())) { + throw new RuntimeException("chars/codePoints() failed!"); + } + } + + static void testCPs(char[] cc, int[] expected) { + String str = new String(cc); + if (!Arrays.equals(expected, str.codePoints().toArray())) { + throw new RuntimeException("chars/codePoints() failed!"); + } + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/CharAt.java 2015-10-30 14:04:05.513252134 -0700 @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.stream.IntStream; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.charAt. + * @run testng/othervm -XX:+CompactStrings CharAt + * @run testng/othervm -XX:-CompactStrings CharAt + */ + +public class CharAt extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] { STRING_L1, new char[] { 'A' } }, + new Object[] { STRING_L2, new char[] { 'A', 'B' } }, + new Object[] { STRING_L4, new char[] { 'A', 'B', 'C', 'D' } }, + new Object[] { STRING_LLONG, + new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' } }, + new Object[] { STRING_U1, new char[] { '\uFF21' } }, + new Object[] { STRING_U2, new char[] { '\uFF21', '\uFF22' } }, + new Object[] { STRING_M12, new char[] { '\uFF21', 'A' } }, + new Object[] { STRING_M11, new char[] { 'A', '\uFF21' } }, }; + } + + @Test(dataProvider = "provider") + public void testCharAt(String str, char[] expected) { + map.get(str) + .forEach( + (source, data) -> { + IntStream + .range(0, str.length()) + .forEach( + i -> assertEquals( + str.charAt(i), + expected[i], + String.format( + "testing String(%s).charAt(%d), source : %s, ", + escapeNonASCIIs(data), + i, source))); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/CodePointAt.java 2015-10-30 14:04:06.124249823 -0700 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.stream.IntStream; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.codePointAt. + * @run testng/othervm -XX:+CompactStrings CodePointAt + * @run testng/othervm -XX:-CompactStrings CodePointAt + */ + +public class CodePointAt extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_L1, new int[] { 'A' } }, + new Object[] { STRING_L2, new int[] { 'A', 'B' } }, + new Object[] { STRING_L4, new int[] { 'A', 'B', 'C', 'D' } }, + new Object[] { STRING_LLONG, + new int[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' } }, + new Object[] { STRING_U1, new int[] { '\uFF21' } }, + new Object[] { STRING_U2, new int[] { '\uFF21', '\uFF22' } }, + new Object[] { STRING_M12, new int[] { '\uFF21', 'A' } }, + new Object[] { STRING_M11, new int[] { 'A', '\uFF21' } }, + new Object[] { + STRING_SUPPLEMENTARY, + new int[] { Character.toCodePoint('\uD801', '\uDC00'), + '\uDC00', + Character.toCodePoint('\uD801', '\uDC01'), + '\uDC01', '\uFF21', 'A' }, } }; + } + + @Test(dataProvider = "provider") + public void testCodePointAt(String str, int[] expected) { + map.get(str) + .forEach( + (source, data) -> { + IntStream + .range(0, str.length()) + .forEach( + i -> assertEquals( + str.codePointAt(i), + expected[i], + String.format( + "testing String(%s).codePointAt(%d), source : %s, ", + escapeNonASCIIs(data), + i, source))); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/CodePointBefore.java 2015-10-30 14:04:06.723247554 -0700 @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.stream.IntStream; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.codePointBefore. + * @run testng/othervm -XX:+CompactStrings CodePointBefore + * @run testng/othervm -XX:-CompactStrings CodePointBefore + */ + +public class CodePointBefore extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_L1, new int[] { 'A' } }, + new Object[] { STRING_L2, new int[] { 'A', 'B' } }, + new Object[] { STRING_L4, new int[] { 'A', 'B', 'C', 'D' } }, + new Object[] { STRING_LLONG, + new int[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' } }, + new Object[] { STRING_U1, new int[] { '\uFF21' } }, + new Object[] { STRING_U2, new int[] { '\uFF21', '\uFF22' } }, + new Object[] { STRING_M12, new int[] { '\uFF21', 'A' } }, + new Object[] { STRING_M11, new int[] { 'A', '\uFF21' } }, + new Object[] { + STRING_SUPPLEMENTARY, + new int[] { '\uD801', Character.toCodePoint('\uD801', '\uDC00'), + '\uD801', Character.toCodePoint('\uD801', '\uDC01'), + '\uFF21', 'A' }, } }; + } + + @Test(dataProvider = "provider") + public void testCodePointBefore(String str, int[] expected) { + map.get(str) + .forEach( + (source, data) -> { + IntStream + .range(0, str.length()) + .forEach( + i -> assertEquals( + str.codePointBefore(i + 1), + expected[i], + String.format( + "testing String(%s).codePointBefore(%d), source : %s, ", + escapeNonASCIIs(data), + i + 1, source))); + }); + } + +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/CodePointCount.java 2015-10-30 14:04:07.341245216 -0700 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.codePointCount. + * @run testng/othervm -XX:+CompactStrings CodePointCount + * @run testng/othervm -XX:-CompactStrings CodePointCount + */ + +public class CodePointCount extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { new Object[] { STRING_EMPTY, 0, 0, 0 }, + new Object[] { STRING_L1, 0, 1, 1 }, + new Object[] { STRING_L1, 1, 1, 0 }, + new Object[] { STRING_L2, 0, 2, 2 }, + new Object[] { STRING_L2, 0, 1, 1 }, + new Object[] { STRING_L2, 1, 2, 1 }, + new Object[] { STRING_L4, 0, 4, 4 }, + new Object[] { STRING_L4, 0, 1, 1 }, + new Object[] { STRING_L4, 2, 4, 2 }, + new Object[] { STRING_LLONG, 0, 8, 8 }, + new Object[] { STRING_LLONG, 0, 5, 5 }, + new Object[] { STRING_LLONG, 4, 8, 4 }, + new Object[] { STRING_LLONG, 0, 7, 7 }, + new Object[] { STRING_U1, 0, 1, 1 }, + new Object[] { STRING_U2, 0, 2, 2 }, + new Object[] { STRING_U2, 0, 1, 1 }, + new Object[] { STRING_U2, 1, 2, 1 }, + new Object[] { STRING_M12, 0, 2, 2 }, + new Object[] { STRING_M12, 0, 1, 1 }, + new Object[] { STRING_M12, 1, 2, 1 }, + new Object[] { STRING_M11, 0, 2, 2 }, + new Object[] { STRING_M11, 0, 1, 1 }, + new Object[] { STRING_M11, 1, 2, 1 }, + new Object[] { STRING_SUPPLEMENTARY, 0, 1, 1 }, + new Object[] { STRING_SUPPLEMENTARY, 0, 2, 1 }, + new Object[] { STRING_SUPPLEMENTARY, 0, 3, 2 }, + new Object[] { STRING_SUPPLEMENTARY, 0, 5, 3 }, + new Object[] { STRING_SUPPLEMENTARY, 0, 6, 4 }, + new Object[] { STRING_SUPPLEMENTARY, 1, 4, 2 }, + new Object[] { STRING_SUPPLEMENTARY, 1, 6, 4 }, + new Object[] { STRING_SUPPLEMENTARY, 2, 4, 1 },}; + } + + @Test(dataProvider = "provider") + public void testCodePointCount(String str, int beginIndex, int endIndex, + int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.codePointCount(beginIndex, endIndex), + expected, + String.format( + "testing String(%s).codePointCount(%d, %d), source : %s, ", + escapeNonASCIIs(data), beginIndex, + endIndex, source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/CompactString.java 2015-10-30 14:04:07.917243034 -0700 @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.Map; + +import org.testng.annotations.BeforeClass; + +/* + * Base class of tests for Compact String. + * + */ +public class CompactString { + + final Map> map = new HashMap<>(); + + enum StringSources { + EMPTY(STRING_EMPTY, BYTE_ARRAY_EMTPY, CHAR_ARRAY_EMPTY, + POINT_ARRAY_EMTPY), LDUPLICATE(STRING_LDUPLICATE, + BYTE_ARRAY_LDUPLICATE, CHAR_ARRAY_LDUPLICATE, + POINT_ARRAY_LDUPLICATE), LLONG(STRING_LLONG, BYTE_ARRAY_LLONG, + CHAR_ARRAY_LLONG, POINT_ARRAY_LLONG), L1(STRING_L1, + BYTE_ARRAY_L1, CHAR_ARRAY_L1, POINT_ARRAY_L1), L2(STRING_L2, + BYTE_ARRAY_L2, CHAR_ARRAY_L2, POINT_ARRAY_L2), L4(STRING_L4, + BYTE_ARRAY_L4, CHAR_ARRAY_L4, POINT_ARRAY_L4), UDUPLICATE( + STRING_UDUPLICATE, BYTE_ARRAY_UDUPLICATE, + CHAR_ARRAY_UDUPLICATE, POINT_ARRAY_UDUPLICATE), U1(STRING_U1, + BYTE_ARRAY_U1, CHAR_ARRAY_U1, POINT_ARRAY_U1), U2(STRING_U2, + BYTE_ARRAY_U2, CHAR_ARRAY_U2, POINT_ARRAY_U2), MDUPLICATE1( + STRING_MDUPLICATE1, BYTE_ARRAY_MDUPLICATE1, + CHAR_ARRAY_MDUPLICATE1, POINT_ARRAY_MDUPLICATE1), MDUPLICATE2( + STRING_MDUPLICATE2, BYTE_ARRAY_MDUPLICATE2, + CHAR_ARRAY_MDUPLICATE2, POINT_ARRAY_MDUPLICATE2), MLONG1( + STRING_MLONG1, BYTE_ARRAY_MLONG1, CHAR_ARRAY_MLONG1, + POINT_ARRAY_MLONG1), MLONG2(STRING_MLONG2, BYTE_ARRAY_MLONG2, + CHAR_ARRAY_MLONG2, POINT_ARRAY_MLONG2), M11(STRING_M11, + BYTE_ARRAY_M11, CHAR_ARRAY_M11, POINT_ARRAY_M11), M12( + STRING_M12, BYTE_ARRAY_M12, CHAR_ARRAY_M12, POINT_ARRAY_M12), SUPPLEMENTARY( + STRING_SUPPLEMENTARY, BYTE_ARRAY_SUPPLEMENTARY, + CHAR_ARRAY_SUPPLEMENTARY, POINT_ARRAY_SUPPLEMENTARY), SUPPLEMENTARY_LOWERCASE( + STRING_SUPPLEMENTARY_LOWERCASE, + BYTE_ARRAY_SUPPLEMENTARY_LOWERCASE, + CHAR_ARRAY_SUPPLEMENTARY_LOWERCASE, + POINT_ARRAY_SUPPLEMENTARY_LOWERCASE); + + private StringSources(String s, byte[] b, char[] c, int[] i) { + str = s; + ba = b; + ca = c; + ia = i; + } + + String getString() { + return str; + } + + byte[] getByteArray() { + return ba; + } + + char[] getCharArray() { + return ca; + } + + int[] getIntArray() { + return ia; + } + + private final String str; + private final byte[] ba; + private final char[] ca; + private final int[] ia; + } + + protected static final String DEFAULT_CHARSET_NAME = "UTF-8"; + protected static final Charset DEFAULT_CHARSET = Charset + .forName(DEFAULT_CHARSET_NAME); + + protected static final String STRING_EMPTY = ""; + protected static final byte[] BYTE_ARRAY_EMTPY = new byte[0]; + protected static final char[] CHAR_ARRAY_EMPTY = new char[0]; + protected static final int[] POINT_ARRAY_EMTPY = new int[0]; + + protected static final String STRING_LDUPLICATE = "ABABABABAB"; + protected static final byte[] BYTE_ARRAY_LDUPLICATE = new byte[] { 'A', 'B', + 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B' }; + protected static final char[] CHAR_ARRAY_LDUPLICATE = new char[] { 'A', 'B', + 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B' }; + protected static final int[] POINT_ARRAY_LDUPLICATE = new int[] { 'A', 'B', + 'A', 'B', 'A', 'B', 'A', 'B', 'A', 'B' }; + + protected static final String STRING_LLONG = "ABCDEFGH"; + protected static final byte[] BYTE_ARRAY_LLONG = new byte[] { 'A', 'B', 'C', + 'D', 'E', 'F', 'G', 'H' }; + protected static final char[] CHAR_ARRAY_LLONG = new char[] { 'A', 'B', 'C', + 'D', 'E', 'F', 'G', 'H' }; + protected static final int[] POINT_ARRAY_LLONG = new int[] { 'A', 'B', 'C', + 'D', 'E', 'F', 'G', 'H' }; + + protected static final String STRING_L1 = "A"; + protected static final byte[] BYTE_ARRAY_L1 = new byte[] { 'A' }; + protected static final char[] CHAR_ARRAY_L1 = new char[] { 'A' }; + protected static final int[] POINT_ARRAY_L1 = new int[] { 'A' }; + + protected static final String STRING_L2 = "AB"; + protected static final byte[] BYTE_ARRAY_L2 = new byte[] { 'A', 'B' }; + protected static final char[] CHAR_ARRAY_L2 = new char[] { 'A', 'B' }; + protected static final int[] POINT_ARRAY_L2 = new int[] { 'A', 'B' }; + + protected static final String STRING_L4 = "ABCD"; + protected static final byte[] BYTE_ARRAY_L4 = new byte[] { 'A', 'B', 'C', 'D' }; + protected static final char[] CHAR_ARRAY_L4 = new char[] { 'A', 'B', 'C', 'D' }; + protected static final int[] POINT_ARRAY_L4 = new int[] { 'A', 'B', 'C', 'D' }; + + /* + * Because right now ASCII is the default encoding parameter for source code + * in JDK build environment, so we escape them. same as below. + */ + protected static final String STRING_UDUPLICATE = "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22"; + protected static final byte[] BYTE_ARRAY_UDUPLICATE = getBytes(STRING_UDUPLICATE); + protected static final char[] CHAR_ARRAY_UDUPLICATE = new char[] { '\uFF21', + '\uFF22', '\uFF21', '\uFF22', '\uFF21', '\uFF22', '\uFF21', + '\uFF22', '\uFF21', '\uFF22' }; + protected static final int[] POINT_ARRAY_UDUPLICATE = new int[] { '\uFF21', + '\uFF22', '\uFF21', '\uFF22', '\uFF21', '\uFF22', '\uFF21', + '\uFF22', '\uFF21', '\uFF22' }; + + protected static final String STRING_U1 = "\uFF21"; + protected static final byte[] BYTE_ARRAY_U1 = getBytes(STRING_U1); + protected static final char[] CHAR_ARRAY_U1 = new char[] { '\uFF21' }; + protected static final int[] POINT_ARRAY_U1 = new int[] { '\uFF21' }; + + protected static final String STRING_U2 = "\uFF21\uFF22"; + protected static final byte[] BYTE_ARRAY_U2 = getBytes(STRING_U2); + protected static final char[] CHAR_ARRAY_U2 = new char[] { '\uFF21', '\uFF22' }; + protected static final int[] POINT_ARRAY_U2 = new int[] { '\uFF21', '\uFF22' }; + + protected static final String STRING_MDUPLICATE1 = "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A"; + protected static final byte[] BYTE_ARRAY_MDUPLICATE1 = getBytes(STRING_MDUPLICATE1); + protected static final char[] CHAR_ARRAY_MDUPLICATE1 = new char[] { '\uFF21', + 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A' }; + protected static final int[] POINT_ARRAY_MDUPLICATE1 = new int[] { '\uFF21', + 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A' }; + + protected static final String STRING_MDUPLICATE2 = "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21"; + protected static final byte[] BYTE_ARRAY_MDUPLICATE2 = getBytes(STRING_MDUPLICATE2); + protected static final char[] CHAR_ARRAY_MDUPLICATE2 = new char[] { 'A', + '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', + '\uFF21' }; + protected static final int[] POINT_ARRAY_MDUPLICATE2 = new int[] { 'A', + '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', '\uFF21', 'A', + '\uFF21' }; + + protected static final String STRING_MLONG1 = "A\uFF21B\uFF22C\uFF23D\uFF24E\uFF25F\uFF26G\uFF27H\uFF28"; + protected static final byte[] BYTE_ARRAY_MLONG1 = getBytes(STRING_MLONG1); + protected static final char[] CHAR_ARRAY_MLONG1 = new char[] { 'A', '\uFF21', + 'B', '\uFF22', 'C', '\uFF23', 'D', '\uFF24', 'E', '\uFF25', 'F', + '\uFF26', 'G', '\uFF27', 'H', '\uFF28' }; + protected static final int[] POINT_ARRAY_MLONG1 = new int[] { 'A', '\uFF21', + 'B', '\uFF22', 'C', '\uFF23', 'D', '\uFF24', 'E', '\uFF25', 'F', + '\uFF26', 'G', '\uFF27', 'H', '\uFF28' }; + + protected static final String STRING_MLONG2 = "\uFF21A\uFF22B\uFF23C\uFF24D\uFF25E\uFF26F\uFF27G\uFF28H"; + protected static final byte[] BYTE_ARRAY_MLONG2 = getBytes(STRING_MLONG2); + protected static final char[] CHAR_ARRAY_MLONG2 = new char[] { '\uFF21', 'A', + '\uFF22', 'B', '\uFF23', 'C', '\uFF24', 'D', '\uFF25', 'E', + '\uFF26', 'F', '\uFF27', 'G', '\uFF28', 'H' }; + protected static final int[] POINT_ARRAY_MLONG2 = new int[] { '\uFF21', 'A', + '\uFF22', 'B', '\uFF23', 'C', '\uFF24', 'D', '\uFF25', 'E', + '\uFF26', 'F', '\uFF27', 'G', '\uFF28', 'H' }; + + protected static final String STRING_M11 = "A\uFF21"; + protected static final byte[] BYTE_ARRAY_M11 = getBytes(STRING_M11); + protected static final char[] CHAR_ARRAY_M11 = new char[] { 'A', '\uFF21' }; + protected static final int[] POINT_ARRAY_M11 = new int[] { 'A', '\uFF21' }; + + protected static final String STRING_M12 = "\uFF21A"; + protected static final byte[] BYTE_ARRAY_M12 = getBytes(STRING_M12); + protected static final char[] CHAR_ARRAY_M12 = new char[] { '\uFF21', 'A' }; + protected static final int[] POINT_ARRAY_M12 = new int[] { '\uFF21', 'A' }; + + protected static final String STRING_SUPPLEMENTARY = "\uD801\uDC00\uD801\uDC01\uFF21A"; + protected static final byte[] BYTE_ARRAY_SUPPLEMENTARY = getBytes(STRING_SUPPLEMENTARY); + protected static final char[] CHAR_ARRAY_SUPPLEMENTARY = new char[] { + '\uD801', '\uDC00', '\uD801', '\uDC01', '\uFF21', 'A' }; + protected static final int[] POINT_ARRAY_SUPPLEMENTARY = new int[] { + '\uD801', '\uDC00', '\uD801', '\uDC01', '\uFF21', 'A' }; + + protected static final String STRING_SUPPLEMENTARY_LOWERCASE = "\uD801\uDC28\uD801\uDC29\uFF41a"; + protected static final byte[] BYTE_ARRAY_SUPPLEMENTARY_LOWERCASE = getBytes(STRING_SUPPLEMENTARY_LOWERCASE); + protected static final char[] CHAR_ARRAY_SUPPLEMENTARY_LOWERCASE = new char[] { + '\uD801', '\uDC28', '\uD801', '\uDC29', '\uFF41', 'a' }; + protected static final int[] POINT_ARRAY_SUPPLEMENTARY_LOWERCASE = new int[] { + '\uD801', '\uDC28', '\uD801', '\uDC29', '\uFF41', 'a' }; + + protected static final String SRC_BYTE_ARRAY_WITH_CHARSETNAME = "source from byte array with charset name"; + protected static final String SRC_BYTE_ARRAY_WITH_CHARSET = "source from byte array with charset"; + protected static final String SRC_CHAR_ARRAY = "source from char array"; + protected static final String SRC_POINT_ARRAY = "source from code point array"; + protected static final String SRC_STRING = "source from String"; + protected static final String SRC_STRINGBUFFER = "source from StringBuffer"; + protected static final String SRC_STRINGBUILDER = "source from StringBuilder"; + protected static final String SRC_COPYVALUEOF = "source from copyValueOf from char array"; + protected static final String SRC_VALUEOF = "source from valueOf from char array"; + + static { + System.out + .println(String + .format("====== The platform's default charset is \"%s\", we're using \"%s\" for testing.", + Charset.defaultCharset().name(), + DEFAULT_CHARSET_NAME)); + } + + private static byte[] getBytes(String str) { + byte[] res = null; + try { + res = str.getBytes(DEFAULT_CHARSET_NAME); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + throw new RuntimeException("caught UnsupportedEncodingException!!!", e); + } + return res; + } + + private void setUpOneString(String content, byte[] ba, char[] ca, int[] cpa) + throws UnsupportedEncodingException { + final Map m = new HashMap<>(); + m.put(SRC_BYTE_ARRAY_WITH_CHARSETNAME, new String(ba, + DEFAULT_CHARSET_NAME)); + m.put(SRC_BYTE_ARRAY_WITH_CHARSET, new String(ba, DEFAULT_CHARSET)); + m.put(SRC_CHAR_ARRAY, new String(ca)); + m.put(SRC_POINT_ARRAY, new String(cpa, 0, cpa.length)); + m.put(SRC_STRING, new String(content)); + m.put(SRC_STRINGBUFFER, new String(new StringBuffer(content))); + m.put(SRC_STRINGBUILDER, new String(new StringBuilder(content))); + m.put(SRC_COPYVALUEOF, String.copyValueOf(ca)); + m.put(SRC_VALUEOF, String.valueOf(ca)); + map.put(content, m); + } + + /* + * Set up the test data, use 9 ways to construct one String. + * + * @throws UnsupportedEncodingException + * If the named charset is not supported in setUpOneString(xxx). + */ + @BeforeClass + public void setUp() throws UnsupportedEncodingException { + for (StringSources src : StringSources.values()) { + setUpOneString(src.getString(), src.getByteArray(), + src.getCharArray(), src.getIntArray()); + } + } + + /* + * Because right now system default charset in JPRT environment is only + * guaranteed to support ASCII characters in log, so we escape them. + */ + protected String escapeNonASCIIs(String str) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c > 0x7F) { + sb.append("\\u").append(Integer.toHexString((int) c)); + } else { + sb.append(c); + } + } + return sb.toString(); + } + + /* + * Because right now system default charset in JPRT environment is only + * guaranteed to support ASCII characters in log, so we escape them. + */ + protected String escapeNonASCII(char c) { + StringBuilder sb = new StringBuilder(); + if (c > 0x7F) { + sb.append("\\u").append(Integer.toHexString((int) c)); + } else { + sb.append(c); + } + return sb.toString(); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/CompareTo.java 2015-10-30 14:04:08.500240827 -0700 @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.compareTo. + * @run testng/othervm -XX:+CompactStrings CompareTo + * @run testng/othervm -XX:-CompactStrings CompareTo + */ + +public class CompareTo extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "A", -1 }, + new Object[] { STRING_EMPTY, "\uFF21", -1 }, + new Object[] { STRING_L1, "AB", -1 }, + new Object[] { STRING_L1, "A", 0 }, + new Object[] { STRING_L1, "a", -32 }, + new Object[] { STRING_L1, "\uFF21", -65248 }, + new Object[] { STRING_L2, "AB", 0 }, + new Object[] { STRING_L2, "Ab", -32 }, + new Object[] { STRING_L2, "AA", 1 }, + new Object[] { STRING_L2, "\uFF21", -65248 }, + new Object[] { STRING_L2, "A\uFF21", -65247 }, + new Object[] { STRING_L4, "ABC", 1 }, + new Object[] { STRING_L4, "AB", 2 }, + new Object[] { STRING_L4, "ABcD", -32 }, + new Object[] { STRING_L4, "ABCD\uFF21\uFF21", -2 }, + new Object[] { STRING_L4, "ABCD\uFF21", -1 }, + new Object[] { STRING_LLONG, "ABCDEFG\uFF21", -65241 }, + new Object[] { STRING_LLONG, "AB", 6 }, + new Object[] { STRING_LLONG, "ABCD", 4 }, + new Object[] { STRING_LLONG, "ABCDEFGH\uFF21\uFF21", -2 }, + new Object[] { STRING_U1, "\uFF21", 0 }, + new Object[] { STRING_U1, "\uFF22", -1 }, + new Object[] { STRING_U1, "\uFF21\uFF22", -1 }, + new Object[] { STRING_U1, "A", 65248 }, + new Object[] { STRING_U2, "\uFF21\uFF22", 0 }, + new Object[] { STRING_U2, "\uFF22", -1 }, + new Object[] { STRING_U2, "\uFF21\uFF21", 1 }, + new Object[] { STRING_U2, "A", 65248 }, + new Object[] { STRING_M12, "\uFF21A", 0 }, + new Object[] { STRING_M12, "A\uFF21", 65248 }, + new Object[] { STRING_M12, "\uFF21\uFF21", -65248 }, + new Object[] { STRING_M11, "A\uFF21", 0 }, + new Object[] { STRING_M11, "\uFF21A", -65248 }, + new Object[] { STRING_M11, "AA", 65248 }, }; + } + + @Test(dataProvider = "provider") + public void testCompareTo(String str, String anotherString, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.compareTo(anotherString), + expected, + String.format( + "testing String(%s).compareTo(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/CompareToIgnoreCase.java 2015-10-30 14:04:09.106238533 -0700 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.compareToIgnoreCase. + * @run testng/othervm -XX:+CompactStrings CompareToIgnoreCase + * @run testng/othervm -XX:-CompactStrings CompareToIgnoreCase + */ + +public class CompareToIgnoreCase extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "A", -1 }, + new Object[] { STRING_L1, "a", 0 }, + new Object[] { STRING_L1, "A", 0 }, + new Object[] { STRING_L1, "\uFF21", -65248 }, + new Object[] { STRING_L1, "B", -1 }, + new Object[] { STRING_L2, "AB", 0 }, + new Object[] { STRING_L2, "aB", 0 }, + new Object[] { STRING_L2, "\uFF21", -65248 }, + new Object[] { STRING_L2, "A\uFF21", -65247 }, + new Object[] { STRING_L4, "ABCD", 0 }, + new Object[] { STRING_L4, "abcd", 0 }, + new Object[] { STRING_L4, "ABc\uFF21", -65245 }, + new Object[] { STRING_LLONG, "ABCDEFGH", 0 }, + new Object[] { STRING_LLONG, "abcdefgh", 0 }, + new Object[] { STRING_LLONG, "ABCDEFG\uFF21", -65241 }, + new Object[] { STRING_LLONG, "abcdefg\uFF21", -65241 }, + new Object[] { STRING_U1, "\uFF41", 0 }, + new Object[] { STRING_U1, + "\uFF41\uFF42\uFF43\uFF44\uFF45\uFF46\uFF47\uFF48", -7 }, + new Object[] { STRING_U1, "A", 65248 }, + new Object[] { STRING_U2, "\uFF41", 1 }, + new Object[] { STRING_U2, "\uFF41\uFF42", 0 }, + new Object[] { STRING_U2, + "\uFF41\uFF42\uFF43\uFF44\uFF45\uFF46\uFF47\uFF48", -6 }, + new Object[] { STRING_M12, "\uFF41a", 0 }, + new Object[] { STRING_M12, "\uFF41\uFF42", -65249 }, + new Object[] { STRING_M11, "a\uFF41", 0 }, + new Object[] { STRING_M11, "a\uFF42", -1 }, }; + } + + @Test(dataProvider = "provider") + public void testCompareToIgnoreCase(String str, String anotherString, + int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.compareToIgnoreCase(anotherString), + expected, + String.format( + "testing String(%s).compareToIgnoreCase(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/Concat.java 2015-10-30 14:04:09.683236348 -0700 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.concat. + * @run testng/othervm -XX:+CompactStrings Concat + * @run testng/othervm -XX:-CompactStrings Concat + */ + +public class Concat extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] { STRING_EMPTY, "ABC", "ABC" }, + new Object[] { STRING_EMPTY, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "ABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_EMPTY, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_L1, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "AABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_L1, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "A\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_L2, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "ABABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_L2, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "AB\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_L4, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "ABCDABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_L4, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "ABCD\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_LLONG, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "ABCDEFGHABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_LLONG, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "ABCDEFGH\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_U1, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "\uFF21ABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_U1, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "\uFF21\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_U2, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "\uFF21\uFF22ABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_U2, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "\uFF21\uFF22\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_M12, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "\uFF21AABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_M12, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "\uFF21A\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, + new Object[] { STRING_M11, + "ABC".concat("\uFF21\uFF22\uFF23").concat("DEF"), + "A\uFF21ABC\uFF21\uFF22\uFF23DEF" }, + new Object[] { + STRING_M11, + "\uFF21\uFF22\uFF23".concat("ABC").concat( + "\uFF24\uFF25\uFF26"), + "A\uFF21\uFF21\uFF22\uFF23ABC\uFF24\uFF25\uFF26" }, }; + } + + @Test(dataProvider = "provider") + public void testConcat(String str, String anotherString, String expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.concat(anotherString), + expected, + String.format( + "testing String(%s).concat(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/Contains.java 2015-10-30 14:04:10.284234072 -0700 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.contains. + * @run testng/othervm -XX:+CompactStrings Contains + * @run testng/othervm -XX:-CompactStrings Contains + */ + +public class Contains extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "", true }, + new Object[] { STRING_EMPTY, "A", false }, + new Object[] { STRING_EMPTY, "\uFF21", false }, + new Object[] { STRING_L1, "", true }, + new Object[] { STRING_L1, "A", true }, + new Object[] { STRING_L1, "\uFF21", false }, + new Object[] { STRING_L2, "", true }, + new Object[] { STRING_L2, "A", true }, + new Object[] { STRING_L2, "AB", true }, + new Object[] { STRING_L2, "B", true }, + new Object[] { STRING_L2, "ABC", false }, + new Object[] { STRING_L2, "ab", false }, + new Object[] { STRING_L4, "ABCD", true }, + new Object[] { STRING_L4, "BC", true }, + new Object[] { STRING_LLONG, "ABCDEFGH", true }, + new Object[] { STRING_LLONG, "BCDEFGH", true }, + new Object[] { STRING_LLONG, "EF", true }, + new Object[] { STRING_U1, "", true }, + new Object[] { STRING_U1, "\uFF21", true }, + new Object[] { STRING_U1, "a", false }, + new Object[] { STRING_U1, "\uFF21B", false }, + new Object[] { STRING_U2, "", true }, + new Object[] { STRING_U2, "\uFF21\uFF22", true }, + new Object[] { STRING_U2, "a", false }, + new Object[] { STRING_U2, "\uFF21B", false }, + new Object[] { STRING_M12, "\uFF21A", true }, + new Object[] { STRING_M12, "\uFF21", true }, + new Object[] { STRING_M12, "A", true }, + new Object[] { STRING_M12, "A\uFF21", false }, + new Object[] { STRING_M11, "A\uFF21", true }, + new Object[] { STRING_M11, "Ab", false }, }; + } + + @Test(dataProvider = "provider") + public void testContains(String str, String anotherString, boolean expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.contains(anotherString), + expected, + String.format( + "testing String(%s).contains(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/EndsWith.java 2015-10-30 14:04:10.883231805 -0700 @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.endsWith. + * @run testng/othervm -XX:+CompactStrings EndsWith + * @run testng/othervm -XX:-CompactStrings EndsWith + */ + +public class EndsWith extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { new Object[] { STRING_EMPTY, "", true }, + new Object[] { STRING_EMPTY, "A", false }, + new Object[] { STRING_L1, "A", true }, + new Object[] { STRING_L1, "", true }, + new Object[] { STRING_L1, " ", false }, + new Object[] { STRING_L2, "AB", true }, + new Object[] { STRING_L2, "B", true }, + new Object[] { STRING_L2, "", true }, + new Object[] { STRING_L2, "A", false }, + new Object[] { STRING_L4, "ABCD", true }, + new Object[] { STRING_L4, "CD", true }, + new Object[] { STRING_L4, "D", true }, + new Object[] { STRING_L4, "", true }, + new Object[] { STRING_L4, "BC", false }, + new Object[] { STRING_LLONG, "ABCDEFGH", true }, + new Object[] { STRING_LLONG, "EFGH", true }, + new Object[] { STRING_LLONG, "", true }, + new Object[] { STRING_LLONG, "CDEF", false }, + new Object[] { STRING_LLONG, "\uFF28", false }, + new Object[] { STRING_U1, "\uFF21", true }, + new Object[] { STRING_U1, "", true }, + new Object[] { STRING_U1, "\uFF22", false }, + new Object[] { STRING_U1, "B", false }, + new Object[] { STRING_U2, "\uFF21\uFF22", true }, + new Object[] { STRING_U2, "\uFF22", true }, + new Object[] { STRING_U2, "", true }, + new Object[] { STRING_U2, "\uFF21", false }, + new Object[] { STRING_M12, "\uFF21A", true }, + new Object[] { STRING_M12, "A", true }, + new Object[] { STRING_M12, "", true }, + new Object[] { STRING_M12, "AA", false }, + new Object[] { STRING_M11, "A\uFF21", true }, + new Object[] { STRING_M11, "\uFF21", true }, + new Object[] { STRING_M11, "", true }, + new Object[] { STRING_M11, "\uFF21\uFF21", false }, }; + } + + @Test(dataProvider = "provider") + public void testEndsWith(String str, String suffix, boolean expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.endsWith(suffix), + expected, + String.format( + "testing String(%s).endsWith(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(suffix), source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/Equals.java 2015-10-30 14:04:11.495229486 -0700 @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.equals. + * @run testng/othervm -XX:+CompactStrings Equals + * @run testng/othervm -XX:-CompactStrings Equals + */ + +public class Equals extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] { STRING_EMPTY, "", true }, + new Object[] { STRING_EMPTY, "A", false }, + new Object[] { STRING_EMPTY, new StringBuffer(""), false }, + new Object[] { STRING_L1, "A", true }, + new Object[] { STRING_L1, "", false }, + new Object[] { STRING_L1, new StringBuffer("A"), false }, + new Object[] { STRING_L2, "AB", true }, + new Object[] { STRING_L2, "", false }, + new Object[] { STRING_L2, new StringBuilder("AB"), false }, + new Object[] { STRING_L4, "ABCD", true }, + new Object[] { STRING_L4, "abc", false }, + new Object[] { STRING_L4, "", false }, + new Object[] { STRING_LLONG, "ABCDEFGH", true }, + new Object[] { STRING_LLONG, "ABCDEFG", false }, + new Object[] { STRING_LLONG, new StringBuilder("ABCDEFGH"), + false }, + new Object[] { STRING_U1, "\uFF21", true }, + new Object[] { STRING_U1, "", false }, + new Object[] { STRING_U2, "\uFF21\uFF22", true }, + new Object[] { STRING_U2, "\uFF21", false }, + new Object[] { STRING_U2, "", false }, + new Object[] { STRING_U2, new StringBuilder("\uFF21\uFF22"), + false }, + new Object[] { STRING_M12, "\uFF21A", true }, + new Object[] { STRING_M12, "A\uFF21", false }, + new Object[] { STRING_M11, "A\uFF21", true }, + new Object[] { STRING_M11, new StringBuilder("\uFF21A"), false }, }; + } + + @Test(dataProvider = "provider") + public void testEquals(String str, Object obj, boolean expected) { + map.get(str).forEach( + (source, data) -> { + assertEquals(data.equals(obj), expected, String.format( + "testing String(%s).equals(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(obj.toString()), source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/EqualsIgnoreCase.java 2015-10-30 14:04:12.148227014 -0700 @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.equalsIgnoreCase. + * @run testng/othervm -XX:+CompactStrings EqualsIgnoreCase + * @run testng/othervm -XX:-CompactStrings EqualsIgnoreCase + */ + +public class EqualsIgnoreCase extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "", true }, + new Object[] { STRING_L1, "a", true }, + new Object[] { STRING_L2, "aB", true }, + new Object[] { STRING_L4, "AbCd", true }, + new Object[] { STRING_LLONG, "aBcDeFgH", true }, + new Object[] { STRING_U1, "\uFF41", true }, + new Object[] { STRING_U1, "\uFF21", true }, + new Object[] { STRING_U2, "\uFF41\uFF42", true }, + new Object[] { STRING_U2, "\uFF41\uFF22", true }, + new Object[] { STRING_U2, "\uFF21\uFF42", true }, + new Object[] { STRING_M12, "\uFF41a", true }, + new Object[] { STRING_M12, "\uFF21A", true }, + new Object[] { STRING_M11, "a\uFF41", true }, + new Object[] { STRING_M11, "A\uFF21", true }, + + }; + } + + @Test(dataProvider = "provider") + public void testEqualsIgnoreCase(String str, String anotherString, + boolean expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.equalsIgnoreCase(anotherString), + expected, + String.format( + "testing String(%s).equalsIgnoreCase(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/GetChars.java 2015-10-30 14:04:12.778224629 -0700 @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Arrays; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.getChars. + * @run testng/othervm -XX:+CompactStrings GetChars + * @run testng/othervm -XX:-CompactStrings GetChars + */ + +public class GetChars extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, 0, STRING_EMPTY.length(), + new char[STRING_EMPTY.length()], 0, CHAR_ARRAY_EMPTY }, + new Object[] { STRING_L1, 0, STRING_L1.length(), + new char[STRING_L1.length()], 0, CHAR_ARRAY_L1 }, + new Object[] { STRING_L2, 0, STRING_L2.length(), + new char[STRING_L2.length()], 0, CHAR_ARRAY_L2 }, + new Object[] { STRING_L4, 0, STRING_L4.length(), + new char[STRING_L4.length()], 0, CHAR_ARRAY_L4 }, + new Object[] { STRING_LLONG, 0, STRING_LLONG.length(), + new char[STRING_LLONG.length()], 0, CHAR_ARRAY_LLONG }, + new Object[] { STRING_U1, 0, STRING_U1.length(), + new char[STRING_U1.length()], 0, CHAR_ARRAY_U1 }, + new Object[] { STRING_U2, 0, STRING_U2.length(), + new char[STRING_U2.length()], 0, CHAR_ARRAY_U2 }, + new Object[] { STRING_M12, 0, STRING_M12.length(), + new char[STRING_M12.length()], 0, CHAR_ARRAY_M12 }, + new Object[] { STRING_M11, 0, STRING_M11.length(), + new char[STRING_M11.length()], 0, CHAR_ARRAY_M11 }, + new Object[] { STRING_UDUPLICATE, 0, + STRING_UDUPLICATE.length(), + new char[STRING_UDUPLICATE.length()], 0, + CHAR_ARRAY_UDUPLICATE }, + new Object[] { STRING_MDUPLICATE1, 0, + STRING_MDUPLICATE1.length(), + new char[STRING_MDUPLICATE1.length()], 0, + CHAR_ARRAY_MDUPLICATE1 }, }; + } + + @Test(dataProvider = "provider") + public void testGetChars(String str, int srcBegin, int srcEnd, char[] dst, + int dstBegin, char[] expected) { + map.get(str) + .forEach( + (source, data) -> { + data.getChars(srcBegin, srcEnd, dst, dstBegin); + assertTrue( + Arrays.equals(dst, expected), + String.format( + "testing String(%s).getChars(%d, %d, %s, %d), source : %s, ", + escapeNonASCIIs(data), srcBegin, + srcEnd, escapeNonASCIIs(Arrays + .toString(dst)), dstBegin, + source)); + }); + } + +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/IndexOf.java 2015-10-30 14:04:13.423222186 -0700 @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.indexOf. + * @run testng/othervm -XX:+CompactStrings IndexOf + * @run testng/othervm -XX:-CompactStrings IndexOf + */ + +public class IndexOf extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, (int) 'A', -1 }, + new Object[] { STRING_L1, (int) 'A', 0 }, + new Object[] { STRING_L2, (int) 'A', 0 }, + new Object[] { STRING_L2, (int) 'B', 1 }, + new Object[] { STRING_L4, (int) 'A', 0 }, + new Object[] { STRING_L4, (int) 'D', 3 }, + new Object[] { STRING_L4, (int) 'E', -1 }, + new Object[] { STRING_LLONG, (int) 'A', 0 }, + new Object[] { STRING_LLONG, (int) 'H', 7 }, + new Object[] { STRING_U1, (int) '\uFF21', 0 }, + new Object[] { STRING_U1, (int) 'A', -1 }, + new Object[] { STRING_U2, (int) '\uFF21', 0 }, + new Object[] { STRING_U2, (int) '\uFF22', 1 }, + new Object[] { STRING_M12, (int) '\uFF21', 0 }, + new Object[] { STRING_M12, (int) 'A', 1 }, + new Object[] { STRING_M11, (int) 'A', 0 }, + new Object[] { STRING_M11, (int) '\uFF21', 1 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 0 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 1 }, + new Object[] { STRING_SUPPLEMENTARY, 'A', 5 }, + new Object[] { STRING_SUPPLEMENTARY, '\uFF21', 4 }, + new Object[] { STRING_SUPPLEMENTARY, + Character.toCodePoint('\uD801', '\uDC00'), 0 }, + new Object[] { STRING_SUPPLEMENTARY, + Character.toCodePoint('\uD801', '\uDC01'), 2 }, }; + } + + @Test(dataProvider = "provider") + public void testIndexOf(String str, int ch, int expected) { + map.get(str).forEach( + (source, data) -> { + assertEquals(data.indexOf(ch), expected, String.format( + "testing String(%s).indexOf(%d), source : %s, ", + escapeNonASCIIs(data), ch, source)); + }); + } + + @DataProvider + public Object[][] provider2() { + return new Object[][] { + + new Object[] { STRING_EMPTY, (int) 'A', 0, -1 }, + new Object[] { STRING_L1, (int) 'A', 0, 0 }, + new Object[] { STRING_L1, (int) 'A', 1, -1 }, + new Object[] { STRING_L1, (int) 'B', 0, -1 }, + new Object[] { STRING_L2, (int) 'A', 0, 0 }, + new Object[] { STRING_L2, (int) 'A', 1, -1 }, + new Object[] { STRING_L2, (int) 'B', 0, 1 }, + new Object[] { STRING_L2, (int) 'B', 1, 1 }, + new Object[] { STRING_L4, (int) 'A', 0, 0 }, + new Object[] { STRING_L4, (int) 'D', 2, 3 }, + new Object[] { STRING_L4, (int) 'B', 2, -1 }, + new Object[] { STRING_LLONG, (int) 'A', 0, 0 }, + new Object[] { STRING_LLONG, (int) 'H', 5, 7 }, + new Object[] { STRING_U1, (int) '\uFF21', 0, 0 }, + new Object[] { STRING_U1, (int) 'A', 0, -1 }, + new Object[] { STRING_U2, (int) '\uFF21', 0, 0 }, + new Object[] { STRING_U2, (int) '\uFF22', 0, 1 }, + new Object[] { STRING_M12, (int) '\uFF21', 0, 0 }, + new Object[] { STRING_M12, (int) 'A', 1, 1 }, + new Object[] { STRING_M11, (int) 'A', 0, 0 }, + new Object[] { STRING_M11, (int) '\uFF21', 1, 1 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 1, 2 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 1, 1 }, }; + } + + @Test(dataProvider = "provider2") + public void testIndexOf(String str, int ch, int fromIndex, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.indexOf(ch, fromIndex), + expected, + String.format( + "testing String(%s).indexOf(%d, %d), source : %s, ", + escapeNonASCIIs(data), ch, + fromIndex, source)); + }); + } + + @DataProvider + public Object[][] provider3() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "A", -1 }, + new Object[] { STRING_L1, "A", 0 }, + new Object[] { STRING_L1, "AB", -1 }, + new Object[] { STRING_L2, "A", 0 }, + new Object[] { STRING_L2, "B", 1 }, + new Object[] { STRING_L2, "AB", 0 }, + new Object[] { STRING_L2, "AC", -1 }, + new Object[] { STRING_L2, "ABC", -1 }, + new Object[] { STRING_L4, "ABCD", 0 }, + new Object[] { STRING_L4, "D", 3 }, + new Object[] { STRING_LLONG, "ABCDEFGH", 0 }, + new Object[] { STRING_LLONG, "EFGH", 4 }, + new Object[] { STRING_LLONG, "EFGHI", -1 }, + new Object[] { STRING_U1, "\uFF21", 0 }, + new Object[] { STRING_U1, "\uFF21A", -1 }, + new Object[] { STRING_U2, "\uFF21\uFF22", 0 }, + new Object[] { STRING_U2, "\uFF22", 1 }, + new Object[] { STRING_U2, "A\uFF22", -1 }, + new Object[] { STRING_M12, "\uFF21A", 0 }, + new Object[] { STRING_M12, "A", 1 }, + new Object[] { STRING_M12, "\uFF21\uFF21", -1 }, + new Object[] { STRING_M11, "A\uFF21", 0 }, + new Object[] { STRING_M11, "\uFF21", 1 }, + new Object[] { STRING_M11, "A", 0 }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", + 0 }, + new Object[] { STRING_UDUPLICATE, + "\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21", 1 }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21", + -1 }, }; + } + + @Test(dataProvider = "provider3") + public void testIndexOf(String str, String anotherString, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.indexOf(anotherString), + expected, + String.format( + "testing String(%s).indexOf(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + source)); + }); + } + + @DataProvider + public Object[][] provider4() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "A", 0, -1 }, + new Object[] { STRING_L1, "A", 0, 0 }, + new Object[] { STRING_L1, "A", 1, -1 }, + new Object[] { STRING_L1, "AB", 0, -1 }, + new Object[] { STRING_L2, "A", 0, 0 }, + new Object[] { STRING_L2, "B", 0, 1 }, + new Object[] { STRING_L2, "AB", 0, 0 }, + new Object[] { STRING_L2, "AB", 1, -1 }, + new Object[] { STRING_L4, "ABCD", 0, 0 }, + new Object[] { STRING_L4, "BC", 0, 1 }, + new Object[] { STRING_L4, "A", 0, 0 }, + new Object[] { STRING_L4, "CD", 0, 2 }, + new Object[] { STRING_L4, "A", 2, -1 }, + new Object[] { STRING_L4, "ABCDE", 0, -1 }, + new Object[] { STRING_LLONG, "ABCDEFGH", 0, 0 }, + new Object[] { STRING_LLONG, "DEFGH", 0, 3 }, + new Object[] { STRING_LLONG, "A", 0, 0 }, + new Object[] { STRING_LLONG, "GHI", 0, -1 }, + new Object[] { STRING_U1, "\uFF21", 0, 0 }, + new Object[] { STRING_U1, "\uFF21A", 0, -1 }, + new Object[] { STRING_U2, "\uFF21\uFF22", 0, 0 }, + new Object[] { STRING_U2, "\uFF22", 0, 1 }, + new Object[] { STRING_U2, "\uFF21", 1, -1 }, + new Object[] { STRING_M12, "\uFF21A", 0, 0 }, + new Object[] { STRING_M12, "A", 1, 1 }, + new Object[] { STRING_M12, "\uFF21A", 1, -1 }, + new Object[] { STRING_M12, "\uFF21", 0, 0 }, + new Object[] { STRING_M11, "A\uFF21", 0, 0 }, + new Object[] { STRING_M11, "\uFF21", 1, 1 }, + new Object[] { STRING_M11, "A\uFF21", 1, -1 }, + new Object[] { STRING_M11, "A\uFF21A", 0, -1 }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", + 0, 0 }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", + 1, -1 }, + new Object[] { + STRING_UDUPLICATE, + "\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", + 1, 1 }, + new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22\uFF21\uFF22", + 4, 4 }, + new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22\uFF21\uFF22", + 7, -1 }, }; + } + + @Test(dataProvider = "provider4") + public void testIndexOf(String str, String anotherString, int fromIndex, + int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.indexOf(anotherString, fromIndex), + expected, + String.format( + "testing String(%s).indexOf(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + fromIndex, source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/Intern.java 2015-10-30 14:04:13.997220012 -0700 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.intern. + * @run testng/othervm -XX:+CompactStrings Intern + * @run testng/othervm -XX:-CompactStrings Intern + */ + +public class Intern extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "" }, + new Object[] { STRING_L1, "A" }, + new Object[] { STRING_LLONG, "ABCDEFGH" }, + new Object[] { STRING_U1, "\uFF21" }, + new Object[] { STRING_U2, "\uFF21\uFF22" }, + new Object[] { STRING_M12, "\uFF21A" }, + new Object[] { STRING_M11, "A\uFF21" }, + new Object[] { STRING_MDUPLICATE1, + "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A" }, }; + } + + @Test(dataProvider = "provider") + public void testIntern(String str, String expected) { + map.get(str).forEach( + (source, data) -> { + assertTrue(data.intern() == expected, String.format( + "testing String(%s).intern(), source : %s, ", + escapeNonASCIIs(data), source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/LastIndexOf.java 2015-10-30 14:04:14.603217717 -0700 @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.lastIndexOf. + * @run testng/othervm -XX:+CompactStrings LastIndexOf + * @run testng/othervm -XX:-CompactStrings LastIndexOf + */ + +public class LastIndexOf extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, (int) 'A', -1 }, + new Object[] { STRING_L1, (int) 'A', 0 }, + new Object[] { STRING_L2, (int) 'A', 0 }, + new Object[] { STRING_L2, (int) 'B', 1 }, + new Object[] { STRING_L4, (int) 'A', 0 }, + new Object[] { STRING_L4, (int) 'D', 3 }, + new Object[] { STRING_LLONG, (int) 'A', 0 }, + new Object[] { STRING_LLONG, (int) 'H', 7 }, + new Object[] { STRING_U1, (int) '\uFF21', 0 }, + new Object[] { STRING_U1, (int) 'B', -1 }, + new Object[] { STRING_U2, (int) '\uFF21', 0 }, + new Object[] { STRING_U2, (int) '\uFF22', 1 }, + new Object[] { STRING_M12, (int) '\uFF21', 0 }, + new Object[] { STRING_M12, (int) 'A', 1 }, + new Object[] { STRING_M11, (int) 'A', 0 }, + new Object[] { STRING_M11, (int) '\uFF21', 1 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 9 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 8 }, + new Object[] { STRING_SUPPLEMENTARY, + Character.toCodePoint('\uD801', '\uDC01'), 2 }, }; + } + + @Test(dataProvider = "provider") + public void testLastIndexOf(String str, int ch, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.lastIndexOf(ch), + expected, + String.format( + "testing String(%s).lastIndexOf(%d), source : %s, ", + escapeNonASCIIs(data), ch, source)); + }); + } + + @DataProvider + public Object[][] provider2() { + return new Object[][] { + + new Object[] { STRING_EMPTY, (int) 'A', 0, -1 }, + new Object[] { STRING_L1, (int) 'A', 0, 0 }, + new Object[] { STRING_L1, (int) 'A', 1, 0 }, + new Object[] { STRING_L2, (int) 'A', 0, 0 }, + new Object[] { STRING_L2, (int) 'B', 1, 1 }, + new Object[] { STRING_L2, (int) 'B', 2, 1 }, + new Object[] { STRING_L4, (int) 'A', 0, 0 }, + new Object[] { STRING_L4, (int) 'C', 2, 2 }, + new Object[] { STRING_L4, (int) 'C', 1, -1 }, + new Object[] { STRING_LLONG, (int) 'A', 0, 0 }, + new Object[] { STRING_LLONG, (int) 'H', 7, 7 }, + new Object[] { STRING_LLONG, (int) 'H', 6, -1 }, + new Object[] { STRING_U1, (int) '\uFF21', 0, 0 }, + new Object[] { STRING_U1, (int) '\uFF21', 7, 0 }, + new Object[] { STRING_U2, (int) '\uFF21', 0, 0 }, + new Object[] { STRING_U2, (int) '\uFF22', 0, -1 }, + new Object[] { STRING_M12, (int) '\uFF21', 0, 0 }, + new Object[] { STRING_M12, (int) 'A', 1, 1 }, + new Object[] { STRING_M12, (int) 'A', 0, -1 }, + new Object[] { STRING_M11, (int) 'A', 0, 0 }, + new Object[] { STRING_M11, (int) '\uFF21', 1, 1 }, + new Object[] { STRING_M11, (int) '\uFF21', 0, -1 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 5, 4 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF21', 6, 6 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 5, 5 }, + new Object[] { STRING_UDUPLICATE, (int) '\uFF22', 6, 5 }, }; + } + + @Test(dataProvider = "provider2") + public void testLastIndexOf(String str, int ch, int fromIndex, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.lastIndexOf(ch, fromIndex), + expected, + String.format( + "testing String(%s).lastIndexOf(%d, %d), source : %s, ", + escapeNonASCIIs(data), ch, + fromIndex, source)); + }); + } + + @DataProvider + public Object[][] provider3() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "A", -1 }, + new Object[] { STRING_L1, "A", 0 }, + new Object[] { STRING_L1, "AB", -1 }, + + new Object[] { STRING_L2, "AB", 0 }, + new Object[] { STRING_L2, "B", 1 }, + new Object[] { STRING_L4, "ABCD", 0 }, + new Object[] { STRING_L4, "B", 1 }, + new Object[] { STRING_LLONG, "ABCD", 0 }, + new Object[] { STRING_LLONG, "GH", 6 }, + new Object[] { STRING_U1, "\uFF21", 0 }, + new Object[] { STRING_U1, "\uFF22", -1 }, + new Object[] { STRING_U2, "\uFF21\uFF22", 0 }, + new Object[] { STRING_U2, "\uFF22", 1 }, + new Object[] { STRING_M12, "\uFF21A", 0 }, + new Object[] { STRING_M12, "A", 1 }, + new Object[] { STRING_M11, "A\uFF21", 0 }, + new Object[] { STRING_M11, "\uFF21", 1 }, + new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22\uFF21\uFF22", 6 }, + new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22", 8 }, }; + } + + @Test(dataProvider = "provider3") + public void testLastIndexOf(String str, String anotherString, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.lastIndexOf(anotherString), + expected, + String.format( + "testing String(%s).lastIndexOf(%s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + source)); + }); + } + + @DataProvider + public Object[][] provider4() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "A", 0, -1 }, + new Object[] { STRING_L2, "AB", 0, 0 }, + + new Object[] { STRING_L1, "AB", -1, -1 }, + + new Object[] { STRING_L2, "B", 1, 1 }, + new Object[] { STRING_L2, "B", 0, -1 }, + new Object[] { STRING_L4, "ABC", 3, 0 }, + new Object[] { STRING_L4, "ABC", 0, 0 }, + new Object[] { STRING_L4, "ABC", 1, 0 }, + new Object[] { STRING_L4, "BC", 1, 1 }, + new Object[] { STRING_L4, "BC", 0, -1 }, + new Object[] { STRING_LLONG, "ABCDEFGH", 0, 0 }, + new Object[] { STRING_LLONG, "EFGH", 7, 4 }, + new Object[] { STRING_LLONG, "EFGH", 3, -1 }, + new Object[] { STRING_U1, "\uFF21", 0, 0 }, + new Object[] { STRING_U1, "\uFF21", 7, 0 }, + new Object[] { STRING_U2, "\uFF21\uFF22", 0, 0 }, + new Object[] { STRING_U2, "\uFF21\uFF22", 1, 0 }, + new Object[] { STRING_M12, "\uFF21A", 0, 0 }, + new Object[] { STRING_M12, "A", 1, 1 }, + new Object[] { STRING_M12, "A", 0, -1 }, + new Object[] { STRING_M11, "A\uFF21", 0, 0 }, + new Object[] { STRING_M11, "A\uFF21", 1, 0 }, + new Object[] { STRING_M11, "\uFF21", 0, -1 }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", + 9, 0 }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", + 0, 0 }, + new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22", 6, 6 }, + new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22\uFF21", 6, 6 }, }; + } + + @Test(dataProvider = "provider4") + public void testLastIndexOf(String str, String anotherString, + int fromIndex, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.lastIndexOf(anotherString, fromIndex), + expected, + String.format( + "testing String(%s).lastIndexOf(%s, %d), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(anotherString), + fromIndex, source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/Length.java 2015-10-30 14:04:15.179215535 -0700 @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.length. + * @run testng/othervm -XX:+CompactStrings Length + * @run testng/othervm -XX:-CompactStrings Length + */ + +public class Length extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, 0 }, new Object[] { STRING_L1, 1 }, + new Object[] { STRING_L2, 2 }, + new Object[] { STRING_LLONG, 8 }, + new Object[] { STRING_U1, 1 }, new Object[] { STRING_U2, 2 }, + new Object[] { STRING_M12, 2 }, new Object[] { STRING_M11, 2 }, + new Object[] { STRING_UDUPLICATE, 10 }, + new Object[] { STRING_SUPPLEMENTARY, 6 }, }; + } + + @Test(dataProvider = "provider") + public void testLength(String str, int expected) { + map.get(str).forEach( + (source, data) -> { + assertEquals(data.length(), expected, String.format( + "testing String(%s).length(), source : %s, ", + escapeNonASCIIs(data), source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/Numbers.java 2015-10-30 14:04:15.771213294 -0700 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is testing + * Integer/Long's methods related to String. + * @run testng/othervm -XX:+CompactStrings Numbers + * @run testng/othervm -XX:-CompactStrings Numbers + */ + +public class Numbers { + + /* + * Data provider for testIntegerLong + * + * @return input parameter for testIntegerLong + */ + @DataProvider + public Object[][] numbers() { + return new Object[][] { + { Integer.toBinaryString(Integer.MAX_VALUE), + "1111111111111111111111111111111" }, + { Integer.toBinaryString(Integer.MIN_VALUE), + "10000000000000000000000000000000" }, + { Integer.toBinaryString(7), "111" }, + { Integer.toBinaryString(0), "0" }, + { Integer.toOctalString(Integer.MAX_VALUE), "17777777777" }, + { Integer.toOctalString(Integer.MIN_VALUE), "20000000000" }, + { Integer.toOctalString(9), "11" }, + { Integer.toOctalString(0), "0" }, + { Integer.toHexString(Integer.MAX_VALUE), "7fffffff" }, + { Integer.toHexString(Integer.MIN_VALUE), "80000000" }, + { Integer.toHexString(17), "11" }, + { Integer.toHexString(0), "0" }, + { Integer.toString(Integer.MAX_VALUE, 2), + "1111111111111111111111111111111" }, + { Integer.toString(Integer.MIN_VALUE, 2), + "-10000000000000000000000000000000" }, + { Integer.toString(7, 2), "111" }, + { Integer.toString(0, 2), "0" }, + { Integer.toString(Integer.MAX_VALUE, 8), "17777777777" }, + { Integer.toString(Integer.MIN_VALUE, 8), "-20000000000" }, + { Integer.toString(9, 8), "11" }, + { Integer.toString(Integer.MAX_VALUE, 16), "7fffffff" }, + { Integer.toString(Integer.MIN_VALUE, 16), "-80000000" }, + { Integer.toString(17, 16), "11" }, + { Long.toBinaryString(Long.MAX_VALUE), + "111111111111111111111111111111111111111111111111111111111111111" }, + { Long.toBinaryString(Long.MIN_VALUE), + "1000000000000000000000000000000000000000000000000000000000000000" }, + { Long.toOctalString(Long.MAX_VALUE), "777777777777777777777" }, + { Long.toOctalString(Long.MIN_VALUE), "1000000000000000000000" }, + { Long.toHexString(Long.MAX_VALUE), "7fffffffffffffff" }, + { Long.toHexString(Long.MIN_VALUE), "8000000000000000" }, + { Long.toString(Long.MAX_VALUE, 2), + "111111111111111111111111111111111111111111111111111111111111111" }, + { Long.toString(Long.MIN_VALUE, 2), + "-1000000000000000000000000000000000000000000000000000000000000000" }, + { Long.toString(Long.MAX_VALUE, 8), "777777777777777777777" }, + { Long.toString(Long.MIN_VALUE, 8), "-1000000000000000000000" }, + { Long.toString(Long.MAX_VALUE, 16), "7fffffffffffffff" }, + { Long.toString(Long.MIN_VALUE, 16), "-8000000000000000" } }; + } + + /* + * test Integer/Long's methods related to String. + * + * @param res + * real result + * @param expected + * expected result + */ + @Test(dataProvider = "numbers") + public void testIntegerLong(String res, String expected) { + assertEquals(res, expected); + } + +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/OffsetByCodePoints.java 2015-10-30 14:04:16.372211017 -0700 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.offsetByCodePoints. + * @run testng/othervm -XX:+CompactStrings OffsetByCodePoints + * @run testng/othervm -XX:-CompactStrings OffsetByCodePoints + */ + +public class OffsetByCodePoints extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_SUPPLEMENTARY, 0, 1, 2 }, + new Object[] { STRING_SUPPLEMENTARY, 0, 3, 5 }, + new Object[] { STRING_SUPPLEMENTARY, 1, 1, 2 }, + new Object[] { STRING_SUPPLEMENTARY, 1, 3, 5 }, + new Object[] { STRING_SUPPLEMENTARY, 2, 1, 4 }, + new Object[] { STRING_SUPPLEMENTARY, 2, 2, 5 }, + new Object[] { STRING_SUPPLEMENTARY, 2, 3, 6 }, + new Object[] { STRING_SUPPLEMENTARY, 3, 1, 4 }, + new Object[] { STRING_SUPPLEMENTARY, 3, 2, 5 }, + new Object[] { STRING_SUPPLEMENTARY, 3, 3, 6 }, }; + } + + @Test(dataProvider = "provider") + public void testOffsetByCodePoints(String str, int index, + int codePointOffset, int expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.offsetByCodePoints(index, + codePointOffset), + expected, + String.format( + "testing String(%s).offsetByCodePoints(%d, %d), source : %s, ", + escapeNonASCIIs(data), index, + codePointOffset, source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/RegionMatches.java 2015-10-30 14:04:16.938208874 -0700 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.regionMatches. + * @run testng/othervm -XX:+CompactStrings RegionMatches + * @run testng/othervm -XX:-CompactStrings RegionMatches + */ + +public class RegionMatches extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, true, 0, "", 0, 0, true }, + new Object[] { STRING_EMPTY, true, 0, "", 0, 1, false }, + new Object[] { STRING_EMPTY, true, 0, "A", 0, 0, true }, + new Object[] { STRING_EMPTY, true, 0, "", 0, 0, true }, + new Object[] { STRING_EMPTY, true, 0, "", 0, 1, false }, + new Object[] { STRING_L1, false, 0, "a", 0, 1, false }, + new Object[] { STRING_L1, false, 0, "BA", 1, 1, true }, + new Object[] { STRING_L1, false, 0, "Ba", 1, 1, false }, + new Object[] { STRING_L1, true, 0, "a", 0, 1, true }, + new Object[] { STRING_L1, true, 0, "BA", 1, 1, true }, + new Object[] { STRING_L1, true, 0, "Ba", 1, 1, true }, + new Object[] { STRING_L2, true, 1, "b", 0, 1, true }, + new Object[] { STRING_L2, true, 1, "B", 0, 1, true }, + new Object[] { STRING_L2, true, 0, "xaBc", 1, 2, true }, + new Object[] { STRING_L2, false, 0, "AB", 0, 2, true }, + new Object[] { STRING_L2, false, 0, "Ab", 0, 2, false }, + new Object[] { STRING_L2, false, 1, "BAB", 2, 1, true }, + new Object[] { STRING_LLONG, true, 1, "bCdEF", 0, 5, true }, + new Object[] { STRING_LLONG, false, 2, "CDEFG", 0, 5, true }, + new Object[] { STRING_LLONG, true, 2, "CDEFg", 0, 5, true }, + new Object[] { STRING_U1, true, 0, "\uFF41", 0, 1, true }, + new Object[] { STRING_U1, false, 0, "\uFF41", 0, 1, false }, + new Object[] { STRING_MDUPLICATE1, true, 0, "\uFF41a\uFF41", 0, + 3, true }, + new Object[] { STRING_MDUPLICATE1, false, 0, "\uFF21a\uFF21", + 0, 3, false }, + new Object[] { STRING_SUPPLEMENTARY, true, 1, "\uDC00\uD801", + 0, 2, true }, + new Object[] { STRING_SUPPLEMENTARY, true, 4, "\uFF21", 0, 1, + true }, + new Object[] { STRING_SUPPLEMENTARY, true, 5, "A", 0, 1, true }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, false, 0, + "\uD801\uDC28\uD801\uDC29", 0, 4, true }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, true, 1, + "\uDC28\uD801", 0, 2, true }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, true, 1, + "\uDC00\uD801", 0, 2, false }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, true, 4, + "\uFF21", 0, 1, true }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, false, 4, + "\uFF21", 0, 1, false }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, false, 4, + "\uFF41", 0, 1, true }, }; + } + + @Test(dataProvider = "provider") + public void testRegionMatches(String str, boolean ignoreCase, int toffset, + String other, int ooffset, int len, boolean expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.regionMatches(ignoreCase, toffset, + other, ooffset, len), + expected, + String.format( + "testing String(%s).regionMatches(%b, %d, %s, %d, %d), source : %s, ", + escapeNonASCIIs(data), ignoreCase, + toffset, escapeNonASCIIs(other), + ooffset, len, source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/Replace.java 2015-10-30 14:04:17.554206540 -0700 @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.replace. + * @run testng/othervm -XX:+CompactStrings Replace + * @run testng/othervm -XX:-CompactStrings Replace + */ + +public class Replace extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_L1, 'A', 'B', "B" }, + new Object[] { STRING_L1, 'A', 'A', "A" }, + new Object[] { STRING_L1, 'A', '\uFF21', "\uFF21" }, + new Object[] { STRING_L2, 'A', 'B', "BB" }, + new Object[] { STRING_L2, 'B', 'A', "AA" }, + new Object[] { STRING_L2, 'C', 'A', "AB" }, + new Object[] { STRING_L2, 'B', '\uFF21', "A\uFF21" }, + new Object[] { STRING_U1, '\uFF21', 'A', "A" }, + new Object[] { STRING_U1, '\uFF22', 'A', "\uFF21" }, + new Object[] { STRING_U2, '\uFF22', 'A', "\uFF21A" }, + new Object[] { STRING_M12, 'A', '\uFF21', "\uFF21\uFF21" }, + new Object[] { STRING_M11, '\uFF21', 'A', "AA" }, + new Object[] { STRING_UDUPLICATE, '\uFF21', 'A', + "A\uFF22A\uFF22A\uFF22A\uFF22A\uFF22" }, + new Object[] { STRING_MDUPLICATE1, '\uFF21', 'A', "AAAAAAAAAA" }, + new Object[] { STRING_MDUPLICATE1, 'A', '\uFF21', + "\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21" }, }; + } + + @Test(dataProvider = "provider") + public void testReplace(String str, char oldChar, char newChar, + String expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.replace(oldChar, newChar), + expected, + String.format( + "testing String(%s).replace(%s, %s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCII(oldChar), + escapeNonASCII(newChar), source)); + }); + } + + @DataProvider + public Object[][] provider2() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "", "ABC", "ABC" }, + new Object[] { STRING_EMPTY, "", "", "" }, + new Object[] { STRING_L1, "A", "B", "B" }, + new Object[] { STRING_L1, "A", "A", "A" }, + new Object[] { STRING_L2, "B", "\uFF21", "A\uFF21" }, + new Object[] { STRING_LLONG, "BCD", "\uFF21", "A\uFF21EFGH" }, + new Object[] { STRING_U1, "\uFF21", "A", "A" }, + new Object[] { STRING_U1, "\uFF21", "A\uFF21", "A\uFF21" }, + new Object[] { STRING_U2, "\uFF21", "A", "A\uFF22" }, + new Object[] { STRING_U2, "\uFF22", "A", "\uFF21A" }, + new Object[] { STRING_UDUPLICATE, "\uFF21\uFF22", "AB", + "ABABABABAB" }, + new Object[] { STRING_MDUPLICATE1, "\uFF21", "A", "AAAAAAAAAA" }, + new Object[] { STRING_MDUPLICATE1, "A", "\uFF21", + "\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21\uFF21" }, }; + } + + @Test(dataProvider = "provider2") + public void testReplace(String str, CharSequence target, + CharSequence replacement, String expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.replace(target, replacement), + expected, + String.format( + "testing String(%s).replace(%s, %s), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(target.toString()), + escapeNonASCIIs(replacement + .toString()), source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/SerializationTest.java 2015-10-30 14:04:18.135204339 -0700 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static jdk.testlibrary.SerializationUtils.*; +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @library /lib/testlibrary + * @build jdk.testlibrary.SerializationUtils + * @summary Tests Compact String. This one is testing String serialization + * among -XX:+CompactStrings/-XX:-CompactStrings/LegacyString + * @run testng/othervm -XX:+CompactStrings SerializationTest + * @run testng/othervm -XX:-CompactStrings SerializationTest + */ + +public class SerializationTest { + @DataProvider + public Object[][] provider() { + return new Object[][] { + // every byte array is serialized from corresponding String object + // by previous JDK(build 1.8.0_45-b14). + new Object[] { "", new byte[] { -84, -19, 0, 5, 116, 0, 0 } }, + new Object[] { "A", new byte[] { -84, -19, 0, 5, 116, 0, 1, 65 } }, + new Object[] { "AB", new byte[] { -84, -19, 0, 5, 116, 0, 2, 65, 66 } }, + new Object[] { "abcdefghijk", + new byte[] {-84, -19, 0, 5, 116, 0, 11, 97, 98, 99, 100, 101, + 102, 103, 104, 105, 106, 107 } }, + new Object[] { "\uff21", new byte[] { -84, -19, 0, 5, 116, 0, 3, -17, -68, -95 } }, + new Object[] { "\uff21\uff22", new byte[] { -84, -19, 0, 5, 116, 0, 6, -17, -68, + -95, -17, -68, -94 } }, + new Object[] { "\uff21A\uff21A\uff21A\uff21A\uff21A", + new byte[] { -84, -19, 0, 5, 116, 0, 20, -17, -68, -95, 65, -17, -68, + -95, 65, -17, -68, -95, 65, -17, -68, -95, 65, -17, -68, -95, 65 } }, + new Object[] { "A\uff21B\uff22C\uff23D\uff24E\uff25F\uff26G\uff27H\uff28", + new byte[] { -84, -19, 0, 5, 116, 0, 32, 65, -17, -68, -95, 66, -17, -68, + -94, 67, -17, -68, -93, 68, -17, -68, -92, 69, -17, -68, -91, 70, -17, + -68, -90, 71, -17, -68, -89, 72, -17, -68, -88 } }, + new Object[] { "\uff21A\uff22B\uff23C\uff24D\uff25E\uff26F\uff27G\uff28H", + new byte[] { -84, -19, 0, 5, 116, 0, 32, -17, -68, -95, 65, -17, -68, + -94, 66, -17, -68, -93, 67, -17, -68, -92, 68, -17, -68, -91, 69, -17, + -68, -90, 70, -17, -68, -89, 71, -17, -68, -88, 72 } }, + new Object[] { "\ud801\udc00\ud801\udc01\uff21A", + new byte[] { -84, -19, 0, 5, 116, 0, 16, -19, -96, -127, -19, -80, -128, + -19, -96, -127, -19, -80, -127, -17, -68, -95, 65 } }, + new Object[] { "\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22", + new byte[] { -84, -19, 0, 5, 116, 0, 30, -17, -68, -95, -17, -68, -94, -17, + -68, -95, -17, -68, -94, -17, -68, -95, -17, -68, -94, -17, -68, -95, -17, + -68, -94, -17, -68, -95, -17, -68, -94 } } }; + } + + /* + * Verify serialization works between Compact String/Legacy String + */ + @Test(dataProvider = "provider") + public void test(String strContent, byte[] baInJDK8) throws Exception { + // Serialize a String object into byte array. + byte[] ba = serialize(strContent); + assertEquals(ba, baInJDK8); + // Deserialize a String object from byte array which is generated by previous JDK(build 1.8.0_45-b14). + Object obj = deserialize(ba); + assertEquals(obj.getClass(), String.class); + assertEquals((String)obj, strContent); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/Split.java 2015-10-30 14:04:18.728202093 -0700 @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Arrays; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.split. + * @run testng/othervm -XX:+CompactStrings Split + * @run testng/othervm -XX:-CompactStrings Split + */ + +public class Split extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] { STRING_L1, "", 0, new String[] { "A" } }, + new Object[] { STRING_L1, "", 1, new String[] { "A" } }, + new Object[] { STRING_L1, "", 2, new String[] { "A", "" } }, + new Object[] { STRING_L1, "A", 0, new String[] {} }, + new Object[] { STRING_L2, "A", 0, new String[] { "", "B" } }, + new Object[] { STRING_L2, "B", 0, new String[] { "A" } }, + new Object[] { STRING_LLONG, "D", 0, + new String[] { "ABC", "EFGH" } }, + new Object[] { STRING_LLONG, "[D]", 0, + new String[] { "ABC", "EFGH" } }, + new Object[] { STRING_LLONG, "CD", 0, + new String[] { "AB", "EFGH" } }, + new Object[] { STRING_LLONG, "DC", 0, + new String[] { "ABCDEFGH" } }, + new Object[] { STRING_LLONG, "[CF]", 0, + new String[] { "AB", "DE", "GH" } }, + new Object[] { STRING_LLONG, "[CF]", 1, + new String[] { "ABCDEFGH" } }, + new Object[] { STRING_LLONG, "[CF]", 2, + new String[] { "AB", "DEFGH" } }, + new Object[] { STRING_LLONG, "[FC]", 0, + new String[] { "AB", "DE", "GH" } }, + new Object[] { STRING_LLONG, "[FC]", 1, + new String[] { "ABCDEFGH" } }, + new Object[] { STRING_LLONG, "[FC]", 2, + new String[] { "AB", "DEFGH" } }, + new Object[] { STRING_U1, "", 0, new String[] { "\uFF21" } }, + new Object[] { STRING_U1, "", 1, new String[] { "\uFF21" } }, + new Object[] { STRING_U1, "", 2, new String[] { "\uFF21", "" } }, + new Object[] { STRING_U1, "\uFF21", 0, new String[] {} }, + new Object[] { STRING_M12, "\uFF21", 0, + new String[] { "", "A" } }, + new Object[] { STRING_M12, "A", 0, new String[] { "\uFF21" } }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21", + 0, + new String[] { "", "\uFF22", "\uFF22", "\uFF22", + "\uFF22", "\uFF22" } }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21", + 2, + new String[] { "", + "\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22" } }, + new Object[] { + STRING_UDUPLICATE, + "\uFF21", + 4, + new String[] { "", "\uFF22", "\uFF22", + "\uFF22\uFF21\uFF22\uFF21\uFF22" } }, + new Object[] { + STRING_UDUPLICATE, + "\uFF22", + 0, + new String[] { "\uFF21", "\uFF21", "\uFF21", "\uFF21", + "\uFF21" } }, + new Object[] { + STRING_UDUPLICATE, + "\uFF22", + 3, + new String[] { "\uFF21", "\uFF21", + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22" } }, + + new Object[] { STRING_MDUPLICATE1, "\uFF21", 0, + new String[] { "", "A", "A", "A", "A", "A" } }, + new Object[] { STRING_MDUPLICATE1, "\uFF21", 3, + new String[] { "", "A", "A\uFF21A\uFF21A\uFF21A" } }, + new Object[] { + STRING_MDUPLICATE1, + "A", + 0, + new String[] { "\uFF21", "\uFF21", "\uFF21", "\uFF21", + "\uFF21" } }, + new Object[] { + STRING_MDUPLICATE1, + "A", + 4, + new String[] { "\uFF21", "\uFF21", "\uFF21", + "\uFF21A\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY, "\uD801\uDC01", 0, + new String[] { "\uD801\uDC00", "\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY, "\uDC01", 0, + new String[] { "\uD801\uDC00\uD801\uDC01\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY, "\uD801\uDC01", 0, + new String[] { "\uD801\uDC00", "\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY, "[\uD801\uDC01\uFF21]", 0, + new String[] { "\uD801\uDC00", "", "A" } }, + new Object[] { STRING_SUPPLEMENTARY, "[\uD801\uDC01\uFF21]", 1, + new String[] { "\uD801\uDC00\uD801\uDC01\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY, "[\uD801\uDC01\uFF21]", 2, + new String[] { "\uD801\uDC00", "\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY, "[\uFF21\uD801\uDC01]", 0, + new String[] { "\uD801\uDC00", "", "A" } }, + new Object[] { STRING_SUPPLEMENTARY, "[\uFF21\uD801\uDC01]", 1, + new String[] { "\uD801\uDC00\uD801\uDC01\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY, "[\uFF21\uD801\uDC01]", 2, + new String[] { "\uD801\uDC00", "\uFF21A" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, "\uDC01", 0, + new String[] { "\uD801\uDC28\uD801\uDC29\uFF41a" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, "\uD801\uDC29", + 0, new String[] { "\uD801\uDC28", "\uFF41a" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "[\uD801\uDC29\uFF41]", 0, + new String[] { "\uD801\uDC28", "", "a" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "[\uD801\uDC29\uFF41]", 1, + new String[] { "\uD801\uDC28\uD801\uDC29\uFF41a" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "[\uD801\uDC29\uFF41]", 2, + new String[] { "\uD801\uDC28", "\uFF41a" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "[\uFF41\uD801\uDC29]", 0, + new String[] { "\uD801\uDC28", "", "a" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "[\uFF41\uD801\uDC29]", 1, + new String[] { "\uD801\uDC28\uD801\uDC29\uFF41a" } }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "[\uFF41\uD801\uDC29]", 2, + new String[] { "\uD801\uDC28", "\uFF41a" } }, }; + } + + @Test(dataProvider = "provider") + public void testSplit(String str, String regex, int limit, String[] expected) { + map.get(str) + .forEach( + (source, data) -> { + assertTrue( + Arrays.equals(data.split(regex, limit), + expected), + String.format( + "testing String(%s).split(%s, %d), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(regex), limit, + source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/StartsWith.java 2015-10-30 14:04:19.325199832 -0700 @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.startsWith. + * @run testng/othervm -XX:+CompactStrings StartsWith + * @run testng/othervm -XX:-CompactStrings StartsWith + */ + +public class StartsWith extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] {STRING_EMPTY, "", 0, true}, + new Object[] {STRING_EMPTY, "A", 0, false}, + new Object[] {STRING_EMPTY, "", 0, true}, + new Object[] {STRING_EMPTY, "", -1, false}, + new Object[] {STRING_L1, "A", 0, true}, + new Object[] {STRING_L1, "A", -1, false}, + new Object[] {STRING_L1, "A", 1, false}, + new Object[] {STRING_L2, "B", 1, true}, + new Object[] {STRING_L2, "B", 0, false}, + new Object[] {STRING_L2, "A", 0, true}, + new Object[] {STRING_L2, "AB", 1, false}, + new Object[] {STRING_L4, "ABC", 0, true}, + new Object[] {STRING_LLONG, "ABCDEFGH", 0, true}, + new Object[] {STRING_LLONG, "ABCDE", 0, true}, + new Object[] {STRING_LLONG, "CDE", 0, false}, + new Object[] {STRING_LLONG, "FG", 5, true}, + new Object[] {STRING_U1, "\uFF21", 0, true}, + new Object[] {STRING_U1, "", 1, true}, + new Object[] {STRING_U1, "\uFF21", 0, true}, + new Object[] {STRING_U1, "A", 0, false}, + new Object[] {STRING_U2, "\uFF21\uFF22", 0, true}, + new Object[] {STRING_U2, "\uFF21", 0, true}, + new Object[] {STRING_U2, "\uFF22", 0, false}, + new Object[] {STRING_U2, "", 0, true}, + new Object[] {STRING_M12, "\uFF21", 0, true}, + new Object[] {STRING_M12, "\uFF21A", 0, true}, + new Object[] {STRING_M12, "A", 0, false}, + new Object[] {STRING_M12, "\uFF21A", 0, true}, + new Object[] {STRING_M12, "A", 1, true}, + new Object[] {STRING_M11, "A", 0, true}, + new Object[] {STRING_M11, "A\uFF21", 0, true}, + new Object[] {STRING_M11, "A\uFF21", 0, true}, + new Object[] {STRING_M11, "\uFF21", 1, true}, + new Object[] {STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", + 0, true}, + new Object[] {STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", 0, true}, + new Object[] {STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", 2, true}, + new Object[] {STRING_UDUPLICATE, + "\uFF21\uFF22\uFF21\uFF22\uFF21\uFF22", 5, false}, + new Object[] {STRING_MDUPLICATE1, + "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A", 0, true}, + new Object[] {STRING_MDUPLICATE1, + "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21", 0, true}, + new Object[] {STRING_MDUPLICATE1, "A\uFF21A\uFF21A\uFF21A", 1, true}, + new Object[] {STRING_SUPPLEMENTARY, "\uDC01\uFF21", 3, true}, + }; + } + + @Test(dataProvider = "provider") + public void testStartsWith(String str, String prefix, int toffset, + boolean expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.startsWith(prefix, toffset), + expected, + String.format( + "testing String(%s).startsWith(%s, %d), source : %s, ", + escapeNonASCIIs(data), + escapeNonASCIIs(prefix), toffset, + source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/SubString.java 2015-10-30 14:04:19.909197620 -0700 @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.subString. + * @run testng/othervm -XX:+CompactStrings SubString + * @run testng/othervm -XX:-CompactStrings SubString + */ + +public class SubString extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, 0, 0, "" }, + new Object[] { STRING_L1, 0, 1, "A" }, + new Object[] { STRING_L1, 1, 1, "" }, + new Object[] { STRING_L2, 0, 2, "AB" }, + new Object[] { STRING_L2, 1, 2, "B" }, + new Object[] { STRING_LLONG, 0, 8, "ABCDEFGH" }, + new Object[] { STRING_LLONG, 7, 8, "H" }, + new Object[] { STRING_LLONG, 8, 8, "" }, + new Object[] { STRING_LLONG, 3, 7, "DEFG" }, + new Object[] { STRING_U1, 0, 1, "\uFF21" }, + new Object[] { STRING_U1, 1, 1, "" }, + new Object[] { STRING_U1, 0, 0, "" }, + new Object[] { STRING_U2, 0, 2, "\uFF21\uFF22" }, + new Object[] { STRING_U2, 1, 2, "\uFF22" }, + new Object[] { STRING_U2, 2, 2, "" }, + new Object[] { STRING_U2, 0, 2, "\uFF21\uFF22" }, + new Object[] { STRING_U2, 1, 2, "\uFF22" }, + new Object[] { STRING_M12, 1, 2, "A" }, + new Object[] { STRING_M11, 0, 1, "A" }, + new Object[] { STRING_M11, 1, 2, "\uFF21" }, + new Object[] { STRING_UDUPLICATE, 1, 5, + "\uFF22\uFF21\uFF22\uFF21" }, + new Object[] { STRING_MDUPLICATE1, 9, 10, "A" }, + new Object[] { STRING_MDUPLICATE1, 7, 8, "A" }, }; + } + + @Test(dataProvider = "provider") + public void testSubstring(String str, int beginIndex, int endIndex, + String expected) { + map.get(str) + .forEach( + (source, data) -> { + assertEquals( + data.substring(beginIndex, endIndex), + expected, + String.format( + "testing String(%s).substring(%d, %d), source : %s, ", + escapeNonASCIIs(data), beginIndex, + endIndex, source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/ToCharArray.java 2015-10-30 14:04:20.491195415 -0700 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Arrays; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertTrue; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.toCharArray. + * @run testng/othervm -XX:+CompactStrings ToCharArray + * @run testng/othervm -XX:-CompactStrings ToCharArray + */ + +public class ToCharArray extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] { STRING_EMPTY, new char[] {} }, + new Object[] { STRING_L1, new char[] { 'A' } }, + new Object[] { STRING_L2, new char[] { 'A', 'B' } }, + new Object[] { STRING_LLONG, + new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' } }, + new Object[] { STRING_U1, new char[] { '\uFF21' } }, + new Object[] { STRING_U2, new char[] { '\uFF21', '\uFF22' } }, + new Object[] { STRING_M12, new char[] { '\uFF21', 'A' } }, + new Object[] { STRING_M11, new char[] { 'A', '\uFF21' } }, }; + } + + @Test(dataProvider = "provider") + public void testToCharArray(String str, char[] expected) { + map.get(str) + .forEach( + (source, data) -> { + assertTrue( + Arrays.equals(data.toCharArray(), expected), + String.format( + "testing String(%s).toCharArray(), source : %s, ", + escapeNonASCIIs(data), source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/ToLowerCase.java 2015-10-30 14:04:21.062193251 -0700 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.toLowerCase. + * @run testng/othervm -XX:+CompactStrings ToLowerCase + * @run testng/othervm -XX:-CompactStrings ToLowerCase + */ + +public class ToLowerCase extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + new Object[] { STRING_EMPTY, "" }, + new Object[] { STRING_L1, "a" }, + new Object[] { STRING_L2, "ab" }, + new Object[] { STRING_U1, "\uFF41" }, + new Object[] { STRING_MDUPLICATE1, + "\uFF41a\uFF41a\uFF41a\uFF41a\uFF41a" }, + new Object[] { STRING_SUPPLEMENTARY, + "\uD801\uDC28\uD801\uDC29\uFF41a" }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "\uD801\uDC28\uD801\uDC29\uFF41a" }, + new Object[] { STRING_SUPPLEMENTARY, + STRING_SUPPLEMENTARY_LOWERCASE } }; + } + + @Test(dataProvider = "provider") + public void testToLowerCase(String str, String expected) { + map.get(str).forEach( + (source, data) -> { + assertEquals(data.toLowerCase(), expected, String.format( + "testing String(%s).toLowerCase(), source : %s, ", + escapeNonASCIIs(data), source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/ToUpperCase.java 2015-10-30 14:04:21.620191138 -0700 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.toUpperCase. + * @run testng/othervm -XX:+CompactStrings ToUpperCase + * @run testng/othervm -XX:-CompactStrings ToUpperCase + */ + +public class ToUpperCase extends CompactString { + + @DataProvider + public Object[][] provider() { + return new Object[][] { + + new Object[] { STRING_EMPTY, "" }, + new Object[] { STRING_L1, "A" }, + new Object[] { STRING_L2, "AB" }, + new Object[] { STRING_U1, "\uFF21" }, + new Object[] { STRING_MDUPLICATE1, + "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A" }, + new Object[] { STRING_SUPPLEMENTARY, + "\uD801\uDC00\uD801\uDC01\uFF21A" }, + + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + "\uD801\uDC00\uD801\uDC01\uFF21A" }, + new Object[] { STRING_SUPPLEMENTARY_LOWERCASE, + STRING_SUPPLEMENTARY }, }; + } + + @Test(dataProvider = "provider") + public void testToUpperCase(String str, String expected) { + map.get(str).forEach( + (source, data) -> { + assertEquals(data.toUpperCase(), expected, String.format( + "testing String(%s).toUpperCase(), source : %s, ", + escapeNonASCIIs(data), source)); + }); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/Trim.java 2015-10-30 14:04:22.190188978 -0700 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.trim. + * @run testng/othervm -XX:+CompactStrings Trim + * @run testng/othervm -XX:-CompactStrings Trim + */ + +public class Trim { + + /* + * Data provider for testTrim + * + * @return input parameter for testTrim + */ + @DataProvider + public Object[][] trims() { + return new Object[][] { + { " \t \t".trim(), "" }, + { "\t \t ".trim(), "" }, + { "\t A B C\t ".trim(), "A B C" }, + { " \t A B C \t".trim(), "A B C" }, + { "\t \uFF21 \uFF22 \uFF23\t ".trim(), "\uFF21 \uFF22 \uFF23" }, + { " \t \uFF21 \uFF22 \uFF23 \t".trim(), "\uFF21 \uFF22 \uFF23" }, + { " \t \uFF41 \uFF42 \uFF43 \t".trim(), "\uFF41 \uFF42 \uFF43" }, + { " \t A\uFF21 B\uFF22 C\uFF23 \t".trim(), + "A\uFF21 B\uFF22 C\uFF23" } }; + } + + /* + * test trim(). + * + * @param res + * real result + * @param expected + * expected result + */ + @Test(dataProvider = "trims") + public void testTrim(String res, String expected) { + assertEquals(res, expected); + } + +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/String/CompactString/ValueOf.java 2015-10-30 14:04:22.777186754 -0700 @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This one is for String.valueOf. + * valueOf(char[] data) is not tested here. + * @run testng/othervm -XX:+CompactStrings ValueOf + * @run testng/othervm -XX:-CompactStrings ValueOf + */ + +public class ValueOf { + + /* + * Data provider for testValueOf + * + * @return input parameter for testValueOf + */ + @DataProvider + public Object[][] valueOfs() { + return new Object[][] { { String.valueOf(true), "true" }, + { String.valueOf(false), "false" }, + { String.valueOf(1.0f), "1.0" }, + { String.valueOf(0.0f), "0.0" }, + { String.valueOf(Float.MAX_VALUE), "3.4028235E38" }, + { String.valueOf(Float.MIN_VALUE), "1.4E-45" }, + { String.valueOf(1.0d), "1.0" }, + { String.valueOf(0.0d), "0.0" }, + { String.valueOf(Double.MAX_VALUE), "1.7976931348623157E308" }, + { String.valueOf(Double.MIN_VALUE), "4.9E-324" }, + { String.valueOf(1), "1" }, { String.valueOf(0), "0" }, + { String.valueOf(Integer.MAX_VALUE), "2147483647" }, + { String.valueOf(Integer.MIN_VALUE), "-2147483648" }, + { String.valueOf(1L), "1" }, { String.valueOf(0L), "0" }, + { String.valueOf(Long.MAX_VALUE), "9223372036854775807" }, + { String.valueOf(Long.MIN_VALUE), "-9223372036854775808" } }; + } + + /* + * test String.valueOf(xxx). + * + * @param res + * real result + * @param expected + * expected result + */ + @Test(dataProvider = "valueOfs") + public void testValueOf(String res, String expected) { + assertEquals(res, expected); + } + +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/StringBuffer/CompactStringBuffer.java 2015-10-30 14:04:23.392184423 -0700 @@ -0,0 +1,489 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Arrays; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/* + * @test + * @bug 8077559 + * @summary Tests Compact String. This test is testing StringBuffer + * behavior related to Compact String. + * @run testng/othervm -XX:+CompactStrings CompactStringBuffer + * @run testng/othervm -XX:-CompactStrings CompactStringBuffer + */ + +public class CompactStringBuffer { + + /* + * Tests for "A" + */ + @Test + public void testCompactStringBufferForLatinA() { + final String ORIGIN = "A"; + /* + * Because right now ASCII is the default encoding parameter for source + * code in JDK build environment, so we escape them. same as below. + */ + check(new StringBuffer(ORIGIN).append(new char[] { '\uFF21' }), + "A\uFF21"); + check(new StringBuffer(ORIGIN).append(new StringBuffer("\uFF21")), + "A\uFF21"); + check(new StringBuffer(ORIGIN).append("\uFF21"), "A\uFF21"); + check(new StringBuffer(ORIGIN).append(new StringBuffer("\uFF21")), + "A\uFF21"); + check(new StringBuffer(ORIGIN).delete(0, 1), ""); + check(new StringBuffer(ORIGIN).delete(0, 0), "A"); + check(new StringBuffer(ORIGIN).deleteCharAt(0), ""); + assertEquals(new StringBuffer(ORIGIN).indexOf("A", 0), 0); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 0), -1); + assertEquals(new StringBuffer(ORIGIN).indexOf("", 0), 0); + assertEquals(new StringBuffer(ORIGIN).insert(1, "\uD801\uDC00") + .indexOf("A", 0), 0); + assertEquals(new StringBuffer(ORIGIN).insert(0, "\uD801\uDC00") + .indexOf("A", 0), 2); + check(new StringBuffer(ORIGIN).insert(0, new char[] {}), "A"); + check(new StringBuffer(ORIGIN).insert(1, new char[] { '\uFF21' }), + "A\uFF21"); + check(new StringBuffer(ORIGIN).insert(0, new char[] { '\uFF21' }), + "\uFF21A"); + check(new StringBuffer(ORIGIN).insert(0, new StringBuffer("\uFF21")), + "\uFF21A"); + check(new StringBuffer(ORIGIN).insert(1, new StringBuffer("\uFF21")), + "A\uFF21"); + check(new StringBuffer(ORIGIN).insert(0, ""), "A"); + check(new StringBuffer(ORIGIN).insert(0, "\uFF21"), "\uFF21A"); + check(new StringBuffer(ORIGIN).insert(1, "\uFF21"), "A\uFF21"); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), -1); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf(""), 1); + check(new StringBuffer(ORIGIN).replace(0, 0, "\uFF21"), "\uFF21A"); + check(new StringBuffer(ORIGIN).replace(0, 1, "\uFF21"), "\uFF21"); + checkSetCharAt(new StringBuffer(ORIGIN), 0, '\uFF21', "\uFF21"); + checkSetLength(new StringBuffer(ORIGIN), 0, ""); + checkSetLength(new StringBuffer(ORIGIN), 1, "A"); + check(new StringBuffer(ORIGIN).substring(0), "A"); + check(new StringBuffer(ORIGIN).substring(1), ""); + } + + /* + * Tests for "\uFF21" + */ + @Test + public void testCompactStringBufferForNonLatinA() { + final String ORIGIN = "\uFF21"; + check(new StringBuffer(ORIGIN).append(new char[] { 'A' }), "\uFF21A"); + check(new StringBuffer(ORIGIN).append(new StringBuffer("A")), "\uFF21A"); + check(new StringBuffer(ORIGIN).append("A"), "\uFF21A"); + check(new StringBuffer(ORIGIN).append(new StringBuffer("A")), "\uFF21A"); + check(new StringBuffer(ORIGIN).delete(0, 1), ""); + check(new StringBuffer(ORIGIN).delete(0, 0), "\uFF21"); + check(new StringBuffer(ORIGIN).deleteCharAt(0), ""); + assertEquals(new StringBuffer(ORIGIN).indexOf("A", 0), -1); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 0), 0); + assertEquals(new StringBuffer(ORIGIN).indexOf("", 0), 0); + check(new StringBuffer(ORIGIN).insert(0, new char[] {}), "\uFF21"); + check(new StringBuffer(ORIGIN).insert(1, new char[] { 'A' }), "\uFF21A"); + check(new StringBuffer(ORIGIN).insert(0, new char[] { 'A' }), "A\uFF21"); + check(new StringBuffer(ORIGIN).insert(0, new StringBuffer("A")), + "A\uFF21"); + check(new StringBuffer(ORIGIN).insert(1, new StringBuffer("A")), + "\uFF21A"); + check(new StringBuffer(ORIGIN).insert(0, ""), "\uFF21"); + check(new StringBuffer(ORIGIN).insert(0, "A"), "A\uFF21"); + check(new StringBuffer(ORIGIN).insert(1, "A"), "\uFF21A"); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), -1); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 0); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf(""), 1); + check(new StringBuffer(ORIGIN).replace(0, 0, "A"), "A\uFF21"); + check(new StringBuffer(ORIGIN).replace(0, 1, "A"), "A"); + checkSetCharAt(new StringBuffer(ORIGIN), 0, 'A', "A"); + checkSetLength(new StringBuffer(ORIGIN), 0, ""); + checkSetLength(new StringBuffer(ORIGIN), 1, "\uFF21"); + check(new StringBuffer(ORIGIN).substring(0), "\uFF21"); + check(new StringBuffer(ORIGIN).substring(1), ""); + } + + /* + * Tests for "\uFF21A" + */ + @Test + public void testCompactStringBufferForMixedA1() { + final String ORIGIN = "\uFF21A"; + check(new StringBuffer(ORIGIN).delete(0, 1), "A"); + check(new StringBuffer(ORIGIN).delete(1, 2), "\uFF21"); + check(new StringBuffer(ORIGIN).deleteCharAt(1), "\uFF21"); + check(new StringBuffer(ORIGIN).deleteCharAt(0), "A"); + assertEquals(new StringBuffer(ORIGIN).indexOf("A", 0), 1); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 0), 0); + assertEquals(new StringBuffer(ORIGIN).indexOf("", 0), 0); + check(new StringBuffer(ORIGIN).insert(1, new char[] { 'A' }), "\uFF21AA"); + check(new StringBuffer(ORIGIN).insert(0, new char[] { '\uFF21' }), + "\uFF21\uFF21A"); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 1); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 0); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf(""), 2); + check(new StringBuffer(ORIGIN).replace(0, 0, "A"), "A\uFF21A"); + check(new StringBuffer(ORIGIN).replace(0, 1, "A"), "AA"); + checkSetCharAt(new StringBuffer(ORIGIN), 0, 'A', "AA"); + checkSetLength(new StringBuffer(ORIGIN), 0, ""); + checkSetLength(new StringBuffer(ORIGIN), 1, "\uFF21"); + check(new StringBuffer(ORIGIN).substring(0), "\uFF21A"); + check(new StringBuffer(ORIGIN).substring(1), "A"); + } + + /* + * Tests for "A\uFF21" + */ + @Test + public void testCompactStringBufferForMixedA2() { + final String ORIGIN = "A\uFF21"; + check(new StringBuffer(ORIGIN).replace(1, 2, "A"), "AA"); + checkSetLength(new StringBuffer(ORIGIN), 1, "A"); + check(new StringBuffer(ORIGIN).substring(0), "A\uFF21"); + check(new StringBuffer(ORIGIN).substring(1), "\uFF21"); + check(new StringBuffer(ORIGIN).substring(0, 1), "A"); + } + + /* + * Tests for "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A" + */ + @Test + public void testCompactStringBufferForDuplicatedMixedA1() { + final String ORIGIN = "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A"; + checkSetLength(new StringBuffer(ORIGIN), 1, "\uFF21"); + assertEquals(new StringBuffer(ORIGIN).indexOf("A", 5), 5); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 5), 6); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 9); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 8); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf(""), 10); + check(new StringBuffer(ORIGIN).substring(9), "A"); + check(new StringBuffer(ORIGIN).substring(8), "\uFF21A"); + } + + /* + * Tests for "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21" + */ + @Test + public void testCompactStringBufferForDuplicatedMixedA2() { + final String ORIGIN = "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21"; + checkSetLength(new StringBuffer(ORIGIN), 1, "A"); + assertEquals(new StringBuffer(ORIGIN).indexOf("A", 5), 6); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21", 5), 5); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 8); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 9); + check(new StringBuffer(ORIGIN).substring(9), "\uFF21"); + check(new StringBuffer(ORIGIN).substring(8), "A\uFF21"); + } + + /* + * Tests for "\uD801\uDC00\uD801\uDC01" + */ + @Test + public void testCompactStringForSupplementaryCodePoint() { + final String ORIGIN = "\uD801\uDC00\uD801\uDC01"; + check(new StringBuffer(ORIGIN).append("A"), "\uD801\uDC00\uD801\uDC01A"); + check(new StringBuffer(ORIGIN).append("\uFF21"), + "\uD801\uDC00\uD801\uDC01\uFF21"); + check(new StringBuffer(ORIGIN).appendCodePoint('A'), + "\uD801\uDC00\uD801\uDC01A"); + check(new StringBuffer(ORIGIN).appendCodePoint('\uFF21'), + "\uD801\uDC00\uD801\uDC01\uFF21"); + assertEquals(new StringBuffer(ORIGIN).charAt(0), '\uD801'); + assertEquals(new StringBuffer(ORIGIN).codePointAt(0), + Character.codePointAt(ORIGIN, 0)); + assertEquals(new StringBuffer(ORIGIN).codePointAt(1), + Character.codePointAt(ORIGIN, 1)); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(2), + Character.codePointAt(ORIGIN, 0)); + assertEquals(new StringBuffer(ORIGIN).codePointCount(1, 3), 2); + check(new StringBuffer(ORIGIN).delete(0, 2), "\uD801\uDC01"); + check(new StringBuffer(ORIGIN).delete(0, 3), "\uDC01"); + check(new StringBuffer(ORIGIN).deleteCharAt(1), "\uD801\uD801\uDC01"); + checkGetChars(new StringBuffer(ORIGIN), 0, 3, new char[] { '\uD801', + '\uDC00', '\uD801' }); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uD801\uDC01"), 2); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uDC01"), 3); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21"), -1); + assertEquals(new StringBuffer(ORIGIN).indexOf("A"), -1); + check(new StringBuffer(ORIGIN).insert(0, "\uFF21"), + "\uFF21\uD801\uDC00\uD801\uDC01"); + check(new StringBuffer(ORIGIN).insert(1, "\uFF21"), + "\uD801\uFF21\uDC00\uD801\uDC01"); + check(new StringBuffer(ORIGIN).insert(1, "A"), + "\uD801A\uDC00\uD801\uDC01"); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uDC00\uD801"), 1); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uD801"), 2); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), -1); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), -1); + assertEquals(new StringBuffer(ORIGIN).length(), 4); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 2); + check(new StringBuffer(ORIGIN).replace(0, 2, "A"), "A\uD801\uDC01"); + check(new StringBuffer(ORIGIN).replace(0, 3, "A"), "A\uDC01"); + check(new StringBuffer(ORIGIN).replace(0, 2, "\uFF21"), + "\uFF21\uD801\uDC01"); + check(new StringBuffer(ORIGIN).replace(0, 3, "\uFF21"), "\uFF21\uDC01"); + check(new StringBuffer(ORIGIN).reverse(), "\uD801\uDC01\uD801\uDC00"); + checkSetCharAt(new StringBuffer(ORIGIN), 1, '\uDC01', + "\uD801\uDC01\uD801\uDC01"); + checkSetCharAt(new StringBuffer(ORIGIN), 1, 'A', "\uD801A\uD801\uDC01"); + checkSetLength(new StringBuffer(ORIGIN), 2, "\uD801\uDC00"); + checkSetLength(new StringBuffer(ORIGIN), 3, "\uD801\uDC00\uD801"); + check(new StringBuffer(ORIGIN).substring(1, 3), "\uDC00\uD801"); + } + + /* + * Tests for "A\uD801\uDC00\uFF21" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed1() { + final String ORIGIN = "A\uD801\uDC00\uFF21"; + assertEquals(new StringBuffer(ORIGIN).codePointBefore(3), + Character.codePointAt(ORIGIN, 1)); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(2), '\uD801'); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(1), 'A'); + assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 3), 2); + assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 4), 3); + check(new StringBuffer(ORIGIN).delete(0, 1), "\uD801\uDC00\uFF21"); + check(new StringBuffer(ORIGIN).delete(0, 1).delete(2, 3), "\uD801\uDC00"); + check(new StringBuffer(ORIGIN).deleteCharAt(3).deleteCharAt(0), + "\uD801\uDC00"); + assertEquals(new StringBuffer(ORIGIN).indexOf("\uFF21"), 3); + assertEquals(new StringBuffer(ORIGIN).indexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("\uFF21"), 3); + assertEquals(new StringBuffer(ORIGIN).lastIndexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 1); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 3); + check(new StringBuffer(ORIGIN).replace(1, 3, "A"), "AA\uFF21"); + check(new StringBuffer(ORIGIN).replace(1, 4, "A"), "AA"); + check(new StringBuffer(ORIGIN).replace(1, 4, ""), "A"); + check(new StringBuffer(ORIGIN).reverse(), "\uFF21\uD801\uDC00A"); + checkSetLength(new StringBuffer(ORIGIN), 1, "A"); + check(new StringBuffer(ORIGIN).substring(0, 1), "A"); + } + + /* + * Tests for "\uD801\uDC00\uFF21A" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed2() { + final String ORIGIN = "\uD801\uDC00\uFF21A"; + assertEquals(new StringBuffer(ORIGIN).codePointBefore(3), + Character.codePointAt(ORIGIN, 2)); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(2), + Character.codePointAt(ORIGIN, 0)); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(1), '\uD801'); + assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 3), 2); + assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 4), 3); + check(new StringBuffer(ORIGIN).delete(0, 2), "\uFF21A"); + check(new StringBuffer(ORIGIN).delete(0, 3), "A"); + check(new StringBuffer(ORIGIN).deleteCharAt(0).deleteCharAt(0) + .deleteCharAt(0), "A"); + assertEquals(new StringBuffer(ORIGIN).indexOf("A"), 3); + assertEquals(new StringBuffer(ORIGIN).delete(0, 3).indexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).replace(0, 3, "B").indexOf("A"), + 1); + assertEquals(new StringBuffer(ORIGIN).substring(3, 4).indexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 2); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(2, 1), 3); + check(new StringBuffer(ORIGIN).replace(0, 3, "B"), "BA"); + check(new StringBuffer(ORIGIN).reverse(), "A\uFF21\uD801\uDC00"); + } + + /* + * Tests for "\uD801A\uDC00\uFF21" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed3() { + final String ORIGIN = "\uD801A\uDC00\uFF21"; + assertEquals(new StringBuffer(ORIGIN).codePointAt(1), 'A'); + assertEquals(new StringBuffer(ORIGIN).codePointAt(3), '\uFF21'); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(1), '\uD801'); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(2), 'A'); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(3), '\uDC00'); + assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 3), 3); + assertEquals(new StringBuffer(ORIGIN).codePointCount(1, 3), 2); + assertEquals(new StringBuffer(ORIGIN).delete(0, 1).delete(1, 3) + .indexOf("A"), 0); + assertEquals( + new StringBuffer(ORIGIN).replace(0, 1, "B").replace(2, 4, "C") + .indexOf("A"), 1); + assertEquals(new StringBuffer(ORIGIN).substring(1, 4).substring(0, 1) + .indexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 1); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(2, 1), 3); + check(new StringBuffer(ORIGIN).reverse(), "\uFF21\uDC00A\uD801"); + } + + /* + * Tests for "A\uDC01\uFF21\uD801" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed4() { + final String ORIGIN = "A\uDC01\uFF21\uD801"; + assertEquals(new StringBuffer(ORIGIN).codePointAt(1), '\uDC01'); + assertEquals(new StringBuffer(ORIGIN).codePointAt(3), '\uD801'); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(1), 'A'); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(2), '\uDC01'); + assertEquals(new StringBuffer(ORIGIN).codePointBefore(3), '\uFF21'); + assertEquals(new StringBuffer(ORIGIN).codePointCount(0, 3), 3); + assertEquals(new StringBuffer(ORIGIN).codePointCount(1, 3), 2); + assertEquals(new StringBuffer(ORIGIN).delete(1, 4).indexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).replace(1, 4, "B").indexOf("A"), + 0); + assertEquals(new StringBuffer(ORIGIN).substring(0, 1).indexOf("A"), 0); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(0, 1), 1); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuffer(ORIGIN).offsetByCodePoints(2, 1), 3); + check(new StringBuffer(ORIGIN).reverse(), "\uD801\uFF21\uDC01A"); + } + + @Test + public void testCompactStringMisc() { + String ascii = "abcdefgh"; + String asciiMixed = "abc" + "\u4e00\u4e01\u4e02" + "fgh"; + String bmp = "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08"; + String bmpMixed = "\u4e00\u4e01\u4e02" + "ABC" + "\u4e06\u4e07\u4e08"; + + check(new StringBuffer().append(ascii).delete(0, 20).toString(), + ""); + check(new StringBuffer().append(ascii).delete(3, 20).toString(), + "abc"); + check(new StringBuffer().append(ascii).delete(3, 6).toString(), + "abcgh"); + check(new StringBuffer().append(ascii).deleteCharAt(0).toString(), + "bcdefgh"); + check(new StringBuffer().append(ascii).deleteCharAt(3).toString(), + "abcefgh"); + check(new StringBuffer().append(asciiMixed).delete(3, 6).toString(), + "abcfgh"); + check(new StringBuffer().append(asciiMixed).deleteCharAt(3).toString(), + "abc\u4e01\u4e02fgh"); + check(new StringBuffer().append(asciiMixed).deleteCharAt(3) + .deleteCharAt(3) + .deleteCharAt(3).toString(), + "abcfgh"); + check(new StringBuffer().append(bmp).delete(0, 20).toString(), + ""); + check(new StringBuffer().append(bmp).delete(3, 20).toString(), + "\u4e00\u4e01\u4e02"); + check(new StringBuffer().append(bmp).delete(3, 6).toString(), + "\u4e00\u4e01\u4e02\u4e06\u4e07\u4e08"); + check(new StringBuffer().append(bmp).deleteCharAt(0).toString(), + "\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08"); + check(new StringBuffer().append(bmp).deleteCharAt(3).toString(), + "\u4e00\u4e01\u4e02\u4e04\u4e05\u4e06\u4e07\u4e08"); + check(new StringBuffer().append(bmpMixed).delete(3, 6).toString(), + "\u4e00\u4e01\u4e02\u4e06\u4e07\u4e08"); + + //////////////////////////////////////////////////////////////////// + check(new StringBuffer().append(ascii).replace(3, 6, "AB").toString(), + "abcABgh"); + check(new StringBuffer().append(asciiMixed).replace(3, 6, "AB").toString(), + "abcABfgh"); + check(new StringBuffer().append(bmp).replace(3, 6, "AB").toString(), + "\u4e00\u4e01\u4e02AB\u4e06\u4e07\u4e08"); + + check(new StringBuffer().append(bmpMixed).replace(3, 6, "").toString(), + "\u4e00\u4e01\u4e02\u4e06\u4e07\u4e08"); + + check(new StringBuffer().append(ascii).replace(3, 6, "\u4e01\u4e02").toString(), + "abc\u4e01\u4e02gh"); + + //////////////////////////////////////////////////////////////////// + check(new StringBuffer().append(ascii).insert(3, "").toString(), + "abcdefgh"); + check(new StringBuffer().append(ascii).insert(3, "AB").toString(), + "abcABdefgh"); + check(new StringBuffer().append(ascii).insert(3, "\u4e01\u4e02").toString(), + "abc\u4e01\u4e02defgh"); + + check(new StringBuffer().append(asciiMixed).insert(0, 'A').toString(), + "Aabc\u4e00\u4e01\u4e02fgh"); + check(new StringBuffer().append(asciiMixed).insert(3, "A").toString(), + "abcA\u4e00\u4e01\u4e02fgh"); + + check(new StringBuffer().append(ascii).insert(3, 1234567).toString(), + "abc1234567defgh"); + check(new StringBuffer().append(bmp).insert(3, 1234567).toString(), + "\u4e00\u4e01\u4e021234567\u4e03\u4e04\u4e05\u4e06\u4e07\u4e08"); + + //////////////////////////////////////////////////////////////////// + check(new StringBuffer().append(ascii).append(1.23456).toString(), + "abcdefgh1.23456"); + check(new StringBuffer().append(bmp).append(1.23456).toString(), + "\u4e00\u4e01\u4e02\u4e03\u4e04\u4e05\u4e06\u4e07\u4e081.23456"); + } + + private void checkGetChars(StringBuffer sb, int srcBegin, int srcEnd, + char expected[]) { + char[] dst = new char[srcEnd - srcBegin]; + sb.getChars(srcBegin, srcEnd, dst, 0); + assertTrue(Arrays.equals(dst, expected)); + } + + private void checkSetCharAt(StringBuffer sb, int index, char ch, + String expected) { + sb.setCharAt(index, ch); + check(sb, expected); + } + + private void checkSetLength(StringBuffer sb, int newLength, String expected) { + sb.setLength(newLength); + check(sb, expected); + } + + private void check(StringBuffer sb, String expected) { + check(sb.toString(), expected); + } + + private void check(String str, String expected) { + assertTrue(str.equals(expected), String.format( + "Get (%s) but expect (%s), ", escapeNonASCIIs(str), + escapeNonASCIIs(expected))); + } + + /* + * Because right now system default charset in JPRT environment is only + * guaranteed to support ASCII characters in log, so we escape them. + */ + private String escapeNonASCIIs(String str) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c > 0x7F) { + sb.append("\\u").append(Integer.toHexString((int) c)); + } else { + sb.append(c); + } + } + return sb.toString(); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/StringBuffer/CompactStringBufferSerialization.java 2015-10-30 14:04:24.021182041 -0700 @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.*; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static jdk.testlibrary.SerializationUtils.*; +import static org.testng.Assert.*; + +/* + * @test + * @bug 8077559 + * @library /lib/testlibrary + * @build jdk.testlibrary.SerializationUtils + * @summary Tests Compact String. This one is testing StringBuffer serialization + * among -XX:+CompactStrings/-XX:-CompactStrings/LegacyStringBuffer + * @run testng/othervm -XX:+CompactStrings CompactStringBufferSerialization + * @run testng/othervm -XX:-CompactStrings CompactStringBufferSerialization + */ + +public class CompactStringBufferSerialization { + @DataProvider + public Object[][] provider() { + return new Object[][] { + // every byte array is serialized from corresponding StringBuilder object + // by previous JDK(build 1.8.0_45-b14). + new Object[] { + new StringBuffer(""), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 0, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("A"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 1, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 17, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("AB"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 2, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 18, 0, 65, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("abcdefghijk"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 11, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 27, 0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0, 105, 0, + 106, 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("\uff21"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 1, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 17, -1, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("\uff21\uff22"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 2, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 18, -1, 33, -1, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("\uff21A\uff21A\uff21A\uff21A\uff21A"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 10, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 26, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("A\uff21B\uff22C\uff23D\uff24E\uff25F\uff26G\uff27H\uff28"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 16, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 32, 0, 65, -1, 33, 0, 66, -1, 34, 0, 67, -1, 35, 0, 68, -1, 36, 0, 69, -1, 37, + 0, 70, -1, 38, 0, 71, -1, 39, 0, 72, -1, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("\uff21A\uff22B\uff23C\uff24D\uff25E\uff26F\uff27G\uff28H"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 16, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 32, -1, 33, 0, 65, -1, 34, 0, 66, -1, 35, 0, 67, -1, 36, 0, 68, -1, 37, 0, 69, + -1, 38, 0, 70, -1, 39, 0, 71, -1, 40, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("\ud801\udc00\ud801\udc01\uff21A"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 6, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 22, -40, 1, -36, 0, -40, 1, -36, 1, -1, 33, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuffer("\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 22, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 102, + 102, 101, 114, 47, 7, 7, -39, -22, -56, -22, -45, 3, 0, 3, 73, 0, 5, 99, 111, 117, 110, 116, 90, 0, 6, 115, 104, 97, 114, 101, + 100, 91, 0, 5, 118, 97, 108, 117, 101, 116, 0, 2, 91, 67, 120, 112, 0, 0, 0, 10, 0, 117, 114, 0, 2, 91, 67, -80, 38, 102, -80, + -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 26, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1, + 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } } }; + } + + /* + * Verify serialization works between Compact StringBuffer/Legacy StringBuffer + */ + @Test(dataProvider = "provider") + public void test(StringBuffer sbContent, byte[] baInJDK8) throws Exception { + // Serialize a StringBuffer object into byte array. + byte[] ba = serialize(sbContent); + assertEquals(ba, baInJDK8); + // Deserialize a StringBuffer object from byte array which is generated by previous JDK(build 1.8.0_45-b14). + Object obj = deserialize(ba); + assertEquals(obj.getClass(), StringBuffer.class); + assertTrue(equals((StringBuffer)obj, sbContent)); + } + + boolean equals(StringBuffer sb, StringBuffer expected) { + if(sb.length() == expected.length() + && sb.capacity() == expected.capacity() + && sb.toString().equals(expected.toString())) { + return true; + } + return false; + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/StringBuilder/CompactStringBuilder.java 2015-10-30 14:04:24.597179857 -0700 @@ -0,0 +1,414 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.Arrays; + +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +/* + * @test + * @bug 8054307 8077559 + * @summary Tests Compact String. This test is testing StringBuilder + * behavior related to Compact String. + * @run testng/othervm -XX:+CompactStrings CompactStringBuilder + * @run testng/othervm -XX:-CompactStrings CompactStringBuilder + */ + +public class CompactStringBuilder { + + /* + * Tests for "A" + */ + @Test + public void testCompactStringBuilderForLatinA() { + final String ORIGIN = "A"; + /* + * Because right now ASCII is the default encoding parameter for source + * code in JDK build environment, so we escape them. same as below. + */ + check(new StringBuilder(ORIGIN).append(new char[] { '\uFF21' }), + "A\uFF21"); + check(new StringBuilder(ORIGIN).append(new StringBuffer("\uFF21")), + "A\uFF21"); + check(new StringBuilder(ORIGIN).append("\uFF21"), "A\uFF21"); + check(new StringBuilder(ORIGIN).append(new StringBuffer("\uFF21")), + "A\uFF21"); + check(new StringBuilder(ORIGIN).delete(0, 1), ""); + check(new StringBuilder(ORIGIN).delete(0, 0), "A"); + check(new StringBuilder(ORIGIN).deleteCharAt(0), ""); + assertEquals(new StringBuilder(ORIGIN).indexOf("A", 0), 0); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 0), -1); + assertEquals(new StringBuilder(ORIGIN).indexOf("", 0), 0); + assertEquals(new StringBuilder(ORIGIN).insert(1, "\uD801\uDC00") + .indexOf("A", 0), 0); + assertEquals(new StringBuilder(ORIGIN).insert(0, "\uD801\uDC00") + .indexOf("A", 0), 2); + check(new StringBuilder(ORIGIN).insert(0, new char[] {}), "A"); + check(new StringBuilder(ORIGIN).insert(1, new char[] { '\uFF21' }), + "A\uFF21"); + check(new StringBuilder(ORIGIN).insert(0, new char[] { '\uFF21' }), + "\uFF21A"); + check(new StringBuilder(ORIGIN).insert(0, new StringBuffer("\uFF21")), + "\uFF21A"); + check(new StringBuilder(ORIGIN).insert(1, new StringBuffer("\uFF21")), + "A\uFF21"); + check(new StringBuilder(ORIGIN).insert(0, ""), "A"); + check(new StringBuilder(ORIGIN).insert(0, "\uFF21"), "\uFF21A"); + check(new StringBuilder(ORIGIN).insert(1, "\uFF21"), "A\uFF21"); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), -1); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf(""), 1); + check(new StringBuilder(ORIGIN).replace(0, 0, "\uFF21"), "\uFF21A"); + check(new StringBuilder(ORIGIN).replace(0, 1, "\uFF21"), "\uFF21"); + checkSetCharAt(new StringBuilder(ORIGIN), 0, '\uFF21', "\uFF21"); + checkSetLength(new StringBuilder(ORIGIN), 0, ""); + checkSetLength(new StringBuilder(ORIGIN), 1, "A"); + check(new StringBuilder(ORIGIN).substring(0), "A"); + check(new StringBuilder(ORIGIN).substring(1), ""); + } + + /* + * Tests for "\uFF21" + */ + @Test + public void testCompactStringBuilderForNonLatinA() { + final String ORIGIN = "\uFF21"; + check(new StringBuilder(ORIGIN).append(new char[] { 'A' }), "\uFF21A"); + check(new StringBuilder(ORIGIN).append(new StringBuffer("A")), "\uFF21A"); + check(new StringBuilder(ORIGIN).append("A"), "\uFF21A"); + check(new StringBuilder(ORIGIN).append(new StringBuffer("A")), "\uFF21A"); + check(new StringBuilder(ORIGIN).delete(0, 1), ""); + check(new StringBuilder(ORIGIN).delete(0, 0), "\uFF21"); + check(new StringBuilder(ORIGIN).deleteCharAt(0), ""); + assertEquals(new StringBuilder(ORIGIN).indexOf("A", 0), -1); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 0), 0); + assertEquals(new StringBuilder(ORIGIN).indexOf("", 0), 0); + check(new StringBuilder(ORIGIN).insert(0, new char[] {}), "\uFF21"); + check(new StringBuilder(ORIGIN).insert(1, new char[] { 'A' }), "\uFF21A"); + check(new StringBuilder(ORIGIN).insert(0, new char[] { 'A' }), "A\uFF21"); + check(new StringBuilder(ORIGIN).insert(0, new StringBuffer("A")), + "A\uFF21"); + check(new StringBuilder(ORIGIN).insert(1, new StringBuffer("A")), + "\uFF21A"); + check(new StringBuilder(ORIGIN).insert(0, ""), "\uFF21"); + check(new StringBuilder(ORIGIN).insert(0, "A"), "A\uFF21"); + check(new StringBuilder(ORIGIN).insert(1, "A"), "\uFF21A"); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), -1); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 0); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf(""), 1); + check(new StringBuilder(ORIGIN).replace(0, 0, "A"), "A\uFF21"); + check(new StringBuilder(ORIGIN).replace(0, 1, "A"), "A"); + checkSetCharAt(new StringBuilder(ORIGIN), 0, 'A', "A"); + checkSetLength(new StringBuilder(ORIGIN), 0, ""); + checkSetLength(new StringBuilder(ORIGIN), 1, "\uFF21"); + check(new StringBuilder(ORIGIN).substring(0), "\uFF21"); + check(new StringBuilder(ORIGIN).substring(1), ""); + } + + /* + * Tests for "\uFF21A" + */ + @Test + public void testCompactStringBuilderForMixedA1() { + final String ORIGIN = "\uFF21A"; + check(new StringBuilder(ORIGIN).delete(0, 1), "A"); + check(new StringBuilder(ORIGIN).delete(1, 2), "\uFF21"); + check(new StringBuilder(ORIGIN).deleteCharAt(1), "\uFF21"); + check(new StringBuilder(ORIGIN).deleteCharAt(0), "A"); + assertEquals(new StringBuilder(ORIGIN).indexOf("A", 0), 1); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 0), 0); + assertEquals(new StringBuilder(ORIGIN).indexOf("", 0), 0); + check(new StringBuilder(ORIGIN).insert(1, new char[] { 'A' }), + "\uFF21AA"); + check(new StringBuilder(ORIGIN).insert(0, new char[] { '\uFF21' }), + "\uFF21\uFF21A"); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 1); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 0); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf(""), 2); + check(new StringBuilder(ORIGIN).replace(0, 0, "A"), "A\uFF21A"); + check(new StringBuilder(ORIGIN).replace(0, 1, "A"), "AA"); + checkSetCharAt(new StringBuilder(ORIGIN), 0, 'A', "AA"); + checkSetLength(new StringBuilder(ORIGIN), 0, ""); + checkSetLength(new StringBuilder(ORIGIN), 1, "\uFF21"); + check(new StringBuilder(ORIGIN).substring(0), "\uFF21A"); + check(new StringBuilder(ORIGIN).substring(1), "A"); + } + + /* + * Tests for "A\uFF21" + */ + @Test + public void testCompactStringBuilderForMixedA2() { + final String ORIGIN = "A\uFF21"; + check(new StringBuilder(ORIGIN).replace(1, 2, "A"), "AA"); + checkSetLength(new StringBuilder(ORIGIN), 1, "A"); + check(new StringBuilder(ORIGIN).substring(0), "A\uFF21"); + check(new StringBuilder(ORIGIN).substring(1), "\uFF21"); + check(new StringBuilder(ORIGIN).substring(0, 1), "A"); + } + + /* + * Tests for "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A" + */ + @Test + public void testCompactStringBuilderForDuplicatedMixedA1() { + final String ORIGIN = "\uFF21A\uFF21A\uFF21A\uFF21A\uFF21A"; + checkSetLength(new StringBuilder(ORIGIN), 1, "\uFF21"); + assertEquals(new StringBuilder(ORIGIN).indexOf("A", 5), 5); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 5), 6); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 9); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 8); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf(""), 10); + check(new StringBuilder(ORIGIN).substring(9), "A"); + check(new StringBuilder(ORIGIN).substring(8), "\uFF21A"); + } + + /* + * Tests for "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21" + */ + @Test + public void testCompactStringBuilderForDuplicatedMixedA2() { + final String ORIGIN = "A\uFF21A\uFF21A\uFF21A\uFF21A\uFF21"; + checkSetLength(new StringBuilder(ORIGIN), 1, "A"); + assertEquals(new StringBuilder(ORIGIN).indexOf("A", 5), 6); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21", 5), 5); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 8); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 9); + check(new StringBuilder(ORIGIN).substring(9), "\uFF21"); + check(new StringBuilder(ORIGIN).substring(8), "A\uFF21"); + } + + /* + * Tests for "\uD801\uDC00\uD801\uDC01" + */ + @Test + public void testCompactStringForSupplementaryCodePoint() { + final String ORIGIN = "\uD801\uDC00\uD801\uDC01"; + check(new StringBuilder(ORIGIN).append("A"), "\uD801\uDC00\uD801\uDC01A"); + check(new StringBuilder(ORIGIN).append("\uFF21"), + "\uD801\uDC00\uD801\uDC01\uFF21"); + check(new StringBuilder(ORIGIN).appendCodePoint('A'), + "\uD801\uDC00\uD801\uDC01A"); + check(new StringBuilder(ORIGIN).appendCodePoint('\uFF21'), + "\uD801\uDC00\uD801\uDC01\uFF21"); + assertEquals(new StringBuilder(ORIGIN).charAt(0), '\uD801'); + assertEquals(new StringBuilder(ORIGIN).codePointAt(0), + Character.codePointAt(ORIGIN, 0)); + assertEquals(new StringBuilder(ORIGIN).codePointAt(1), + Character.codePointAt(ORIGIN, 1)); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(2), + Character.codePointAt(ORIGIN, 0)); + assertEquals(new StringBuilder(ORIGIN).codePointCount(1, 3), 2); + check(new StringBuilder(ORIGIN).delete(0, 2), "\uD801\uDC01"); + check(new StringBuilder(ORIGIN).delete(0, 3), "\uDC01"); + check(new StringBuilder(ORIGIN).deleteCharAt(1), "\uD801\uD801\uDC01"); + checkGetChars(new StringBuilder(ORIGIN), 0, 3, new char[] { '\uD801', + '\uDC00', '\uD801' }); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uD801\uDC01"), 2); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uDC01"), 3); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21"), -1); + assertEquals(new StringBuilder(ORIGIN).indexOf("A"), -1); + check(new StringBuilder(ORIGIN).insert(0, "\uFF21"), + "\uFF21\uD801\uDC00\uD801\uDC01"); + check(new StringBuilder(ORIGIN).insert(1, "\uFF21"), + "\uD801\uFF21\uDC00\uD801\uDC01"); + check(new StringBuilder(ORIGIN).insert(1, "A"), + "\uD801A\uDC00\uD801\uDC01"); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uDC00\uD801"), 1); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uD801"), 2); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), -1); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), -1); + assertEquals(new StringBuilder(ORIGIN).length(), 4); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 2); + check(new StringBuilder(ORIGIN).replace(0, 2, "A"), "A\uD801\uDC01"); + check(new StringBuilder(ORIGIN).replace(0, 3, "A"), "A\uDC01"); + check(new StringBuilder(ORIGIN).replace(0, 2, "\uFF21"), + "\uFF21\uD801\uDC01"); + check(new StringBuilder(ORIGIN).replace(0, 3, "\uFF21"), "\uFF21\uDC01"); + check(new StringBuilder(ORIGIN).reverse(), "\uD801\uDC01\uD801\uDC00"); + checkSetCharAt(new StringBuilder(ORIGIN), 1, '\uDC01', + "\uD801\uDC01\uD801\uDC01"); + checkSetCharAt(new StringBuilder(ORIGIN), 1, 'A', "\uD801A\uD801\uDC01"); + checkSetLength(new StringBuilder(ORIGIN), 2, "\uD801\uDC00"); + checkSetLength(new StringBuilder(ORIGIN), 3, "\uD801\uDC00\uD801"); + check(new StringBuilder(ORIGIN).substring(1, 3), "\uDC00\uD801"); + } + + /* + * Tests for "A\uD801\uDC00\uFF21" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed1() { + final String ORIGIN = "A\uD801\uDC00\uFF21"; + assertEquals(new StringBuilder(ORIGIN).codePointBefore(3), + Character.codePointAt(ORIGIN, 1)); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(2), '\uD801'); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(1), 'A'); + assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 3), 2); + assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 4), 3); + check(new StringBuilder(ORIGIN).delete(0, 1), "\uD801\uDC00\uFF21"); + check(new StringBuilder(ORIGIN).delete(0, 1).delete(2, 3), + "\uD801\uDC00"); + check(new StringBuilder(ORIGIN).deleteCharAt(3).deleteCharAt(0), + "\uD801\uDC00"); + assertEquals(new StringBuilder(ORIGIN).indexOf("\uFF21"), 3); + assertEquals(new StringBuilder(ORIGIN).indexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("\uFF21"), 3); + assertEquals(new StringBuilder(ORIGIN).lastIndexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 1); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 3); + check(new StringBuilder(ORIGIN).replace(1, 3, "A"), "AA\uFF21"); + check(new StringBuilder(ORIGIN).replace(1, 4, "A"), "AA"); + check(new StringBuilder(ORIGIN).replace(1, 4, ""), "A"); + check(new StringBuilder(ORIGIN).reverse(), "\uFF21\uD801\uDC00A"); + checkSetLength(new StringBuilder(ORIGIN), 1, "A"); + check(new StringBuilder(ORIGIN).substring(0, 1), "A"); + } + + /* + * Tests for "\uD801\uDC00\uFF21A" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed2() { + final String ORIGIN = "\uD801\uDC00\uFF21A"; + assertEquals(new StringBuilder(ORIGIN).codePointBefore(3), + Character.codePointAt(ORIGIN, 2)); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(2), + Character.codePointAt(ORIGIN, 0)); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(1), '\uD801'); + assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 3), 2); + assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 4), 3); + check(new StringBuilder(ORIGIN).delete(0, 2), "\uFF21A"); + check(new StringBuilder(ORIGIN).delete(0, 3), "A"); + check(new StringBuilder(ORIGIN).deleteCharAt(0).deleteCharAt(0) + .deleteCharAt(0), "A"); + assertEquals(new StringBuilder(ORIGIN).indexOf("A"), 3); + assertEquals(new StringBuilder(ORIGIN).delete(0, 3).indexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).replace(0, 3, "B").indexOf("A"), + 1); + assertEquals(new StringBuilder(ORIGIN).substring(3, 4).indexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 2); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(2, 1), 3); + check(new StringBuilder(ORIGIN).replace(0, 3, "B"), "BA"); + check(new StringBuilder(ORIGIN).reverse(), "A\uFF21\uD801\uDC00"); + } + + /* + * Tests for "\uD801A\uDC00\uFF21" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed3() { + final String ORIGIN = "\uD801A\uDC00\uFF21"; + assertEquals(new StringBuilder(ORIGIN).codePointAt(1), 'A'); + assertEquals(new StringBuilder(ORIGIN).codePointAt(3), '\uFF21'); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(1), '\uD801'); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(2), 'A'); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(3), '\uDC00'); + assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 3), 3); + assertEquals(new StringBuilder(ORIGIN).codePointCount(1, 3), 2); + assertEquals(new StringBuilder(ORIGIN).delete(0, 1).delete(1, 3) + .indexOf("A"), 0); + assertEquals( + new StringBuilder(ORIGIN).replace(0, 1, "B").replace(2, 4, "C") + .indexOf("A"), 1); + assertEquals(new StringBuilder(ORIGIN).substring(1, 4).substring(0, 1) + .indexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 1); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(2, 1), 3); + check(new StringBuilder(ORIGIN).reverse(), "\uFF21\uDC00A\uD801"); + } + + /* + * Tests for "A\uDC01\uFF21\uD801" + */ + @Test + public void testCompactStringForSupplementaryCodePointMixed4() { + final String ORIGIN = "A\uDC01\uFF21\uD801"; + assertEquals(new StringBuilder(ORIGIN).codePointAt(1), '\uDC01'); + assertEquals(new StringBuilder(ORIGIN).codePointAt(3), '\uD801'); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(1), 'A'); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(2), '\uDC01'); + assertEquals(new StringBuilder(ORIGIN).codePointBefore(3), '\uFF21'); + assertEquals(new StringBuilder(ORIGIN).codePointCount(0, 3), 3); + assertEquals(new StringBuilder(ORIGIN).codePointCount(1, 3), 2); + assertEquals(new StringBuilder(ORIGIN).delete(1, 4).indexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).replace(1, 4, "B").indexOf("A"), + 0); + assertEquals(new StringBuilder(ORIGIN).substring(0, 1).indexOf("A"), 0); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(0, 1), 1); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(1, 1), 2); + assertEquals(new StringBuilder(ORIGIN).offsetByCodePoints(2, 1), 3); + check(new StringBuilder(ORIGIN).reverse(), "\uD801\uFF21\uDC01A"); + } + + private void checkGetChars(StringBuilder sb, int srcBegin, int srcEnd, + char expected[]) { + char[] dst = new char[srcEnd - srcBegin]; + sb.getChars(srcBegin, srcEnd, dst, 0); + assertTrue(Arrays.equals(dst, expected)); + } + + private void checkSetCharAt(StringBuilder sb, int index, char ch, + String expected) { + sb.setCharAt(index, ch); + check(sb, expected); + } + + private void checkSetLength(StringBuilder sb, int newLength, String expected) { + sb.setLength(newLength); + check(sb, expected); + } + + private void check(StringBuilder sb, String expected) { + check(sb.toString(), expected); + } + + private void check(String str, String expected) { + assertTrue(str.equals(expected), String.format( + "Get (%s) but expect (%s), ", escapeNonASCIIs(str), + escapeNonASCIIs(expected))); + } + + /* + * Because right now system default charset in JPRT environment is only + * guaranteed to support ASCII characters in log, so we escape them. + */ + private String escapeNonASCIIs(String str) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (c > 0x7F) { + sb.append("\\u").append(Integer.toHexString((int) c)); + } else { + sb.append(c); + } + } + return sb.toString(); + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/java/lang/StringBuilder/CompactStringBuilderSerialization.java 2015-10-30 14:04:25.180177648 -0700 @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.io.*; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static jdk.testlibrary.SerializationUtils.*; +import static org.testng.Assert.*; + +/* + * @test + * @bug 8077559 + * @library /lib/testlibrary + * @build jdk.testlibrary.SerializationUtils + * @summary Tests Compact String. This one is testing StringBuilder serialization + * among -XX:+CompactStrings/-XX:-CompactStrings/LegacyStringBuilder + * @run testng/othervm -XX:+CompactStrings CompactStringBuilderSerialization + * @run testng/othervm -XX:-CompactStrings CompactStringBuilderSerialization + */ + +public class CompactStringBuilderSerialization { + @DataProvider + public Object[][] provider() { + return new Object[][] { + // every byte array is serialized from corresponding StringBuffer object + // by previous JDK(build 1.8.0_45-b14). + new Object[] { + new StringBuilder(""), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 0, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("A"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 1, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 17, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("AB"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 2, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 18, 0, 65, 0, 66, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("abcdefghijk"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 11, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 27, 0, 97, 0, 98, 0, 99, 0, 100, 0, 101, 0, 102, 0, 103, 0, 104, 0, + 105, 0, 106, 0, 107, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("\uff21"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 1, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 17, -1, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("\uff21\uff22"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 2, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 18, -1, 33, -1, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("\uff21A\uff21A\uff21A\uff21A\uff21A"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 10, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 26, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65, -1, 33, 0, 65, -1, + 33, 0, 65, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("A\uff21B\uff22C\uff23D\uff24E\uff25F\uff26G\uff27H\uff28"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 16, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 32, 0, 65, -1, 33, 0, 66, -1, 34, 0, 67, -1, 35, 0, 68, -1, 36, 0, + 69, -1, 37, 0, 70, -1, 38, 0, 71, -1, 39, 0, 72, -1, 40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("\uff21A\uff22B\uff23C\uff24D\uff25E\uff26F\uff27G\uff28H"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 16, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 32, -1, 33, 0, 65, -1, 34, 0, 66, -1, 35, 0, 67, -1, 36, 0, 68, -1, + 37, 0, 69, -1, 38, 0, 70, -1, 39, 0, 71, -1, 40, 0, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("\ud801\udc00\ud801\udc01\uff21A"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 6, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 22, -40, 1, -36, 0, -40, 1, -36, 1, -1, 33, 0, 65, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } }, + new Object[] { + new StringBuilder("\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22\uff21\uff22"), + new byte[] { -84, -19, 0, 5, 115, 114, 0, 23, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 83, 116, 114, 105, 110, 103, 66, 117, 105, + 108, 100, 101, 114, 60, -43, -5, 20, 90, 76, 106, -53, 3, 0, 0, 120, 112, 119, 4, 0, 0, 0, 10, 117, 114, 0, 2, 91, 67, -80, 38, + 102, -80, -30, 93, -124, -84, 2, 0, 0, 120, 112, 0, 0, 0, 26, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1, 34, -1, 33, -1, 34, + -1, 33, -1, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120 } } }; + } + + /* + * Verify serialization works between Compact StringBuilder/Legacy StringBuilder + */ + @Test(dataProvider = "provider") + public void test(StringBuilder sbContent, byte[] baInJDK8) throws Exception { + // Serialize a StringBuilder object into byte array. + byte[] ba = serialize(sbContent); + assertEquals(ba, baInJDK8); + // Deserialize a StringBuilder object from byte array which is generated by previous JDK(build 1.8.0_45-b14). + Object obj = deserialize(ba); + assertEquals(obj.getClass(), StringBuilder.class); + assertTrue(equals((StringBuilder)obj, sbContent)); + } + + boolean equals(StringBuilder sb, StringBuilder expected) { + if(sb.length() == expected.length() + && sb.capacity() == expected.capacity() + && sb.toString().equals(expected.toString())) { + return true; + } + return false; + } +} --- /dev/null 2015-07-25 15:02:20.097409468 -0700 +++ new/test/lib/testlibrary/jdk/testlibrary/SerializationUtils.java 2015-10-30 14:04:25.762175443 -0700 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package jdk.testlibrary; + +import java.io.*; + +/** + * Common library for various test serialization utility functions. + */ +public final class SerializationUtils { + /* + * Serialize an object into byte array. + */ + public static byte[] serialize(Object obj) throws Exception { + try (ByteArrayOutputStream bs = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bs);) { + out.writeObject(obj); + return bs.toByteArray(); + } + } + + /* + * Deserialize an object from byte array. + */ + public static Object deserialize(byte[] ba) throws Exception { + try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(ba));) { + return in.readObject(); + } + } +}