diff --git a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java index e0c3aa19f54..b40f6274412 100644 --- a/src/java.base/share/classes/java/lang/AbstractStringBuilder.java +++ b/src/java.base/share/classes/java/lang/AbstractStringBuilder.java @@ -640,14 +640,11 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence int count = this.count; byte[] val = this.value; if (isLatin1()) { - val[count++] = 'n'; - val[count++] = 'u'; - val[count++] = 'l'; - val[count++] = 'l'; + StringLatin1.putCharsAt(val, count, 'n', 'u', 'l', 'l'); } else { - count = StringUTF16.putCharsAt(val, count, 'n', 'u', 'l', 'l'); + StringUTF16.putCharsAt(val, count, 'n', 'u', 'l', 'l'); } - this.count = count; + this.count = count + 4; return this; } @@ -772,25 +769,18 @@ abstract sealed class AbstractStringBuilder implements Appendable, CharSequence byte[] val = this.value; if (isLatin1()) { if (b) { - val[count++] = 't'; - val[count++] = 'r'; - val[count++] = 'u'; - val[count++] = 'e'; + StringLatin1.putCharsAt(val, count, 't', 'r', 'u', 'e'); } else { - val[count++] = 'f'; - val[count++] = 'a'; - val[count++] = 'l'; - val[count++] = 's'; - val[count++] = 'e'; + StringLatin1.putCharsAt(val, count, 'f', 'a', 'l', 's', 'e'); } } else { if (b) { - count = StringUTF16.putCharsAt(val, count, 't', 'r', 'u', 'e'); + StringUTF16.putCharsAt(val, count, 't', 'r', 'u', 'e'); } else { - count = StringUTF16.putCharsAt(val, count, 'f', 'a', 'l', 's', 'e'); + StringUTF16.putCharsAt(val, count, 'f', 'a', 'l', 's', 'e'); } } - this.count = count; + this.count = count + (b ? 4 : 5); return this; } diff --git a/src/java.base/share/classes/java/lang/StringLatin1.java b/src/java.base/share/classes/java/lang/StringLatin1.java index abec36af1d9..c12b8afc21f 100644 --- a/src/java.base/share/classes/java/lang/StringLatin1.java +++ b/src/java.base/share/classes/java/lang/StringLatin1.java @@ -32,6 +32,7 @@ import java.util.function.Consumer; import java.util.function.IntConsumer; import java.util.stream.Stream; import java.util.stream.StreamSupport; +import jdk.internal.misc.Unsafe; import jdk.internal.util.ArraysSupport; import jdk.internal.util.DecimalDigits; import jdk.internal.vm.annotation.IntrinsicCandidate; @@ -42,6 +43,8 @@ import static java.lang.String.checkIndex; import static java.lang.String.checkOffset; final class StringLatin1 { + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + public static char charAt(byte[] value, int index) { checkIndex(index, value.length); return (char)(value[index] & 0xff); @@ -824,6 +827,27 @@ final class StringLatin1 { return StreamSupport.stream(LinesSpliterator.spliterator(value), false); } + static void putCharsAt(byte[] val, int index, int c1, int c2, int c3, int c4) { + assert index >= 0 && index + 3 < length(val) : "Trusted caller missed bounds check"; + // Don't use the putChar method, Its instrinsic will cause C2 unable to combining values into larger stores. + long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + index; + UNSAFE.putByte(val, address , (byte)(c1)); + UNSAFE.putByte(val, address + 1, (byte)(c2)); + UNSAFE.putByte(val, address + 2, (byte)(c3)); + UNSAFE.putByte(val, address + 3, (byte)(c4)); + } + + static void putCharsAt(byte[] val, int index, int c1, int c2, int c3, int c4, int c5) { + assert index >= 0 && index + 4 < length(val) : "Trusted caller missed bounds check"; + // Don't use the putChar method, Its instrinsic will cause C2 unable to combining values into larger stores. + long address = Unsafe.ARRAY_BYTE_BASE_OFFSET + index; + UNSAFE.putByte(val, address , (byte)(c1)); + UNSAFE.putByte(val, address + 1, (byte)(c2)); + UNSAFE.putByte(val, address + 2, (byte)(c3)); + UNSAFE.putByte(val, address + 3, (byte)(c4)); + UNSAFE.putByte(val, address + 4, (byte)(c5)); + } + public static void putChar(byte[] val, int index, int c) { //assert (canEncode(c)); val[index] = (byte)(c); diff --git a/src/java.base/share/classes/java/lang/StringUTF16.java b/src/java.base/share/classes/java/lang/StringUTF16.java index 000483f29bc..f04b991827f 100644 --- a/src/java.base/share/classes/java/lang/StringUTF16.java +++ b/src/java.base/share/classes/java/lang/StringUTF16.java @@ -43,7 +43,6 @@ import static java.lang.String.UTF16; import static java.lang.String.LATIN1; final class StringUTF16 { - // Return a new byte array for a UTF16-coded string for len chars // Throw an exception if out of range public static byte[] newBytesFor(int len) { @@ -1548,27 +1547,20 @@ final class StringUTF16 { return true; } - public static int putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) { - int end = i + 4; - checkBoundsBeginEnd(i, end, value); - putChar(value, i++, c1); - putChar(value, i++, c2); - putChar(value, i++, c3); - putChar(value, i++, c4); - assert(i == end); - return end; + static void putCharsAt(byte[] val, int index, int c1, int c2, int c3, int c4) { + assert index >= 0 && index + 3 < length(val) : "Trusted caller missed bounds check"; + putChar(val, index , c1); + putChar(val, index + 1, c2); + putChar(val, index + 2, c3); + putChar(val, index + 3, c4); } - public static int putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) { - int end = i + 5; - checkBoundsBeginEnd(i, end, value); - putChar(value, i++, c1); - putChar(value, i++, c2); - putChar(value, i++, c3); - putChar(value, i++, c4); - putChar(value, i++, c5); - assert(i == end); - return end; + static void putCharsAt(byte[] val, int index, int c1, int c2, int c3, int c4, int c5) { + putChar(val, index , c1); + putChar(val, index + 1, c2); + putChar(val, index + 2, c3); + putChar(val, index + 3, c4); + putChar(val, index + 4, c5); } public static char charAt(byte[] value, int index) { diff --git a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java index a24d7b98ada..5ecc01aa2bc 100644 --- a/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java +++ b/test/hotspot/jtreg/compiler/patches/java.base/java/lang/Helper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -133,11 +133,17 @@ public class Helper { } public static int putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4) { - return StringUTF16.putCharsAt(value, i, c1, c2, c3, c4); + int end = i + 4; + StringUTF16.checkBoundsBeginEnd(i, end, value); + StringUTF16.putCharsAt(value, i, c1, c2, c3, c4); + return end; } public static int putCharsAt(byte[] value, int i, char c1, char c2, char c3, char c4, char c5) { - return StringUTF16.putCharsAt(value, i, c1, c2, c3, c4, c5); + int end = i + 5; + StringUTF16.checkBoundsBeginEnd(i, end, value); + StringUTF16.putCharsAt(value, i, c1, c2, c3, c4, c5); + return end; } public static char charAt(byte[] value, int index) { diff --git a/test/micro/org/openjdk/bench/java/lang/StringBuilders.java b/test/micro/org/openjdk/bench/java/lang/StringBuilders.java index e7579d72ecb..ed5c0d30db8 100644 --- a/test/micro/org/openjdk/bench/java/lang/StringBuilders.java +++ b/test/micro/org/openjdk/bench/java/lang/StringBuilders.java @@ -226,17 +226,66 @@ public class StringBuilders { @Benchmark - public String toStringCharWithBool8() { - StringBuilder result = new StringBuilder(); - result.append(true); - result.append(false); - result.append(true); - result.append(true); - result.append(false); - result.append(true); - result.append(false); - result.append(false); - return result.toString(); + public int appendWithBool8Latin1() { + StringBuilder buf = sbLatin1; + buf.setLength(0); + buf.append(true); + buf.append(false); + buf.append(true); + buf.append(true); + buf.append(false); + buf.append(true); + buf.append(false); + buf.append(false); + return buf.length(); + } + + + @Benchmark + public int appendWithBool8Utf16() { + StringBuilder buf = sbUtf16; + buf.setLength(0); + buf.append(true); + buf.append(false); + buf.append(true); + buf.append(true); + buf.append(false); + buf.append(true); + buf.append(false); + buf.append(false); + return buf.length(); + } + + + @Benchmark + public int appendWithNull8Latin1() { + StringBuilder buf = sbLatin1; + buf.setLength(0); + buf.append((String) null); + buf.append((String) null); + buf.append((String) null); + buf.append((String) null); + buf.append((String) null); + buf.append((String) null); + buf.append((String) null); + buf.append((String) null); + return buf.length(); + } + + + @Benchmark + public int appendWithNull8Utf16() { + StringBuilder buf = sbUtf16; + buf.setLength(0); + buf.append((String) null); + buf.append((String) null); + buf.append((String) null); + buf.append((String) null); + buf.append((String) null); + buf.append((String) null); + buf.append((String) null); + buf.append((String) null); + return buf.length(); }