diff --git a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template index 09c48e86bfc9f..07c7d3c7d26f2 100644 --- a/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template +++ b/src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template @@ -416,6 +416,16 @@ class Direct$Type$Buffer$RW$$BO$ #end[rw] } + public $Type$Buffer put(int index, $Type$Buffer src, int offset, int length) { +#if[rw] + checkSegment(); + super.put(index, src, offset, length); + return this; +#else[rw] + throw new ReadOnlyBufferException(); +#end[rw] + } + public $Type$Buffer put($type$[] src, int offset, int length) { #if[rw] checkSegment(); 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 263e699ac1f23..e4a89efc6d8f2 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 @@ -251,6 +251,16 @@ class Heap$Type$Buffer$RW$ #end[rw] } + public $Type$Buffer put(int index, $Type$Buffer src, int offset, int length) { +#if[rw] + checkSegment(); + super.put(index, src, offset, length); + return this; +#else[rw] + throw new ReadOnlyBufferException(); +#end[rw] + } + public $Type$Buffer put(int index, $type$[] src, int offset, int length) { #if[rw] checkSegment(); 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 4804f4b43190b..8a5be7ceaf6ac 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 @@ -956,6 +956,76 @@ public abstract class $Type$Buffer if (n > limit() - pos) throw new BufferOverflowException(); + putBuffer(pos, src, srcPos, n); + + position(pos + n); + src.position(srcPos + n); + + return this; + } + + /** + * Absolute bulk put method  (optional operation). + * + *

This method transfers {@code length} $type$s into this buffer from + * the given source buffer, starting at the given {@code offset} in the + * source buffer and the given {@code index} in this buffer. The positions + * of both buffers are unchanged. + * + *

In other words, an invocation of this method of the form + * dst.put(index, src, offset, length) + * has exactly the same effect as the loop + * + *

