Skip to content

Commit

Permalink
8219014: (bf) Add absolute bulk put methods which accept a source Buffer
Browse files Browse the repository at this point in the history
Reviewed-by: psandoz, alanb
  • Loading branch information
Brian Burkhalter committed Nov 5, 2020
1 parent 3a02578 commit a50fdd5
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 19 deletions.
10 changes: 10 additions & 0 deletions src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template
Expand Up @@ -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();
Expand Down
10 changes: 10 additions & 0 deletions src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template
Expand Up @@ -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();
Expand Down
80 changes: 73 additions & 7 deletions src/java.base/share/classes/java/nio/X-Buffer.java.template
Expand Up @@ -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 <i>put</i> method&nbsp;&nbsp;<i>(optional operation)</i>.
*
* <p> 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.
*
* <p> In other words, an invocation of this method of the form
* <code>dst.put(index,&nbsp;src,&nbsp;offset,&nbsp;length)</code>
* has exactly the same effect as the loop
*
* <pre>{@code
* for (int i = offset, j = index; i < offset + length; i++, j++)
* dst.put(j, src.get(i));
* }</pre>
*
* 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]
Expand Down Expand Up @@ -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;
}

/**
Expand Down
108 changes: 96 additions & 12 deletions test/jdk/java/nio/Buffer/BulkPutBuffer.java
Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand All @@ -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));
Expand All @@ -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));

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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++) {
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down

1 comment on commit a50fdd5

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

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

Please sign in to comment.