diff --git a/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template b/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template index 2ead79fc86b..b77e45ee156 100644 --- a/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -278,6 +278,60 @@ class Heap$Type$Buffer$RW$ #if[char] + // + // Use getChars() to load chars directly into the heap buffer array. + // For a String or StringBuffer source this improves performance if + // a proper subsequence is being appended as copying to a new intermediate + // String object is avoided. For a StringBuilder where either a subsequence + // or the full sequence of chars is being appended, copying the chars to + // an intermedite String in StringBuilder::toString is avoided. + // + private $Type$Buffer appendChars(CharSequence csq, int start, int end) { + checkSession(); + + int length = end - start; + int pos = position(); + int lim = limit(); + int rem = (pos <= lim) ? lim - pos : 0; + if (length > rem) + throw new BufferOverflowException(); + + if (csq instanceof String str) { + str.getChars(start, end, hb, ix(pos)); + } else if (csq instanceof StringBuilder sb) { + sb.getChars(start, end, hb, ix(pos)); + } else if (csq instanceof StringBuffer sb) { + sb.getChars(start, end, hb, ix(pos)); + } + + position(pos + length); + + return this; + } + + public $Type$Buffer append(CharSequence csq) { +#if[rw] + if (csq instanceof StringBuilder) + return appendChars(csq, 0, csq.length()); + + return super.append(csq); +#else[rw] + throw new ReadOnlyBufferException(); +#end[rw] + } + + public $Type$Buffer append(CharSequence csq, int start, int end) { +#if[rw] + if (csq instanceof String || csq instanceof StringBuffer || + csq instanceof StringBuilder) + return appendChars(csq, start, end); + + return super.append(csq, start, end); +#else[rw] + throw new ReadOnlyBufferException(); +#end[rw] + } + public $Type$Buffer put(String src, int start, int end) { #if[rw] checkSession(); diff --git a/src/java.base/share/classes/java/nio/X-Buffer.java.template b/src/java.base/share/classes/java/nio/X-Buffer.java.template index 6bee52279ca..6c496667d25 100644 --- a/src/java.base/share/classes/java/nio/X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/X-Buffer.java.template @@ -2005,6 +2005,8 @@ public abstract sealed class $Type$Buffer public $Type$Buffer append(CharSequence csq) { if (csq == null) return put("null"); + else if (csq instanceof CharBuffer cb) + return put(cb); else return put(csq.toString()); } @@ -2042,6 +2044,13 @@ public abstract sealed class $Type$Buffer * @since 1.5 */ public $Type$Buffer append(CharSequence csq, int start, int end) { + if (csq instanceof CharBuffer cb) { + int pos = position(); + int length = end - start; + put(pos, cb, start, length); + position(pos + length); + return this; + } CharSequence cs = (csq == null ? "null" : csq); return put(cs.subSequence(start, end).toString()); } diff --git a/test/micro/org/openjdk/bench/java/nio/CharBufferAppend.java b/test/micro/org/openjdk/bench/java/nio/CharBufferAppend.java new file mode 100644 index 00000000000..ea3ef4ab0fe --- /dev/null +++ b/test/micro/org/openjdk/bench/java/nio/CharBufferAppend.java @@ -0,0 +1,141 @@ +/* + * 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.nio; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; + +/** + * Benchmark for {@code CharBuffer} implementations of the {@code Appendable} + * methods which accept a {@code CharSequence} source. + */ +@Warmup(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@Measurement(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +@Fork(1) +public class CharBufferAppend { + + static final int SIZE = 8192; + + static String str; + static StringBuffer strbuf; + static StringBuilder strbld; + static CharBuffer hbDst; + static CharBuffer hbSrc; + static CharBuffer dbSrc; + static CharBuffer dbDst; + + static { + char[] chars = new char[SIZE]; + Arrays.fill(chars, (char)27); + + strbld = new StringBuilder(SIZE); + strbld.append(chars); + + str = strbld.toString(); + + strbuf = new StringBuffer(SIZE); + strbuf.append(chars); + + hbDst = CharBuffer.allocate(SIZE); + hbSrc = CharBuffer.wrap(chars); + + dbDst = ByteBuffer.allocateDirect(2*SIZE).asCharBuffer(); + dbSrc = ByteBuffer.allocateDirect(2*SIZE).asCharBuffer(); + dbSrc.put(chars); + dbSrc.clear(); + }; + + @Benchmark + public CharBuffer appendDirectToDirect() { + dbDst.clear(); + dbSrc.clear(); + return dbDst.append(dbSrc); + } + + @Benchmark + public CharBuffer appendDirectToHeap() { + hbDst.clear(); + dbSrc.clear(); + return hbDst.append(dbSrc); + } + + @Benchmark + public CharBuffer appendHeapToHeap() { + hbDst.clear(); + hbSrc.clear(); + return hbDst.append(hbSrc); + } + + @Benchmark + public CharBuffer appendHeapToDirect() { + dbDst.clear(); + hbSrc.clear(); + return dbDst.append(hbSrc); + } + + @Benchmark + public CharBuffer appendString() { + hbDst.clear(); + return hbDst.append(str); + } + + @Benchmark + public CharBuffer appendStringBuffer() { + hbDst.clear(); + return hbDst.append(strbuf); + } + + @Benchmark + public CharBuffer appendStringBuilder() { + hbDst.clear(); + return hbDst.append(strbld); + } + + @Benchmark + public CharBuffer appendSubString() { + hbDst.clear(); + return hbDst.append(str, SIZE/4, 3*SIZE/4); + } + + @Benchmark + public CharBuffer appendSubStringBuffer() { + hbDst.clear(); + return hbDst.append(strbuf, SIZE/4, 3*SIZE/4); + } + + @Benchmark + public CharBuffer appendSubStringBuilder() { + hbDst.clear(); + return hbDst.append(strbld, SIZE/4, 3*SIZE/4); + } +}