{@code
+     * for (int i = offset, j = index; i < offset + length; i++, j++)
+     *     dst.put(j, src.get(i));
+     * }
+ * + * except that it first checks the consistency of the supplied parameters + * and it is potentially much more efficient. If this buffer and + * the source buffer share the same backing array or memory, then the + * result will be as if the source elements were first copied to an + * intermediate location before being written into this buffer. + * + * @param index + * The index in this buffer at which the first $type$ will be + * written; must be non-negative and less than {@code limit()} + * + * @param src + * The buffer from which $type$s are to be read + * + * @param offset + * The index within the source buffer of the first $type$ to be + * read; must be non-negative and less than {@code src.limit()} + * + * @param length + * The number of $type$s to be read from the given buffer; + * must be non-negative and no larger than the smaller of + * {@code limit() - index} and {@code src.limit() - offset} + * + * @return This buffer + * + * @throws IndexOutOfBoundsException + * If the preconditions on the {@code index}, {@code offset}, and + * {@code length} parameters do not hold + * + * @throws ReadOnlyBufferException + * If this buffer is read-only + * + * @since 16 + */ + public $Type$Buffer put(int index, $Type$Buffer src, int offset, int length) { + Objects.checkFromIndexSize(index, length, limit()); + Objects.checkFromIndexSize(offset, length, src.limit()); + if (isReadOnly()) + throw new ReadOnlyBufferException(); + + putBuffer(index, src, offset, length); + + return this; + } + + void putBuffer(int pos, $Type$Buffer src, int srcPos, int n) { Object srcBase = src.base(); #if[char] @@ -999,18 +1069,14 @@ public abstract class $Type$Buffer } } #end[!byte] - - position(pos + n); - src.position(srcPos + n); #if[char] } else { // src.isAddressable() == false assert StringCharBuffer.class.isInstance(src); - for (int i = 0; i < n; i++) - put(src.get()); + int posMax = pos + n; + for (int i = pos, j = srcPos; i < posMax; i++, j++) + put(i, src.get(j)); } #end[char] - - return this; } /** diff --git a/test/jdk/java/nio/Buffer/BulkPutBuffer.java b/test/jdk/java/nio/Buffer/BulkPutBuffer.java index fc3c55b9187cd..c5c4d0bcd6de6 100644 --- a/test/jdk/java/nio/Buffer/BulkPutBuffer.java +++ b/test/jdk/java/nio/Buffer/BulkPutBuffer.java @@ -49,7 +49,7 @@ /* * @test - * @bug 8245121 + * @bug 8219014 8245121 * @summary Ensure that a bulk put of a buffer into another is correct. * @compile --enable-preview -source ${jdk.version} BulkPutBuffer.java * @run testng/othervm --enable-preview BulkPutBuffer @@ -142,9 +142,11 @@ public static class BufferProxy { MethodHandle alloc; MethodHandle allocBB; MethodHandle allocDirect; + MethodHandle asReadOnlyBuffer; MethodHandle asTypeBuffer; MethodHandle putAbs; MethodHandle getAbs; + MethodHandle putBufAbs; MethodHandle putBufRel; MethodHandle equals; @@ -168,6 +170,9 @@ public static class BufferProxy { MethodType.methodType(ByteBuffer.class, int.class)); allocDirect = lookup.findStatic(ByteBuffer.class, "allocateDirect", MethodType.methodType(ByteBuffer.class, int.class)); + + asReadOnlyBuffer = lookup.findVirtual(bufferType, + "asReadOnlyBuffer", MethodType.methodType(bufferType)); if (elementType != byte.class) { asTypeBuffer = lookup.findVirtual(ByteBuffer.class, "as" + name + "Buffer", MethodType.methodType(bufferType)); @@ -177,8 +182,13 @@ public static class BufferProxy { MethodType.methodType(bufferType, int.class, elementType)); getAbs = lookup.findVirtual(bufferType, "get", MethodType.methodType(elementType, int.class)); + + putBufAbs = lookup.findVirtual(bufferType, "put", + MethodType.methodType(bufferType, int.class, bufferType, + int.class, int.class)); putBufRel = lookup.findVirtual(bufferType, "put", MethodType.methodType(bufferType, bufferType)); + equals = lookup.findVirtual(bufferType, "equals", MethodType.methodType(boolean.class, Object.class)); @@ -239,6 +249,25 @@ void copy(Buffer src, int srcOff, Buffer dst, int dstOff, int length) } } + Buffer asReadOnlyBuffer(Buffer buf) throws Throwable { + try { + return (Buffer)asReadOnlyBuffer.invoke(buf); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + void put(Buffer src, int srcOff, Buffer dst, int dstOff, int length) + throws Throwable { + try { + putBufAbs.invoke(dst, dstOff, src, srcOff, length); + } catch (ReadOnlyBufferException ro) { + throw ro; + } catch (Exception e) { + throw new AssertionError(e); + } + } + void put(Buffer src, Buffer dst) throws Throwable { try { putBufRel.invoke(dst, src); @@ -294,6 +323,40 @@ static Object[][] proxyPairs() { return args.toArray(Object[][]::new); } + private static void expectThrows(Class exClass, Assert.ThrowingRunnable r) { + try { + r.run(); + } catch(Throwable e) { + if (e.getClass() != exClass && e.getCause().getClass() != exClass) { + throw new RuntimeException("Expected " + exClass + + "; got " + e.getCause().getClass(), e); + } + } + } + + @Test(dataProvider = "proxies") + public static void testExceptions(BufferProxy bp) throws Throwable { + int cap = 27; + Buffer buf = bp.create(cap); + + expectThrows(IndexOutOfBoundsException.class, + () -> bp.put(buf, -1, buf, 0, 1)); + expectThrows(IndexOutOfBoundsException.class, + () -> bp.put(buf, 0, buf, -1, 1)); + expectThrows(IndexOutOfBoundsException.class, + () -> bp.put(buf, 1, buf, 0, cap)); + expectThrows(IndexOutOfBoundsException.class, + () -> bp.put(buf, 0, buf, 1, cap)); + expectThrows(IndexOutOfBoundsException.class, + () -> bp.put(buf, 0, buf, 0, cap + 1)); + expectThrows(IndexOutOfBoundsException.class, + () -> bp.put(buf, 0, buf, 0, Integer.MAX_VALUE)); + + Buffer rob = buf.isReadOnly() ? buf : bp.asReadOnlyBuffer(buf); + expectThrows(ReadOnlyBufferException.class, + () -> bp.put(buf, 0, rob, 0, cap)); + } + @Test(dataProvider = "proxies") public static void testSelf(BufferProxy bp) throws Throwable { for (int i = 0; i < ITERATIONS; i++) { @@ -311,22 +374,27 @@ public static void testSelf(BufferProxy bp) throws Throwable { Assert.expectThrows(ReadOnlyBufferException.class, () -> bp.copy(lower, 0, lowerCopy, 0, lowerLength)); break; - } else { - bp.copy(lower, 0, lowerCopy, 0, lowerLength); } + bp.copy(lower, 0, lowerCopy, 0, lowerLength); int middleOffset = RND.nextInt(1 + cap/2); Buffer middle = buf.slice(middleOffset, lowerLength); + Buffer middleCopy = bp.create(lowerLength); + bp.copy(middle, 0, middleCopy, 0, lowerLength); - if (middle.isReadOnly()) { - Assert.expectThrows(ReadOnlyBufferException.class, - () -> bp.put(lower, middle)); - break; - } else { - bp.put(lower, middle); - } + bp.put(lower, middle); middle.flip(); + Assert.assertTrue(bp.equals(lowerCopy, middle), + String.format("%d %s %d %d %d %d%n", SEED, + buf.getClass().getName(), cap, + lowerOffset, lowerLength, middleOffset)); + + bp.copy(lowerCopy, 0, buf, lowerOffset, lowerLength); + bp.copy(middleCopy, 0, buf, middleOffset, lowerLength); + + bp.put(buf, lowerOffset, buf, middleOffset, lowerLength); + Assert.assertTrue(bp.equals(lowerCopy, middle), String.format("%d %s %d %d %d %d%n", SEED, buf.getClass().getName(), cap, @@ -361,13 +429,29 @@ public static void testPairs(BufferProxy bp, BufferProxy sbp) throws Throwable { Assert.expectThrows(ReadOnlyBufferException.class, () -> bp.put(src, buf)); break; - } else { - bp.put(src, buf); } + Buffer backup = bp.create(slim - spos); + bp.copy(buf, pos, backup, 0, backup.capacity()); + bp.put(src, buf); + buf.reset(); src.reset(); + Assert.assertTrue(bp.equals(src, buf), + String.format("%d %s %d %d %d %s %d %d %d%n", SEED, + buf.getClass().getName(), cap, pos, lim, + src.getClass().getName(), scap, spos, slim)); + + src.clear(); + buf.clear(); + bp.copy(backup, 0, buf, pos, backup.capacity()); + bp.put(src, spos, buf, pos, backup.capacity()); + src.position(spos); + src.limit(slim); + buf.position(pos); + buf.limit(lim); + Assert.assertTrue(bp.equals(src, buf), String.format("%d %s %d %d %d %s %d %d %d%n", SEED, buf.getClass().getName(), cap, pos, lim,