8141132: JEP 254: Compact Strings
Adopt a more space-efficient internal representation for strings.
Co-authored-by: Brent Christian <brent.christian@oracle.com>
Co-authored-by: Vivek Deshpande <vivek.r.deshpande@intel.com>
Co-authored-by: Charlie Hunt <charlie.hunt@oracle.com>
Co-authored-by: Vladimir Kozlov <vladimir.kozlov@oracle.com>
Co-authored-by: Roger Riggs <roger.riggs@oracle.com>
Co-authored-by: Xueming Shen <xueming.shen@oracle.com>
Co-authored-by: Aleksey Shipilev <aleksey.shipilev@oracle.com>
Co-authored-by: Sandhya Viswanathan <sandhya.viswanathan@intel.com>
Reviewed-by: alanb, bdelsart, coleenp, iklam, jiangli, jrose, kevinw, naoto, pliden, roland, smarks, twisti
diff --git a/jdk/make/data/charsetmapping/DoubleByte-X.java.template b/jdk/make/data/charsetmapping/DoubleByte-X.java.template
index 4ef582c..d97e2c4 100644
--- a/jdk/make/data/charsetmapping/DoubleByte-X.java.template
+++ b/jdk/make/data/charsetmapping/DoubleByte-X.java.template
@@ -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$
diff --git a/jdk/make/data/charsetmapping/SingleByte-X.java.template b/jdk/make/data/charsetmapping/SingleByte-X.java.template
index 82af052..2acb1ef 100644
--- a/jdk/make/data/charsetmapping/SingleByte-X.java.template
+++ b/jdk/make/data/charsetmapping/SingleByte-X.java.template
@@ -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$
diff --git a/jdk/make/mapfiles/libjava/mapfile-vers b/jdk/make/mapfiles/libjava/mapfile-vers
index c2e022c..7ae48fe 100644
--- a/jdk/make/mapfiles/libjava/mapfile-vers
+++ b/jdk/make/mapfiles/libjava/mapfile-vers
@@ -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;
diff --git a/jdk/make/mapfiles/libjava/reorder-sparc b/jdk/make/mapfiles/libjava/reorder-sparc
index 5ae7cca..3994c91 100644
--- a/jdk/make/mapfiles/libjava/reorder-sparc
+++ b/jdk/make/mapfiles/libjava/reorder-sparc
@@ -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;
diff --git a/jdk/make/mapfiles/libjava/reorder-sparcv9 b/jdk/make/mapfiles/libjava/reorder-sparcv9
index f109866..63a667f 100644
--- a/jdk/make/mapfiles/libjava/reorder-sparcv9
+++ b/jdk/make/mapfiles/libjava/reorder-sparcv9
@@ -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;
diff --git a/jdk/make/mapfiles/libjava/reorder-x86 b/jdk/make/mapfiles/libjava/reorder-x86
index 03609c1..c6c3fce 100644
--- a/jdk/make/mapfiles/libjava/reorder-x86
+++ b/jdk/make/mapfiles/libjava/reorder-x86
@@ -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;
diff --git a/jdk/make/src/classes/build/tools/charsetmapping/DBCS.java b/jdk/make/src/classes/build/tools/charsetmapping/DBCS.java
index bd4bd71..9ebadde 100644
--- a/jdk/make/src/classes/build/tools/charsetmapping/DBCS.java
+++ b/jdk/make/src/classes/build/tools/charsetmapping/DBCS.java
@@ -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)
diff --git a/jdk/make/src/classes/build/tools/charsetmapping/SBCS.java b/jdk/make/src/classes/build/tools/charsetmapping/SBCS.java
index fa294f1..02c8d63 100644
--- a/jdk/make/src/classes/build/tools/charsetmapping/SBCS.java
+++ b/jdk/make/src/classes/build/tools/charsetmapping/SBCS.java
@@ -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);
}
diff --git a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java
index a921600..65f3c49 100644
--- a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java
+++ b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java
@@ -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.
* <p>
@@ -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 <hi=0, low> pair storage.
+ */
+ private void inflate() {
+ if (!isLatin1()) {
+ return;
+ }
+ byte[] buf = StringUTF16.newBytesFor(value.length);
+ StringLatin1.inflateSB(value, buf, 0, count);
+ this.value = buf;
+ this.coder = UTF16;
}
/**
@@ -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');
}
-
+ ensureCapacityInternal(newLength);
+ if (count < newLength) {
+ 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<end; i++)
- value[dstOffset++] = s.charAt(i);
+ shift(dstOffset, len);
count += len;
+ putCharsAt(dstOffset, s, start, end);
return this;
}
@@ -1191,10 +1268,18 @@
* @throws IndexOutOfBoundsException if the offset is invalid.
*/
public AbstractStringBuilder insert(int offset, char c) {
+ checkOffset(offset, count);
ensureCapacityInternal(count + 1);
- System.arraycopy(value, offset, value, offset + 1, count - offset);
- value[offset] = c;
+ shift(offset, 1);
count += 1;
+ if (isLatin1() && StringLatin1.canEncode(c)) {
+ value[offset] = (byte)c;
+ } else {
+ if (isLatin1()) {
+ inflate();
+ }
+ StringUTF16.putCharSB(value, offset, c);
+ }
return this;
}
@@ -1326,7 +1411,7 @@
* or {@code -1} if there is no such occurrence.
*/
public int indexOf(String str, int fromIndex) {
- return String.indexOf(value, 0, count, str, fromIndex);
+ return String.indexOf(value, coder, count, str, fromIndex);
}
/**
@@ -1366,7 +1451,7 @@
* or {@code -1} if there is no such occurrence.
*/
public int lastIndexOf(String str, int fromIndex) {
- return String.lastIndexOf(value, 0, count, str, fromIndex);
+ return String.lastIndexOf(value, coder, count, str, fromIndex);
}
/**
@@ -1392,34 +1477,47 @@
* @return a reference to this object.
*/
public AbstractStringBuilder reverse() {
- boolean hasSurrogates = false;
+ byte[] val = this.value;
+ int count = this.count;
+ int coder = this.coder;
int n = count - 1;
- for (int j = (n-1) >> 1; j >= 0; j--) {
- int k = n - j;
- char cj = value[j];
- char ck = value[k];
- value[j] = ck;
- value[k] = cj;
- if (Character.isSurrogate(cj) ||
- Character.isSurrogate(ck)) {
- hasSurrogates = true;
+ if (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;
}
- }
- if (hasSurrogates) {
- reverseAllValidSurrogatePairs();
+ } else {
+ checkOffset(count, val.length >> 1);
+ boolean hasSurrogates = false;
+ for (int j = (n-1) >> 1; j >= 0; j--) {
+ int k = n - j;
+ char cj = StringUTF16.getChar(val, j);
+ char ck = StringUTF16.getChar(val, k);
+ StringUTF16.putChar(val, j, ck);
+ StringUTF16.putChar(val, k, cj);
+ if (Character.isSurrogate(cj) ||
+ Character.isSurrogate(ck)) {
+ hasSurrogates = true;
+ }
+ }
+ if (hasSurrogates) {
+ reverseAllValidSurrogatePairs(val, count);
+ }
}
return this;
}
/** Outlined helper method for reverse() */
- private void reverseAllValidSurrogatePairs() {
+ 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);
+ }
+ }
}
diff --git a/jdk/src/java.base/share/classes/java/lang/Integer.java b/jdk/src/java.base/share/classes/java/lang/Integer.java
index ed401db..e956c35 100644
--- a/jdk/src/java.base/share/classes/java/lang/Integer.java
+++ b/jdk/src/java.base/share/classes/java/lang/Integer.java
@@ -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,7 @@
*
* 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;
@@ -449,8 +507,8 @@
// really: r = i - (q * 100);
r = i - ((q << 6) + (q << 5) + (q << 2));
i = q;
- buf [--charPos] = DigitOnes[r];
- buf [--charPos] = DigitTens[r];
+ buf [--charPos] = (byte)DigitOnes[r];
+ buf [--charPos] = (byte)DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
@@ -458,12 +516,46 @@
for (;;) {
q = (i * 52429) >>> (16+3);
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
- buf [--charPos] = digits [r];
+ buf [--charPos] = (byte)digits [r];
i = q;
if (i == 0) break;
}
if (sign != 0) {
- buf [--charPos] = sign;
+ buf [--charPos] = (byte)sign;
+ }
+ }
+
+ static void getCharsUTF16(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;
+ StringUTF16.putChar(buf, --charPos, DigitOnes[r]);
+ StringUTF16.putChar(buf, --charPos, 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) ...
+ StringUTF16.putChar(buf, --charPos, Integer.digits[r]);
+ i = q;
+ if (i == 0) break;
+ }
+ if (sign != 0) {
+ StringUTF16.putChar(buf, --charPos, sign);
}
}
diff --git a/jdk/src/java.base/share/classes/java/lang/Long.java b/jdk/src/java.base/share/classes/java/lang/Long.java
index 4f248d7..b68e2c2 100644
--- a/jdk/src/java.base/share/classes/java/lang/Long.java
+++ b/jdk/src/java.base/share/classes/java/lang/Long.java
@@ -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,7 @@
*
* 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;
@@ -448,8 +506,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];
+ buf[--charPos] = (byte)Integer.DigitOnes[r];
+ buf[--charPos] = (byte)Integer.DigitTens[r];
}
// Get 2 digits/iteration using ints
@@ -460,8 +518,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];
+ buf[--charPos] = (byte)Integer.DigitOnes[r];
+ buf[--charPos] = (byte)Integer.DigitTens[r];
}
// Fall thru to fast mode for smaller numbers
@@ -469,12 +527,59 @@
for (;;) {
q2 = (i2 * 52429) >>> (16+3);
r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ...
- buf[--charPos] = Integer.digits[r];
+ buf[--charPos] = (byte)Integer.digits[r];
i2 = q2;
if (i2 == 0) break;
}
if (sign != 0) {
- buf[--charPos] = sign;
+ buf[--charPos] = (byte)sign;
+ }
+ }
+
+ static void getCharsUTF16(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;
+ StringUTF16.putChar(buf, --charPos, Integer.DigitOnes[r]);
+ StringUTF16.putChar(buf, --charPos, 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;
+ StringUTF16.putChar(buf, --charPos, Integer.DigitOnes[r]);
+ StringUTF16.putChar(buf, --charPos, 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) ...
+ StringUTF16.putChar(buf, --charPos, Integer.digits[r]);
+ i2 = q2;
+ if (i2 == 0) break;
+ }
+ if (sign != 0) {
+ StringUTF16.putChar(buf, --charPos, sign);
}
}
diff --git a/jdk/src/java.base/share/classes/java/lang/String.java b/jdk/src/java.base/share/classes/java/lang/String.java
index ce4262e..4d4ab35 100644
--- a/jdk/src/java.base/share/classes/java/lang/String.java
+++ b/jdk/src/java.base/share/classes/java/lang/String.java
@@ -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<String>, 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);
+ checkBoundsOffCount(offset, count, codePoints.length);
+ if (count == 0) {
+ this.value = "".value;
+ this.coder = "".coder;
+ return;
}
- if (count <= 0) {
- if (count < 0) {
- throw new StringIndexOutOfBoundsException(count);
- }
- if (offset <= codePoints.length) {
- this.value = "".value;
+ 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);
+ if (isLatin1()) {
+ checkIndex(index, value.length);
+ return value[index] & 0xff;
}
- return Character.codePointAtImpl(value, index, value.length);
+ 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}</ul>
*/
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
- if (srcBegin < 0) {
- throw new StringIndexOutOfBoundsException(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);
}
- if (srcEnd > value.length) {
- throw new StringIndexOutOfBoundsException(srcEnd);
- }
- if (srcBegin > srcEnd) {
- throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
- }
- 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;
- }
+ byte v1[] = value;
+ byte v2[] = anotherString.value;
+ if (coder() == anotherString.coder()) {
+ return isLatin1() ? StringLatin1.compareTo(v1, v2)
+ : StringUTF16.compareTo(v1, v2);
}
- return len1 - len2;
- }
+ 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)) {
+ || (toffset > (long)length() - len)
+ || (ooffset > (long)other.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;
- }
- }
- return false;
+ 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 true;
+ return isLatin1()
+ ? StringLatin1.regionMatchesCI_UTF16(tv, toffset, ov, ooffset, len)
+ : StringUTF16.regionMatchesCI_Latin1(tv, toffset, ov, ooffset, len);
}
/**
@@ -1423,19 +1441,31 @@
* </pre>
*/
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.
+ * @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(char[] source, int sourceOffset, int sourceCount,
- String target, int fromIndex) {
- return indexOf(source, sourceOffset, sourceCount,
- target.value, 0, target.value.length,
- fromIndex);
- }
+ static int indexOf(byte[] src, byte srcCoder, int srcCount,
+ String tgtStr, int 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 indexOf(char[] source, int sourceOffset, int sourceCount,
- char[] target, int targetOffset, int targetCount,
- int fromIndex) {
- if (fromIndex >= sourceCount) {
- return (targetCount == 0 ? sourceCount : -1);
+ byte[] tgt = tgtStr.value;
+ byte tgtCoder = tgtStr.coder();
+ int tgtCount = tgtStr.length();
+
+ 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.
+ * @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(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) {
+ 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 <a
@@ -3016,93 +2680,11 @@
@Override
public IntStream chars() {
return StreamSupport.intStream(
- new IntCharArraySpliterator(value, Spliterator.IMMUTABLE), false);
+ isLatin1() ? new StringLatin1.CharsSpliterator(value, Spliterator.IMMUTABLE)
+ : new StringUTF16.CharsSpliterator(value, Spliterator.IMMUTABLE),
+ false);
}
- static class CodePointsSpliterator 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;
-
- CodePointsSpliterator(char[] array, int acs) {
- this(array, 0, array.length, acs);
- }
-
- CodePointsSpliterator(char[] 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(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);
+ }
+ }
}
diff --git a/jdk/src/java.base/share/classes/java/lang/StringBuffer.java b/jdk/src/java.base/share/classes/java/lang/StringBuffer.java
index a206454..65bbe14 100644
--- a/jdk/src/java.base/share/classes/java/lang/StringBuffer.java
+++ b/jdk/src/java.base/share/classes/java/lang/StringBuffer.java
@@ -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);
+ }
}
diff --git a/jdk/src/java.base/share/classes/java/lang/StringBuilder.java b/jdk/src/java.base/share/classes/java/lang/StringBuilder.java
index 9d4ccf3..7d1e46a 100644
--- a/jdk/src/java.base/share/classes/java/lang/StringBuilder.java
+++ b/jdk/src/java.base/share/classes/java/lang/StringBuilder.java
@@ -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);
}
}
diff --git a/jdk/src/java.base/share/classes/java/lang/StringCoding.java b/jdk/src/java.base/share/classes/java/lang/StringCoding.java
index d770156..b1e25d5 100644
--- a/jdk/src/java.base/share/classes/java/lang/StringCoding.java
+++ b/jdk/src/java.base/share/classes/java/lang/StringCoding.java
@@ -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);
- }
- return safeTrim(ba, bb.position(), cs, isTrusted);
}
+ if (ce instanceof ArrayEncoder) {
+ 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);
}
}
- static byte[] encode(String charsetName, char[] ca, int off, int len)
+ @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++;
+ }
+ dst[dp++] = '?';
+ len = sl - sp;
+ }
+ }
+ if (dp == dst.length) {
+ return dst;
+ }
+ return Arrays.copyOf(dst, dp);
+ }
+
+ 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.
diff --git a/jdk/src/java.base/share/classes/java/lang/StringDecoderUTF8.java b/jdk/src/java.base/share/classes/java/lang/StringDecoderUTF8.java
new file mode 100644
index 0000000..1b504df
--- /dev/null
+++ b/jdk/src/java.base/share/classes/java/lang/StringDecoderUTF8.java
@@ -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);
+ }
+}
diff --git a/jdk/src/java.base/share/classes/java/lang/StringLatin1.java b/jdk/src/java.base/share/classes/java/lang/StringLatin1.java
new file mode 100644
index 0000000..eb8ddc6
--- /dev/null
+++ b/jdk/src/java.base/share/classes/java/lang/StringLatin1.java
@@ -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);
+ }
+}
diff --git a/jdk/src/java.base/share/classes/java/lang/StringUTF16.java b/jdk/src/java.base/share/classes/java/lang/StringUTF16.java
new file mode 100644
index 0000000..56550d7
--- /dev/null
+++ b/jdk/src/java.base/share/classes/java/lang/StringUTF16.java
@@ -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 <high, low> 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;
+}
diff --git a/jdk/src/java.base/share/classes/java/util/Arrays.java b/jdk/src/java.base/share/classes/java/util/Arrays.java
index 3596298..ac09b59 100644
--- a/jdk/src/java.base/share/classes/java/util/Arrays.java
+++ b/jdk/src/java.base/share/classes/java/util/Arrays.java
@@ -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;
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java b/jdk/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java
index 6b0abe9..0ba7330 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/ArrayDecoder.java
@@ -32,4 +32,8 @@
public interface ArrayDecoder {
int decode(byte[] src, int off, int len, char[] dst);
+
+ default boolean isASCIICompatible() {
+ return false;
+ }
}
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java b/jdk/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java
index 2ef46e7..b4ced42 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/ArrayEncoder.java
@@ -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;
+ }
}
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/DoubleByte.java b/jdk/src/java.base/share/classes/sun/nio/cs/DoubleByte.java
index 6121ab2..9f8d97f 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/DoubleByte.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/DoubleByte.java
@@ -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);
}
}
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/HKSCS.java b/jdk/src/java.base/share/classes/sun/nio/cs/HKSCS.java
index 773c7a0..ed920e7 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/HKSCS.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/HKSCS.java
@@ -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 {
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java b/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java
index 3018133..b5d93a8f 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/ISO_8859_1.java
@@ -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;
+ }
}
}
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java b/jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java
index 6fd6faf..29a4246 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/SingleByte.java
@@ -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.
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java b/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java
new file mode 100644
index 0000000..f1608ca
--- /dev/null
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/StringUTF16.java
@@ -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();
+}
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java b/jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java
index 816a1ac..39b7df0 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/US_ASCII.java
@@ -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;
+ }
}
}
diff --git a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java
index 3ee2341..06a4340 100644
--- a/jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java
+++ b/jdk/src/java.base/share/classes/sun/nio/cs/UTF_8.java
@@ -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;
+ }
}
}
diff --git a/jdk/src/java.base/share/native/libjava/String.c b/jdk/src/java.base/share/native/libjava/String.c
index 7c8170e..cc7f740 100644
--- a/jdk/src/java.base/share/native/libjava/String.c
+++ b/jdk/src/java.base/share/native/libjava/String.c
@@ -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;
+ }
+}
diff --git a/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/Big5_Solaris.java.template b/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/Big5_Solaris.java.template
index e1810bd9..2099fbb 100644
--- a/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/Big5_Solaris.java.template
+++ b/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/Big5_Solaris.java.template
@@ -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;
diff --git a/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/IBM834.java b/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/IBM834.java
index 513dc55..aeb4ded 100644
--- a/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/IBM834.java
+++ b/jdk/src/jdk.charsets/share/classes/sun/nio/cs/ext/IBM834.java
@@ -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) {
diff --git a/jdk/test/java/lang/String/Chars.java b/jdk/test/java/lang/String/Chars.java
new file mode 100644
index 0000000..ab6771b
--- /dev/null
+++ b/jdk/test/java/lang/String/Chars.java
@@ -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!");
+ }
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/CharAt.java b/jdk/test/java/lang/String/CompactString/CharAt.java
new file mode 100644
index 0000000..f70c143
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/CharAt.java
@@ -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)));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/CodePointAt.java b/jdk/test/java/lang/String/CompactString/CodePointAt.java
new file mode 100644
index 0000000..5333f0b
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/CodePointAt.java
@@ -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)));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/CodePointBefore.java b/jdk/test/java/lang/String/CompactString/CodePointBefore.java
new file mode 100644
index 0000000..81f16bc
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/CodePointBefore.java
@@ -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)));
+ });
+ }
+
+}
diff --git a/jdk/test/java/lang/String/CompactString/CodePointCount.java b/jdk/test/java/lang/String/CompactString/CodePointCount.java
new file mode 100644
index 0000000..0ddc82a
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/CodePointCount.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/CompactString.java b/jdk/test/java/lang/String/CompactString/CompactString.java
new file mode 100644
index 0000000..c22eacc
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/CompactString.java
@@ -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<String, Map<String, String>> 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<String, String> 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();
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/CompareTo.java b/jdk/test/java/lang/String/CompactString/CompareTo.java
new file mode 100644
index 0000000..96f07cd
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/CompareTo.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/CompareToIgnoreCase.java b/jdk/test/java/lang/String/CompactString/CompareToIgnoreCase.java
new file mode 100644
index 0000000..3b9cef7
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/CompareToIgnoreCase.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Concat.java b/jdk/test/java/lang/String/CompactString/Concat.java
new file mode 100644
index 0000000..de12389
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Concat.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Contains.java b/jdk/test/java/lang/String/CompactString/Contains.java
new file mode 100644
index 0000000..ad182b8
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Contains.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/EndsWith.java b/jdk/test/java/lang/String/CompactString/EndsWith.java
new file mode 100644
index 0000000..07485b4
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/EndsWith.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Equals.java b/jdk/test/java/lang/String/CompactString/Equals.java
new file mode 100644
index 0000000..101a015
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Equals.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/EqualsIgnoreCase.java b/jdk/test/java/lang/String/CompactString/EqualsIgnoreCase.java
new file mode 100644
index 0000000..0721c07
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/EqualsIgnoreCase.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/GetChars.java b/jdk/test/java/lang/String/CompactString/GetChars.java
new file mode 100644
index 0000000..3ea3dce
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/GetChars.java
@@ -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));
+ });
+ }
+
+}
diff --git a/jdk/test/java/lang/String/CompactString/IndexOf.java b/jdk/test/java/lang/String/CompactString/IndexOf.java
new file mode 100644
index 0000000..49fd9e5
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/IndexOf.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Intern.java b/jdk/test/java/lang/String/CompactString/Intern.java
new file mode 100644
index 0000000..fc2b06d
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Intern.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/LastIndexOf.java b/jdk/test/java/lang/String/CompactString/LastIndexOf.java
new file mode 100644
index 0000000..eccc9cc
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/LastIndexOf.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Length.java b/jdk/test/java/lang/String/CompactString/Length.java
new file mode 100644
index 0000000..ab2b9d5
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Length.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Numbers.java b/jdk/test/java/lang/String/CompactString/Numbers.java
new file mode 100644
index 0000000..ad9f17a
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Numbers.java
@@ -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);
+ }
+
+}
diff --git a/jdk/test/java/lang/String/CompactString/OffsetByCodePoints.java b/jdk/test/java/lang/String/CompactString/OffsetByCodePoints.java
new file mode 100644
index 0000000..44ed607
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/OffsetByCodePoints.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/RegionMatches.java b/jdk/test/java/lang/String/CompactString/RegionMatches.java
new file mode 100644
index 0000000..5301c0e
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/RegionMatches.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Replace.java b/jdk/test/java/lang/String/CompactString/Replace.java
new file mode 100644
index 0000000..95540f1
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Replace.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/SerializationTest.java b/jdk/test/java/lang/String/CompactString/SerializationTest.java
new file mode 100644
index 0000000..e4c94c5
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/SerializationTest.java
@@ -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);
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Split.java b/jdk/test/java/lang/String/CompactString/Split.java
new file mode 100644
index 0000000..2707ae3
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Split.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/StartsWith.java b/jdk/test/java/lang/String/CompactString/StartsWith.java
new file mode 100644
index 0000000..97109bb
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/StartsWith.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/SubString.java b/jdk/test/java/lang/String/CompactString/SubString.java
new file mode 100644
index 0000000..f34f161
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/SubString.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/ToCharArray.java b/jdk/test/java/lang/String/CompactString/ToCharArray.java
new file mode 100644
index 0000000..bf7dbde
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/ToCharArray.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/ToLowerCase.java b/jdk/test/java/lang/String/CompactString/ToLowerCase.java
new file mode 100644
index 0000000..aa49aad
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/ToLowerCase.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/ToUpperCase.java b/jdk/test/java/lang/String/CompactString/ToUpperCase.java
new file mode 100644
index 0000000..d3eced3
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/ToUpperCase.java
@@ -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));
+ });
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/Trim.java b/jdk/test/java/lang/String/CompactString/Trim.java
new file mode 100644
index 0000000..03d0f9f
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/Trim.java
@@ -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);
+ }
+
+}
diff --git a/jdk/test/java/lang/String/CompactString/VMOptionsTest.java b/jdk/test/java/lang/String/CompactString/VMOptionsTest.java
new file mode 100644
index 0000000..7fb4781
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/VMOptionsTest.java
@@ -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 java.io.*;
+import java.lang.reflect.Field;
+
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.*;
+
+/*
+ * @test
+ * @bug 8077559
+ * @summary Tests Compact String. This one is testing
+ * if Compact String enable/disable VM Options is indeed working in String class,
+ * it's verified by testing if the VM option affect coder and
+ * COMPACT_STRINGS field in String class.
+ * @run testng/othervm -XX:+CompactStrings -DCompactStringEnabled=true VMOptionsTest
+ * @run testng/othervm -XX:-CompactStrings -DCompactStringEnabled=false VMOptionsTest
+ * @run testng/othervm -DCompactStringEnabled=true VMOptionsTest
+ */
+
+public class VMOptionsTest {
+ boolean compactStringEnabled;
+ // corresponding "COMPACT_STRINGS" field in String class.
+ Field COMPACT_STRINGS;
+ // corresponding "coder" field in String class.
+ Field coder;
+
+ // corresponding coder type in String class.
+ final byte LATIN1 = 0;
+ final byte UTF16 = 1;
+
+ @BeforeClass
+ public void setUp() throws Exception {
+ compactStringEnabled = Boolean.valueOf(System.getProperty("CompactStringEnabled", null));
+ COMPACT_STRINGS = String.class.getDeclaredField("COMPACT_STRINGS");
+ COMPACT_STRINGS.setAccessible(true);
+ coder = String.class.getDeclaredField("coder");
+ coder.setAccessible(true);
+ }
+
+ @DataProvider
+ public Object[][] provider() {
+ return new Object[][] {
+ new Object[] {"", LATIN1},
+ new Object[] {"abc", LATIN1},
+ new Object[] {"A\uff21", UTF16},
+ new Object[] {"\uff21\uff22", UTF16}
+ };
+ }
+
+ /*
+ * verify the coder field in String objects.
+ */
+ @Test(dataProvider = "provider")
+ public void testCoder(String str, byte expected) throws Exception {
+ byte c = (byte) coder.get(str);
+ expected = compactStringEnabled ? expected : UTF16;
+ assertEquals(c, expected);
+ }
+
+ /*
+ * verify the COMPACT_STRINGS flag in String objects.
+ */
+ @Test(dataProvider = "provider")
+ public void testCompactStringFlag(String str, byte ignore) throws Exception {
+ assertTrue(COMPACT_STRINGS.get(str).equals(compactStringEnabled));
+ }
+}
diff --git a/jdk/test/java/lang/String/CompactString/ValueOf.java b/jdk/test/java/lang/String/CompactString/ValueOf.java
new file mode 100644
index 0000000..cf47416
--- /dev/null
+++ b/jdk/test/java/lang/String/CompactString/ValueOf.java
@@ -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);
+ }
+
+}
diff --git a/jdk/test/java/lang/String/LiteralReplace.java b/jdk/test/java/lang/String/LiteralReplace.java
index f72e105..32fc779 100644
--- a/jdk/test/java/lang/String/LiteralReplace.java
+++ b/jdk/test/java/lang/String/LiteralReplace.java
@@ -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",
+ "",
+ ""},
};
}
diff --git a/jdk/test/java/lang/String/ToLowerCase.java b/jdk/test/java/lang/String/ToLowerCase.java
index 784f810..9b5574d 100644
--- a/jdk/test/java/lang/String/ToLowerCase.java
+++ b/jdk/test/java/lang/String/ToLowerCase.java
@@ -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();
}
- }
+ }
}
diff --git a/jdk/test/java/lang/String/ToUpperCase.java b/jdk/test/java/lang/String/ToUpperCase.java
index 020c2ae..b7cebc1 100644
--- a/jdk/test/java/lang/String/ToUpperCase.java
+++ b/jdk/test/java/lang/String/ToUpperCase.java
@@ -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();
}
- }
+ }
}
diff --git a/jdk/test/java/lang/StringBuffer/CompactStringBuffer.java b/jdk/test/java/lang/StringBuffer/CompactStringBuffer.java
new file mode 100644
index 0000000..bc0ec5f
--- /dev/null
+++ b/jdk/test/java/lang/StringBuffer/CompactStringBuffer.java
@@ -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();
+ }
+}
diff --git a/jdk/test/java/lang/StringBuffer/CompactStringBufferSerialization.java b/jdk/test/java/lang/StringBuffer/CompactStringBufferSerialization.java
new file mode 100644
index 0000000..346c824
--- /dev/null
+++ b/jdk/test/java/lang/StringBuffer/CompactStringBufferSerialization.java
@@ -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;
+ }
+}
diff --git a/jdk/test/java/lang/StringBuffer/Exceptions.java b/jdk/test/java/lang/StringBuffer/Exceptions.java
index 5f68b80..061b3d7 100644
--- a/jdk/test/java/lang/StringBuffer/Exceptions.java
+++ b/jdk/test/java/lang/StringBuffer/Exceptions.java
@@ -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");
diff --git a/jdk/test/java/lang/StringBuilder/BuilderForwarding.java b/jdk/test/java/lang/StringBuilder/BuilderForwarding.java
index a78c323..82e4010 100644
--- a/jdk/test/java/lang/StringBuilder/BuilderForwarding.java
+++ b/jdk/test/java/lang/StringBuilder/BuilderForwarding.java
@@ -264,4 +264,3 @@
}
}
}
-
diff --git a/jdk/test/java/lang/StringBuilder/CompactStringBuilder.java b/jdk/test/java/lang/StringBuilder/CompactStringBuilder.java
new file mode 100644
index 0000000..7c69326
--- /dev/null
+++ b/jdk/test/java/lang/StringBuilder/CompactStringBuilder.java
@@ -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();
+ }
+}
diff --git a/jdk/test/java/lang/StringBuilder/CompactStringBuilderSerialization.java b/jdk/test/java/lang/StringBuilder/CompactStringBuilderSerialization.java
new file mode 100644
index 0000000..0c622d7
--- /dev/null
+++ b/jdk/test/java/lang/StringBuilder/CompactStringBuilderSerialization.java
@@ -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;
+ }
+}
diff --git a/jdk/test/java/lang/StringBuilder/Exceptions.java b/jdk/test/java/lang/StringBuilder/Exceptions.java
index e1686b0..bee53bc 100644
--- a/jdk/test/java/lang/StringBuilder/Exceptions.java
+++ b/jdk/test/java/lang/StringBuilder/Exceptions.java
@@ -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");
diff --git a/jdk/test/lib/testlibrary/jdk/testlibrary/SerializationUtils.java b/jdk/test/lib/testlibrary/jdk/testlibrary/SerializationUtils.java
new file mode 100644
index 0000000..3dbc666
--- /dev/null
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/SerializationUtils.java
@@ -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();
+ }
+ }
+}
diff --git a/jdk/test/sun/nio/cs/TestStringCoding.java b/jdk/test/sun/nio/cs/TestStringCoding.java
index f9f7021..4dd85f4 100644
--- a/jdk/test/sun/nio/cs/TestStringCoding.java
+++ b/jdk/test/sun/nio/cs/TestStringCoding.java
@@ -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 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[] 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 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
diff --git a/jdk/test/sun/nio/cs/TestStringCodingUTF8.java b/jdk/test/sun/nio/cs/TestStringCodingUTF8.java
index b936838..ad07f19 100644
--- a/jdk/test/sun/nio/cs/TestStringCodingUTF8.java
+++ b/jdk/test/sun/nio/cs/TestStringCodingUTF8.java
@@ -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<Integer> list = new ArrayList<>(0x20000);
for (int i = 0; i < 0x20000; i++) {
list.add(i, i);