Skip to content

Commit a50fdd5

Browse files
author
Brian Burkhalter
committed
8219014: (bf) Add absolute bulk put methods which accept a source Buffer
Reviewed-by: psandoz, alanb
1 parent 3a02578 commit a50fdd5

File tree

4 files changed

+189
-19
lines changed

4 files changed

+189
-19
lines changed

src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,6 +416,16 @@ class Direct$Type$Buffer$RW$$BO$
416416
#end[rw]
417417
}
418418

419+
public $Type$Buffer put(int index, $Type$Buffer src, int offset, int length) {
420+
#if[rw]
421+
checkSegment();
422+
super.put(index, src, offset, length);
423+
return this;
424+
#else[rw]
425+
throw new ReadOnlyBufferException();
426+
#end[rw]
427+
}
428+
419429
public $Type$Buffer put($type$[] src, int offset, int length) {
420430
#if[rw]
421431
checkSegment();

src/java.base/share/classes/java/nio/Heap-X-Buffer.java.template

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,16 @@ class Heap$Type$Buffer$RW$
251251
#end[rw]
252252
}
253253

254+
public $Type$Buffer put(int index, $Type$Buffer src, int offset, int length) {
255+
#if[rw]
256+
checkSegment();
257+
super.put(index, src, offset, length);
258+
return this;
259+
#else[rw]
260+
throw new ReadOnlyBufferException();
261+
#end[rw]
262+
}
263+
254264
public $Type$Buffer put(int index, $type$[] src, int offset, int length) {
255265
#if[rw]
256266
checkSegment();

src/java.base/share/classes/java/nio/X-Buffer.java.template

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,76 @@ public abstract class $Type$Buffer
956956
if (n > limit() - pos)
957957
throw new BufferOverflowException();
958958

959+
putBuffer(pos, src, srcPos, n);
960+
961+
position(pos + n);
962+
src.position(srcPos + n);
963+
964+
return this;
965+
}
966+
967+
/**
968+
* Absolute bulk <i>put</i> method&nbsp;&nbsp;<i>(optional operation)</i>.
969+
*
970+
* <p> This method transfers {@code length} $type$s into this buffer from
971+
* the given source buffer, starting at the given {@code offset} in the
972+
* source buffer and the given {@code index} in this buffer. The positions
973+
* of both buffers are unchanged.
974+
*
975+
* <p> In other words, an invocation of this method of the form
976+
* <code>dst.put(index,&nbsp;src,&nbsp;offset,&nbsp;length)</code>
977+
* has exactly the same effect as the loop
978+
*
979+
* <pre>{@code
980+
* for (int i = offset, j = index; i < offset + length; i++, j++)
981+
* dst.put(j, src.get(i));
982+
* }</pre>
983+
*
984+
* except that it first checks the consistency of the supplied parameters
985+
* and it is potentially much more efficient. If this buffer and
986+
* the source buffer share the same backing array or memory, then the
987+
* result will be as if the source elements were first copied to an
988+
* intermediate location before being written into this buffer.
989+
*
990+
* @param index
991+
* The index in this buffer at which the first $type$ will be
992+
* written; must be non-negative and less than {@code limit()}
993+
*
994+
* @param src
995+
* The buffer from which $type$s are to be read
996+
*
997+
* @param offset
998+
* The index within the source buffer of the first $type$ to be
999+
* read; must be non-negative and less than {@code src.limit()}
1000+
*
1001+
* @param length
1002+
* The number of $type$s to be read from the given buffer;
1003+
* must be non-negative and no larger than the smaller of
1004+
* {@code limit() - index} and {@code src.limit() - offset}
1005+
*
1006+
* @return This buffer
1007+
*
1008+
* @throws IndexOutOfBoundsException
1009+
* If the preconditions on the {@code index}, {@code offset}, and
1010+
* {@code length} parameters do not hold
1011+
*
1012+
* @throws ReadOnlyBufferException
1013+
* If this buffer is read-only
1014+
*
1015+
* @since 16
1016+
*/
1017+
public $Type$Buffer put(int index, $Type$Buffer src, int offset, int length) {
1018+
Objects.checkFromIndexSize(index, length, limit());
1019+
Objects.checkFromIndexSize(offset, length, src.limit());
1020+
if (isReadOnly())
1021+
throw new ReadOnlyBufferException();
1022+
1023+
putBuffer(index, src, offset, length);
1024+
1025+
return this;
1026+
}
1027+
1028+
void putBuffer(int pos, $Type$Buffer src, int srcPos, int n) {
9591029
Object srcBase = src.base();
9601030

9611031
#if[char]
@@ -999,18 +1069,14 @@ public abstract class $Type$Buffer
9991069
}
10001070
}
10011071
#end[!byte]
1002-
1003-
position(pos + n);
1004-
src.position(srcPos + n);
10051072
#if[char]
10061073
} else { // src.isAddressable() == false
10071074
assert StringCharBuffer.class.isInstance(src);
1008-
for (int i = 0; i < n; i++)
1009-
put(src.get());
1075+
int posMax = pos + n;
1076+
for (int i = pos, j = srcPos; i < posMax; i++, j++)
1077+
put(i, src.get(j));
10101078
}
10111079
#end[char]
1012-
1013-
return this;
10141080
}
10151081

