8014854: (bf) CharBuffer.chars too slow with default implementation
Reviewed-by: erikj, briangoetz, henryjen, psandoz, mduigou
--- a/jdk/makefiles/CompileJavaClasses.gmk Fri May 31 09:30:44 2013 +0100
+++ b/jdk/makefiles/CompileJavaClasses.gmk Fri May 31 12:17:30 2013 +0100
@@ -342,7 +342,7 @@
DISABLE_SJAVAC:=true,\
SRC:=$(JDK_TOPDIR)/src/macosx/native/jobjc/src/core/java \
$(JDK_TOPDIR)/src/macosx/native/jobjc/src/runtime-additions/java \
- $(JDK_OUTPUTDIR)/gensrc, \
+ $(JDK_OUTPUTDIR)/gensrc_jobjc/src, \
INCLUDES := com/apple/jobjc,\
EXCLUDES := tests/java/com/apple/jobjc,\
BIN:=$(JDK_OUTPUTDIR)/jobjc_classes,\
@@ -355,7 +355,7 @@
SETUP:=GENERATE_JDKBYTECODE,\
SRC:=$(JDK_TOPDIR)/src/macosx/native/jobjc/src/core/java \
$(JDK_TOPDIR)/src/macosx/native/jobjc/src/runtime-additions/java \
- $(JDK_OUTPUTDIR)/gensrc, \
+ $(JDK_OUTPUTDIR)/gensrc_jobjc/src, \
INCLUDES := com/apple/jobjc,\
EXCLUDES := tests/java/com/apple/jobjc,\
BIN:=$(JDK_OUTPUTDIR)/jobjc_classes_headers,\
--- a/jdk/makefiles/GensrcBuffer.gmk Fri May 31 09:30:44 2013 +0100
+++ b/jdk/makefiles/GensrcBuffer.gmk Fri May 31 12:17:30 2013 +0100
@@ -69,6 +69,9 @@
$1_fulltype := character
$1_Fulltype := Character
$1_category := integralType
+ $1_streams := streamableType
+ $1_streamtype := int
+ $1_Streamtype := Int
$1_LBPV := 1
endif
@@ -97,7 +100,7 @@
$1_Type := Long
$1_fulltype := long
$1_Fulltype := Long
- $1_category := integralType
+ $1_category := integralType
$1_LBPV := 3
endif
@@ -231,10 +234,13 @@
$(TOOL_SPP) < $$($1_SRC) > $$($1_OUT).tmp \
-K$$($1_type) \
-K$$($1_category) \
+ -K$$($1_streams) \
-Dtype=$$($1_type) \
-DType=$$($1_Type) \
-Dfulltype=$$($1_fulltype) \
-DFulltype=$$($1_Fulltype) \
+ -Dstreamtype=$$($1_streamtype) \
+ -DStreamtype=$$($1_Streamtype) \
-Dx=$$($1_x) \
-Dmemtype=$$($1_memtype) \
-DMemtype=$$($1_Memtype) \
--- a/jdk/src/share/classes/java/nio/Buffer.java Fri May 31 09:30:44 2013 +0100
+++ b/jdk/src/share/classes/java/nio/Buffer.java Fri May 31 12:17:30 2013 +0100
@@ -25,6 +25,7 @@
package java.nio;
+import java.util.Spliterator;
/**
* A container for data of a specific primitive type.
@@ -173,6 +174,13 @@
public abstract class Buffer {
+ /**
+ * The characteristics of Spliterators that traverse and split elements
+ * maintained in Buffers.
+ */
+ static final int SPLITERATOR_CHARACTERISTICS =
+ Spliterator.SIZED | Spliterator.SUBSIZED | Spliterator.ORDERED;
+
// Invariants: mark <= position <= limit <= capacity
private int mark = -1;
private int position = 0;
--- a/jdk/src/share/classes/java/nio/ByteBufferAs-X-Buffer.java.template Fri May 31 09:30:44 2013 +0100
+++ b/jdk/src/share/classes/java/nio/ByteBufferAs-X-Buffer.java.template Fri May 31 12:17:30 2013 +0100
@@ -115,6 +115,12 @@
return Bits.get$Type$$BO$(bb, ix(checkIndex(i)));
}
+#if[streamableType]
+ $type$ getUnchecked(int i) {
+ return Bits.get$Type$$BO$(bb, ix(i));
+ }
+#end[streamableType]
+
#end[rw]
public $Type$Buffer put($type$ x) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/java/nio/CharBufferSpliterator.java Fri May 31 12:17:30 2013 +0100
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 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. Oracle designates this
+* particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package java.nio;
+
+import java.util.Comparator;
+import java.util.Spliterator;
+import java.util.function.IntConsumer;
+
+/**
+ * A Spliterator.OfInt for sources that traverse and split elements
+ * maintained in a CharBuffer.
+ *
+ * @implNote
+ * The implementation is based on the code for the Array-based spliterators.
+ */
+class CharBufferSpliterator implements Spliterator.OfInt {
+ private final CharBuffer buffer;
+ private int index; // current index, modified on advance/split
+ private final int limit;
+
+ CharBufferSpliterator(CharBuffer buffer) {
+ this(buffer, buffer.position(), buffer.limit());
+ }
+
+ CharBufferSpliterator(CharBuffer buffer, int origin, int limit) {
+ assert origin <= limit;
+ this.buffer = buffer;
+ this.index = (origin <= limit) ? origin : limit;
+ this.limit = limit;
+ }
+
+ @Override
+ public OfInt trySplit() {
+ int lo = index, mid = (lo + limit) >>> 1;
+ return (lo >= mid)
+ ? null
+ : new CharBufferSpliterator(buffer, lo, index = mid);
+ }
+
+ @Override
+ public void forEachRemaining(IntConsumer action) {
+ if (action == null)
+ throw new NullPointerException();
+ CharBuffer cb = buffer;
+ int i = index;
+ int hi = limit;
+ index = hi;
+ while (i < hi) {
+ action.accept(cb.getUnchecked(i++));
+ }
+ }
+
+ @Override
+ public boolean tryAdvance(IntConsumer action) {
+ if (action == null)
+ throw new NullPointerException();
+ if (index >= 0 && index < limit) {
+ action.accept(buffer.getUnchecked(index++));
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public long estimateSize() {
+ return (long)(limit - index);
+ }
+
+ @Override
+ public int characteristics() {
+ return Buffer.SPLITERATOR_CHARACTERISTICS;
+ }
+}
--- a/jdk/src/share/classes/java/nio/Direct-X-Buffer.java.template Fri May 31 09:30:44 2013 +0100
+++ b/jdk/src/share/classes/java/nio/Direct-X-Buffer.java.template Fri May 31 12:17:30 2013 +0100
@@ -253,6 +253,12 @@
return $fromBits$($swap$(unsafe.get$Swaptype$(ix(checkIndex(i)))));
}
+#if[streamableType]
+ $type$ getUnchecked(int i) {
+ return $fromBits$($swap$(unsafe.get$Swaptype$(ix(i))));
+ }
+#end[streamableType]
+
public $Type$Buffer get($type$[] dst, int offset, int length) {
#if[rw]
if ((length << $LG_BYTES_PER_VALUE$) > Bits.JNI_COPY_TO_ARRAY_THRESHOLD) {
--- a/jdk/src/share/classes/java/nio/Heap-X-Buffer.java.template Fri May 31 09:30:44 2013 +0100
+++ b/jdk/src/share/classes/java/nio/Heap-X-Buffer.java.template Fri May 31 12:17:30 2013 +0100
@@ -139,6 +139,12 @@
return hb[ix(checkIndex(i))];
}
+#if[streamableType]
+ $type$ getUnchecked(int i) {
+ return hb[ix(i)];
+ }
+#end[streamableType]
+
public $Type$Buffer get($type$[] dst, int offset, int length) {
checkBounds(offset, length, dst.length);
if (length > remaining())
--- a/jdk/src/share/classes/java/nio/StringCharBuffer.java Fri May 31 09:30:44 2013 +0100
+++ b/jdk/src/share/classes/java/nio/StringCharBuffer.java Fri May 31 12:17:30 2013 +0100
@@ -77,6 +77,10 @@
return str.charAt(checkIndex(index) + offset);
}
+ char getUnchecked(int index) {
+ return str.charAt(index + offset);
+ }
+
// ## Override bulk get methods for better performance
public final CharBuffer put(char c) {
--- a/jdk/src/share/classes/java/nio/X-Buffer.java.template Fri May 31 09:30:44 2013 +0100
+++ b/jdk/src/share/classes/java/nio/X-Buffer.java.template Fri May 31 12:17:30 2013 +0100
@@ -30,6 +30,11 @@
#if[char]
import java.io.IOException;
#end[char]
+#if[streamableType]
+import java.util.Spliterator;
+import java.util.stream.StreamSupport;
+import java.util.stream.$Streamtype$Stream;
+#end[streamableType]
/**
* $A$ $type$ buffer.
@@ -589,6 +594,19 @@
*/
public abstract $type$ get(int index);
+#if[streamableType]
+ /**
+ * Absolute <i>get</i> method. Reads the $type$ at the given
+ * index without any validation of the index.
+ *
+ * @param index
+ * The index from which the $type$ will be read
+ *
+ * @return The $type$ at the given index
+ */
+ abstract $type$ getUnchecked(int index); // package-private
+#end[streamableType]
+
/**
* Absolute <i>put</i> method <i>(optional operation)</i>.
*
@@ -1458,4 +1476,16 @@
#end[byte]
+#if[streamableType]
+
+#if[char]
+ @Override
+#end[char]
+ public $Streamtype$Stream $type$s() {
+ return StreamSupport.$streamtype$Stream(() -> new $Type$BufferSpliterator(this),
+ Buffer.SPLITERATOR_CHARACTERISTICS);
+ }
+
+#end[streamableType]
+
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/nio/Buffer/Chars.java Fri May 31 12:17:30 2013 +0100
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 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 8014854
+ * @summary Exercises CharBuffer#chars on each of the CharBuffer types
+ * @run testng Chars
+ */
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.CharBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+public class Chars {
+
+ static final Random RAND = new Random();
+
+ static final int SIZE = 128 + RAND.nextInt(1024);
+
+ /**
+ * Randomize the char buffer's position and limit.
+ */
+ static CharBuffer randomizeRange(CharBuffer cb) {
+ int mid = cb.capacity() >>> 1;
+ int start = RAND.nextInt(mid);
+ int end = mid + RAND.nextInt(mid);
+ cb.position(start);
+ cb.limit(end);
+ return cb;
+ }
+
+ /**
+ * Randomize the char buffer's contents, position and limit.
+ */
+ static CharBuffer randomize(CharBuffer cb) {
+ while (cb.hasRemaining()) {
+ cb.put((char)RAND.nextInt());
+ }
+ return randomizeRange(cb);
+ }
+
+ /**
+ * Sums the remaining chars in the char buffer.
+ */
+ static int intSum(CharBuffer cb) {
+ int sum = 0;
+ cb.mark();
+ while (cb.hasRemaining()) {
+ sum += cb.get();
+ }
+ cb.reset();
+ return sum;
+ }
+
+ /**
+ * Creates char buffers to test, adding them to the given list.
+ */
+ static void addCases(CharBuffer cb, List<CharBuffer> buffers) {
+ randomize(cb);
+ buffers.add(cb);
+
+ buffers.add(cb.slice());
+ buffers.add(cb.duplicate());
+ buffers.add(cb.asReadOnlyBuffer());
+
+ buffers.add(randomizeRange(cb.slice()));
+ buffers.add(randomizeRange(cb.duplicate()));
+ buffers.add(randomizeRange(cb.asReadOnlyBuffer()));
+ }
+
+ @DataProvider(name = "charbuffers")
+ public Object[][] createCharBuffers() {
+ List<CharBuffer> buffers = new ArrayList<>();
+
+ // heap
+ addCases(CharBuffer.allocate(SIZE), buffers);
+ addCases(CharBuffer.wrap(new char[SIZE]), buffers);
+ addCases(ByteBuffer.allocate(SIZE*2).order(ByteOrder.BIG_ENDIAN).asCharBuffer(),
+ buffers);
+ addCases(ByteBuffer.allocate(SIZE*2).order(ByteOrder.LITTLE_ENDIAN).asCharBuffer(),
+ buffers);
+
+ // direct
+ addCases(ByteBuffer.allocateDirect(SIZE*2).order(ByteOrder.BIG_ENDIAN).asCharBuffer(),
+ buffers);
+ addCases(ByteBuffer.allocateDirect(SIZE*2).order(ByteOrder.LITTLE_ENDIAN).asCharBuffer(),
+ buffers);
+
+ // read-only buffer backed by a CharSequence
+ buffers.add(CharBuffer.wrap(randomize(CharBuffer.allocate(SIZE))));
+
+ Object[][] params = new Object[buffers.size()][];
+ for (int i = 0; i < buffers.size(); i++) {
+ CharBuffer cb = buffers.get(i);
+ params[i] = new Object[] { cb.getClass().getName(), cb };
+ }
+
+ return params;
+ }
+
+ @Test(dataProvider = "charbuffers")
+ public void testChars(String type, CharBuffer cb) {
+ System.out.format("%s position=%d, limit=%d%n", type, cb.position(), cb.limit());
+ int expected = intSum(cb);
+ assertEquals(cb.chars().sum(), expected);
+ assertEquals(cb.chars().parallel().sum(), expected);
+ }
+}