Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 25 additions & 35 deletions src/java.base/share/classes/java/lang/String.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import jdk.internal.util.ArraysSupport;
import jdk.internal.util.Preconditions;
import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.vm.annotation.IntrinsicCandidate;
Expand Down Expand Up @@ -1272,8 +1273,7 @@ private static void throwUnmappable(int off) {
}

private static void throwUnmappable(byte[] val) {
int dp = 0;
while (dp < val.length && val[dp] >=0) { dp++; }
int dp = StringCoding.countPositives(val, 0, val.length);
throwUnmappable(dp);
}

Expand Down Expand Up @@ -1870,23 +1870,17 @@ private boolean nonSyncContentEquals(AbstractStringBuilder sb) {
if (len != sb.length()) {
return false;
}
byte v1[] = value;
byte v2[] = sb.getValue();
byte[] v1 = value;
byte[] v2 = sb.getValue();
byte coder = coder();
if (coder == sb.getCoder()) {
int n = v1.length;
for (int i = 0; i < n; i++) {
if (v1[i] != v2[i]) {
return false;
}
}
return v1.length <= v2.length && ArraysSupport.mismatch(v1, v2, v1.length) < 0;
} else {
if (coder != LATIN1) { // utf16 str and latin1 abs can never be "equal"
return false;
}
return StringUTF16.contentEquals(v1, v2, len);
}
return true;
}