10161082
/**

test/jdk/java/nio/Buffer/BulkPutBuffer.java

Lines changed: 96 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949

5050
/*
5151
* @test
52-
* @bug 8245121
52+
* @bug 8219014 8245121
5353
* @summary Ensure that a bulk put of a buffer into another is correct.
5454
* @compile --enable-preview -source ${jdk.version} BulkPutBuffer.java
5555
* @run testng/othervm --enable-preview BulkPutBuffer
@@ -142,9 +142,11 @@ public static class BufferProxy {
142142
MethodHandle alloc;
143143
MethodHandle allocBB;
144144
MethodHandle allocDirect;
145+
MethodHandle asReadOnlyBuffer;
145146
MethodHandle asTypeBuffer;
146147
MethodHandle putAbs;
147148
MethodHandle getAbs;
149+
MethodHandle putBufAbs;
148150
MethodHandle putBufRel;
149151
MethodHandle equals;
150152

@@ -168,6 +170,9 @@ public static class BufferProxy {
168170
MethodType.methodType(ByteBuffer.class, int.class));
169171
allocDirect = lookup.findStatic(ByteBuffer.class, "allocateDirect",
170172
MethodType.methodType(ByteBuffer.class, int.class));
173+
174+
asReadOnlyBuffer = lookup.findVirtual(bufferType,
175+
"asReadOnlyBuffer", MethodType.methodType(bufferType));
171176
if (elementType != byte.class) {
172177
asTypeBuffer = lookup.findVirtual(ByteBuffer.class,
173178
"as" + name + "Buffer", MethodType.methodType(bufferType));
@@ -177,8 +182,13 @@ public static class BufferProxy {
177182
MethodType.methodType(bufferType, int.class, elementType));
178183
getAbs = lookup.findVirtual(bufferType, "get",
179184
MethodType.methodType(elementType, int.class));
185+
186+
putBufAbs = lookup.findVirtual(bufferType, "put",
187+
MethodType.methodType(bufferType, int.class, bufferType,
188+
int.class, int.class));
180189
putBufRel = lookup.findVirtual(bufferType, "put",
181190
MethodType.methodType(bufferType, bufferType));
191+
182192
equals = lookup.findVirtual(bufferType, "equals",
183193
MethodType.methodType(boolean.class, Object.class));
184194

@@ -239,6 +249,25 @@ void copy(Buffer src, int srcOff, Buffer dst, int dstOff, int length)
239249
}
240250
}
241251

252+
Buffer asReadOnlyBuffer(Buffer buf) throws Throwable {
253+
try {
254+
return (Buffer)asReadOnlyBuffer.invoke(buf);
255+
} catch (Exception e) {
256+
throw new AssertionError(e);
257+
}
258+
}
259+
260+
void put(Buffer src, int srcOff, Buffer dst, int dstOff, int length)
261+
throws Throwable {
262+
try {
263+
putBufAbs.invoke(dst, dstOff, src, srcOff, length);
264+
} catch (ReadOnlyBufferException ro) {
265+
throw ro;
266+
} catch (Exception e) {
267+
throw new AssertionError(e);
268+
}
269+
}
270+
242271
void put(Buffer src, Buffer dst) throws Throwable {
243272
try {
244273
putBufRel.invoke(dst, src);
@@ -294,6 +323,40 @@ static Object[][] proxyPairs() {
294323
return args.toArray(Object[][]::new);
295324
}
296325

326+
private static void expectThrows(Class<?> exClass, Assert.ThrowingRunnable r) {
327+
try {
328+
r.run();
329+
} catch(Throwable e) {
330+
if (e.getClass() != exClass && e.getCause().getClass() != exClass) {
331+
throw new RuntimeException("Expected " + exClass +
332+
"; got " + e.getCause().getClass(), e);
333+
}
334+
}
335+
}
336+
337+
@Test(dataProvider = "proxies")
338+
public static void testExceptions(BufferProxy bp) throws Throwable {
339+
int cap = 27;
340+
Buffer buf = bp.create(cap);
341+
342+
expectThrows(IndexOutOfBoundsException.class,
343+
() -> bp.put(buf, -1, buf, 0, 1));
344+
expectThrows(IndexOutOfBoundsException.class,
345+
() -> bp.put(buf, 0, buf, -1, 1));
346+
expectThrows(IndexOutOfBoundsException.class,
347+
() -> bp.put(buf, 1, buf, 0, cap));
348+
expectThrows(IndexOutOfBoundsException.class,
349+
() -> bp.put(buf, 0, buf, 1, cap));
350+
expectThrows(IndexOutOfBoundsException.class,
351+
() -> bp.put(buf, 0, buf, 0, cap + 1));
352+
expectThrows(IndexOutOfBoundsException.class,
353+
() -> bp.put(buf, 0, buf, 0, Integer.MAX_VALUE));
354+
355+
Buffer rob = buf.isReadOnly() ? buf : bp.asReadOnlyBuffer(buf);
356+
expectThrows(ReadOnlyBufferException.class,
357+
() -> bp.put(buf, 0, rob, 0, cap));
358+
}
359+
297360
@Test(dataProvider = "proxies")
298361
public static void testSelf(BufferProxy bp) throws Throwable {
299362
for (int i = 0; i < ITERATIONS; i++) {
@@ -311,22 +374,27 @@ public static void testSelf(BufferProxy bp) throws Throwable {
311374
Assert.expectThrows(ReadOnlyBufferException.class,
312375
() -> bp.copy(lower, 0, lowerCopy, 0, lowerLength));
313376
break;
314-
} else {
315-
bp.copy(lower, 0, lowerCopy, 0, lowerLength);
316377
}
378+
bp.copy(lower, 0, lowerCopy, 0, lowerLength);
317379

318380
int middleOffset = RND.nextInt(1 + cap/2);
319381
Buffer middle = buf.slice(middleOffset, lowerLength);
382+
Buffer middleCopy = bp.create(lowerLength);
383+
bp.copy(middle, 0, middleCopy, 0, lowerLength);
320384

321-
if (middle.isReadOnly()) {
322-
Assert.expectThrows(ReadOnlyBufferException.class,
323-
() -> bp.put(lower, middle));
324-
break;
325-
} else {
326-
bp.put(lower, middle);
327-
}
385+
bp.put(lower, middle);
328386
middle.flip();
329387

388+
Assert.assertTrue(bp.equals(lowerCopy, middle),
389+
String.format("%d %s %d %d %d %d%n", SEED,
390+
buf.getClass().getName(), cap,
391+
lowerOffset, lowerLength, middleOffset));
392+
393+
bp.copy(lowerCopy, 0, buf, lowerOffset, lowerLength);
394+
bp.copy(middleCopy, 0, buf, middleOffset, lowerLength);
395+
396+
bp.put(buf, lowerOffset, buf, middleOffset, lowerLength);
397+
330398
Assert.assertTrue(bp.equals(lowerCopy, middle),
331399
String.format("%d %s %d %d %d %d%n", SEED,
332400
buf.getClass().getName(), cap,
@@ -361,13 +429,29 @@ public static void testPairs(BufferProxy bp, BufferProxy sbp) throws Throwable {
361429
Assert.expectThrows(ReadOnlyBufferException.class,
362430
() -> bp.put(src, buf));
363431
break;
364-
} else {
365-
bp.put(src, buf);
366432
}
367433

434+
Buffer backup = bp.create(slim - spos);
435+
bp.copy(buf, pos, backup, 0, backup.capacity());
436+
bp.put(src, buf);
437+
368438
buf.reset();
369439
src.reset();
370440

441+
Assert.assertTrue(bp.equals(src, buf),
442+
String.format("%d %s %d %d %d %s %d %d %d%n", SEED,
443+
buf.getClass().getName(), cap, pos, lim,
444+
src.getClass().getName(), scap, spos, slim));
445+
446+
src.clear();
447+
buf.clear();
448+
bp.copy(backup, 0, buf, pos, backup.capacity());
449+
bp.put(src, spos, buf, pos, backup.capacity());
450+
src.position(spos);
451+
src.limit(slim);
452+
buf.position(pos);
453+
buf.limit(lim);
454+
371455
Assert.assertTrue(bp.equals(src, buf),
372456
String.format("%d %s %d %d %d %s %d %d %d%n", SEED,
373457
buf.getClass().getName(), cap, pos, lim,

0 commit comments

Comments
 (0)