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
This commit is contained in:
Tobias Hartmann 2015-11-03 09:42:11 +01:00
parent b7dca6971d
commit 4ed5b73f3d
76 changed files with 8755 additions and 1288 deletions

View file

@ -50,12 +50,12 @@ public class $NAME_CLZ$ extends Charset
public CharsetDecoder newDecoder() { public CharsetDecoder newDecoder() {
initb2c(); 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() { public CharsetEncoder newEncoder() {
initc2b(); initc2b();
return new DoubleByte.Encoder$ENCTYPE$(this, $ENC_REPLACEMENT$ c2b, c2bIndex); return new DoubleByte.Encoder$ENCTYPE$(this, $ENC_REPLACEMENT$ c2b, c2bIndex, $ASCIICOMPATIBLE$);
} }
$B2C$ $B2C$

View file

@ -48,11 +48,11 @@ public class $NAME_CLZ$ extends Charset implements HistoricallyNamedCharset
} }
public CharsetDecoder newDecoder() { public CharsetDecoder newDecoder() {
return new SingleByte.Decoder(this, b2c); return new SingleByte.Decoder(this, b2c, $ASCIICOMPATIBLE$);
} }
public CharsetEncoder newEncoder() { public CharsetEncoder newEncoder() {
return new SingleByte.Encoder(this, c2b, c2bIndex); return new SingleByte.Encoder(this, c2b, c2bIndex, $ASCIICOMPATIBLE$);
} }
private final static String b2cTable = $B2CTABLE$ private final static String b2cTable = $B2CTABLE$

View file

