undefect. CWE-407 — 63 sites patched across 27 ecosystems

Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com

Patches, unit tests, benchmarks, whitepaper, and outreach briefs.
Public domain — no copyright claimed. Use freely.
This commit is contained in:
russell@unturf.com 2026-03-26 17:11:57 -04:00
commit 0a580b313d
70422 changed files with 17213626 additions and 0 deletions

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2012, 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 6206780
* @summary Test StringBuilder.append(StringBuffer);
* @key randomness
*/
import java.util.Random;
public class AppendStringBuffer {
private static Random generator = new Random();
public static void main(String[] args) throws Exception {
for (int i=0; i<1000; i++) {
StringBuffer sb1 = generateTestBuffer(10, 100);
StringBuffer sb2 = generateTestBuffer(10, 100);
StringBuffer sb3 = generateTestBuffer(10, 100);
String s1 = sb1.toString();
String s2 = sb2.toString();
String s3 = sb3.toString();
String concatResult = new String(s1+s2+s3);
StringBuilder test = new StringBuilder();
test.append(sb1);
test.append(sb2);
test.append(sb3);
if (!test.toString().equals(concatResult))
throw new RuntimeException("StringBuffer.append failure");
}
}
private static int getRandomIndex(int constraint1, int constraint2) {
int range = constraint2 - constraint1;
int x = generator.nextInt(range);
return constraint1 + x;
}
private static StringBuffer generateTestBuffer(int min, int max) {
StringBuffer aNewStringBuffer = new StringBuffer(120);
int aNewLength = getRandomIndex(min, max);
for(int y=0; y<aNewLength; y++) {
int achar = generator.nextInt(30)+30;
char test = (char)(achar);
aNewStringBuffer.append(test);
}
return aNewStringBuffer;
}
}

View file

@ -0,0 +1,266 @@
/*
* Copyright (c) 2012, 2013, 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 6206780
* @summary Test forwarding of methods to super in StringBuilder
* @author Jim Gish <jim.gish@oracle.com>
*/
import java.util.ArrayList;
import java.util.List;
public class BuilderForwarding {
private static final String A_STRING_BUFFER_VAL = "aStringBuffer";
private static final String A_STRING_BUILDER_VAL = "aStringBuilder";
private static final String A_STRING_VAL = "aString";
private static final String NON_EMPTY_VAL = "NonEmpty";
public BuilderForwarding() {
System.out.println( "Starting BuilderForwarding");
}
public static void main(String... args) {
new BuilderForwarding().executeTestMethods();
}
public void executeTestMethods() {
appendCharSequence();
indexOfString();
indexOfStringIntNull();
indexOfStringNull();
indexOfStringint();
insertintCharSequence();
insertintObject();
insertintboolean();
insertintchar();
insertintdouble();
insertintfloat();
insertintint();
insertintlong();
lastIndexOfString();
lastIndexOfStringint();
}
public void appendCharSequence() {
// three different flavors of CharSequence
CharSequence aString = A_STRING_VAL;
CharSequence aStringBuilder = new StringBuilder(A_STRING_BUILDER_VAL);
CharSequence aStringBuffer = new StringBuffer(A_STRING_BUFFER_VAL);
assertEquals( /*actual*/ new StringBuilder().append(aString).toString(), /*expected*/ A_STRING_VAL );
assertEquals( new StringBuilder().append(aStringBuilder).toString(), A_STRING_BUILDER_VAL );
assertEquals( new StringBuilder().append(aStringBuffer).toString(), A_STRING_BUFFER_VAL );
assertEquals( /*actual*/ new StringBuilder(NON_EMPTY_VAL).append(aString).toString(), NON_EMPTY_VAL+A_STRING_VAL );
assertEquals( new StringBuilder(NON_EMPTY_VAL).append(aStringBuilder).toString(), NON_EMPTY_VAL+A_STRING_BUILDER_VAL );
assertEquals( new StringBuilder(NON_EMPTY_VAL).append(aStringBuffer).toString(), NON_EMPTY_VAL+A_STRING_BUFFER_VAL );
}
public void indexOfString() {
StringBuilder sb = new StringBuilder();
// should be NPE if null passed
try {
sb.indexOf(null);
throw new RuntimeException("Test failed: should have thrown NPE");
} catch (NullPointerException npe) {
// expected: passed
} catch (Throwable t) {
throw new RuntimeException("Test failed: should have thrown NPE. Instead threw "
+ t);
}
sb = new StringBuilder("xyz");
assertEquals( sb.indexOf("y"), 1 );
assertEquals( sb.indexOf("not found"), -1 );
}
public void indexOfStringint() {
StringBuilder sb = new StringBuilder();
// should be NPE if null passed
try {
sb.indexOf(null,1);
throw new RuntimeException("Test failed: should have thrown NPE");
} catch (NullPointerException npe) {
// expected: passed
} catch (Throwable t) {
throw new RuntimeException("Test failed: should have thrown NPE");
}
sb = new StringBuilder("xyyz");
assertEquals( sb.indexOf("y",0), 1 );
assertEquals( sb.indexOf("y",1), 1 );
assertEquals( sb.indexOf("y",2), 2 );
assertEquals( sb.indexOf("not found"), -1 );
}
public void indexOfStringIntNull() {
StringBuffer sb = new StringBuffer();
// should be NPE if null passed
try {
sb.indexOf(null,1);
throw new RuntimeException("Test failed: should have thrown NPE");
} catch (NullPointerException npe) {
// expected: passed
} catch (Throwable t) {
throw new RuntimeException("Test failed: should have thrown NPE. Instead threw "
+ t);
}
}
public void indexOfStringNull() {
StringBuilder sb = new StringBuilder();
// should be NPE if null passed
try {
sb.indexOf(null);
throw new RuntimeException("Test failed: should have thrown NPE");
} catch (NullPointerException npe) {
// expected: passed
} catch (Throwable t) {
throw new RuntimeException("Test failed: should have thrown NPE. Instead threw "
+ t);
}
}
public void insertintboolean() {
boolean b = true;
StringBuilder sb = new StringBuilder("012345");
assertEquals( sb.insert( 2, b).toString(), "01true2345");
}
public void insertintchar() {
char c = 'C';
StringBuilder sb = new StringBuilder("012345");
assertEquals( sb.insert( 2, c ).toString(), "01C2345");
}
public void insertintCharSequence() {
final String initString = "012345";
// three different flavors of CharSequence
CharSequence aString = A_STRING_VAL;
CharSequence aStringBuilder = new StringBuilder(A_STRING_BUILDER_VAL);
CharSequence aStringBuffer = new StringBuffer(A_STRING_BUFFER_VAL);
assertEquals( new StringBuilder(initString).insert(2, aString).toString(), "01"+A_STRING_VAL+"2345" );
assertEquals( new StringBuilder(initString).insert(2, aStringBuilder).toString(), "01"+A_STRING_BUILDER_VAL+"2345" );
assertEquals( new StringBuilder(initString).insert(2, aStringBuffer).toString(), "01"+A_STRING_BUFFER_VAL+"2345" );
try {
new StringBuilder(initString).insert(7, aString);
throw new RuntimeException("Test failed: should have thrown IndexOutOfBoundsException");
} catch (IndexOutOfBoundsException soob) {
// expected: passed
} catch (Throwable t) {
throw new RuntimeException("Test failed: should have thrown IndexOutOfBoundsException, but instead threw " + t.getMessage());
}
}
public void insertintdouble() {
double d = 99d;
StringBuilder sb = new StringBuilder("012345");
assertEquals( sb.insert( 2, d ).toString(), "0199.02345"); }
public void insertintfloat() {
float f = 99.0f;
StringBuilder sb = new StringBuilder("012345");
assertEquals( sb.insert( 2, f ).toString(), "0199.02345"); }
public void insertintint() {
int i = 99;
StringBuilder sb = new StringBuilder("012345");
assertEquals( sb.insert( 2, i ).toString(), "01992345");
}
public void insertintlong() {
long l = 99;
StringBuilder sb = new StringBuilder("012345");
assertEquals( sb.insert( 2, l ).toString(), "01992345"); }
public void insertintObject() {
StringBuilder sb = new StringBuilder("012345");
List<String> ls = new ArrayList<String>();
ls.add("A"); ls.add("B");
String lsString = ls.toString();
assertEquals( sb.insert(2, ls).toString(), "01"+lsString+"2345");
try {
sb.insert(sb.length()+1, ls);
throw new RuntimeException("Test failed: should have thrown StringIndexOutOfBoundsException");
} catch (StringIndexOutOfBoundsException soob) {
// expected: passed
} catch (Throwable t) {
throw new RuntimeException("Test failed: should have thrown StringIndexOutOfBoundsException, but instead threw:"
+ t);
}
}
public void lastIndexOfString() {
String xyz = "xyz";
String xyz3 = "xyzxyzxyz";
StringBuilder sb = new StringBuilder(xyz3);
int pos = sb.lastIndexOf("xyz");
assertEquals( pos, 2*xyz.length() );
}
public void lastIndexOfStringint() {
StringBuilder sb = new StringBuilder("xyzxyzxyz");
int pos = sb.lastIndexOf("xyz",5);
assertEquals( pos, 3 );
pos = sb.lastIndexOf("xyz", 6);
assertEquals( pos, 6 );
}
public void assertEquals( String actual, String expected) {
if (!actual.equals( expected )) {
throw new RuntimeException( "Test failed: actual = '" + actual +
"', expected = '" + expected + "'");
}
}
public void assertEquals( int actual, int expected) {
if (actual != expected) {
throw new RuntimeException( "Test failed: actual = '" + actual +
"', expected = '" + expected + "'");
}
}
}

View file