/**
Expand Down Expand Up @@ -2024,8 +2018,8 @@ public boolean equalsIgnoreCase(String anotherString) {
* lexicographically greater than the string argument.
*/
public int compareTo(String anotherString) {
byte v1[] = value;
byte v2[] = anotherString.value;
byte[] v1 = value;
byte[] v2 = anotherString.value;
byte coder = coder();
if (coder == anotherString.coder()) {
return coder == LATIN1 ? StringLatin1.compareTo(v1, v2)
Expand Down Expand Up @@ -2060,8 +2054,8 @@ private static class CaseInsensitiveComparator
private static final long serialVersionUID = 8575799808933029326L;

public int compare(String s1, String s2) {
byte v1[] = s1.value;
byte v2[] = s2.value;
byte[] v1 = s1.value;
byte[] v2 = s2.value;
byte coder = s1.coder();
if (coder == s2.coder()) {
return coder == LATIN1 ? StringLatin1.compareToCI(v1, v2)
Expand Down Expand Up @@ -2136,26 +2130,23 @@ public int compareToIgnoreCase(String str) {
* {@code false} otherwise.
*/
public boolean regionMatches(int toffset, String other, int ooffset, int len) {
byte tv[] = value;
byte ov[] = other.value;
// Note: toffset, ooffset, or len might be near -1>>>1.
if ((ooffset < 0) || (toffset < 0) ||
(toffset > (long)length() - len) ||
(ooffset > (long)other.length() - len)) {
return false;
}
byte[] tv = value;
byte[] ov = other.value;
byte coder = coder();
if (coder == other.coder()) {
if (!isLatin1() && (len > 0)) {
toffset = toffset << 1;
ooffset = ooffset << 1;
len = len << 1;
}
while (len-- > 0) {
if (tv[toffset++] != ov[ooffset++]) {
return false;
}
if (coder == UTF16) {
toffset <<= UTF16;
ooffset <<= UTF16;
len <<= UTF16;
}
return ArraysSupport.mismatch(tv, toffset,
ov, ooffset, len) < 0;
} else {
if (coder == LATIN1) {
while (len-- > 0) {
Expand Down Expand Up @@ -2235,8 +2226,8 @@ public boolean regionMatches(boolean ignoreCase, int toffset,
|| (ooffset > (long)other.length() - len)) {
return false;
}
byte tv[] = value;
byte ov[] = other.value;
byte[] tv = value;
byte[] ov = other.value;
byte coder = coder();
if (coder == other.coder()) {
return coder == LATIN1
Expand Down Expand Up @@ -2270,18 +2261,17 @@ public boolean startsWith(String prefix, int toffset) {
if (toffset < 0 || toffset > length() - prefix.length()) {
return false;
}
byte ta[] = value;
byte pa[] = prefix.value;
byte[] ta = value;
byte[] pa = prefix.value;
int po = 0;
int pc = pa.length;
byte coder = coder();
if (coder == prefix.coder()) {
int to = (coder == LATIN1) ? toffset : toffset << 1;
while (po < pc) {
if (ta[to++] != pa[po++]) {
return false;
}
if (coder == UTF16) {
toffset <<= UTF16;
}
return ArraysSupport.mismatch(ta, toffset,
pa, 0, pc) < 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

offset <<= coder is only obvious if the reader knows the value of LATIN1, maybe it would be simpler to read if you kept "int to"?

} else {
if (coder == LATIN1) { // && pcoder == UTF16
return false;
Expand Down
8 changes: 2 additions & 6 deletions src/java.base/share/classes/java/lang/StringLatin1.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,8 @@ public static int compareTo(byte[] value, byte[] other) {

public static int compareTo(byte[] value, byte[] other, int len1, int len2) {
int lim = Math.min(len1, len2);
for (int k = 0; k < lim; k++) {
if (value[k] != other[k]) {
return getChar(value, k) - getChar(other, k);
}
}
return len1 - len2;
int k = ArraysSupport.mismatch(value, other, lim);
return (k < 0) ? len1 - len2 : getChar(value, k) - getChar(other, k);
}

@IntrinsicCandidate
Expand Down
13 changes: 13 additions & 0 deletions test/micro/org/openjdk/bench/java/lang/StringBuilders.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ public class StringBuilders {
private String[] str3p9p8;
private String[] str22p40p31;
private StringBuilder sbLatin1;
private StringBuilder sbLatin2;
private StringBuilder sbUtf16;
private StringBuilder sbUtf17;

@Setup
public void setup() {
Expand All @@ -64,7 +66,9 @@ public void setup() {
str3p9p8 = new String[]{"123", "123456789", "12345678"};
str22p40p31 = new String[]{"1234567890123456789012", "1234567890123456789012345678901234567890", "1234567890123456789012345678901"};
sbLatin1 = new StringBuilder("Latin1 string");
sbLatin2 = new StringBuilder("Latin1 string");
sbUtf16 = new StringBuilder("UTF-\uFF11\uFF16 string");
sbUtf17 = new StringBuilder("UTF-\uFF11\uFF16 string");
}

@Benchmark
Expand Down Expand Up @@ -250,6 +254,15 @@ public String toStringCharWithFloat8() {
return result.toString();
}

@Benchmark
public int compareToLatin1() {
return sbLatin1.compareTo(sbLatin2);
}

@Benchmark
public int compareToUTF16() {
return sbUtf16.compareTo(sbUtf17);
}

@Benchmark
public String toStringCharWithMixed8() {
Expand Down
87 changes: 87 additions & 0 deletions test/micro/org/openjdk/bench/java/lang/StringComparisons.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package org.openjdk.bench.java.lang;

import org.openjdk.jmh.annotations.*;

import java.util.concurrent.TimeUnit;

/*
* This benchmark naively explores String::startsWith and other String
* comparison methods
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Warmup(iterations = 5, time = 1)
@Measurement(iterations = 5, time = 1)
@Fork(value = 3)
public class StringComparisons {

@Param({"6", "15", "1024"})
public int size;

@Param({"true", "false"})
public boolean utf16;

public String string;
public String equalString;
public String endsWithA;
public String endsWithB;
public String startsWithA;

@Setup
public void setup() {
String c = utf16 ? "\uff11" : "c";
string = c.repeat(size);
equalString = c.repeat(size);
endsWithA = c.repeat(size).concat("A");
endsWithB = c.repeat(size).concat("B");
startsWithA = "A" + (c.repeat(size));
}

@Benchmark
public boolean startsWith() {
return endsWithA.startsWith(string);
}

@Benchmark
public boolean endsWith() {
return startsWithA.endsWith(string);
}

@Benchmark
public boolean regionMatches() {
return endsWithA.regionMatches(0, endsWithB, 0, endsWithB.length());
}

@Benchmark
public boolean regionMatchesRange() {
return startsWithA.regionMatches(1, endsWithB, 0, endsWithB.length() - 1);
}

@Benchmark
public boolean regionMatchesCI() {
return endsWithA.regionMatches(true, 0, endsWithB, 0, endsWithB.length());
}
}
23 changes: 0 additions & 23 deletions test/micro/org/openjdk/bench/java/lang/StringOther.java
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,9 @@ public class StringOther {
private String testString;
private Random rnd;

private String str1, str2, str3, str4;
private String str1UP;

@Setup
public void setup() {
testString = "Idealism is what precedes experience; cynicism is what follows.";
str1 = "vm-guld vm-guld vm-guld";
str1UP = str1.toUpperCase(Locale.ROOT);
str2 = "vm-guld vm-guld vm-guldx";
str3 = "vm-guld vm-guld vm-guldx";
str4 = "adadaskasdjierudks";
rnd = new Random();
}

Expand All @@ -70,15 +62,6 @@ public void charAt(Blackhole bh) {
}
}

@Benchmark
public int compareTo() {
int total = 0;
total += str1.compareTo(str2);
total += str2.compareTo(str3);
total += str3.compareTo(str4);
return total;
}

/**
* Creates (hopefully) unique Strings and internizes them, creating a zillion forgettable strings in the JVMs string
* pool.
Expand All @@ -94,10 +77,4 @@ public String internUnique() {
return String.valueOf(rnd.nextInt()).intern();
}

@Benchmark
public void regionMatchesLatin1(Blackhole bh) {
bh.consume(str1.regionMatches(true, 0, str2, 0, str1.length()));
bh.consume(str2.regionMatches(true, 16, str1UP, 0, 8));
bh.consume(str3.regionMatches(true, 6, str4, 1, 2));
}
}