@ -211,6 +211,7 @@ SUNWprivate_1.1 {
Java_java_lang_SecurityManager_getClassContext; Java_java_lang_SecurityManager_getClassContext;
Java_java_lang_Shutdown_halt0; Java_java_lang_Shutdown_halt0;
Java_java_lang_String_intern; Java_java_lang_String_intern;
Java_java_lang_StringUTF16_isBigEndian;
Java_java_lang_System_identityHashCode; Java_java_lang_System_identityHashCode;
Java_java_lang_System_initProperties; Java_java_lang_System_initProperties;
Java_java_lang_System_mapLibraryName; Java_java_lang_System_mapLibraryName;

View file

@ -57,6 +57,7 @@ text: .text%Java_java_io_UnixFileSystem_list;
text: .text%JNU_ClassString; text: .text%JNU_ClassString;
text: .text%JNU_CopyObjectArray; text: .text%JNU_CopyObjectArray;
text: .text%Java_java_lang_String_intern; 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_findLoadedClass0;
text: .text%Java_java_lang_ClassLoader_findBootstrapClass; text: .text%Java_java_lang_ClassLoader_findBootstrapClass;
text: .text%Java_java_lang_Throwable_fillInStackTrace; text: .text%Java_java_lang_Throwable_fillInStackTrace;

View file

@ -29,6 +29,7 @@ text: .text%Java_sun_reflect_Reflection_getCallerClass__;
text: .text%Java_sun_reflect_Reflection_getCallerClass__I; text: .text%Java_sun_reflect_Reflection_getCallerClass__I;
text: .text%Java_java_lang_Class_forName0; text: .text%Java_java_lang_Class_forName0;
text: .text%Java_java_lang_String_intern; 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_Float_floatToRawIntBits;
text: .text%Java_java_lang_Double_doubleToRawLongBits; text: .text%Java_java_lang_Double_doubleToRawLongBits;
text: .text%Java_java_lang_ClassLoader_findLoadedClass0; text: .text%Java_java_lang_ClassLoader_findLoadedClass0;

View file

@ -31,6 +31,7 @@ text: .text%Java_sun_reflect_Reflection_getCallerClass__;
text: .text%Java_sun_reflect_Reflection_getCallerClass__I; text: .text%Java_sun_reflect_Reflection_getCallerClass__I;
text: .text%Java_java_lang_Class_forName0; text: .text%Java_java_lang_Class_forName0;
text: .text%Java_java_lang_String_intern; text: .text%Java_java_lang_String_intern;
text: .text%Java_java_lang_StringUTF16_isBigEndian;
text: .text%Java_sun_reflect_NativeConstructorAccessorImpl_newInstance0; text: .text%Java_sun_reflect_NativeConstructorAccessorImpl_newInstance0;
text: .text%Java_java_lang_Throwable_fillInStackTrace; text: .text%Java_java_lang_Throwable_fillInStackTrace;
text: .text%Java_java_lang_System_setOut0; text: .text%Java_java_lang_System_setOut0;

View file

@ -197,6 +197,7 @@ public class DBCS {
.replace("$B1MAX$" , "0x" + Integer.toString(b1Max, 16)) .replace("$B1MAX$" , "0x" + Integer.toString(b1Max, 16))
.replace("$B2MIN$" , "0x" + Integer.toString(b2Min, 16)) .replace("$B2MIN$" , "0x" + Integer.toString(b2Min, 16))
.replace("$B2MAX$" , "0x" + Integer.toString(b2Max, 16)) .replace("$B2MAX$" , "0x" + Integer.toString(b2Max, 16))
.replace("$ASCIICOMPATIBLE$", isASCII ? "true" : "false")
.replace("$B2C$", b2c) .replace("$B2C$", b2c)
.replace("$C2BLENGTH$", "0x" + Integer.toString(c2bOff, 16)) .replace("$C2BLENGTH$", "0x" + Integer.toString(c2bOff, 16))
.replace("$NONROUNDTRIP_B2C$", b2cNR) .replace("$NONROUNDTRIP_B2C$", b2cNR)

View file

@ -175,6 +175,9 @@ public class SBCS {
else else
line = " return (cs instanceof " + clzName + ");"; line = " return (cs instanceof " + clzName + ");";
} }
if (line.indexOf("$ASCIICOMPATIBLE$") != -1) {
line = line.replace("$ASCIICOMPATIBLE$", isASCII ? "true" : "false");
}
if (line.indexOf("$B2CTABLE$") != -1) { if (line.indexOf("$B2CTABLE$") != -1) {
line = line.replace("$B2CTABLE$", b2c); line = line.replace("$B2CTABLE$", b2c);
} }

View file

@ -31,6 +31,12 @@ import java.util.Spliterator;
import java.util.stream.IntStream; import java.util.stream.IntStream;
import java.util.stream.StreamSupport; 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. * A mutable sequence of characters.
* <p> * <p>
@ -51,7 +57,12 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
/** /**
* The value is used for character storage. * 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. * The count is the number of characters used.
@ -68,7 +79,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* Creates an AbstractStringBuilder of the specified capacity. * Creates an AbstractStringBuilder of the specified capacity.
*/ */
AbstractStringBuilder(int 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 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* @return the current capacity * @return the current capacity
*/ */
public int capacity() { public int capacity() {
return value.length; return value.length >> coder;
} }
/** /**
@ -110,9 +127,10 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* @param minimumCapacity the minimum desired capacity. * @param minimumCapacity the minimum desired capacity.
*/ */
public void ensureCapacity(int minimumCapacity) { public void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > 0) if (minimumCapacity > 0) {
ensureCapacityInternal(minimumCapacity); ensureCapacityInternal(minimumCapacity);
} }
}
/** /**
* This method has the same contract as ensureCapacity, but is * This method has the same contract as ensureCapacity, but is
@ -120,24 +138,48 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
*/ */
private void ensureCapacityInternal(int minimumCapacity) { private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code // overflow-conscious code
if (minimumCapacity - value.length > 0) int capacity = value.length >> coder;
if (minimumCapacity - capacity > 0) {
expandCapacity(minimumCapacity); expandCapacity(minimumCapacity);
} }
}
/** /**
* This implements the expansion semantics of ensureCapacity with no * This implements the expansion semantics of ensureCapacity with no
* size check or synchronization. * size check or synchronization.
*/ */
void expandCapacity(int minimumCapacity) { private void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2; int newCapacity = (value.length >> coder) * 2 + 2;
if (newCapacity - minimumCapacity < 0) if (newCapacity - minimumCapacity < 0) {
newCapacity = minimumCapacity; newCapacity = minimumCapacity;
}
if (newCapacity < 0) { if (newCapacity < 0) {
if (minimumCapacity < 0) // overflow if (minimumCapacity < 0) {// overflow
throw new OutOfMemoryError(); throw new OutOfMemoryError();
}
newCapacity = Integer.MAX_VALUE; 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 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* returned by a subsequent call to the {@link #capacity()} method. * returned by a subsequent call to the {@link #capacity()} method.
*/ */
public void trimToSize() { public void trimToSize() {
if (count < value.length) { int length = count << coder;
value = Arrays.copyOf(value, count); if (length < value.length) {
value = Arrays.copyOf(value, length);
} }
} }
@ -179,14 +222,17 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* {@code newLength} argument is negative. * {@code newLength} argument is negative.
*/ */
public void setLength(int newLength) { public void setLength(int newLength) {
if (newLength < 0) if (newLength < 0) {
throw new StringIndexOutOfBoundsException(newLength); 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; count = newLength;
} }
@ -209,9 +255,11 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
*/ */
@Override @Override
public char charAt(int index) { public char charAt(int index) {
if ((index < 0) || (index >= count)) checkIndex(index, count);
throw new StringIndexOutOfBoundsException(index); if (isLatin1()) {
return value[index]; return (char)(value[index] & 0xff);
}
return StringUTF16.charAt(value, index);
} }
/** /**
@ -236,10 +284,11 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* sequence. * sequence.
*/ */
public int codePointAt(int index) { public int codePointAt(int index) {
if ((index < 0) || (index >= count)) { checkIndex(index, count);
throw new StringIndexOutOfBoundsException(index); if (isLatin1()) {
return value[index] & 0xff;
} }
return Character.codePointAtImpl(value, index, count); return StringUTF16.codePointAtSB(value, index, count);
} }
/** /**
@ -265,10 +314,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
*/ */
public int codePointBefore(int index) { public int codePointBefore(int index) {
int i = index - 1; int i = index - 1;
if ((i < 0) || (i >= count)) { if (i < 0 || i >= count) {
throw new StringIndexOutOfBoundsException(index); 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 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) { if (beginIndex < 0 || endIndex > count || beginIndex > endIndex) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
} }
return Character.codePointCountImpl(value, beginIndex, endIndex - beginIndex); if (isLatin1()) {
return endIndex - beginIndex;
}
return StringUTF16.codePointCountSB(value, beginIndex, endIndex);
} }
/** /**
@ -321,7 +376,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
if (index < 0 || index > count) { if (index < 0 || index > count) {
throw new IndexOutOfBoundsException(); throw new IndexOutOfBoundsException();
} }
return Character.offsetByCodePointsImpl(value, 0, count, return Character.offsetByCodePoints(this,
index, codePointOffset); index, codePointOffset);
} }
@ -355,13 +410,14 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
*/ */
public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
{ {
if (srcBegin < 0) checkRangeSIOOBE(srcBegin, srcEnd, count); // compatible to old version
throw new StringIndexOutOfBoundsException(srcBegin); int n = srcEnd - srcBegin;
if ((srcEnd < 0) || (srcEnd > count)) checkRange(dstBegin, dstBegin + n, dst.length);
throw new StringIndexOutOfBoundsException(srcEnd); if (isLatin1()) {
if (srcBegin > srcEnd) StringLatin1.getCharsSB(value, srcBegin, srcEnd, dst, dstBegin);
throw new StringIndexOutOfBoundsException("srcBegin > srcEnd"); } else {
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin); StringUTF16.getCharsSB(value, srcBegin, srcEnd, dst, dstBegin);
}
} }
/** /**
@ -379,9 +435,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* negative or greater than or equal to {@code length()}. * negative or greater than or equal to {@code length()}.
*/ */
public void setCharAt(int index, char ch) { public void setCharAt(int index, char ch) {
if ((index < 0) || (index >= count)) checkIndex(index, count);
throw new StringIndexOutOfBoundsException(index); if (isLatin1() && StringLatin1.canEncode(ch)) {
value[index] = ch; value[index] = (byte)ch;
} else {
if (isLatin1()) {
inflate();
}
StringUTF16.putCharSB(value, index, ch);
}
} }
/** /**
@ -418,35 +480,34 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* @return a reference to this object. * @return a reference to this object.
*/ */
public AbstractStringBuilder append(String str) { public AbstractStringBuilder append(String str) {
if (str == null) if (str == null) {
return appendNull(); return appendNull();
}
int len = str.length(); int len = str.length();
ensureCapacityInternal(count + len); ensureCapacityInternal(count + len);
str.getChars(0, len, value, count); putStringAt(count, str);
count += len; count += len;
return this; return this;
} }
// Documentation in subclasses because of synchro difference // Documentation in subclasses because of synchro difference
public AbstractStringBuilder append(StringBuffer sb) { public AbstractStringBuilder append(StringBuffer sb) {
if (sb == null) return this.append((AbstractStringBuilder)sb);
return appendNull();
int len = sb.length();
ensureCapacityInternal(count + len);
sb.getChars(0, len, value, count);
count += len;
return this;
} }
/** /**
* @since 1.8 * @since 1.8
*/ */
AbstractStringBuilder append(AbstractStringBuilder asb) { AbstractStringBuilder append(AbstractStringBuilder asb) {
if (asb == null) if (asb == null) {
return appendNull(); return appendNull();
}
int len = asb.length(); int len = asb.length();
ensureCapacityInternal(count + len); ensureCapacityInternal(count + len);
asb.getChars(0, len, value, count); if (getCoder() != asb.getCoder()) {
inflate();
}
asb.getBytes(value, count, coder);
count += len; count += len;
return this; return this;
} }
@ -454,25 +515,35 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
// Documentation in subclasses because of synchro difference // Documentation in subclasses because of synchro difference
@Override @Override
public AbstractStringBuilder append(CharSequence s) { public AbstractStringBuilder append(CharSequence s) {
if (s == null) if (s == null) {
return appendNull(); return appendNull();
if (s instanceof String) }
if (s instanceof String) {
return this.append((String)s); return this.append((String)s);
if (s instanceof AbstractStringBuilder) }
if (s instanceof AbstractStringBuilder) {
return this.append((AbstractStringBuilder)s); return this.append((AbstractStringBuilder)s);
}
return this.append(s, 0, s.length()); return this.append(s, 0, s.length());
} }
private AbstractStringBuilder appendNull() { private AbstractStringBuilder appendNull() {
int c = count; ensureCapacityInternal(count + 4);
ensureCapacityInternal(c + 4); int count = this.count;
final char[] value = this.value; byte[] val = this.value;
value[c++] = 'n'; if (isLatin1()) {
value[c++] = 'u'; val[count++] = 'n';
value[c++] = 'l'; val[count++] = 'u';
value[c++] = 'l'; val[count++] = 'l';
count = c; 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; return this;
} }
@ -507,21 +578,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
*/ */
@Override @Override
public AbstractStringBuilder append(CharSequence s, int start, int end) { public AbstractStringBuilder append(CharSequence s, int start, int end) {
if (s == null) if (s == null) {
s = "null"; s = "null";
if ((start < 0) || (start > end) || (end > s.length())) }
throw new IndexOutOfBoundsException( checkRange(start, end, s.length());
"start " + start + ", end " + end + ", s.length() "
+ s.length());
int len = end - start; int len = end - start;
ensureCapacityInternal(count + len); ensureCapacityInternal(count + len);
if (s instanceof String) { appendChars(s, start, end);
((String)s).getChars(start, end, value, count);
} else {
for (int i = start, j = count; i < end; i++, j++)
value[j] = s.charAt(i);
}
count += len;
return this; return this;
} }
@ -544,8 +607,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
public AbstractStringBuilder append(char[] str) { public AbstractStringBuilder append(char[] str) {
int len = str.length; int len = str.length;
ensureCapacityInternal(count + len); ensureCapacityInternal(count + len);
System.arraycopy(str, 0, value, count, len); appendChars(str, 0, len);
count += len;
return this; return this;
} }
@ -572,10 +634,10 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* or {@code offset+len > str.length} * or {@code offset+len > str.length}
*/ */
public AbstractStringBuilder append(char str[], int offset, int len) { public AbstractStringBuilder append(char str[], int offset, int len) {
if (len > 0) // let arraycopy report AIOOBE for len < 0 int end = offset + len;
checkRange(offset, end, str.length);
ensureCapacityInternal(count + len); ensureCapacityInternal(count + len);
System.arraycopy(str, offset, value, count, len); appendChars(str, offset, end);
count += len;
return this; return this;
} }
@ -592,20 +654,39 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* @return a reference to this object. * @return a reference to this object.
*/ */
public AbstractStringBuilder append(boolean b) { public AbstractStringBuilder append(boolean b) {
ensureCapacityInternal(count + (b ? 4 : 5));
int count = this.count;
byte[] val = this.value;
if (isLatin1()) {
if (b) { if (b) {
ensureCapacityInternal(count + 4); val[count++] = 't';
value[count++] = 't'; val[count++] = 'r';
value[count++] = 'r'; val[count++] = 'u';
value[count++] = 'u'; val[count++] = 'e';
value[count++] = 'e';
} else { } else {
ensureCapacityInternal(count + 5); val[count++] = 'f';
value[count++] = 'f'; val[count++] = 'a';
value[count++] = 'a'; val[count++] = 'l';
value[count++] = 'l'; val[count++] = 's';
value[count++] = 's'; val[count++] = 'e';
value[count++] = 'e';
} }
} else {
if (b) {
checkOffset(count + 4, val.length >> 1);
StringUTF16.putChar(val, count++, 't');
StringUTF16.putChar(val, count++, 'r');
StringUTF16.putChar(val, count++, 'u');
StringUTF16.putChar(val, count++, 'e');
} else {
checkOffset(count + 5, val.length >> 1);
StringUTF16.putChar(val, count++, 'f');
StringUTF16.putChar(val, count++, 'a');
StringUTF16.putChar(val, count++, 'l');
StringUTF16.putChar(val, count++, 's');
StringUTF16.putChar(val, count++, 'e');
}
}
this.count = count;
return this; return this;
} }
@ -627,7 +708,14 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
@Override @Override
public AbstractStringBuilder append(char c) { public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1); 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; return this;
} }
@ -652,7 +740,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
: Integer.stringSize(i); : Integer.stringSize(i);
int spaceNeeded = count + appendedLength; int spaceNeeded = count + appendedLength;
ensureCapacityInternal(spaceNeeded); ensureCapacityInternal(spaceNeeded);
if (isLatin1()) {
Integer.getChars(i, spaceNeeded, value); Integer.getChars(i, spaceNeeded, value);
} else {
byte[] val = this.value;
checkOffset(spaceNeeded, val.length >> 1);
Integer.getCharsUTF16(i, spaceNeeded, val);
}
count = spaceNeeded; count = spaceNeeded;
return this; return this;
} }
@ -678,7 +772,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
: Long.stringSize(l); : Long.stringSize(l);
int spaceNeeded = count + appendedLength; int spaceNeeded = count + appendedLength;
ensureCapacityInternal(spaceNeeded); ensureCapacityInternal(spaceNeeded);
if (isLatin1()) {
Long.getChars(l, spaceNeeded, value); Long.getChars(l, spaceNeeded, value);
} else {
byte[] val = this.value;
checkOffset(spaceNeeded, val.length >> 1);
Long.getCharsUTF16(l, spaceNeeded, val);
}
count = spaceNeeded; count = spaceNeeded;
return this; return this;
} }
@ -732,15 +832,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* greater than {@code end}. * greater than {@code end}.
*/ */
public AbstractStringBuilder delete(int start, int end) { public AbstractStringBuilder delete(int start, int end) {
if (start < 0) if (end > count) {
throw new StringIndexOutOfBoundsException(start);
if (end > count)
end = count; end = count;
if (start > end) }
throw new StringIndexOutOfBoundsException(); checkRangeSIOOBE(start, end, count);
int len = end - start; int len = end - start;
if (len > 0) { if (len > 0) {
System.arraycopy(value, start+len, value, start, count-end); shift(end, -len);
count -= len; count -= len;
} }
return this; return this;
@ -766,20 +864,10 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* {@code codePoint} isn't a valid Unicode code point * {@code codePoint} isn't a valid Unicode code point
*/ */
public AbstractStringBuilder appendCodePoint(int codePoint) { public AbstractStringBuilder appendCodePoint(int codePoint) {
final int count = this.count;
if (Character.isBmpCodePoint(codePoint)) { if (Character.isBmpCodePoint(codePoint)) {
ensureCapacityInternal(count + 1); return append((char)codePoint);
value[count] = (char) codePoint;
this.count = count + 1;
} else if (Character.isValidCodePoint(codePoint)) {
ensureCapacityInternal(count + 2);
Character.toSurrogates(codePoint, value, count);
this.count = count + 2;
} else {
throw new IllegalArgumentException();
} }
return this; return append(Character.toChars(codePoint));
} }
/** /**
@ -800,9 +888,8 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* {@code length()}. * {@code length()}.
*/ */
public AbstractStringBuilder deleteCharAt(int index) { public AbstractStringBuilder deleteCharAt(int index) {
if ((index < 0) || (index >= count)) checkIndex(index, count);
throw new StringIndexOutOfBoundsException(index); shift(index + 1, -1);
System.arraycopy(value, index+1, value, index, count-index-1);
count--; count--;
return this; return this;
} }
@ -827,22 +914,16 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* greater than {@code end}. * greater than {@code end}.
*/ */
public AbstractStringBuilder replace(int start, int end, String str) { public AbstractStringBuilder replace(int start, int end, String str) {
if (start < 0) if (end > count) {
throw new StringIndexOutOfBoundsException(start);
if (start > count)
throw new StringIndexOutOfBoundsException("start > length()");
if (start > end)
throw new StringIndexOutOfBoundsException("start > end");
if (end > count)
end = count; end = count;
}
checkRangeSIOOBE(start, end, count);
int len = str.length(); int len = str.length();
int newCount = count + len - (end - start); int newCount = count + len - (end - start);
ensureCapacityInternal(newCount); ensureCapacityInternal(newCount);
shift(end, newCount - count);
System.arraycopy(value, end, value, start + len, count - end);
str.getChars(value, start);
count = newCount; count = newCount;
putStringAt(start, str);
return this; return this;
} }
@ -907,13 +988,16 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* greater than {@code end}. * greater than {@code end}.
*/ */
public String substring(int start, int end) { public String substring(int start, int end) {
if (start < 0) checkRangeSIOOBE(start, end, count);
throw new StringIndexOutOfBoundsException(start); if (isLatin1()) {
if (end > count) return StringLatin1.newString(value, start, end - start);
throw new StringIndexOutOfBoundsException(end); }
if (start > end) return StringUTF16.newStringSB(value, start, end - start);
throw new StringIndexOutOfBoundsException(end - start); }
return new String(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 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
public AbstractStringBuilder insert(int index, char[] str, int offset, public AbstractStringBuilder insert(int index, char[] str, int offset,
int len) int len)
{ {
if ((index < 0) || (index > length())) checkOffset(index, count);
throw new StringIndexOutOfBoundsException(index); checkRangeSIOOBE(offset, offset + len, str.length);
if ((offset < 0) || (len < 0) || (offset > str.length - len))
throw new StringIndexOutOfBoundsException(
"offset " + offset + ", len " + len + ", str.length "
+ str.length);
ensureCapacityInternal(count + len); ensureCapacityInternal(count + len);
System.arraycopy(value, index, value, index + len, count - index); shift(index, len);
System.arraycopy(str, offset, value, index, len);
count += len; count += len;
putCharsAt(index, str, offset, offset + len);
return this; return this;
} }
@ -1008,15 +1088,15 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* @throws StringIndexOutOfBoundsException if the offset is invalid. * @throws StringIndexOutOfBoundsException if the offset is invalid.
*/ */
public AbstractStringBuilder insert(int offset, String str) { public AbstractStringBuilder insert(int offset, String str) {
if ((offset < 0) || (offset > length())) checkOffset(offset, count);
throw new StringIndexOutOfBoundsException(offset); if (str == null) {
if (str == null)
str = "null"; str = "null";
}
int len = str.length(); int len = str.length();
ensureCapacityInternal(count + len); ensureCapacityInternal(count + len);
System.arraycopy(value, offset, value, offset + len, count - offset); shift(offset, len);
str.getChars(value, offset);
count += len; count += len;
putStringAt(offset, str);
return this; return this;
} }
@ -1045,13 +1125,12 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* @throws StringIndexOutOfBoundsException if the offset is invalid. * @throws StringIndexOutOfBoundsException if the offset is invalid.
*/ */
public AbstractStringBuilder insert(int offset, char[] str) { public AbstractStringBuilder insert(int offset, char[] str) {
if ((offset < 0) || (offset > length())) checkOffset(offset, count);
throw new StringIndexOutOfBoundsException(offset);
int len = str.length; int len = str.length;
ensureCapacityInternal(count + len); ensureCapacityInternal(count + len);
System.arraycopy(value, offset, value, offset + len, count - offset); shift(offset, len);
System.arraycopy(str, 0, value, offset, len);
count += len; count += len;
putCharsAt(offset, str, 0, len);
return this; return this;
} }
@ -1077,10 +1156,12 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* @throws IndexOutOfBoundsException if the offset is invalid. * @throws IndexOutOfBoundsException if the offset is invalid.
*/ */
public AbstractStringBuilder insert(int dstOffset, CharSequence s) { public AbstractStringBuilder insert(int dstOffset, CharSequence s) {
if (s == null) if (s == null) {
s = "null"; s = "null";
if (s instanceof String) }
if (s instanceof String) {
return this.insert(dstOffset, (String)s); return this.insert(dstOffset, (String)s);
}
return this.insert(dstOffset, s, 0, s.length()); return this.insert(dstOffset, s, 0, s.length());
} }
@ -1129,22 +1210,18 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* {@code end} is greater than {@code s.length()} * {@code end} is greater than {@code s.length()}
*/ */
public AbstractStringBuilder insert(int dstOffset, CharSequence s, public AbstractStringBuilder insert(int dstOffset, CharSequence s,
int start, int end) { int start, int end)
if (s == null) {
if (s == null) {
s = "null"; s = "null";
if ((dstOffset < 0) || (dstOffset > this.length())) }
throw new IndexOutOfBoundsException("dstOffset "+dstOffset); checkOffset(dstOffset, count);
if ((start < 0) || (end < 0) || (start > end) || (end > s.length())) checkRange(start, end, s.length());
throw new IndexOutOfBoundsException(
"start " + start + ", end " + end + ", s.length() "
+ s.length());
int len = end - start; int len = end - start;
ensureCapacityInternal(count + len); ensureCapacityInternal(count + len);
System.arraycopy(value, dstOffset, value, dstOffset + len, shift(dstOffset, len);
count - dstOffset);
for (int i=start; i<end; i++)
value[dstOffset++] = s.charAt(i);
count += len; count += len;
putCharsAt(dstOffset, s, start, end);
return this; return this;
} }
@ -1191,10 +1268,18 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* @throws IndexOutOfBoundsException if the offset is invalid. * @throws IndexOutOfBoundsException if the offset is invalid.
*/ */
public AbstractStringBuilder insert(int offset, char c) { public AbstractStringBuilder insert(int offset, char c) {
checkOffset(offset, count);
ensureCapacityInternal(count + 1); ensureCapacityInternal(count + 1);
System.arraycopy(value, offset, value, offset + 1, count - offset); shift(offset, 1);
value[offset] = c;
count += 1; count += 1;
if (isLatin1() && StringLatin1.canEncode(c)) {
value[offset] = (byte)c;
} else {
if (isLatin1()) {
inflate();
}
StringUTF16.putCharSB(value, offset, c);
}
return this; return this;
} }
@ -1326,7 +1411,7 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* or {@code -1} if there is no such occurrence. * or {@code -1} if there is no such occurrence.
*/ */
public int indexOf(String str, int fromIndex) { 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 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* or {@code -1} if there is no such occurrence. * or {@code -1} if there is no such occurrence.
*/ */
public int lastIndexOf(String str, int fromIndex) { 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 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
* @return a reference to this object. * @return a reference to this object.
*/ */
public AbstractStringBuilder reverse() { public AbstractStringBuilder reverse() {
boolean hasSurrogates = false; byte[] val = this.value;
int count = this.count;
int coder = this.coder;
int n = count - 1; int n = count - 1;
if (COMPACT_STRINGS && coder == LATIN1) {
for (int j = (n-1) >> 1; j >= 0; j--) { for (int j = (n-1) >> 1; j >= 0; j--) {
int k = n - j; int k = n - j;
char cj = value[j]; byte cj = val[j];
char ck = value[k]; val[j] = val[k];
value[j] = ck; val[k] = cj;
value[k] = cj; }
} else {
checkOffset(count, val.length >> 1);
boolean hasSurrogates = false;
for (int j = (n-1) >> 1; j >= 0; j--) {
int k = n - j;
char cj = StringUTF16.getChar(val, j);
char ck = StringUTF16.getChar(val, k);
StringUTF16.putChar(val, j, ck);
StringUTF16.putChar(val, k, cj);
if (Character.isSurrogate(cj) || if (Character.isSurrogate(cj) ||
Character.isSurrogate(ck)) { Character.isSurrogate(ck)) {
hasSurrogates = true; hasSurrogates = true;
} }
} }
if (hasSurrogates) { if (hasSurrogates) {
reverseAllValidSurrogatePairs(); reverseAllValidSurrogatePairs(val, count);
}
} }
return this; return this;
} }
/** Outlined helper method for reverse() */ /** Outlined helper method for reverse() */
private void reverseAllValidSurrogatePairs() { private void reverseAllValidSurrogatePairs(byte[] val, int count) {
for (int i = 0; i < count - 1; i++) { for (int i = 0; i < count - 1; i++) {
char c2 = value[i]; char c2 = StringUTF16.getChar(val, i);
if (Character.isLowSurrogate(c2)) { if (Character.isLowSurrogate(c2)) {
char c1 = value[i + 1]; char c1 = StringUTF16.getChar(val, i + 1);
if (Character.isHighSurrogate(c1)) { if (Character.isHighSurrogate(c1)) {
value[i++] = c1; StringUTF16.putChar(val, i++, c1);
value[i] = c2; StringUTF16.putChar(val, i, c2);
} }
} }
} }
@ -1444,10 +1542,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
*/ */
@Override @Override
public IntStream chars() { 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 // Reuse String-based spliterator. This requires a supplier to
// capture the value and count when the terminal operation is executed // capture the value and count when the terminal operation is executed
return StreamSupport.intStream( 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, Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED,
false); false);
} }
@ -1458,10 +1559,13 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
*/ */
@Override @Override
public IntStream codePoints() { 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 // Reuse String-based spliterator. This requires a supplier to
// capture the value and count when the terminal operation is executed // capture the value and count when the terminal operation is executed
return StreamSupport.intStream( 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, Spliterator.ORDERED,
false); false);
} }
@ -1469,8 +1573,147 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
/** /**
* Needed by {@code String} for the contentEquals method. * Needed by {@code String} for the contentEquals method.
*/ */
final char[] getValue() { final byte[] getValue() {
return value; 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);
}
}
} }

View file

@ -29,6 +29,10 @@ import java.lang.annotation.Native;
import java.util.Objects; import java.util.Objects;
import jdk.internal.HotSpotIntrinsicCandidate; 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 * The {@code Integer} class wraps a value of the primitive type
* {@code int} in an object. An object of type {@code Integer} * {@code int} in an object. An object of type {@code Integer}
@ -138,7 +142,8 @@ public final class Integer extends Number implements Comparable<Integer> {
return toString(i); return toString(i);
} }
char buf[] = new char[33]; if (COMPACT_STRINGS) {
byte[] buf = new byte[33];
boolean negative = (i < 0); boolean negative = (i < 0);
int charPos = 32; int charPos = 32;
@ -147,16 +152,37 @@ public final class Integer extends Number implements Comparable<Integer> {
} }
while (i <= -radix) { while (i <= -radix) {
buf[charPos--] = digits[-(i % radix)]; buf[charPos--] = (byte)digits[-(i % radix)];
i = i / radix; i = i / radix;
} }
buf[charPos] = digits[-i]; buf[charPos] = (byte)digits[-i];
if (negative) { if (negative) {
buf[--charPos] = '-'; buf[--charPos] = '-';
} }
return new String(buf, charPos, (33 - 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) {
StringUTF16.putChar(buf, charPos--, digits[-(i % radix)]);
i = i / radix;
}
StringUTF16.putChar(buf, charPos, digits[-i]);
if (negative) {
StringUTF16.putChar(buf, --charPos, '-');
}
return StringUTF16.newString(buf, charPos, (33 - charPos));
} }
/** /**
@ -312,12 +338,16 @@ public final class Integer extends Number implements Comparable<Integer> {
// assert shift > 0 && shift <=5 : "Illegal shift value"; // assert shift > 0 && shift <=5 : "Illegal shift value";
int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val); int mag = Integer.SIZE - Integer.numberOfLeadingZeros(val);
int chars = Math.max(((mag + (shift - 1)) / shift), 1); int chars = Math.max(((mag + (shift - 1)) / shift), 1);
char[] buf = new char[chars];
if (COMPACT_STRINGS) {
byte[] buf = new byte[chars];
formatUnsignedInt(val, shift, buf, 0, chars); formatUnsignedInt(val, shift, buf, 0, chars);
return new String(buf, LATIN1);
// Use special constructor which takes over "buf". } else {
return new String(buf, true); byte[] buf = new byte[chars * 2];
formatUnsignedIntUTF16(val, shift, buf, 0, chars);
return new String(buf, UTF16);
}
} }
/** /**
@ -344,6 +374,28 @@ public final class Integer extends Number implements Comparable<Integer> {
} while (charPos > offset); } 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 = { static final char [] DigitTens = {
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0',
'1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1',
@ -401,9 +453,15 @@ public final class Integer extends Number implements Comparable<Integer> {
if (i == Integer.MIN_VALUE) if (i == Integer.MIN_VALUE)
return "-2147483648"; return "-2147483648";
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size]; if (COMPACT_STRINGS) {
byte[] buf = new byte[size];
getChars(i, size, buf); getChars(i, size, buf);
return new String(buf, true); 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 @@ public final class Integer extends Number implements Comparable<Integer> {
* *
* Will fail if i == Integer.MIN_VALUE * 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 q, r;
int charPos = index; int charPos = index;
char sign = 0; char sign = 0;
@ -449,8 +507,8 @@ public final class Integer extends Number implements Comparable<Integer> {
// really: r = i - (q * 100); // really: r = i - (q * 100);
r = i - ((q << 6) + (q << 5) + (q << 2)); r = i - ((q << 6) + (q << 5) + (q << 2));
i = q; i = q;
buf [--charPos] = DigitOnes[r]; buf [--charPos] = (byte)DigitOnes[r];
buf [--charPos] = DigitTens[r]; buf [--charPos] = (byte)DigitTens[r];
} }
// Fall thru to fast mode for smaller numbers // Fall thru to fast mode for smaller numbers
@ -458,12 +516,46 @@ public final class Integer extends Number implements Comparable<Integer> {
for (;;) { for (;;) {
q = (i * 52429) >>> (16+3); q = (i * 52429) >>> (16+3);
r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ... r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ...
buf [--charPos] = digits [r]; buf [--charPos] = (byte)digits [r];
i = q; i = q;
if (i == 0) break; if (i == 0) break;
} }
if (sign != 0) { 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);
} }
} }

View file

@ -30,6 +30,9 @@ import java.math.*;
import java.util.Objects; import java.util.Objects;
import jdk.internal.HotSpotIntrinsicCandidate; 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 * The {@code Long} class wraps a value of the primitive type {@code
@ -124,7 +127,9 @@ public final class Long extends Number implements Comparable<Long> {
radix = 10; radix = 10;
if (radix == 10) if (radix == 10)
return toString(i); return toString(i);
char[] buf = new char[65];
if (COMPACT_STRINGS) {
byte[] buf = new byte[65];
int charPos = 64; int charPos = 64;
boolean negative = (i < 0); boolean negative = (i < 0);
@ -133,16 +138,35 @@ public final class Long extends Number implements Comparable<Long> {
} }
while (i <= -radix) { while (i <= -radix) {
buf[charPos--] = Integer.digits[(int)(-(i % radix))]; buf[charPos--] = (byte)Integer.digits[(int)(-(i % radix))];
i = i / radix; i = i / radix;
} }
buf[charPos] = Integer.digits[(int)(-i)]; buf[charPos] = (byte)Integer.digits[(int)(-i)];
if (negative) { if (negative) {
buf[--charPos] = '-'; buf[--charPos] = '-';
} }
return StringLatin1.newString(buf, charPos, (65 - charPos));
}
return toStringUTF16(i, radix);
}
return new String(buf, charPos, (65 - charPos)); 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) {
StringUTF16.putChar(buf, charPos--, Integer.digits[(int)(-(i % radix))]);
i = i / radix;
}
StringUTF16.putChar(buf, charPos, Integer.digits[(int)(-i)]);
if (negative) {
StringUTF16.putChar(buf, --charPos, '-');
}
return StringUTF16.newString(buf, charPos, (65 - charPos));
} }
/** /**
@ -355,10 +379,16 @@ public final class Long extends Number implements Comparable<Long> {
// assert shift > 0 && shift <=5 : "Illegal shift value"; // assert shift > 0 && shift <=5 : "Illegal shift value";
int mag = Long.SIZE - Long.numberOfLeadingZeros(val); int mag = Long.SIZE - Long.numberOfLeadingZeros(val);
int chars = Math.max(((mag + (shift - 1)) / shift), 1); int chars = Math.max(((mag + (shift - 1)) / shift), 1);
char[] buf = new char[chars];
formatUnsignedLong(val, shift, buf, 0, chars); if (COMPACT_STRINGS) {
return new String(buf, true); 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 @@ public final class Long extends Number implements Comparable<Long> {
} while (charPos > offset); } 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 * Returns a {@code String} object representing the specified
* {@code long}. The argument is converted to signed decimal * {@code long}. The argument is converted to signed decimal
@ -399,9 +451,15 @@ public final class Long extends Number implements Comparable<Long> {
if (i == Long.MIN_VALUE) if (i == Long.MIN_VALUE)
return "-9223372036854775808"; return "-9223372036854775808";
int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i);
char[] buf = new char[size]; if (COMPACT_STRINGS) {
byte[] buf = new byte[size];
getChars(i, size, buf); getChars(i, size, buf);
return new String(buf, true); 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 @@ public final class Long extends Number implements Comparable<Long> {
* *
* Will fail if i == Long.MIN_VALUE * 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; long q;
int r; int r;
int charPos = index; int charPos = index;
@ -448,8 +506,8 @@ public final class Long extends Number implements Comparable<Long> {
// really: r = i - (q * 100); // really: r = i - (q * 100);
r = (int)(i - ((q << 6) + (q << 5) + (q << 2))); r = (int)(i - ((q << 6) + (q << 5) + (q << 2)));
i = q; i = q;
buf[--charPos] = Integer.DigitOnes[r]; buf[--charPos] = (byte)Integer.DigitOnes[r];
buf[--charPos] = Integer.DigitTens[r]; buf[--charPos] = (byte)Integer.DigitTens[r];
} }
// Get 2 digits/iteration using ints // Get 2 digits/iteration using ints
@ -460,8 +518,8 @@ public final class Long extends Number implements Comparable<Long> {
// really: r = i2 - (q * 100); // really: r = i2 - (q * 100);
r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2)); r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2));
i2 = q2; i2 = q2;
buf[--charPos] = Integer.DigitOnes[r]; buf[--charPos] = (byte)Integer.DigitOnes[r];
buf[--charPos] = Integer.DigitTens[r]; buf[--charPos] = (byte)Integer.DigitTens[r];
} }
// Fall thru to fast mode for smaller numbers // Fall thru to fast mode for smaller numbers
@ -469,12 +527,59 @@ public final class Long extends Number implements Comparable<Long> {
for (;;) { for (;;) {
q2 = (i2 * 52429) >>> (16+3); q2 = (i2 * 52429) >>> (16+3);
r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ... r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ...
buf[--charPos] = Integer.digits[r]; buf[--charPos] = (byte)Integer.digits[r];
i2 = q2; i2 = q2;
if (i2 == 0) break; if (i2 == 0) break;
} }
if (sign != 0) { 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);
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -104,7 +104,7 @@ import jdk.internal.HotSpotIntrinsicCandidate;
* A cache of the last value returned by toString. Cleared * A cache of the last value returned by toString. Cleared
* whenever the StringBuffer is modified. * whenever the StringBuffer is modified.
*/ */
private transient char[] toStringCache; private transient String toStringCache;
/** use serialVersionUID from JDK 1.0.2 for interoperability */ /** use serialVersionUID from JDK 1.0.2 for interoperability */
static final long serialVersionUID = 3388685877147921107L; static final long serialVersionUID = 3388685877147921107L;
@ -169,15 +169,13 @@ import jdk.internal.HotSpotIntrinsicCandidate;
@Override @Override
public synchronized int capacity() { public synchronized int capacity() {
return value.length; return super.capacity();
} }
@Override @Override
public synchronized void ensureCapacity(int minimumCapacity) { public synchronized void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > value.length) { super.ensureCapacity(minimumCapacity);
expandCapacity(minimumCapacity);
}
} }
/** /**
@ -204,9 +202,7 @@ import jdk.internal.HotSpotIntrinsicCandidate;
*/ */
@Override @Override
public synchronized char charAt(int index) { public synchronized char charAt(int index) {
if ((index < 0) || (index >= count)) return super.charAt(index);
throw new StringIndexOutOfBoundsException(index);
return value[index];
} }
/** /**
@ -261,10 +257,8 @@ import jdk.internal.HotSpotIntrinsicCandidate;
*/ */
@Override @Override
public synchronized void setCharAt(int index, char ch) { public synchronized void setCharAt(int index, char ch) {
if ((index < 0) || (index >= count))
throw new StringIndexOutOfBoundsException(index);
toStringCache = null; toStringCache = null;
value[index] = ch; super.setCharAt(index, ch);
} }
@Override @Override
@ -680,9 +674,11 @@ import jdk.internal.HotSpotIntrinsicCandidate;
@HotSpotIntrinsicCandidate @HotSpotIntrinsicCandidate
public synchronized String toString() { public synchronized String toString() {
if (toStringCache == null) { 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 @@ import jdk.internal.HotSpotIntrinsicCandidate;
private synchronized void writeObject(java.io.ObjectOutputStream s) private synchronized void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException { throws java.io.IOException {
java.io.ObjectOutputStream.PutField fields = s.putFields(); 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("count", count);
fields.put("shared", false); fields.put("shared", false);
s.writeFields(); s.writeFields();
@ -723,7 +725,12 @@ import jdk.internal.HotSpotIntrinsicCandidate;
private void readObject(java.io.ObjectInputStream s) private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException { throws java.io.IOException, ClassNotFoundException {
java.io.ObjectInputStream.GetField fields = s.readFields(); 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); count = fields.get("count", 0);
} }
protected synchronized void getBytes(byte dst[], int dstBegin, byte coder) {
super.getBytes(dst, dstBegin, coder);
}
} }

View file

@ -412,7 +412,8 @@ public final class StringBuilder
@HotSpotIntrinsicCandidate @HotSpotIntrinsicCandidate
public String toString() { public String toString() {
// Create a copy, don't share the array // 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 @@ public final class StringBuilder
throws java.io.IOException { throws java.io.IOException {
s.defaultWriteObject(); s.defaultWriteObject();
s.writeInt(count); 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 @@ public final class StringBuilder
throws java.io.IOException, ClassNotFoundException { throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject(); s.defaultReadObject();
count = s.readInt(); count = s.readInt();
value = (char[]) s.readObject(); char[] val = (char[]) s.readObject();
initBytes(val, 0, val.length);
} }
} }

View file

@ -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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -38,11 +38,19 @@ import java.nio.charset.CodingErrorAction;
import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException; import java.nio.charset.UnsupportedCharsetException;
import java.util.Arrays; import java.util.Arrays;
import jdk.internal.HotSpotIntrinsicCandidate;
import sun.misc.MessageUtils; import sun.misc.MessageUtils;
import sun.nio.cs.HistoricallyNamedCharset; import sun.nio.cs.HistoricallyNamedCharset;
import sun.nio.cs.ArrayDecoder; import sun.nio.cs.ArrayDecoder;
import sun.nio.cs.ArrayEncoder; 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. * Utility class for string encoding and decoding.
*/ */
@ -72,23 +80,13 @@ class StringCoding {
// Trim the given byte array to the given length // 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)) if (len == ba.length && (isTrusted || System.getSecurityManager() == null))
return ba; return ba;
else else
return Arrays.copyOf(ba, len); 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) { private static int scale(int len, float expansionFactor) {
// We need to perform double, not float, arithmetic; otherwise // We need to perform double, not float, arithmetic; otherwise
// we lose low order bits when len is larger than 2**24. // we lose low order bits when len is larger than 2**24.
@ -117,21 +115,64 @@ class StringCoding {
} }
} }
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 -- // -- Decoding --
private static class StringDecoder { static class StringDecoder {
private final String requestedCharsetName; private final String requestedCharsetName;
private final Charset cs; private final Charset cs;
private final boolean isASCIICompatible;
private final CharsetDecoder cd; 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.requestedCharsetName = rcn;
this.cs = cs; this.cs = cs;
this.cd = cs.newDecoder() this.cd = cs.newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE) .onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(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() { String charsetName() {
@ -144,15 +185,25 @@ class StringCoding {
return requestedCharsetName; 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()); int en = scale(len, cd.maxCharsPerByte());
char[] ca = new char[en]; char[] ca = new char[en];
if (len == 0)
return ca;
if (cd instanceof ArrayDecoder) { if (cd instanceof ArrayDecoder) {
int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca); int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca);
return safeTrim(ca, clen, cs, isTrusted); return result.with(ca, 0, clen);
} else { }
cd.reset(); cd.reset();
ByteBuffer bb = ByteBuffer.wrap(ba, off, len); ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
CharBuffer cb = CharBuffer.wrap(ca); CharBuffer cb = CharBuffer.wrap(ca);
@ -168,12 +219,24 @@ class StringCoding {
// so this shouldn't happen // so this shouldn't happen
throw new Error(x); throw new Error(x);
} }
return safeTrim(ca, cb.position(), cs, isTrusted); 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 {
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 throws UnsupportedEncodingException
{ {
StringDecoder sd = deref(decoder); StringDecoder sd = deref(decoder);
@ -183,8 +246,15 @@ class StringCoding {
sd = null; sd = null;
try { try {
Charset cs = lookupCharset(csn); Charset cs = lookupCharset(csn);
if (cs != null) 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); sd = new StringDecoder(cs, csn);
}
}
} catch (IllegalCharsetNameException x) {} } catch (IllegalCharsetNameException x) {}
if (sd == null) if (sd == null)
throw new UnsupportedEncodingException(csn); throw new UnsupportedEncodingException(csn);
@ -193,7 +263,7 @@ class StringCoding {
return sd.decode(ba, off, len); 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 // (1)We never cache the "external" cs, the only benefit of creating
// an additional StringDe/Encoder object to wrap it is to share the // 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 // de/encode() method. These SD/E objects are short-lived, the young-gen
@ -210,25 +280,39 @@ class StringCoding {
// check (... && (isTrusted || SM == null || getClassLoader0())) in trim // check (... && (isTrusted || SM == null || getClassLoader0())) in trim
// but it then can be argued that the SM is null when the operation // but it then can be argued that the SM is null when the operation
// is started... // is started...
if (cs == UTF_8) {
return StringDecoderUTF8.decode(ba, off, len, new Result());
}
CharsetDecoder cd = cs.newDecoder(); 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()); int en = scale(len, cd.maxCharsPerByte());
char[] ca = new char[en]; if (len == 0) {
if (len == 0) return new Result().with();
return ca; }
boolean isTrusted = false; if (System.getSecurityManager() != null &&
if (System.getSecurityManager() != null) { cs.getClass().getClassLoader0() != null) {
if (!(isTrusted = (cs.getClass().getClassLoader0() == null))) {
ba = Arrays.copyOfRange(ba, off, off + len); ba = Arrays.copyOfRange(ba, off, off + len);
off = 0; off = 0;
} }
}
cd.onMalformedInput(CodingErrorAction.REPLACE) cd.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE)
.reset(); .reset();
char[] ca = new char[en];
if (cd instanceof ArrayDecoder) { if (cd instanceof ArrayDecoder) {
int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca); int clen = ((ArrayDecoder)cd).decode(ba, off, len, ca);
return safeTrim(ca, clen, cs, isTrusted); return new Result().with(ca, 0, clen);
} else { }
ByteBuffer bb = ByteBuffer.wrap(ba, off, len); ByteBuffer bb = ByteBuffer.wrap(ba, off, len);
CharBuffer cb = CharBuffer.wrap(ca); CharBuffer cb = CharBuffer.wrap(ca);
try { try {
@ -243,11 +327,10 @@ class StringCoding {
// so this shouldn't happen // so this shouldn't happen
throw new Error(x); throw new Error(x);
} }
return safeTrim(ca, cb.position(), cs, isTrusted); 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(); String csn = Charset.defaultCharset().name();
try { try {
// use charset name decode() variant which provides caching. // use charset name decode() variant which provides caching.
@ -273,6 +356,7 @@ class StringCoding {
private static class StringEncoder { private static class StringEncoder {
private Charset cs; private Charset cs;
private CharsetEncoder ce; private CharsetEncoder ce;
private final boolean isASCIICompatible;
private final String requestedCharsetName; private final String requestedCharsetName;
private final boolean isTrusted; private final boolean isTrusted;
@ -283,6 +367,8 @@ class StringCoding {
.onMalformedInput(CodingErrorAction.REPLACE) .onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE); .onUnmappableCharacter(CodingErrorAction.REPLACE);
this.isTrusted = (cs.getClass().getClassLoader0() == null); this.isTrusted = (cs.getClass().getClassLoader0() == null);
this.isASCIICompatible = (ce instanceof ArrayEncoder) &&
((ArrayEncoder)ce).isASCIICompatible();
} }
String charsetName() { String charsetName() {
@ -295,18 +381,33 @@ class StringCoding {
return requestedCharsetName; 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()); int en = scale(len, ce.maxBytesPerChar());
byte[] ba = new byte[en]; byte[] ba = new byte[en];
if (len == 0) if (len == 0) {
return ba; return ba;
}
if (ce instanceof ArrayEncoder) { if (ce instanceof ArrayEncoder) {
int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba); if (!isTrusted) {
return safeTrim(ba, blen, cs, isTrusted); val = Arrays.copyOf(val, val.length);
} else { }
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(); ce.reset();
ByteBuffer bb = ByteBuffer.wrap(ba); ByteBuffer bb = ByteBuffer.wrap(ba);
CharBuffer cb = CharBuffer.wrap(ca, off, len); CharBuffer cb = CharBuffer.wrap(ca, 0, len);
try { try {
CoderResult cr = ce.encode(cb, bb, true); CoderResult cr = ce.encode(cb, bb, true);
if (!cr.isUnderflow()) if (!cr.isUnderflow())
@ -319,12 +420,147 @@ class StringCoding {
// so this shouldn't happen // so this shouldn't happen
throw new Error(x); throw new Error(x);
} }
return safeTrim(ba, bb.position(), cs, isTrusted); 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 throws UnsupportedEncodingException
{ {
StringEncoder se = deref(encoder); StringEncoder se = deref(encoder);
@ -334,38 +570,65 @@ class StringCoding {
se = null; se = null;
try { try {
Charset cs = lookupCharset(csn); 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); se = new StringEncoder(cs, csn);
}
} catch (IllegalCharsetNameException x) {} } catch (IllegalCharsetNameException x) {}
if (se == null) if (se == null) {
throw new UnsupportedEncodingException (csn); throw new UnsupportedEncodingException (csn);
}
set(encoder, se); 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(); 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()); int en = scale(len, ce.maxBytesPerChar());
byte[] ba = new byte[en]; byte[] ba = new byte[en];
if (len == 0) if (len == 0) {
return ba; 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) ce.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE) .onUnmappableCharacter(CodingErrorAction.REPLACE)
.reset(); .reset();
if (ce instanceof ArrayEncoder) { if (ce instanceof ArrayEncoder) {
int blen = ((ArrayEncoder)ce).encode(ca, off, len, ba); if (!isTrusted) {
return safeTrim(ba, blen, cs, isTrusted); val = Arrays.copyOf(val, val.length);
} else { }
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); ByteBuffer bb = ByteBuffer.wrap(ba);
CharBuffer cb = CharBuffer.wrap(ca, off, len); CharBuffer cb = CharBuffer.wrap(ca, 0, len);
try { try {
CoderResult cr = ce.encode(cb, bb, true); CoderResult cr = ce.encode(cb, bb, true);
if (!cr.isUnderflow()) if (!cr.isUnderflow())
@ -376,20 +639,19 @@ class StringCoding {
} catch (CharacterCodingException x) { } catch (CharacterCodingException x) {
throw new Error(x); throw new Error(x);
} }
return safeTrim(ba, bb.position(), cs, isTrusted); 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(); String csn = Charset.defaultCharset().name();
try { try {
// use charset name encode() variant which provides caching. // use charset name encode() variant which provides caching.
return encode(csn, ca, off, len); return encode(csn, coder, val);
} catch (UnsupportedEncodingException x) { } catch (UnsupportedEncodingException x) {
warnUnsupportedCharset(csn); warnUnsupportedCharset(csn);
} }
try { try {
return encode("ISO-8859-1", ca, off, len); return encode("ISO-8859-1", coder, val);
} catch (UnsupportedEncodingException x) { } catch (UnsupportedEncodingException x) {
// If this code is hit during VM initialization, MessageUtils is // If this code is hit during VM initialization, MessageUtils is
// the only way we will be able to get any kind of error message. // the only way we will be able to get any kind of error message.

View file

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

View file

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

View file

@ -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;
}

View file

@ -2685,6 +2685,7 @@ public class Arrays {
* @param a2 the other array to be tested for equality * @param a2 the other array to be tested for equality
* @return {@code true} if the two arrays are equal * @return {@code true} if the two arrays are equal
*/ */
@HotSpotIntrinsicCandidate
public static boolean equals(byte[] a, byte[] a2) { public static boolean equals(byte[] a, byte[] a2) {
if (a==a2) if (a==a2)
return true; return true;

View file

@ -32,4 +32,8 @@ package sun.nio.cs;
public interface ArrayDecoder { public interface ArrayDecoder {
int decode(byte[] src, int off, int len, char[] dst); int decode(byte[] src, int off, int len, char[] dst);
default boolean isASCIICompatible() {
return false;
}
} }

View file

@ -26,10 +26,24 @@
package sun.nio.cs; 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. * unmappable input.
*/ */
public interface ArrayEncoder { public interface ArrayEncoder {
// is only used by j.u.zip.ZipCoder for utf8
int encode(char[] src, int off, int len, byte[] dst); 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;
}
} }

View file

@ -115,6 +115,7 @@ public class DoubleByte {
final char[] b2cSB; final char[] b2cSB;
final int b2Min; final int b2Min;
final int b2Max; final int b2Max;
final boolean isASCIICompatible;
// for SimpleEUC override // for SimpleEUC override
protected CoderResult crMalformedOrUnderFlow(int b) { protected CoderResult crMalformedOrUnderFlow(int b) {
@ -132,16 +133,23 @@ public class DoubleByte {
public Decoder(Charset cs, float avgcpb, float maxcpb, public Decoder(Charset cs, float avgcpb, float maxcpb,
char[][] b2c, char[] b2cSB, char[][] b2c, char[] b2cSB,
int b2Min, int b2Max) { int b2Min, int b2Max,
boolean isASCIICompatible) {
super(cs, avgcpb, maxcpb); super(cs, avgcpb, maxcpb);
this.b2c = b2c; this.b2c = b2c;
this.b2cSB = b2cSB; this.b2cSB = b2cSB;
this.b2Min = b2Min; this.b2Min = b2Min;
this.b2Max = b2Max; 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) { 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) { protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
@ -215,6 +223,7 @@ public class DoubleByte {
return decodeBufferLoop(src, dst); return decodeBufferLoop(src, dst);
} }
@Override
public int decode(byte[] src, int sp, int len, char[] dst) { public int decode(byte[] src, int sp, int len, char[] dst) {
int dp = 0; int dp = 0;
int sl = sp + len; int sl = sp + len;
@ -243,6 +252,11 @@ public class DoubleByte {
return dp; return dp;
} }
@Override
public boolean isASCIICompatible() {
return isASCIICompatible;
}
public void implReset() { public void implReset() {
super.implReset(); super.implReset();
} }
@ -273,9 +287,15 @@ public class DoubleByte {
private static final int SI = 0x0f; private static final int SI = 0x0f;
private int currentState; private int currentState;
public Decoder_EBCDIC(Charset cs,
char[][] b2c, char[] b2cSB, int b2Min, int b2Max,
boolean isASCIICompatible) {
super(cs, b2c, b2cSB, b2Min, b2Max, isASCIICompatible);
}
public Decoder_EBCDIC(Charset cs, public Decoder_EBCDIC(Charset cs,
char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
super(cs, b2c, b2cSB, b2Min, b2Max); super(cs, b2c, b2cSB, b2Min, b2Max, false);
} }
public void implReset() { public void implReset() {
@ -403,6 +423,7 @@ public class DoubleByte {
} }
} }
@Override
public int decode(byte[] src, int sp, int len, char[] dst) { public int decode(byte[] src, int sp, int len, char[] dst) {
int dp = 0; int dp = 0;
int sl = sp + len; int sl = sp + len;
@ -451,8 +472,13 @@ public class DoubleByte {
b2cSB_UNMAPPABLE = new char[0x100]; b2cSB_UNMAPPABLE = new char[0x100];
Arrays.fill(b2cSB_UNMAPPABLE, UNMAPPABLE_DECODING); 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) { 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 @@ public class DoubleByte {
private final int SS3 = 0x8F; private final int SS3 = 0x8F;
public Decoder_EUC_SIM(Charset cs, public Decoder_EUC_SIM(Charset cs,
char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { char[][] b2c, char[] b2cSB, int b2Min, int b2Max,
super(cs, b2c, b2cSB, b2Min, b2Max); boolean isASCIICompatible) {
super(cs, b2c, b2cSB, b2Min, b2Max, isASCIICompatible);
} }
// No support provided for G2/G3 for SimpleEUC // No support provided for G2/G3 for SimpleEUC
@ -481,6 +508,7 @@ public class DoubleByte {
return CoderResult.unmappableForLength(2); return CoderResult.unmappableForLength(2);
} }
@Override
public int decode(byte[] src, int sp, int len, char[] dst) { public int decode(byte[] src, int sp, int len, char[] dst) {
int dp = 0; int dp = 0;
int sl = sp + len; int sl = sp + len;
@ -515,17 +543,25 @@ public class DoubleByte {
private final char[] c2b; private final char[] c2b;
private final char[] c2bIndex; private final char[] c2bIndex;
protected Surrogate.Parser sgp; protected Surrogate.Parser sgp;
final boolean isASCIICompatible;
public Encoder(Charset cs, char[] c2b, char[] c2bIndex) { 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); super(cs, 2.0f, 2.0f);
this.c2b = c2b; this.c2b = c2b;
this.c2bIndex = c2bIndex; 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); super(cs, avg, max, repl);
this.c2b = c2b; this.c2b = c2b;
this.c2bIndex = c2bIndex; this.c2bIndex = c2bIndex;
this.isASCIICompatible = isASCIICompatible;
} }
public boolean canEncode(char c) { public boolean canEncode(char c) {
@ -624,6 +660,7 @@ public class DoubleByte {
repl = newReplacement; repl = newReplacement;
} }
@Override
public int encode(char[] src, int sp, int len, byte[] dst) { public int encode(char[] src, int sp, int len, byte[] dst) {
int dp = 0; int dp = 0;
int sl = sp + len; int sl = sp + len;
@ -647,11 +684,69 @@ public class DoubleByte {
} else { // SingleByte } else { // SingleByte
dst[dp++] = (byte)bb; 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; 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) { public int encodeChar(char ch) {
return c2b[c2bIndex[ch >> 8] + (ch & 0xff)]; return c2b[c2bIndex[ch >> 8] + (ch & 0xff)];
} }
@ -741,9 +836,11 @@ public class DoubleByte {
} }
public static class Encoder_DBCSONLY extends Encoder { public static class Encoder_DBCSONLY extends Encoder {
public Encoder_DBCSONLY(Charset cs, byte[] repl, public Encoder_DBCSONLY(Charset cs, byte[] repl,
char[] c2b, char[] c2bIndex) { char[] c2b, char[] c2bIndex,
super(cs, 2.0f, 2.0f, repl, c2b, c2bIndex); boolean isASCIICompatible) {
super(cs, 2.0f, 2.0f, repl, c2b, c2bIndex, isASCIICompatible);
} }
public int encodeChar(char ch) { public int encodeChar(char ch) {
@ -754,8 +851,6 @@ public class DoubleByte {
} }
} }
public static class Encoder_EBCDIC extends Encoder { public static class Encoder_EBCDIC extends Encoder {
static final int SBCS = 0; static final int SBCS = 0;
static final int DBCS = 1; static final int DBCS = 1;
@ -764,8 +859,9 @@ public class DoubleByte {
protected int currentState = SBCS; protected int currentState = SBCS;
public Encoder_EBCDIC(Charset cs, char[] c2b, char[] c2bIndex) { public Encoder_EBCDIC(Charset cs, char[] c2b, char[] c2bIndex,
super(cs, 4.0f, 5.0f, new byte[] {(byte)0x6f}, c2b, c2bIndex); boolean isASCIICompatible) {
super(cs, 4.0f, 5.0f, new byte[] {(byte)0x6f}, c2b, c2bIndex, isASCIICompatible);
} }
protected void implReset() { protected void implReset() {
@ -878,6 +974,7 @@ public class DoubleByte {
} }
} }
@Override
public int encode(char[] src, int sp, int len, byte[] dst) { public int encode(char[] src, int sp, int len, byte[] dst) {
int dp = 0; int dp = 0;
int sl = sp + len; int sl = sp + len;
@ -917,12 +1014,88 @@ public class DoubleByte {
} }
return dp; 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 // EUC_SIMPLE
public static class Encoder_EUC_SIM extends Encoder { public static class Encoder_EUC_SIM extends Encoder {
public Encoder_EUC_SIM(Charset cs, char[] c2b, char[] c2bIndex) { public Encoder_EUC_SIM(Charset cs, char[] c2b, char[] c2bIndex,
super(cs, c2b, c2bIndex); boolean isASCIICompatible) {
super(cs, c2b, c2bIndex, isASCIICompatible);
} }
} }

View file

@ -53,7 +53,7 @@ public class HKSCS {
// super(cs, 0.5f, 1.0f); // super(cs, 0.5f, 1.0f);
// need to extends DoubleByte.Decoder so the // need to extends DoubleByte.Decoder so the
// sun.io can use it. this implementation // 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.big5Dec = big5Dec;
this.b2cBmp = b2cBmp; this.b2cBmp = b2cBmp;
this.b2cSupp = b2cSupp; this.b2cSupp = b2cSupp;
@ -239,7 +239,7 @@ public class HKSCS {
char[][] c2bBmp, char[][] c2bBmp,
char[][] c2bSupp) char[][] c2bSupp)
{ {
super(cs, null, null); super(cs, null, null, true);
this.big5Enc = big5Enc; this.big5Enc = big5Enc;
this.c2bBmp = c2bBmp; this.c2bBmp = c2bBmp;
this.c2bSupp = c2bSupp; this.c2bSupp = c2bSupp;
@ -389,6 +389,33 @@ public class HKSCS {
return dp; 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 char[] C2B_UNMAPPABLE = new char[0x100];
static { static {

View file

@ -132,6 +132,10 @@ class ISO_8859_1
dst[dp++] = (char)(src[sp++] & 0xff); dst[dp++] = (char)(src[sp++] & 0xff);
return dp; return dp;
} }
public boolean isASCIICompatible() {
return true;
}
} }
private static class Encoder extends CharsetEncoder private static class Encoder extends CharsetEncoder
@ -297,5 +301,9 @@ class ISO_8859_1
} }
return dp; return dp;
} }
public boolean isASCIICompatible() {
return true;
}
} }
} }

View file

@ -49,10 +49,18 @@ public class SingleByte
public static final class Decoder extends CharsetDecoder public static final class Decoder extends CharsetDecoder
implements ArrayDecoder { implements ArrayDecoder {
private final char[] b2c; private final char[] b2c;
private final boolean isASCIICompatible;
public Decoder(Charset cs, char[] b2c) { public Decoder(Charset cs, char[] b2c) {
super(cs, 1.0f, 1.0f); super(cs, 1.0f, 1.0f);
this.b2c = b2c; 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) { private CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
@ -116,6 +124,7 @@ public class SingleByte
repl = newReplacement.charAt(0); repl = newReplacement.charAt(0);
} }
@Override
public int decode(byte[] src, int sp, int len, char[] dst) { public int decode(byte[] src, int sp, int len, char[] dst) {
if (len > dst.length) if (len > dst.length)
len = dst.length; len = dst.length;
@ -129,6 +138,11 @@ public class SingleByte
} }
return dp; return dp;
} }
@Override
public boolean isASCIICompatible() {
return isASCIICompatible;
}
} }
public static final class Encoder extends CharsetEncoder public static final class Encoder extends CharsetEncoder
@ -136,11 +150,13 @@ public class SingleByte
private Surrogate.Parser sgp; private Surrogate.Parser sgp;
private final char[] c2b; private final char[] c2b;
private final char[] c2bIndex; 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); super(cs, 1.0f, 1.0f);
this.c2b = c2b; this.c2b = c2b;
this.c2bIndex = c2bIndex; this.c2bIndex = c2bIndex;
this.isASCIICompatible = isASCIICompatible;
} }
public boolean canEncode(char c) { public boolean canEncode(char c) {
@ -252,6 +268,51 @@ public class SingleByte
} }
return dp; 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. // init the c2b and c2bIndex tables from b2c.

View file

@ -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();
}

View file

@ -146,6 +146,10 @@ public class US_ASCII
} }
return dp; return dp;
} }
public boolean isASCIICompatible() {
return true;
}
} }
private static class Encoder extends CharsetEncoder private static class Encoder extends CharsetEncoder
@ -259,6 +263,10 @@ public class US_ASCII
} }
return dp; return dp;
} }
public boolean isASCIICompatible() {
return true;
}
} }
} }

View file

@ -549,6 +549,10 @@ class UTF_8 extends Unicode
} }
return dp; return dp;
} }
public boolean isASCIICompatible() {
return true;
}
} }
private static final class Encoder extends CharsetEncoder private static final class Encoder extends CharsetEncoder
@ -742,5 +746,9 @@ class UTF_8 extends Unicode
} }
return dp; return dp;
} }
public boolean isASCIICompatible() {
return true;
}
} }
} }