@ -0,0 +1,182 @@
/*
* Copyright (c) 2016, 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 8149330
* @summary Basic set of tests of capacity management
* @run testng Capacity
*/
import java.lang.reflect.Field;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.SplittableRandom;
import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;
import static org.testng.Assert.*;
public class Capacity {
static final int DEFAULT_CAPACITY = 16;
private static int newCapacity(int oldCapacity,
int desiredCapacity)
{
return Math.max(oldCapacity * 2 + 2, desiredCapacity);
}
private static int nextNewCapacity(int oldCapacity) {
return newCapacity(oldCapacity, oldCapacity + 1);
}
@Test(dataProvider = "singleChar")
public void defaultCapacity(Character ch) {
StringBuilder sb = new StringBuilder();
assertEquals(sb.capacity(), DEFAULT_CAPACITY);
for (int i = 0; i < DEFAULT_CAPACITY; i++) {
sb.append(ch);
assertEquals(sb.capacity(), DEFAULT_CAPACITY);
}
sb.append(ch);
assertEquals(sb.capacity(), nextNewCapacity(DEFAULT_CAPACITY));
}
@Test(dataProvider = "charCapacity")
public void explicitCapacity(Character ch, int initCapacity) {
StringBuilder sb = new StringBuilder(initCapacity);
assertEquals(sb.capacity(), initCapacity);
for (int i = 0; i < initCapacity; i++) {
sb.append(ch);
assertEquals(sb.capacity(), initCapacity);
}
sb.append(ch);
assertEquals(sb.capacity(), nextNewCapacity(initCapacity));
}
@Test(dataProvider = "singleChar")
public void sbFromString(Character ch) {
String s = "string " + ch;
int expectedCapacity = s.length() + DEFAULT_CAPACITY;
StringBuilder sb = new StringBuilder(s);
assertEquals(sb.capacity(), expectedCapacity);
for (int i = 0; i < DEFAULT_CAPACITY; i++) {
sb.append(ch);
assertEquals(sb.capacity(), expectedCapacity);
}
sb.append(ch);
assertEquals(sb.capacity(), nextNewCapacity(expectedCapacity));
}
@Test(dataProvider = "singleChar")
public void sbFromCharSeq(Character ch) {
CharSequence cs = new MyCharSeq("char seq " + ch);
int expectedCapacity = cs.length() + DEFAULT_CAPACITY;
StringBuilder sb = new StringBuilder(cs);
assertEquals(sb.capacity(), expectedCapacity);
for (int i = 0; i < DEFAULT_CAPACITY; i++) {
sb.append(ch);
assertEquals(sb.capacity(), expectedCapacity);
}
sb.append(ch);
assertEquals(sb.capacity(), nextNewCapacity(expectedCapacity));
}
@Test(dataProvider = "charCapacity")
public void ensureCapacity(Character ch, int cap) {
StringBuilder sb = new StringBuilder(0);
assertEquals(sb.capacity(), 0);
sb.ensureCapacity(cap); // only has effect if cap > 0
int newCap = (cap == 0) ? 0 : newCapacity(0, cap);
assertEquals(sb.capacity(), newCap);
sb.ensureCapacity(newCap + 1);
assertEquals(sb.capacity(), nextNewCapacity(newCap));
sb.append(ch);
assertEquals(sb.capacity(), nextNewCapacity(newCap));
}
@Test(dataProvider = "negativeCapacity",
expectedExceptions = NegativeArraySizeException.class)
public void negativeInitialCapacity(int negCap) {
StringBuilder sb = new StringBuilder(negCap);
}
@Test(dataProvider = "negativeCapacity")
public void ensureNegativeCapacity(int negCap) {
StringBuilder sb = new StringBuilder();
sb.ensureCapacity(negCap);
assertEquals(sb.capacity(), DEFAULT_CAPACITY);
}
@Test(dataProvider = "charCapacity")
public void trimToSize(Character ch, int cap) {
StringBuilder sb = new StringBuilder(cap);
int halfOfCap = cap / 2;
for (int i = 0; i < halfOfCap; i++) {
sb.append(ch);
}
sb.trimToSize();
// according to the spec, capacity doesn't have to
// become exactly the size
assertTrue(sb.capacity() >= halfOfCap);
}
@DataProvider
public Object[][] singleChar() {
return new Object[][] { {'J'}, {'\u042b'} };
}
@DataProvider
public Object[][] charCapacity() {
return new Object[][] {
{'J', 0},
{'J', 1},
{'J', 15},
{'J', DEFAULT_CAPACITY},
{'J', 1024},
{'\u042b', 0},
{'\u042b', 1},
{'\u042b', 15},
{'\u042b', DEFAULT_CAPACITY},
{'\u042b', 1024},
};
}
@DataProvider
public Object[][] negativeCapacity() {
return new Object[][] { {-1}, {Integer.MIN_VALUE} };
}
private static class MyCharSeq implements CharSequence {
private CharSequence s;
public MyCharSeq(CharSequence s) { this.s = s; }
public char charAt(int i) { return s.charAt(i); }
public int length() { return s.length(); }
public CharSequence subSequence(int st, int e) {
return s.subSequence(st, e);
}
public String toString() { return s.toString(); }
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2021, 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 8271732
* @summary Basic test that charAt throws IIOBE as expected for out of bounds indexes.
* @run testng CharAt
*/
import java.util.List;
import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;
import static org.testng.Assert.*;
public class CharAt {
/**
* StringBuilder/-Buffer.charAt throws:
* IndexOutOfBoundsException - if index is negative or greater than or equal to length().
* the test inputs, expected to throw IndexOutOfBoundsException.
*/
@Test
public void charAtIIOBE() {
StringBuilder sb = new StringBuilder("test");
StringBuffer sbuf = new StringBuffer("test");
StringBuilder sbUtf16 = new StringBuilder("\uFF34est"); // Fullwidth Latin Capital Letter T
StringBuffer sbufUtf16 = new StringBuffer("\uFF34est");
List<Integer> outOfBoundsIndices = List.of(Integer.MIN_VALUE, -2, -1, 4, 5, Integer.MAX_VALUE);
for (int index : outOfBoundsIndices) {
try {
sb.charAt(index);
fail("StringBuilder.charAt index: " + index + " does not throw IOOBE as expected");
} catch (IndexOutOfBoundsException ex) {
// OK
}
try {
sbUtf16.charAt(index);
fail("StringBuilder.charAt index: " + index + " does not throw IOOBE as expected (UTF-16)");
} catch (IndexOutOfBoundsException ex) {
// OK
}
try {
sbuf.charAt(index);
fail("StringBuffer.charAt index: " + index + " does not throw IOOBE as expected");
} catch (IndexOutOfBoundsException ex) {
// OK
}
try {
sbufUtf16.charAt(index);
fail("StringBuffer.charAt index: " + index + " does not throw IOOBE as expected (UTF-16)");
} catch (IndexOutOfBoundsException ex) {
// OK
}
}
}
}

View file

@ -0,0 +1,503 @@
/*
* Copyright (c) 2015, 2025, 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 8351443
* @summary Tests Compact String. This test is testing StringBuilder
* behavior related to Compact String.
* @run testng/othervm/timeout=480 -XX:+CompactStrings CompactStringBuilder
* @run testng/othervm/timeout=480 -XX:-CompactStrings CompactStringBuilder
* @run testng/othervm/timeout=480 -Xcomp 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 char[] { 'a', 'b', 'c'}),
"Aabc");
check(new StringBuilder(ORIGIN).append(new char[] { 'a', 'b', 'c'}, 1, 2),
"Abc");
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");
}
/*
* Tests for maybeLatin1 attribute
*/
@Test
public void testCompactStringForMaybeLatin1() {
StringBuilder sb = new StringBuilder("A\uDC01");
sb.setLength(0); // maybeLatin1 become true
check(sb, "");
check(new StringBuilder(sb).append('A'), "A");
check(new StringBuilder().append(sb), "");
sb = new StringBuilder("A\uDC01");
sb.setCharAt(1, 'B'); // maybeLatin1 become true
check(sb, "AB");
check(new StringBuilder(sb).append('A'), "ABA");
check(new StringBuilder().append(sb), "AB");
sb = new StringBuilder("A\uDC01");
sb.deleteCharAt(1); // maybeLatin1 become true
check(sb, "A");
check(new StringBuilder(sb).append('B'), "AB");
check(new StringBuilder().append(sb), "A");
sb = new StringBuilder("A\uDC01\uFF21\uD801");
sb.delete(1, 4);
check(sb, "A"); // maybeLatin1 become true
check(new StringBuilder(sb).append('B'), "AB");
check(new StringBuilder().append(sb), "A");
sb = new StringBuilder("A\uDC01\uFF21\uD801");
sb.replace(1, 4, "B");
check(sb, "AB"); // maybeLatin1 become true
check(new StringBuilder(sb).append('A'), "ABA");
check(new StringBuilder().append(sb), "AB");
}
// Test cases to force expanding the capacity during replace.
// Start with a known capacity and a known initial value that almost fills it.
// Use both latin1 and utf16 initial values and replacement values.
// Iterate through cases of SB.replace start and end values and various lengths of replacement.
// The results are checked by composing the new string using concatenation of the segments.
@Test
public void testGrowingCapacityReplace() {
final int INIT_CAPACITY = 8;
final String[] INITIAL_CHARS = {"A", "\u0100"};
final String[] REPLACEMENT_CHARS = {"B", "\u0101"};
for (String INITIAL : INITIAL_CHARS) {
for (String REPLACEMENT : REPLACEMENT_CHARS) {
for (int initLen = INIT_CAPACITY - 1; initLen < INIT_CAPACITY + 2; initLen++) {
final String orig = INITIAL.repeat(initLen);
for (int start = INIT_CAPACITY - 4; start < orig.length(); start++) {
for (int end = start; end < orig.length(); end++) {
for (int insLen = 0; insLen < 2; insLen++) {
final String repl = REPLACEMENT.repeat(insLen);
var sb = new StringBuilder(INIT_CAPACITY)
.append(orig);
int capBefore = sb.capacity();
sb.replace(start, end, repl);
int capAfter = sb.capacity();
String expected = genReplacementString(orig, start, end, repl);
try {
check(sb, expected);
} catch (Throwable ex) {
System.out.printf("repl: \"%s\", actual: %s, expected: %s%n", repl, sb, expected);
System.out.printf(" insLen: %d, gap: %d, beforeLen: %d, afterLen: %d, capBefore: %d, capAfter: %d%n",
insLen, (end - start), orig.length(), sb.length(),
capBefore, capAfter);
throw ex;
}
}
}
}
}
}
}
}
// Construct the replacement string using string concat of the segments
private static String genReplacementString(String orig, int start, int end, String repl) {
return orig.substring(0, start) +
repl +
orig.substring(end, orig.length());
}
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)));
}
/*
* Escape non-ASCII characters since not all systems support 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, 2017, 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.test.lib.util.SerializationUtils.*;
import static org.testng.Assert.*;
/*
* @test
* @bug 8077559
* @library /test/lib
* @build jdk.test.lib.util.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

@ -0,0 +1,136 @@
/*
* Copyright (c) 2018, 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.Iterator;
import java.util.Set;
import java.util.TreeSet;
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* @test
* @bug 8137326
* @summary Test to verify the Comparable implementation for the StringBuilder class.
* @run testng Comparison
*/
public class Comparison {
static char SEP = ':';
static String[][] books = {
{"Biography", "Steve Jobs"},
{"Biography", "Elon Musk: Tesla, SpaceX, and the Quest for a Fantastic Future"},
{"Law", "Law 101: Everything You Need to Know About American Law, Fourth Edition"},
{"Law", "The Tools of Argument: How the Best Lawyers Think, Argue, and Win"},
{"History", "The History Book (Big Ideas Simply Explained)"},
{"History", "A People's History of the United States"},
};
/**
* Verifies the Comparable implementation by comparing with two TreeSet that
* contain either StringBuilder or String.
*/
@Test
public void compareWithString() {
Set<StringBuilder> sbSet = constructSBSet();
Set<String> sSet = constructStringSet();
Iterator<StringBuilder> iSB = sbSet.iterator();
Iterator<String> iS = sSet.iterator();
while (iSB.hasNext()) {
String temp1 = iSB.next().toString();
System.out.println(temp1);
String temp2 = iS.next();
System.out.println(temp2);
Assert.assertTrue(temp1.equals(temp2), "Comparing item by item");
}
}
/**
* Compares between StringBuilders
*/
@Test
public void testCompare() {
StringBuilder sb1 = generateTestBuffer(65, 70, 97, 102);
StringBuilder sb2 = generateTestBuffer(65, 70, 97, 102);
StringBuilder sb3 = generateTestBuffer(65, 71, 97, 103);
System.out.println(sb1.toString());
System.out.println(sb2.toString());
System.out.println(sb3.toString());
Assert.assertTrue(sb1.compareTo(sb2) == 0, "Compare sb1 and sb2");
Assert.assertFalse(sb1.compareTo(sb3) == 0, "Compare sb1 and sb3");
}
/**
* Verifies that the comparison is from index 0 to length() - 1 of the two
* character sequences.
*/
@Test
public void testModifiedSequence() {
StringBuilder sb1 = generateTestBuffer(65, 70, 97, 102);
StringBuilder sb2 = generateTestBuffer(65, 70, 98, 103);
// contain different character sequences
Assert.assertFalse(sb1.compareTo(sb2) == 0, "Compare the sequences before truncation");
// the first 5 characters however are the same
sb1.setLength(5);
sb2.setLength(5);
System.out.println(sb1.toString());
System.out.println(sb2.toString());
Assert.assertTrue(sb1.compareTo(sb2) == 0, "Compare sb1 and sb2");
Assert.assertTrue(sb1.toString().compareTo(sb2.toString()) == 0, "Compare strings of sb1 and sb2");
}
private Set<String> constructStringSet() {
Set<String> sSet = new TreeSet<>();
for (String[] book : books) {
sSet.add(book[0] + SEP + book[1]);
}
return sSet;
}
private Set<StringBuilder> constructSBSet() {
Set<StringBuilder> sbSet = new TreeSet<>();
for (String[] book : books) {
sbSet.add(new StringBuilder(book[0]).append(SEP).append(book[1]));
}
return sbSet;
}
private static StringBuilder generateTestBuffer(int from1, int to1,
int from2, int to2) {
StringBuilder aBuffer = new StringBuilder(50);
for (int i = from1; i < to1; i++) {
aBuffer.append((char)i);
}
for (int i = from2; i < to2; i++) {
aBuffer.append((char)i);
}
return aBuffer;
}
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2010, 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 6955504 6992121
* @summary Test the StringBuilder.ensureCapacity() with negative minimumCapacity
* and append() method with negative length input argument.
* Also, test the StringBuffer class.
*/
import java.util.ArrayList;
import java.util.Vector;
public class EnsureCapacity {
public static void main(String[] args) {
testStringBuilder();
testStringBuffer();
}
private static void checkCapacity(int before, int after) {
if (before != after) {
throw new RuntimeException("capacity is expected to be unchanged: " +
"before=" + before + " after=" + after);
}
}
private static void testStringBuilder() {
StringBuilder sb = new StringBuilder("abc");
int cap = sb.capacity();
// test if negative minimumCapacity
sb.ensureCapacity(Integer.MIN_VALUE);
checkCapacity(cap, sb.capacity());
try {
char[] str = {'a', 'b', 'c', 'd'};
// test if negative length
sb.append(str, 0, Integer.MIN_VALUE + 10);
throw new RuntimeException("IndexOutOfBoundsException not thrown");
} catch (IndexOutOfBoundsException ex) {
}
}
private static void testStringBuffer() {
StringBuffer sb = new StringBuffer("abc");
int cap = sb.capacity();
// test if negative minimumCapacity
sb.ensureCapacity(Integer.MIN_VALUE);
checkCapacity(cap, sb.capacity());
try {
char[] str = {'a', 'b', 'c', 'd'};
// test if negative length
sb.append(str, 0, Integer.MIN_VALUE + 10);
throw new RuntimeException("IndexOutOfBoundsException not thrown");
} catch (IndexOutOfBoundsException ex) {
}
}
}

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2005, 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 6248507
* @summary Verify that exceptions are thrown as expected.
*/
public class Exceptions {
private static boolean ok = true;
private static void fail(Throwable ex, String s, Throwable got) {
ok = false;
System.err.println("expected "
+ ex.getClass().getName() + ": " + ex.getMessage()
+ " for " + s
+ " got "
+ got.getClass().getName() + ": " + got.getMessage()
+ " - FAILED");
}
private static void pass(String s) {
System.out.println(s + " -- OK");
}
private static void tryCatch(String s, Throwable ex, Runnable thunk) {
Throwable t = null;
try {
thunk.run();
} catch (Throwable x) {
if (ex.getClass().isAssignableFrom(x.getClass()))
t = x;
else
x.printStackTrace();
}
if ((t == null) && (ex != null))
fail(ex, s, t);
String msg = (ex == null ? null : ex.getMessage());
if ((msg != null) && !msg.equals(t.getMessage()))
fail(ex, s, t);
else
pass(s);
}
public static void main(String [] args) {
System.out.println("StringBuilder()");
tryCatch(" no args", null, new Runnable() {
public void run() {
new StringBuilder();
}});
System.out.println("StringBuilder(int length)");
tryCatch(" 1", null, new Runnable() {
public void run() {
new StringBuilder(1);
}});
tryCatch(" -1", new NegativeArraySizeException(), new Runnable() {
public void run() {
new StringBuilder(-1);
}});
System.out.println("StringBuilder(String str)");
tryCatch(" null", new NullPointerException(), new Runnable() {
public void run() {
new StringBuilder(null);
}});
tryCatch(" foo", null, new Runnable() {
public void run() {
new StringBuilder("foo");
}});
System.out.println("StringBuilder.replace(int start, int end, String str)");
tryCatch(" -1, 2, \" \"",
new StringIndexOutOfBoundsException("Range [-1, 2) out of bounds for length 7"),
new Runnable() {
public void run() {
StringBuilder sb = new StringBuilder("hilbert");
sb.replace(-1, 2, " ");
}});
tryCatch(" 7, 8, \" \"",
new StringIndexOutOfBoundsException("Range [7, 6) out of bounds for length 6"),
new Runnable() {
public void run() {
StringBuilder sb = new StringBuilder("banach");
sb.replace(7, 8, " ");
}});
tryCatch(" 2, 1, \" \"",
new StringIndexOutOfBoundsException("Range [2, 1) out of bounds for length 7"),
new Runnable() {
public void run() {
StringBuilder sb = new StringBuilder("riemann");
sb.replace(2, 1, " ");
}});
if (!ok) {
throw new RuntimeException("Some tests FAILED");
} else {
System.out.println("All tests PASSED");
}
}
}

View file

@ -0,0 +1,133 @@
/*
* Copyright (c) 2016, 2025, 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 jdk.internal.util.ArraysSupport;
import java.util.Arrays;
/**
* @test
* @bug 8149330 8218227 8351443
* @summary Capacity should not get close to Integer.MAX_VALUE unless
* necessary
* @modules java.base/jdk.internal.util
* @requires (sun.arch.data.model == "64" & os.maxMemory >= 8G)
* @run main/othervm -Xms8G -Xmx8G -XX:-CompactStrings -Xlog:gc HugeCapacity false
* @run main/othervm -Xms8G -Xmx8G -XX:+CompactStrings -Xlog:gc HugeCapacity true
*/
public class HugeCapacity {
private static int failures = 0;
public static void main(String[] args) {
if (args.length == 0) {
throw new IllegalArgumentException("Need the argument");
}
boolean isCompact = Boolean.parseBoolean(args[0]);
testLatin1(isCompact);
testUtf16();
testHugeInitialString();
testHugeInitialCharSequence();
testHugePlus(isCompact);
if (failures > 0) {
throw new RuntimeException(failures + " tests failed");
}
}
private static void testLatin1(boolean isCompact) {
try {
int divisor = isCompact ? 2 : 4;
StringBuilder sb = new StringBuilder();
sb.ensureCapacity(Integer.MAX_VALUE / divisor);
sb.ensureCapacity(Integer.MAX_VALUE / divisor + 1);
} catch (OutOfMemoryError oom) {
oom.printStackTrace();
failures++;
}
}
private static void testUtf16() {
try {
StringBuilder sb = new StringBuilder();
sb.append('\u042b');
sb.ensureCapacity(Integer.MAX_VALUE / 4);
sb.ensureCapacity(Integer.MAX_VALUE / 4 + 1);
} catch (OutOfMemoryError oom) {
oom.printStackTrace();
failures++;
}
}
private static void testHugeInitialString() {
try {
String str = "Z".repeat(ArraysSupport.SOFT_MAX_ARRAY_LENGTH);
StringBuilder sb = new StringBuilder(str);
} catch (OutOfMemoryError ignore) {
} catch (Throwable unexpected) {
unexpected.printStackTrace();
failures++;
}
}
private static void testHugeInitialCharSequence() {
try {
CharSequence seq = new MyHugeCharSeq();
StringBuilder sb = new StringBuilder(seq);
} catch (OutOfMemoryError ignore) {
} catch (Throwable unexpected) {
unexpected.printStackTrace();
failures++;
}
}
private static class MyHugeCharSeq implements CharSequence {
public char charAt(int i) {
throw new UnsupportedOperationException();
}
public int length() { return Integer.MAX_VALUE; }
public CharSequence subSequence(int st, int e) {
throw new UnsupportedOperationException();
}
public String toString() { return ""; }
}
// Test creating and appending the max size string for -XX:+CompactStrings and -XX:-CompactStrings
private static void testHugePlus(boolean isCompact) {
int repeatCount = (isCompact) ? Integer.MAX_VALUE / 2 - 1 : Integer.MAX_VALUE / 4 - 1;
char[] chars = new char[repeatCount];
char[] aChar = {'A', '\uff21'};
for (char ch : aChar) {
try {
int size = (ch > 0xff) ? repeatCount / 2 : repeatCount;
Arrays.fill(chars, 0, size, ch);
StringBuilder b = new StringBuilder(0);
b.append(chars, 0, size);
b.append(chars, 0, size);
} catch (Throwable unexpected) {
unexpected.printStackTrace();
failures++;
}
}
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2003, 2020, 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.Test;
import static org.testng.Assert.assertEquals;
/**
* @test
* @bug 4914802 8257511
* @summary Test StringBuilder.insert sanity tests
* @run testng/othervm -XX:-CompactStrings Insert
* @run testng/othervm -XX:+CompactStrings Insert
*/
@Test
public class Insert {
public void insertFalse() {
// Caused an infinite loop before 4914802
StringBuilder sb = new StringBuilder();
assertEquals("false", sb.insert(0, false).toString());
}
public void insertOffset() {
// 8254082 made the String variant cause an AIOOBE, fixed in 8257511
assertEquals("efabc", new StringBuilder("abc").insert(0, "def", 1, 3).toString());
assertEquals("efabc", new StringBuilder("abc").insert(0, new StringBuilder("def"), 1, 3).toString());
// insert(I[CII) and insert(ILjava/lang/CharSequence;II) are inconsistently specified
assertEquals("efabc", new StringBuilder("abc").insert(0, new char[] {'d', 'e', 'f'}, 1, 2).toString());
}
}

View file

@ -0,0 +1,302 @@
/*
* Copyright (c) 2025, 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
* @summary Test multi-threaded use of StringBuilder
* @compile --release 8 RacingSBThreads.java
* @run main/othervm -esa RacingSBThreads read
* @run main/othervm -esa RacingSBThreads insert
* @run main/othervm -esa RacingSBThreads append
*/
import java.nio.CharBuffer;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
/**
* Test racing accesses in StringBuilder.
* Test source code should be compatible with JDK 8 to allow testing on older versions.
*/
public class RacingSBThreads {
private static final int TIMEOUT_SEC = 1; // Duration to run each test case
private static final int N = 1_000_000; // static number of iterations for writes and modifies
private static final int LEN = 100_000; // Length of initial SB
// Strings available to be used as the initial contents of a StringBuilder
private static final String UTF16_CHARS = initString('\u1000', LEN);
private static final String LATIN1_CHARS = initString('a', LEN);
// Cache jtreg timeout factor to allow test to be run as a standalone main()
private static final double TIMEOUT_FACTOR = Double.parseDouble(System.getProperty("test.timeout.factor", "1.0"));
// Constant arguments available to be passed to StringBuilder operations
private static final StringBuilder otherSB = new StringBuilder("ab\uFF21\uFF22");
private static final StringBuilder otherLongerSB = new StringBuilder("abcde\uFF21\uFF22\uFF23\uFF24\uFF25");
// Create a String with a repeated character
private static String initString(char c, int len) {
char[] chars = new char[len];
Arrays.fill(chars, c);
return new String(chars);
}
// Plain unsynchronized reference to a StringBuilder
// Updated by the writer thread
// Read by the reader thread
private StringBuilder buf;
// The current stress test case
private final StressKind stressKind;
// Count of faults, zero if no faults found
private final AtomicInteger faultCount = new AtomicInteger(0);
/**
* Run the stress cases indicated by command line arguments or run all cases.
* Running each for TIMEOUT_SEC seconds or until a failure.
* The timeout/test duration can be scaled by setting System property
* `test.timeout.factor` to a double value, for example, `-Dtest.timeout.factor=2.0`
* @param args command line arguments
*/
public static void main(String[] args) {
Duration duration = Duration.ofSeconds((long)(TIMEOUT_SEC * TIMEOUT_FACTOR));
StressKind[] kinds = StressKind.values();
if (args.length > 0) {
// Parse explicitly supplied StressKind arguments
try {
kinds = Arrays.stream(args)
.map((s) -> StressKind.valueOf(s.toUpperCase(Locale.ROOT)))
.toArray(StressKind[]::new);
} catch (Exception ex) {
System.out.println("Invalid StressKind arguments: " + Arrays.toString(args));
return;
}
}
// Run each kind for the duration
int totalFaults = 0;
for (StressKind sk : kinds) {
Instant end = Instant.now().plus(duration); // note clock time, not runtime
while (Instant.now().isBefore(end)) {
int faultCount = new RacingSBThreads(sk).stress();
if (faultCount > 0) {
System.out.printf("ERROR: Test case %s, %d faults%n", sk, faultCount);
}
totalFaults += faultCount;
}
}
if (totalFaults > 0) {
throw new AssertionError("Total faults: " + totalFaults);
}
}
// Enum of the various test cases with a lambda to invoke for each
enum StressKind {
/**
* Reading characters should always be one of the known values being written to the destination
*/
READ(LATIN1_CHARS, (sb, chr) -> {
char ch = sb.charAt(LEN * 4 / 5);
if (ch != chr & ch != (chr & 0xff) & ch != chr >> 8) {
throw new AssertionError("Unexpected characters in buffer: 0x" + Integer.toHexString(ch));
}
}),
/**
* Insert another StringBuilder; in the face of racy changes to the destination
*/
INSERT(LATIN1_CHARS, (sb, C) -> {
sb.insert(sb.length() - 1, otherLongerSB, 0, otherLongerSB.length());
}),
/**
* Appending a StringBuilder in the face of racy changes to the destination
*/
APPEND(LATIN1_CHARS, (sb, C) -> {
sb.append(otherSB, 0, otherSB.length());
}),
;
private final BiConsumer<StringBuilder,Character> func;
private final String sbInitString;
/**
* Defines a test case.
* @param sbInitString the initial contents of the StringBuilder; chooses the coder
* @param func the test BiConsumer to apply to the StringBuilder
*/
private StressKind(String sbInitString, BiConsumer<StringBuilder,Character> func) {
this.func = func;
this.sbInitString = sbInitString;
}
}
public RacingSBThreads(StressKind stressKind) {
this.stressKind = stressKind;
}
/**
* Run the stress case.
* One thread continuously creates a StringBuilder and fills it before trimming it to zero.
* The other thread performs the test case on the same StringBuilder (without any synchronization)
* @return the count of faults
*/
private int stress() {
PokeBuilder r = new PokeBuilder(this, N);
Writer w = new Writer(this, N);
Thread writer = new Thread(w::createShrink);
Thread reader = new Thread(r::readModify);
writer.start();
reader.start();
join(reader);
System.out.println(r);
writer.interrupt();
join(writer);
System.out.println(w);
return r.racing.faultCount.get();
}
/**
* Wait for a thread to terminate.
* @param thread a thread to wait for
*/
private void join(Thread thread) {
do {
try {
thread.join();
break;
} catch (InterruptedException ie) {
// ignore and retry
}
} while (true);
}
/**
* Run a StressKind case in a loop keeping track of exceptions.
* The StringBuilder under test is shared with the writer task without benefit of synchronization.
*/
private static class PokeBuilder {
private final RacingSBThreads racing;
private final int iterations;
private int nulls;
private int bounds;
private int pokeCycles;
private int bufChanges;
public PokeBuilder(RacingSBThreads racing, int iterations) {
this.racing = racing;
this.iterations = iterations;
nulls = 0;
bounds = 0;
pokeCycles = 0;
bufChanges = 0;
}
// Repeatedly change the racy StringBuilder, ignoring and counting exceptions
private void readModify() {
System.out.println("Starting " + racing.stressKind);
sleep(100);
for (int i = 0; i < iterations; ++i) {
pokeCycles++;
StringBuilder sb = racing.buf; // read once
try {
if (sb.length() > Integer.MAX_VALUE / 4) {
sb.setLength(Integer.MAX_VALUE / 4);
}
// Invoke the test case
racing.stressKind.func.accept(sb, racing.stressKind.sbInitString.charAt(0));
if (sb != racing.buf) {
bufChanges++;
}
} catch (NullPointerException e) {
++nulls;
} catch (IndexOutOfBoundsException e) {
++bounds;
} catch (AssertionError ae) {
racing.faultCount.incrementAndGet();
throw ae;
}
}
}
private static void sleep(int i) {
try {
Thread.sleep(i);
} catch (InterruptedException ignored) {
}
}
public String toString() {
return String.format("pokeCycles:%d, bounds:%d, bufChanges:%d, nulls=%d",
pokeCycles, bounds, bufChanges, nulls);
}
}
/**
* Repeatedly create and append strings to a StringBuilder shared through fields of RacingSBThreads.
* The StringBuilder is created new on each iteration and truncated at the end of each iteration.
* Exceptions are counted and reported.
*/
private static class Writer {
private final RacingSBThreads racing;
private final int iterations;
private int sumWriter;
private int writeCycles;
private int putBounds;
public Writer(RacingSBThreads racing, int iterations) {
this.racing = racing;
this.iterations = iterations;
}
private void createShrink() {
for (int i = 0; i < iterations; ++i) {
if (i % 100_000 == 0) {
if (Thread.interrupted()) {
break;
}
}
try {
++writeCycles;
racing.buf = new StringBuilder(racing.stressKind.sbInitString);
racing.buf.append(UTF16_CHARS);
sumWriter += racing.buf.length();
racing.buf.setLength(0);
racing.buf.trimToSize();
} catch (Exception ex) {
++putBounds;
}
}
}
public String toString() {
return String.format("writeCycles:%d, bounds:%d, sumWriter=%d", writeCycles, putBounds, sumWriter);
}
}
}

View file

@ -0,0 +1,337 @@
/*
* Copyright (c) 2025, 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 8351443
* @summary Randomized test of StringBuilder
* @run main StressSBTest
*/
import java.lang.System.Logger;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.CharBuffer;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.IntStream;
public class StressSBTest {
// Logger for info and errors
static final Logger LOG = System.getLogger("StressSBTest");
// Number of concurrent platform threads
private final static int N_THREADS = 11;
// Base length of test run
private final static Duration N_Duration = Duration.ofSeconds(2);
// Cache jtreg timeout factor to allow test to be run as a standalone main()
private static final double TIMEOUT_FACTOR =
Double.parseDouble(System.getProperty("test.timeout.factor", "1.0"));
/**
* Run the stress test with a fixed set of parameters.
*
* @param ignored
*/
public static void main(String[] ignored) {
Duration duration = Duration.ofMillis((long) (N_Duration.toMillis() * TIMEOUT_FACTOR));
new StressSBTest().stress(N_THREADS, duration);
}
/**
* Stress test using a number of platform threads for a duration.
* Success is marked by a lack of exceptions.
* Logging output indicates the number of operations performed by each thread
*
* @param numThreads the number of threads
* @param duration a Duration, typically a few seconds
*/
public void stress(int numThreads, Duration duration) {
StressOps stressOps = new StressOps();
final StringBuilder sb = new StringBuilder("abcdefghijklmnopqrstuvwxyz");
// A single StringBuilder is used to exercise the API in the face of racy behavior
List<Thread> threads = IntStream.range(0, numThreads).mapToObj(i -> {
try {
Thread t = Thread.ofPlatform().start(() -> stressOps.randomStress(sb, duration));
LOG.log(Logger.Level.DEBUG, t);
return t;
} catch (Throwable t) {
LOG.log(Logger.Level.ERROR, "Thread.ofPlatform", t);
}
return null;
})
.filter(t -> t != null)
.toList();
threads.forEach(t -> {
try {
t.join();
} catch (InterruptedException ie) {
// ignore
}
});
LOG.log(Logger.Level.INFO, "Completed: threads: %d, duration: %s".formatted(numThreads, duration));
}
/**
* Methods and support for stress testing StringBuilder.
*/
private static class StressOps {
private static final Random randomGen = new Random();
private static final StringBuilder stringBuilder = new StringBuilder("wxyz");
private static final StringBuffer stringBuffer = new StringBuffer("wxyz");
private static final CharBuffer charBuffer = CharBuffer.allocate(10).append("charBuffer");
private static final char[] charArray = "xyz".toCharArray();
private static final CharSequence charSequence = "now is the time";
// Cache of mappings of all StringBuilder public instand methods mh -> Method
private static Map<MethodHandle, Method> testMethodsCache;
// Active map of SB methods mh -> Method
private final Map<MethodHandle, Method> testMethods;
public StressOps() {
if (testMethodsCache == null) {
testMethodsCache = findTestMethods();
}
testMethods = testMethodsCache;
}
/**
* Stress test randomizes methods acting on a StringBuilder.
* Exceptions are logged but do no affect success/failure result.
*
* @param sb a StringBuilder
* @param duration a Duration of time to stress
*/
public void randomStress(StringBuilder sb, Duration duration) {
MethodHandle[] ops = testMethods.keySet().toArray(new MethodHandle[0]);
int count = 0;
int worked = 0;
Instant end = Instant.now().plus(duration);
while (Instant.now().isBefore(end)) {
int j = randomGen.nextInt(ops.length);
var mh = ops[j];
String name = testMethods.get(mh).getName();
MethodType mt = mh.type();
try {
count++;
if (invokeByMethodNameAndType(name, mt, sb)) {
worked++;
}
} catch (Throwable t) {
LOG.log(Logger.Level.DEBUG, "Exception testing " + name + mt, t);
}
}
LOG.log(Logger.Level.INFO, "StringBuilder method calls: %d, successful: %d".formatted(count, worked));
}
/**
* {@return Return a map of all the public instance methods of java.lang.StringBuilder
* mapping MethodHandle to the Method}
*/
private Map<MethodHandle, Method> findTestMethods() {
MethodHandles.Lookup lookup = MethodHandles.lookup();
Method[] methods = StringBuilder.class.getDeclaredMethods();
Map<MethodHandle, Method> map = new HashMap<>(methods.length);
Arrays.stream(methods)
.filter(m -> Modifier.isPublic(m.getModifiers()) &&
!Modifier.isStatic(m.getModifiers()))
.forEach(m -> {
try {
MethodHandle mh = lookup.unreflect(m);
map.put(mh, m);
} catch (IllegalAccessException iae) {
LOG.log(Logger.Level.DEBUG, "AsbTest annotated method not accessible", iae);
}
});
LOG.log(Logger.Level.DEBUG, "%d StringBuilder test methods".formatted(map.size()));
return map;
}
/**
* Invoke the indicated method on the StringBuilder
*
* @param name the method name
* @param mt the method type
* @param sb a StringBuilder
* @return true if the method returned a non-null result
*/
private boolean invokeByMethodNameAndType(String name, MethodType mt, StringBuilder sb) {
String s = name + mt.toString();
Object o = switch (s) {
case "length(StringBuilder)int" -> sb.length();
case "toString(StringBuilder)String" -> sb.toString();
case "append(StringBuilder,StringBuffer)AbstractStringBuilder" ->
sb.append(stringBuffer);
case "append(StringBuilder,StringBuffer)StringBuilder" -> sb.append(stringBuffer);
case "append(StringBuilder,CharSequence)AbstractStringBuilder" ->
sb.append(charBuffer);
case "append(StringBuilder,CharSequence)StringBuilder" -> sb.append(charBuffer);
case "append(StringBuilder,CharSequence)Appendable" -> sb.append(charBuffer);
case "append(StringBuilder,CharSequence,int,int)AbstractStringBuilder" ->
sb.append("abc", 1, 2);
case "append(StringBuilder,CharSequence,int,int)StringBuilder" ->
sb.append("abc", 1, 2);
case "append(StringBuilder,CharSequence,int,int)Appendable" ->
sb.append("abc", 1, 2);
case "append(StringBuilder,char[])AbstractStringBuilder" -> sb.append(charArray);
case "append(StringBuilder,char[])StringBuilder" -> sb.append(charArray);
case "append(StringBuilder,String)AbstractStringBuilder" -> sb.append("Abcdefg");
case "append(StringBuilder,String)StringBuilder" -> sb.append("abcdefg");
case "append(StringBuilder,Object)AbstractStringBuilder" -> new Object();
case "append(StringBuilder,Object)StringBuilder" -> sb.append(new Object());
case "append(StringBuilder,char)Appendable" -> sb.append('\uff21');
case "append(StringBuilder,char)AbstractStringBuilder" -> sb.append('A');
case "append(StringBuilder,char)StringBuilder" -> sb.append('B');
case "append(StringBuilder,int)AbstractStringBuilder" -> sb.append(987654321);
case "append(StringBuilder,int)StringBuilder" -> sb.append(987654321);
case "append(StringBuilder,long)AbstractStringBuilder" ->
sb.append(987654321987654321L);
case "append(StringBuilder,long)StringBuilder" -> sb.append(987654321987654321L);
case "append(StringBuilder,float)AbstractStringBuilder" -> sb.append(Math.PI);
case "append(StringBuilder,float)StringBuilder" -> sb.append(Math.PI);
case "append(StringBuilder,double)AbstractStringBuilder" -> sb.append(Math.TAU);
case "append(StringBuilder,double)StringBuilder" -> sb.append(Math.TAU);
case "append(StringBuilder,boolean)AbstractStringBuilder" -> sb.append(true);
case "append(StringBuilder,boolean)StringBuilder" -> sb.append(false);
case "append(StringBuilder,char[],int,int)AbstractStringBuilder" ->
sb.append(charArray, 3, 4);
case "append(StringBuilder,char[],int,int)StringBuilder" ->
sb.append(charArray, 4, 3);
case "reverse(StringBuilder)StringBuilder" -> sb.reverse();
case "reverse(StringBuilder)AbstractStringBuilder" -> sb.reverse();
case "getChars(StringBuilder,int,int,char[],int)void" -> {
sb.getChars(2, 4, charArray, 3);
yield sb;
}
case "compareTo(StringBuilder,Object)int" -> sb.compareTo(stringBuilder);
case "compareTo(StringBuilder,StringBuilder)int" -> sb.compareTo(stringBuilder);
case "indexOf(StringBuilder,String,int)int" -> sb.indexOf("A", 2);
case "indexOf(StringBuilder,String)int" -> sb.indexOf("B");
case "insert(StringBuilder,int,CharSequence)StringBuilder" ->
sb.insert(2, charSequence);
case "insert(StringBuilder,int,CharSequence)AbstractStringBuilder" ->
sb.insert(2, charSequence);
case "insert(StringBuilder,int,String)StringBuilder" -> sb.insert(2, "Now");
case "insert(StringBuilder,int,String)AbstractStringBuilder" ->
sb.insert(2, "Then");
case "insert(StringBuilder,int,char[])StringBuilder" -> sb.insert(4, charArray);
case "insert(StringBuilder,int,char[])AbstractStringBuilder" ->
sb.insert(4, charArray);
case "insert(StringBuilder,int,Object)AbstractStringBuilder" ->
sb.insert(3, new Object());
case "insert(StringBuilder,int,Object)StringBuilder" -> sb.insert(4, "RUST");
case "insert(StringBuilder,int,char[],int,int)AbstractStringBuilder" ->
sb.insert(2, charArray, 0, 4);
case "insert(StringBuilder,int,char[],int,int)StringBuilder" ->
sb.insert(2, charArray, 0, 4);
case "insert(StringBuilder,int,int)StringBuilder" -> sb.insert(2, 46000);
case "insert(StringBuilder,int,int)AbstractStringBuilder" -> sb.insert(3, 47000);
case "insert(StringBuilder,int,double)StringBuilder" -> sb.insert(2, Math.PI);
case "insert(StringBuilder,int,double)AbstractStringBuilder" ->
sb.insert(2, Math.PI);
case "insert(StringBuilder,int,float)StringBuilder" -> sb.insert(2, Math.TAU);
case "insert(StringBuilder,int,float)AbstractStringBuilder" ->
sb.insert(2, Math.TAU);
case "insert(StringBuilder,int,long)StringBuilder" -> sb.insert(2, 42L);
case "insert(StringBuilder,int,long)AbstractStringBuilder" -> sb.insert(2, 42L);
case "insert(StringBuilder,int,char)StringBuilder" -> sb.insert(2, 'Z');
case "insert(StringBuilder,int,char)AbstractStringBuilder" -> sb.insert(2, 'Z');
case "insert(StringBuilder,int,boolean)StringBuilder" -> sb.insert(2, true);
case "insert(StringBuilder,int,boolean)AbstractStringBuilder" ->
sb.insert(3, false);
case "insert(StringBuilder,int,CharSequence,int,int)StringBuilder" ->
sb.insert(4, charSequence, 4, 5);
case "insert(StringBuilder,int,CharSequence,int,int)AbstractStringBuilder" ->
sb.insert(4, charSequence, 5, 4);
case "charAt(StringBuilder,int)char" -> sb.charAt(5);
case "codePointAt(StringBuilder,int)int" -> sb.codePointAt(4);
case "codePointBefore(StringBuilder,int)int" -> sb.codePointBefore(3);
case "codePointCount(StringBuilder,int,int)int" -> sb.codePointCount(3, 9);
case "offsetByCodePoints(StringBuilder,int,int)int" -> sb.offsetByCodePoints(3, 7);
case "lastIndexOf(StringBuilder,String,int)int" -> sb.lastIndexOf("A", 45);
case "lastIndexOf(StringBuilder,String)int" -> sb.lastIndexOf("B");
case "substring(StringBuilder,int)String" -> sb.substring(6);
case "substring(StringBuilder,int,int)String" -> sb.substring(6, 9);
case "replace(StringBuilder,int,int,String)StringBuilder" ->
sb.replace(2, 5, "xyz");
case "replace(StringBuilder,int,int,String)AbstractStringBuilder" ->
sb.replace(2, 5, "XYZ");
case "repeat(StringBuilder,CharSequence,int)StringBuilder" -> sb.repeat('Z', 6);
case "repeat(StringBuilder,CharSequence,int)AbstractStringBuilder" ->
sb.repeat('X', 42);
case "repeat(StringBuilder,int,int)AbstractStringBuilder" -> sb.repeat(101, 25);
case "repeat(StringBuilder,int,int)StringBuilder" -> sb.repeat(102, 24);
case "codePoints(StringBuilder)IntStream" -> sb.codePoints().count();
case "subSequence(StringBuilder,int,int)CharSequence" -> sb.subSequence(3, 9);
case "chars(StringBuilder)IntStream" -> sb.chars().count();
case "setLength(StringBuilder,int)void" -> {
sb.setLength(45);
yield 45;
}
case "capacity(StringBuilder)int" -> sb.capacity();
case "ensureCapacity(StringBuilder,int)void" -> {
sb.ensureCapacity(150);
yield 150;
}
case "trimToSize(StringBuilder)void" -> {
sb.trimToSize();
yield sb.length();
}
case "setCharAt(StringBuilder,int,char)void" -> {
sb.setCharAt(6, 'T');
yield 'T';
}
case "appendCodePoint(StringBuilder,int)StringBuilder" ->
sb.appendCodePoint(0xff21);
case "appendCodePoint(StringBuilder,int)AbstractStringBuilder" ->
sb.appendCodePoint(0xff21);
case "delete(StringBuilder,int,int)StringBuilder" -> sb.delete(5, 20);
case "delete(StringBuilder,int,int)AbstractStringBuilder" -> sb.delete(6, 21);
case "deleteCharAt(StringBuilder,int)AbstractStringBuilder" -> sb.deleteCharAt(7);
case "deleteCharAt(StringBuilder,int)StringBuilder" -> sb.deleteCharAt(8);
case "equals(Object,Object)boolean" -> false;
case "hashCode(Object)int" -> sb.hashCode();
case "getClass(Object)Class" -> sb.getClass();
case "isEmpty(CharSequence)boolean" -> null;
default -> {
System.out.println("not executing: " + s);
yield null;
}
};
return o != null;
}
}
}

View file

@ -0,0 +1,207 @@
/*
* Copyright (c) 2023, 2024, 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.Test;
import static org.testng.Assert.assertEquals;
import java.util.Arrays;
/**
* @test
* @bug 8302323 8322512
* @summary Test StringBuffer.repeat sanity tests
* @run testng/othervm -XX:-CompactStrings StringBufferRepeat
* @run testng/othervm -XX:+CompactStrings StringBufferRepeat
*/
@Test
public class StringBufferRepeat {
private static class MyChars implements CharSequence {
private static final char[] DATA = new char[] { 'a', 'b', 'c' };
@Override
public int length() {
return DATA.length;
}
@Override
public char charAt(int index) {
return DATA[index];
}
@Override
public CharSequence subSequence(int start, int end) {
return new String(Arrays.copyOfRange(DATA, start, end));
}
}
private static final MyChars MYCHARS = new MyChars();
public void sanity() {
StringBuffer sb = new StringBuffer();
// prime the StringBuffer
sb.append("repeat");
// single character Latin1
sb.repeat('1', 0);
sb.repeat('2', 1);
sb.repeat('3', 5);
// single string Latin1 (optimized)
sb.repeat("1", 0);
sb.repeat("2", 1);
sb.repeat("3", 5);
// multi string Latin1
sb.repeat("-1", 0);
sb.repeat("-2", 1);
sb.repeat("-3", 5);
// single character UTF16
sb.repeat('\u2460', 0);
sb.repeat('\u2461', 1);
sb.repeat('\u2462', 5);
// single string UTF16 (optimized)
sb.repeat("\u2460", 0);
sb.repeat("\u2461", 1);
sb.repeat("\u2462", 5);
// multi string UTF16
sb.repeat("-\u2460", 0);
sb.repeat("-\u2461", 1);
sb.repeat("-\u2462", 5);
// CharSequence
sb.repeat(MYCHARS, 3);
// null
sb.repeat((String)null, 0);
sb.repeat((String)null, 1);
sb.repeat((String)null, 5);
sb.repeat((CharSequence)null, 0);
sb.repeat((CharSequence)null, 1);
sb.repeat((CharSequence)null, 5);
String expected = "repeat233333233333-2-3-3-3-3-3\u2461\u2462\u2462\u2462\u2462\u2462\u2461\u2462\u2462\u2462\u2462\u2462-\u2461-\u2462-\u2462-\u2462-\u2462-\u2462abcabcabc" +
"nullnullnullnullnullnullnullnullnullnullnullnull";
assertEquals(expected, sb.toString());
// Codepoints
sb.setLength(0);
sb.repeat(0, 0);
sb.repeat(0, 1);
sb.repeat(0, 5);
sb.repeat((int)' ', 0);
sb.repeat((int)' ', 1);
sb.repeat((int)' ', 5);
sb.repeat(0x2460, 0);
sb.repeat(0x2461, 1);
sb.repeat(0x2462, 5);
sb.repeat(0x10FFFF, 0);
sb.repeat(0x10FFFF, 1);
sb.repeat(0x10FFFF, 5);
expected = "\u0000\u0000\u0000\u0000\u0000\u0000\u0020\u0020\u0020\u0020\u0020\u0020\u2461\u2462\u2462\u2462\u2462\u2462\udbff\udfff\udbff\udfff\udbff\udfff\udbff\udfff\udbff\udfff\udbff\udfff";
assertEquals(expected, sb.toString());
// toStringCache
sb.setLength(0);
sb.toString();
sb.repeat('*', 5);
expected = "*****";
assertEquals(sb.toString(), expected);
sb.setLength(0);
sb.toString();
sb.repeat("*", 5);
assertEquals(sb.toString(), expected);
}
public void exceptions() {
StringBuffer sb = new StringBuffer();
try {
sb.repeat(' ', Integer.MAX_VALUE);
throw new RuntimeException("No OutOfMemoryError thrown");
} catch (OutOfMemoryError | IndexOutOfBoundsException ex) {
// Okay
}
try {
sb.repeat(" ", Integer.MAX_VALUE);
throw new RuntimeException("No OutOfMemoryError thrown");
} catch (OutOfMemoryError | IndexOutOfBoundsException ex) {
// Okay
}
try {
sb.repeat(MYCHARS, Integer.MAX_VALUE);
throw new RuntimeException("No OutOfMemoryError thrown");
} catch (OutOfMemoryError | IndexOutOfBoundsException ex) {
// Okay
}
try {
sb.repeat(' ', -1);
throw new RuntimeException("No IllegalArgumentException thrown");
} catch (IllegalArgumentException ex) {
// Okay
}
try {
sb.repeat("abc", -1);
throw new RuntimeException("No IllegalArgumentException thrown");
} catch (IllegalArgumentException ex) {
// Okay
}
try {
sb.repeat(MYCHARS, -1);
throw new RuntimeException("No IllegalArgumentException thrown");
} catch (IllegalArgumentException ex) {
// Okay
}
try {
sb.repeat(0x10FFFF + 1, -1);
throw new RuntimeException("No IllegalArgumentException thrown");
} catch (IllegalArgumentException ex) {
// Okay
}
try {
sb.repeat(-1, -1);
throw new RuntimeException("No IllegalArgumentException thrown");
} catch (IllegalArgumentException ex) {
// Okay
}
}
}

View file

@ -0,0 +1,194 @@
/*
* Copyright (c) 2023, 2025, 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.Test;
import static org.testng.Assert.assertEquals;
import java.util.Arrays;
/**
* @test
* @bug 8302323 8351443
* @summary Test StringBuilder.repeat sanity tests
* @run testng/othervm -XX:-CompactStrings StringBuilderRepeat
* @run testng/othervm -XX:+CompactStrings StringBuilderRepeat
*/
@Test
public class StringBuilderRepeat {
private static class MyChars implements CharSequence {
private static final char[] DATA = new char[] { 'a', 'b', 'c' };
@Override
public int length() {
return DATA.length;
}
@Override
public char charAt(int index) {
return DATA[index];
}
@Override
public CharSequence subSequence(int start, int end) {
return new String(Arrays.copyOfRange(DATA, start, end));
}
}
private static final MyChars MYCHARS = new MyChars();
public void sanity() {
StringBuilder sb = new StringBuilder();
// prime the StringBuilder
sb.append("repeat");
// single character Latin1
sb.repeat('1', 0);
sb.repeat('2', 1);
sb.repeat('3', 5);
// single string Latin1 (optimized)
sb.repeat("1", 0);
sb.repeat("2", 1);
sb.repeat("3", 5);
// multi string Latin1
sb.repeat("-1", 0);
sb.repeat("-2", 1);
sb.repeat("-3", 5);
// single character UTF16
sb.repeat('\u2460', 0);
sb.repeat('\u2461', 1);
sb.repeat('\u2462', 5);
// single string UTF16 (optimized)
sb.repeat("\u2460", 0);
sb.repeat("\u2461", 1);
sb.repeat("\u2462", 5);
// multi string UTF16
sb.repeat("-\u2460", 0);
sb.repeat("-\u2461", 1);
sb.repeat("-\u2462", 5);
// CharSequence
sb.repeat(MYCHARS, 3);
// null
sb.repeat((String)null, 0);
sb.repeat((String)null, 1);
sb.repeat((String)null, 5);
sb.repeat((CharSequence)null, 0);
sb.repeat((CharSequence)null, 1);
sb.repeat((CharSequence)null, 5);
String expected = "repeat233333233333-2-3-3-3-3-3\u2461\u2462\u2462\u2462\u2462\u2462\u2461\u2462\u2462\u2462\u2462\u2462-\u2461-\u2462-\u2462-\u2462-\u2462-\u2462abcabcabc" +
"nullnullnullnullnullnullnullnullnullnullnullnull";
assertEquals(sb.toString(), expected);
// Codepoints
sb.setLength(0);
sb.repeat(0, 0);
sb.repeat(0, 1);
sb.repeat(0, 5);
sb.repeat((int)' ', 0);
sb.repeat((int)' ', 1);
sb.repeat((int)' ', 5);
sb.repeat(0x2460, 0);
sb.repeat(0x2461, 1);
sb.repeat(0x2462, 5);
sb.repeat(0x10FFFF, 0);
sb.repeat(0x10FFFF, 1);
sb.repeat(0x10FFFF, 5);
expected = "\u0000\u0000\u0000\u0000\u0000\u0000\u0020\u0020\u0020\u0020\u0020\u0020\u2461\u2462\u2462\u2462\u2462\u2462\udbff\udfff\udbff\udfff\udbff\udfff\udbff\udfff\udbff\udfff\udbff\udfff";
assertEquals(sb.toString(), expected);
}
public void exceptions() {
StringBuilder sb = new StringBuilder();
try {
sb.repeat(' ', Integer.MAX_VALUE);
throw new RuntimeException("No OutOfMemoryError thrown");
} catch (OutOfMemoryError | IndexOutOfBoundsException ex) {
// Okay
}
try {
sb.repeat(" ", Integer.MAX_VALUE);
throw new RuntimeException("No OutOfMemoryError thrown");
} catch (OutOfMemoryError | IndexOutOfBoundsException ex) {
// Okay
}
try {
sb.repeat(MYCHARS, Integer.MAX_VALUE);
throw new RuntimeException("No OutOfMemoryError thrown");
} catch (OutOfMemoryError | IndexOutOfBoundsException ex) {
// Okay
}
try {
sb.repeat(' ', -1);
throw new RuntimeException("No IllegalArgumentException thrown");
} catch (IllegalArgumentException ex) {
// Okay
}
try {
sb.repeat("abc", -1);
throw new RuntimeException("No IllegalArgumentException thrown");
} catch (IllegalArgumentException ex) {
// Okay
}
try {
sb.repeat(MYCHARS, -1);
throw new RuntimeException("No IllegalArgumentException thrown");
} catch (IllegalArgumentException ex) {
// Okay
}
try {
sb.repeat(0x10FFFF + 1, -1);
throw new RuntimeException("No IllegalArgumentException thrown");
} catch (IllegalArgumentException ex) {
// Okay
}
try {
sb.repeat(-1, -1);
throw new RuntimeException("No IllegalArgumentException thrown");
} catch (IllegalArgumentException ex) {
// Okay
}
}
}

View file

@ -0,0 +1,431 @@
/*
* Copyright (c) 2003, 2013, 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 4533872 4915683 4985217 5017280
* @summary Unit tests for supplementary character support (JSR-204)
*/
public class Supplementary {
public static void main(String[] args) {
test1(); // Test for codePointAt(int index)
test2(); // Test for codePointBefore(int index)
test3(); // Test for reverse()
test4(); // Test for appendCodePoint(int codePoint)
test5(); // Test for codePointCount(int beginIndex, int endIndex)
test6(); // Test for offsetByCodePoints(int index, int offset)
testDontReadOutOfBoundsTrailingSurrogate();
}
/* Text strings which are used as input data.
* The comment above each text string means the index of each 16-bit char
* for convenience.
*/
static final String[] input = {
/* 111 1 111111 22222
0123 4 5678 9 012 3 456789 01234 */
"abc\uD800\uDC00def\uD800\uD800ab\uD800\uDC00cdefa\uDC00bcdef",
/* 1 1111 1111 1 222
0 12345 6789 0 1234 5678 9 012 */
"\uD800defg\uD800hij\uD800\uDC00klm\uDC00nop\uDC00\uD800rt\uDC00",
/* 11 1 1111 1 112 222
0 12345 6 78901 2 3456 7 890 123 */
"\uDC00abcd\uDBFF\uDFFFefgh\uD800\uDC009ik\uDC00\uDC00lm\uDC00no\uD800",
/* 111 111111 1 22 2
0 1 2345 678 9 012 345678 9 01 2 */
"\uD800\uDC00!#$\uD800%&\uD800\uDC00;+\uDC00<>;=^\uDC00\\@\uD800\uDC00",
// includes an undefined supplementary character in Unicode 4.0.0
/* 1 11 1 1111 1
0 1 2345 6 789 0 12 3 4567 8 */
"\uDB40\uDE00abc\uDE01\uDB40de\uDB40\uDE02f\uDB40\uDE03ghi\uDB40\uDE02",
};
/* Expected results for:
* test1(): for codePointAt()
*
* Each character in each array is the golden data for each text string
* in the above input data. For example, the first data in each array is
* for the first input string.
*/
static final int[][] golden1 = {
{'a', 0xD800, 0xDC00, 0x10000, 0xE0200}, // codePointAt(0)
{0xD800, 0x10000, 'g', 0xDC00, 0xE0202}, // codePointAt(9)
{'f', 0xDC00, 0xD800, 0xDC00, 0xDE02}, // codePointAt(length-1)
};
/*
* Test for codePointAt(int index) method
*/
static void test1() {
for (int i = 0; i < input.length; i++) {
StringBuilder sb = new StringBuilder(input[i]);
/*
* Normal case
*/
testCodePoint(At, sb, 0, golden1[0][i]);
testCodePoint(At, sb, 9, golden1[1][i]);
testCodePoint(At, sb, sb.length()-1, golden1[2][i]);
/*
* Abnormal case - verify that an exception is thrown.
*/
testCodePoint(At, sb, -1);
testCodePoint(At, sb, sb.length());
}
}
/* Expected results for:
* test2(): for codePointBefore()
*
* Each character in each array is the golden data for each text string
* in the above input data. For example, the first data in each array is
* for the first input string.
*/
static final int[][] golden2 = {
{'a', 0xD800, 0xDC00, 0xD800, 0xDB40}, // codePointBefore(1)
{0xD800, 'l', 0x10000, 0xDC00, 0xDB40}, // codePointBefore(13)
{'f', 0xDC00, 0xD800, 0x10000, 0xE0202}, // codePointBefore(length)
};
/*
* Test for codePointBefore(int index) method
*/
static void test2() {
for (int i = 0; i < input.length; i++) {
StringBuilder sb = new StringBuilder(input[i]);
/*
* Normal case
*/
testCodePoint(Before, sb, 1, golden2[0][i]);
testCodePoint(Before, sb, 13, golden2[1][i]);
testCodePoint(Before, sb, sb.length(), golden2[2][i]);
/*
* Abnormal case - verify that an exception is thrown.
*/
testCodePoint(Before, sb, 0);
testCodePoint(Before, sb, sb.length()+1);
}
}
/* Expected results for:
* test3(): for reverse()
*
* Unlike golden1 and golden2, each array is the golden data for each text
* string in the above input data. For example, the first array is for
* the first input string.
*/
static final String[] golden3 = {
"fedcb\uDC00afedc\uD800\uDC00ba\uD800\uD800fed\uD800\uDC00cba",
"\uDC00tr\uD800\uDC00pon\uDC00mlk\uD800\uDC00jih\uD800gfed\uD800",
"\uD800on\uDC00ml\uDC00\uDC00ki9\uD800\uDC00hgfe\uDBFF\uDFFFdcba\uDC00",
"\uD800\uDC00@\\\uDC00^=;><\uDC00+;\uD800\uDC00&%\uD800$#!\uD800\uDC00",
// includes an undefined supplementary character in Unicode 4.0.0
"\uDB40\uDE02ihg\uDB40\uDE03f\uDB40\uDE02ed\uDB40\uDE01cba\uDB40\uDE00",
};
// Additional input data & expected result for test3()
static final String[][] testdata1 = {
{"a\uD800\uDC00", "\uD800\uDC00a"},
{"a\uDC00\uD800", "\uD800\uDC00a"},
{"\uD800\uDC00a", "a\uD800\uDC00"},
{"\uDC00\uD800a", "a\uD800\uDC00"},
{"\uDC00\uD800\uD801", "\uD801\uD800\uDC00"},
{"\uDC00\uD800\uDC01", "\uD800\uDC01\uDC00"},
{"\uD801\uD800\uDC00", "\uD800\uDC00\uD801"},
{"\uD800\uDC01\uDC00", "\uDC00\uD800\uDC01"},
{"\uD800\uDC00\uDC01\uD801", "\uD801\uDC01\uD800\uDC00"},
};
/*
* Test for reverse() method
*/
static void test3() {
for (int i = 0; i < input.length; i++) {
StringBuilder sb = new StringBuilder(input[i]).reverse();
check(!golden3[i].equals(sb.toString()),
"reverse() for <" + toHexString(input[i]) + ">",
sb, golden3[i]);
}
for (int i = 0; i < testdata1.length; i++) {
StringBuilder sb = new StringBuilder(testdata1[i][0]).reverse();
check(!testdata1[i][1].equals(sb.toString()),
"reverse() for <" + toHexString(testdata1[i][0]) + ">",
sb, testdata1[i][1]);
}
}
/**
* Test for appendCodePoint() method
*/
static void test4() {
for (int i = 0; i < input.length; i++) {
String s = input[i];
StringBuilder sb = new StringBuilder();
int c;
for (int j = 0; j < s.length(); j += Character.charCount(c)) {
c = s.codePointAt(j);
StringBuilder rsb = sb.appendCodePoint(c);
check(sb != rsb, "appendCodePoint returned a wrong object");
int sbc = sb.codePointAt(j);
check(sbc != c, "appendCodePoint("+j+") != c", sbc, c);
}
check(!s.equals(sb.toString()),
"appendCodePoint() produced a wrong result with input["+i+"]");
}
// test exception
testAppendCodePoint(-1, IllegalArgumentException.class);
testAppendCodePoint(Character.MAX_CODE_POINT+1, IllegalArgumentException.class);
}
/**
* Test codePointCount(int, int)
*
* This test case assumes that
* Character.codePointCount(CharSequence, int, int) works
* correctly.
*/
static void test5() {
for (int i = 0; i < input.length; i++) {
String s = input[i];
StringBuilder sb = new StringBuilder(s);
int length = sb.length();
for (int j = 0; j <= length; j++) {
int result = sb.codePointCount(j, length);
int expected = Character.codePointCount(sb, j, length);
check(result != expected, "codePointCount(input["+i+"], "+j+", "+length+")",
result, expected);
}
for (int j = length; j >= 0; j--) {
int result = sb.codePointCount(0, j);
int expected = Character.codePointCount(sb, 0, j);
check(result != expected, "codePointCount(input["+i+"], 0, "+j+")",
result, expected);
}
// test exceptions
testCodePointCount(null, 0, 0, NullPointerException.class);
testCodePointCount(sb, -1, length, IndexOutOfBoundsException.class);
testCodePointCount(sb, 0, length+1, IndexOutOfBoundsException.class);
testCodePointCount(sb, length, length-1, IndexOutOfBoundsException.class);
}
}
/**
* Test offsetByCodePoints(int, int)
*
* This test case assumes that
* Character.codePointCount(CharSequence, int, int) works
* correctly.
*/
static void test6() {
for (int i = 0; i < input.length; i++) {
String s = input[i];
StringBuilder sb = new StringBuilder(s);
int length = s.length();
for (int j = 0; j <= length; j++) {
int nCodePoints = Character.codePointCount(sb, j, length);
int result = sb.offsetByCodePoints(j, nCodePoints);
check(result != length,
"offsetByCodePoints(input["+i+"], "+j+", "+nCodePoints+")",
result, length);
result = sb.offsetByCodePoints(length, -nCodePoints);
int expected = j;
if (j > 0 && j < length) {
int cp = sb.codePointBefore(j+1);
if (Character.isSupplementaryCodePoint(cp)) {
expected--;
}
}
check(result != expected,
"offsetByCodePoints(input["+i+"], "+j+", "+(-nCodePoints)+")",
result, expected);
}
for (int j = length; j >= 0; j--) {
int nCodePoints = Character.codePointCount(sb, 0, j);
int result = sb.offsetByCodePoints(0, nCodePoints);
int expected = j;
if (j > 0 && j < length) {
int cp = sb.codePointAt(j-1);
if (Character.isSupplementaryCodePoint(cp)) {
expected++;
}
}
check(result != expected,
"offsetByCodePoints(input["+i+"], 0, "+nCodePoints+")",
result, expected);
result = sb.offsetByCodePoints(j, -nCodePoints);
check(result != 0,
"offsetBycodePoints(input["+i+"], "+j+", "+(-nCodePoints)+")",
result, 0);
}
// test exceptions
testOffsetByCodePoints(null, 0, 0, NullPointerException.class);
testOffsetByCodePoints(sb, -1, length, IndexOutOfBoundsException.class);
testOffsetByCodePoints(sb, 0, length+1, IndexOutOfBoundsException.class);
testOffsetByCodePoints(sb, 1, -2, IndexOutOfBoundsException.class);
testOffsetByCodePoints(sb, length, length-1, IndexOutOfBoundsException.class);
testOffsetByCodePoints(sb, length, -(length+1), IndexOutOfBoundsException.class);
}
}
static void testDontReadOutOfBoundsTrailingSurrogate() {
StringBuilder sb = new StringBuilder();
int suppl = Character.MIN_SUPPLEMENTARY_CODE_POINT;
sb.appendCodePoint(suppl);
check(sb.codePointAt(0) != (int) suppl,
"codePointAt(0)", sb.codePointAt(0), suppl);
check(sb.length() != 2, "sb.length()");
sb.setLength(1);
check(sb.length() != 1, "sb.length()");
check(sb.codePointAt(0) != Character.highSurrogate(suppl),
"codePointAt(0)",
sb.codePointAt(0), Character.highSurrogate(suppl));
}
static final boolean At = true, Before = false;
static void testCodePoint(boolean isAt, StringBuilder sb, int index, int expected) {
int c = isAt ? sb.codePointAt(index) : sb.codePointBefore(index);
check(c != expected,
"codePoint" + (isAt ? "At" : "Before") + "(" + index + ") for <"
+ sb + ">", c, expected);
}
static void testCodePoint(boolean isAt, StringBuilder sb, int index) {
boolean exceptionOccurred = false;
try {
int c = isAt ? sb.codePointAt(index) : sb.codePointBefore(index);
}
catch (StringIndexOutOfBoundsException e) {
exceptionOccurred = true;
}
check(!exceptionOccurred,
"codePoint" + (isAt ? "At" : "Before") + "(" + index + ") for <"
+ sb + "> should throw StringIndexOutOfBoundsPointerException.");
}
static void testAppendCodePoint(int codePoint, Class expectedException) {
try {
new StringBuilder().appendCodePoint(codePoint);
} catch (Exception e) {
if (expectedException.isInstance(e)) {
return;
}
throw new RuntimeException("Error: Unexpected exception", e);
}
check(true, "appendCodePoint(" + toHexString(codePoint) + ") didn't throw "
+ expectedException.getName());
}
static void testCodePointCount(StringBuilder sb, int beginIndex, int endIndex,
Class expectedException) {
try {
int n = sb.codePointCount(beginIndex, endIndex);
} catch (Exception e) {
if (expectedException.isInstance(e)) {
return;
}
throw new RuntimeException("Error: Unexpected exception", e);
}
check(true, "codePointCount() didn't throw " + expectedException.getName());
}
static void testOffsetByCodePoints(StringBuilder sb, int index, int offset,
Class expectedException) {
try {
int n = sb.offsetByCodePoints(index, offset);
} catch (Exception e) {
if (expectedException.isInstance(e)) {
return;
}
throw new RuntimeException("Error: Unexpected exception", e);
}
check(true, "offsetByCodePoints() didn't throw " + expectedException.getName());
}
static void check(boolean err, String msg) {
if (err) {
throw new RuntimeException("Error: " + msg);
}
}
static void check(boolean err, String s, int got, int expected) {
if (err) {
throw new RuntimeException("Error: " + s
+ " returned an unexpected value. got "
+ toHexString(got)
+ ", expected "
+ toHexString(expected));
}
}
static void check(boolean err, String s, StringBuilder got, String expected) {
if (err) {
throw new RuntimeException("Error: " + s
+ " returned an unexpected value. got <"
+ toHexString(got.toString())
+ ">, expected <"
+ toHexString(expected)
+ ">");
}
}
private static String toHexString(int c) {
return "0x" + Integer.toHexString(c);
}
private static String toHexString(String s) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
sb.append(" 0x");
if (c < 0x10) sb.append('0');
if (c < 0x100) sb.append('0');
if (c < 0x1000) sb.append('0');
sb.append(Integer.toHexString(c));
}
sb.append(' ');
return sb.toString();
}
}