View file

@ -31,3 +31,14 @@ Java_java_lang_String_intern(JNIEnv *env, jobject this)
{ {
return JVM_InternString(env, this); 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;
}
}

View file

@ -51,12 +51,12 @@ public class Big5_Solaris extends Charset implements HistoricallyNamedCharset
public CharsetDecoder newDecoder() { public CharsetDecoder newDecoder() {
initb2c(); initb2c();
return new DoubleByte.Decoder(this, b2c, b2cSB, 0x40, 0xfe); return new DoubleByte.Decoder(this, b2c, b2cSB, 0x40, 0xfe, true);
} }
public CharsetEncoder newEncoder() { public CharsetEncoder newEncoder() {
initc2b(); initc2b();
return new DoubleByte.Encoder(this, c2b, c2bIndex); return new DoubleByte.Encoder(this, c2b, c2bIndex, true);
} }
static char[][] b2c; static char[][] b2c;

View file

@ -62,7 +62,7 @@ public class IBM834 extends Charset
protected static class Encoder extends DoubleByte.Encoder_DBCSONLY { protected static class Encoder extends DoubleByte.Encoder_DBCSONLY {
public Encoder(Charset cs) { public Encoder(Charset cs) {
super(cs, new byte[] {(byte)0xfe, (byte)0xfe}, super(cs, new byte[] {(byte)0xfe, (byte)0xfe},
IBM933.c2b, IBM933.c2bIndex); IBM933.c2b, IBM933.c2bIndex, false);
} }
public int encodeChar(char ch) { public int encodeChar(char ch) {

View file

@ -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!");
}
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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();
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -22,7 +22,7 @@
*/ */
/* @test /* @test
* @bug 8058779 * @bug 8058779 8054307
* @library /lib/testlibrary/ * @library /lib/testlibrary/
* @build jdk.testlibrary.RandomFactory * @build jdk.testlibrary.RandomFactory
* @run testng LiteralReplace * @run testng LiteralReplace
@ -104,6 +104,109 @@ public class LiteralReplace {
{"abcdefgh", "[a-h]", "X", "abcdefgh"}, {"abcdefgh", "[a-h]", "X", "abcdefgh"},
{"aa+", "a+", "", "a"}, {"aa+", "a+", "", "a"},
{"^abc$", "abc", "x", "^x$"}, {"^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",
"",
""},
}; };
} }

View file

@ -23,7 +23,7 @@
/* /*
@test @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 @summary toLowerCase should lower-case Greek Sigma correctly depending
on the context (final/non-final). Also it should handle on the context (final/non-final). Also it should handle
Locale specific (lt, tr, and az) lowercasings and supplementary Locale specific (lt, tr, and az) lowercasings and supplementary
@ -134,9 +134,55 @@ public class ToLowerCase {
} }
test(src.toString(), Locale.US, exp.toString()); 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) { 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); String result = in.toLowerCase(locale);
if (!result.equals(expected)) { if (!result.equals(expected)) {
System.err.println("input: " + in + ", locale: " + locale + System.err.println("input: " + in + ", locale: " + locale +

View file

@ -23,7 +23,7 @@
/* /*
@test @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 @summary toUpperCase should upper-case German sharp s correctly even if
it's the only character in the string. should also uppercase it's the only character in the string. should also uppercase
all of the 1:M char mappings correctly. Also it should handle all of the 1:M char mappings correctly. Also it should handle
@ -97,9 +97,61 @@ public class ToUpperCase {
test("A\uD801\uDC44", Locale.ROOT, "A\uD801\uDC1c"); 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\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("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) { 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); String result = in.toUpperCase(locale);
if (!result.equals(expected)) { if (!result.equals(expected)) {
System.err.println("input: " + in + ", locale: " + locale + System.err.println("input: " + in + ", locale: " + locale +

View file

@ -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();
}
}

View file

@ -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;
}
}

View file

@ -94,7 +94,7 @@ public class Exceptions {
System.out.println("StringBuffer.replace(int start, int end, String str)"); System.out.println("StringBuffer.replace(int start, int end, String str)");
tryCatch(" -1, 2, \" \"", tryCatch(" -1, 2, \" \"",
new StringIndexOutOfBoundsException(-1), new StringIndexOutOfBoundsException("start -1, end 2, length 7"),
new Runnable() { new Runnable() {
public void run() { public void run() {
StringBuffer sb = new StringBuffer("hilbert"); StringBuffer sb = new StringBuffer("hilbert");
@ -102,14 +102,14 @@ public class Exceptions {
}}); }});
tryCatch(" 7, 8, \" \"", tryCatch(" 7, 8, \" \"",
new StringIndexOutOfBoundsException("start > length()"), new StringIndexOutOfBoundsException("start 7, end 6, length 6"),
new Runnable() { new Runnable() {
public void run() { public void run() {
StringBuffer sb = new StringBuffer("banach"); StringBuffer sb = new StringBuffer("banach");
sb.replace(7, 8, " "); sb.replace(7, 8, " ");
}}); }});
tryCatch(" 2, 1, \" \"", tryCatch(" 2, 1, \" \"",
new StringIndexOutOfBoundsException("start > end"), new StringIndexOutOfBoundsException("start 2, end 1, length 7"),
new Runnable() { new Runnable() {
public void run() { public void run() {
StringBuffer sb = new StringBuffer("riemann"); StringBuffer sb = new StringBuffer("riemann");

View file

@ -264,4 +264,3 @@ public class BuilderForwarding {
} }
} }
} }

View file

@ -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();
}
}

View file

@ -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;
}
}

View file

@ -94,21 +94,21 @@ public class Exceptions {
System.out.println("StringBuilder.replace(int start, int end, String str)"); System.out.println("StringBuilder.replace(int start, int end, String str)");
tryCatch(" -1, 2, \" \"", tryCatch(" -1, 2, \" \"",
new StringIndexOutOfBoundsException(-1), new StringIndexOutOfBoundsException("start -1, end 2, length 7"),
new Runnable() { new Runnable() {
public void run() { public void run() {
StringBuilder sb = new StringBuilder("hilbert"); StringBuilder sb = new StringBuilder("hilbert");
sb.replace(-1, 2, " "); sb.replace(-1, 2, " ");
}}); }});
tryCatch(" 7, 8, \" \"", tryCatch(" 7, 8, \" \"",
new StringIndexOutOfBoundsException("start > length()"), new StringIndexOutOfBoundsException("start 7, end 6, length 6"),
new Runnable() { new Runnable() {
public void run() { public void run() {
StringBuilder sb = new StringBuilder("banach"); StringBuilder sb = new StringBuilder("banach");
sb.replace(7, 8, " "); sb.replace(7, 8, " ");
}}); }});
tryCatch(" 2, 1, \" \"", tryCatch(" 2, 1, \" \"",
new StringIndexOutOfBoundsException("start > end"), new StringIndexOutOfBoundsException("start 2, end 1, length 7"),
new Runnable() { new Runnable() {
public void run() { public void run() {
StringBuilder sb = new StringBuilder("riemann"); StringBuilder sb = new StringBuilder("riemann");

View file

@ -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();
}
}
}

View file

@ -22,7 +22,7 @@
*/ */
/* @test /* @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 * @summary Test if StringCoding and NIO result have the same de/encoding result
* @modules java.base/sun.nio.cs * @modules java.base/sun.nio.cs
* @run main/othervm/timeout=2000 TestStringCoding * @run main/othervm/timeout=2000 TestStringCoding
@ -36,41 +36,61 @@ import java.nio.charset.*;
public class TestStringCoding { public class TestStringCoding {
public static void main(String[] args) throws Throwable { 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 }) { for (Boolean hasSM: new boolean[] { false, true }) {
if (hasSM) if (hasSM) {
System.setSecurityManager(new PermissiveSecurityManger()); System.setSecurityManager(new PermissiveSecurityManger());
}
for (Charset cs: Charset.availableCharsets().values()) { for (Charset cs: Charset.availableCharsets().values()) {
if ("ISO-2022-CN".equals(cs.name()) || if ("ISO-2022-CN".equals(cs.name()) ||
"x-COMPOUND_TEXT".equals(cs.name()) || "x-COMPOUND_TEXT".equals(cs.name()) ||
"x-JISAutoDetect".equals(cs.name())) "x-JISAutoDetect".equals(cs.name()))
continue; continue;
System.out.printf("Testing(sm=%b) " + cs.name() + "....", hasSM); System.out.printf("Testing(sm=%b) " + cs.name() + "....", hasSM);
// full bmp first
char[] bmpCA = new char[0x10000]; testNewString(cs, testGetBytes(cs, new String(bmp)));
for (int i = 0; i < 0x10000; i++) { testNewString(cs, testGetBytes(cs, new String(latin)));
bmpCA[i] = (char)i; testNewString(cs, testGetBytes(cs, new String(ascii)));
} testGetBytes(cs, testNewString(cs, latinBA));
byte[] sbBA = new byte[0x100]; testGetBytes(cs, testNewString(cs, asciiBA));
for (int i = 0; i < 0x100; i++) {
sbBA[i] = (byte)i;
}
test(cs, bmpCA, sbBA);
// "randomed" sizes // "randomed" sizes
Random rnd = new Random(); Random rnd = new Random();
for (int i = 0; i < 10; i++) { 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); //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 //add a pair of surrogates
int pos = clen / 2; int pos = bmp0.length / 2;
if ((pos + 1) < blen) { if ((pos + 1) < bmp0.length) {
bmpCA[pos] = '\uD800'; bmp0[pos] = '\uD800';
bmpCA[pos+1] = '\uDC00'; 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); testMixed(cs);
System.out.println("done!"); System.out.println("done!");
} }
@ -109,8 +129,9 @@ public class TestStringCoding {
//getBytes(cs); //getBytes(cs);
bmpBA = bmpStr.getBytes(cs); bmpBA = bmpStr.getBytes(cs);
if (!Arrays.equals(bmpBA, baNIO)) if (!Arrays.equals(bmpBA, baNIO)) {
throw new RuntimeException("getBytes(cs) failed -> " + cs.name()); throw new RuntimeException("getBytes(cs) failed -> " + cs.name());
}
//new String(csn); //new String(csn);
String strSC = new String(bmpBA, cs.name()); String strSC = new String(bmpBA, cs.name());
@ -118,49 +139,61 @@ public class TestStringCoding {
if(!strNIO.equals(strSC)) { if(!strNIO.equals(strSC)) {
throw new RuntimeException("new String(csn) failed -> " + cs.name()); throw new RuntimeException("new String(csn) failed -> " + cs.name());
} }
//new String(cs); //new String(cs);
strSC = new String(bmpBA, cs); strSC = new String(bmpBA, cs);
if (!strNIO.equals(strSC)) if (!strNIO.equals(strSC)) {
throw new RuntimeException("new String(cs) failed -> " + cs.name()); throw new RuntimeException("new String(cs) failed -> " + cs.name());
}
} }
static void test(Charset cs, char[] bmpCA, byte[] sbBA) throws Throwable { static byte[] getBytes(CharsetEncoder enc, String str) throws Throwable {
String bmpStr = new String(bmpCA); ByteBuffer bf = enc.reset().encode(CharBuffer.wrap(str.toCharArray()));
CharsetDecoder dec = cs.newDecoder() byte[] ba = new byte[bf.limit()];
.onMalformedInput(CodingErrorAction.REPLACE) bf.get(ba, 0, ba.length);
.onUnmappableCharacter(CodingErrorAction.REPLACE); return ba;
}
static byte[] testGetBytes(Charset cs, String str) throws Throwable {
CharsetEncoder enc = cs.newEncoder() CharsetEncoder enc = cs.newEncoder()
.onMalformedInput(CodingErrorAction.REPLACE) .onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE); .onUnmappableCharacter(CodingErrorAction.REPLACE);
//getBytes(csn); //getBytes(csn);
byte[] baSC = bmpStr.getBytes(cs.name()); byte[] baSC = str.getBytes(cs.name());
ByteBuffer bf = enc.reset().encode(CharBuffer.wrap(bmpCA)); byte[] baNIO = getBytes(enc, str);
byte[] baNIO = new byte[bf.limit()]; if (!Arrays.equals(baSC, baNIO)) {
bf.get(baNIO, 0, baNIO.length);
if (!Arrays.equals(baSC, baNIO))
throw new RuntimeException("getBytes(csn) failed -> " + cs.name()); throw new RuntimeException("getBytes(csn) failed -> " + cs.name());
}
//getBytes(cs); //getBytes(cs);
baSC = bmpStr.getBytes(cs); baSC = str.getBytes(cs);
if (!Arrays.equals(baSC, baNIO)) if (!Arrays.equals(baSC, baNIO)) {
throw new RuntimeException("getBytes(cs) failed -> " + cs.name()); 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); //new String(csn);
String strSC = new String(sbBA, cs.name()); String strSC = new String(ba, cs.name());
String strNIO = dec.reset().decode(ByteBuffer.wrap(sbBA)).toString(); String strNIO = dec.reset().decode(ByteBuffer.wrap(ba)).toString();
if(!strNIO.equals(strSC)) {
if(!strNIO.equals(strSC))
throw new RuntimeException("new String(csn) failed -> " + cs.name()); throw new RuntimeException("new String(csn) failed -> " + cs.name());
}
//new String(cs); //new String(cs);
strSC = new String(sbBA, cs); strSC = new String(ba, cs);
if (!strNIO.equals(strSC)) if (!strNIO.equals(strSC)) {
throw new RuntimeException("new String(cs) failed -> " + cs.name()); throw new RuntimeException("new String(cs)/bmp failed -> " + cs.name());
}
return strSC;
}
static void testSurrogates(Charset cs) throws Throwable {
//encode unmappable surrogates //encode unmappable surrogates
CharsetEncoder enc = cs.newEncoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
if (enc instanceof sun.nio.cs.ArrayEncoder && if (enc instanceof sun.nio.cs.ArrayEncoder &&
cs.contains(Charset.forName("ASCII"))) { cs.contains(Charset.forName("ASCII"))) {
if (cs.name().equals("UTF-8") || // utf8 handles surrogates if (cs.name().equals("UTF-8") || // utf8 handles surrogates

View file

@ -22,7 +22,7 @@
*/ */
/* @test /* @test
@bug 7040220 @bug 7040220 8054307
@summary Test if StringCoding and NIO result have the same de/encoding result for UTF-8 @summary Test if StringCoding and NIO result have the same de/encoding result for UTF-8
* @run main/othervm/timeout=2000 TestStringCodingUTF8 * @run main/othervm/timeout=2000 TestStringCodingUTF8
* @key randomness * @key randomness
@ -50,6 +50,18 @@ public class TestStringCodingUTF8 {
} }
test(cs, bmp, 0, bmp.length); 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); ArrayList<Integer> list = new ArrayList<>(0x20000);
for (int i = 0; i < 0x20000; i++) { for (int i = 0; i < 0x20000; i++) {
list.add(i, i); list.add(i, i);