HBASE-26197 Fix some obvious bugs in MultiByteBuff.put (#3586)

Signed-off-by: stack <stack@apache.org>
Signed-off-by: Duo Zhang <zhangduo@apache.org>
This commit is contained in:
chenglei 2021-09-07 22:25:04 +08:00 committed by Duo Zhang
parent 24971e8073
commit b78d35598c
2 changed files with 208 additions and 23 deletions

View File

@ -736,52 +736,125 @@ public class MultiByteBuff extends ByteBuff {
}
/**
* Copies from a src MBB to this MBB.
* @param offset the position in this MBB to which the copy should happen
* Copies from a src BB to this MBB. This will be absolute positional copying and won't affect the
* position of any of the buffers.
* @param destOffset the position in this MBB to which the copy should happen
* @param src the src MBB
* @param srcOffset the offset in the src MBB from where the elements should be read
* @param length the length upto which the copy should happen
* @throws BufferUnderflowException If there are fewer than length bytes remaining in src
* ByteBuff.
* @throws BufferOverflowException If there is insufficient available space in this MBB for length
* bytes.
*/
@Override
public MultiByteBuff put(int offset, ByteBuff src, int srcOffset, int length) {
public MultiByteBuff put(int destOffset, ByteBuff src, int srcOffset, int length) {
checkRefCount();
int destItemIndex = getItemIndex(offset);
int srcItemIndex = getItemIndex(srcOffset);
int destItemIndex = getItemIndex(destOffset);
int srcItemIndex = getItemIndexForByteBuff(src, srcOffset, length);
ByteBuffer destItem = this.items[destItemIndex];
offset = offset - this.itemBeginPos[destItemIndex];
destOffset = this.getRelativeOffset(destOffset, destItemIndex);
ByteBuffer srcItem = getItemByteBuffer(src, srcItemIndex);
srcOffset = srcOffset - this.itemBeginPos[srcItemIndex];
int toRead, toWrite, toMove;
srcOffset = getRelativeOffsetForByteBuff(src, srcOffset, srcItemIndex);
while (length > 0) {
toWrite = destItem.limit() - offset;
toRead = srcItem.limit() - srcOffset;
toMove = Math.min(length, Math.min(toRead, toWrite));
ByteBufferUtils.copyFromBufferToBuffer(srcItem, destItem, srcOffset, offset, toMove);
int toWrite = destItem.limit() - destOffset;
if (toWrite <= 0) {
throw new BufferOverflowException();
}
int toRead = srcItem.limit() - srcOffset;
if (toRead <= 0) {
throw new BufferUnderflowException();
}
int toMove = Math.min(length, Math.min(toRead, toWrite));
ByteBufferUtils.copyFromBufferToBuffer(srcItem, destItem, srcOffset, destOffset, toMove);
length -= toMove;
if (length == 0) break;
if (length == 0) {
break;
}
if (toRead < toWrite) {
srcItem = getItemByteBuffer(src, ++srcItemIndex);
if (++srcItemIndex >= getItemByteBufferCount(src)) {
throw new BufferUnderflowException();
}
srcItem = getItemByteBuffer(src, srcItemIndex);
srcOffset = 0;
offset += toMove;
destOffset += toMove;
} else if (toRead > toWrite) {
destItem = this.items[++destItemIndex];
offset = 0;
if (++destItemIndex >= this.items.length) {
throw new BufferOverflowException();
}
destItem = this.items[destItemIndex];
destOffset = 0;
srcOffset += toMove;
} else {
// toRead = toWrite case
srcItem = getItemByteBuffer(src, ++srcItemIndex);
if (++srcItemIndex >= getItemByteBufferCount(src)) {
throw new BufferUnderflowException();
}
srcItem = getItemByteBuffer(src, srcItemIndex);
srcOffset = 0;
destItem = this.items[++destItemIndex];
offset = 0;
if (++destItemIndex >= this.items.length) {
throw new BufferOverflowException();
}
destItem = this.items[destItemIndex];
destOffset = 0;
}
}
return this;
}
private static ByteBuffer getItemByteBuffer(ByteBuff buf, int index) {
return (buf instanceof SingleByteBuff) ? buf.nioByteBuffers()[0]
: ((MultiByteBuff) buf).items[index];
private static ByteBuffer getItemByteBuffer(ByteBuff buf, int byteBufferIndex) {
if (buf instanceof SingleByteBuff) {
if (byteBufferIndex != 0) {
throw new IndexOutOfBoundsException(
"index:[" + byteBufferIndex + "],but only index 0 is valid.");
}
return buf.nioByteBuffers()[0];
}
MultiByteBuff multiByteBuff = (MultiByteBuff) buf;
if (byteBufferIndex < 0 || byteBufferIndex >= multiByteBuff.items.length) {
throw new IndexOutOfBoundsException(
"index:[" + byteBufferIndex + "],but only index [0-" + multiByteBuff.items.length
+ ") is valid.");
}
return multiByteBuff.items[byteBufferIndex];
}
private static int getItemIndexForByteBuff(ByteBuff byteBuff, int offset, int length) {
if (byteBuff instanceof SingleByteBuff) {
ByteBuffer byteBuffer = byteBuff.nioByteBuffers()[0];
if (offset + length > byteBuffer.limit()) {
throw new BufferUnderflowException();
}
return 0;
}
MultiByteBuff multiByteBuff = (MultiByteBuff) byteBuff;
return multiByteBuff.getItemIndex(offset);
}
private static int getRelativeOffsetForByteBuff(ByteBuff byteBuff, int globalOffset,
int itemIndex) {
if (byteBuff instanceof SingleByteBuff) {
if (itemIndex != 0) {
throw new IndexOutOfBoundsException("index:[" + itemIndex + "],but only index 0 is valid.");
}
return globalOffset;
}
return ((MultiByteBuff) byteBuff).getRelativeOffset(globalOffset, itemIndex);
}
private int getRelativeOffset(int globalOffset, int itemIndex) {
if (itemIndex < 0 || itemIndex >= this.items.length) {
throw new IndexOutOfBoundsException(
"index:[" + itemIndex + "],but only index [0-" + this.items.length + ") is valid.");
}
return globalOffset - this.itemBeginPos[itemIndex];
}
private static int getItemByteBufferCount(ByteBuff buf) {
return (buf instanceof SingleByteBuff) ? 1 : ((MultiByteBuff) buf).items.length;
}
/**

View File

@ -25,6 +25,7 @@ import static org.junit.Assert.fail;
import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.testclassification.MiscTests;
@ -482,4 +483,115 @@ public class TestMultiByteBuff {
assertEquals(out.position(), 12);
assertTrue(Bytes.equals(Bytes.toBytes("abcdekabcdef"), 0, 12, out.array(), 0, 12));
}
@Test
public void testPositionalPutByteBuff() throws Exception {
ByteBuffer bb1 = ByteBuffer.allocate(100);
ByteBuffer bb2 = ByteBuffer.allocate(100);
MultiByteBuff srcMultiByteBuff = new MultiByteBuff(bb1, bb2);
for (int i = 0; i < 25; i++) {
srcMultiByteBuff.putLong(i * 8L);
}
// Test MultiByteBuff To MultiByteBuff
doTestPositionalPutByteBuff(srcMultiByteBuff);
ByteBuffer bb3 = ByteBuffer.allocate(200);
SingleByteBuff srcSingleByteBuff = new SingleByteBuff(bb3);
for (int i = 0; i < 25; i++) {
srcSingleByteBuff.putLong(i * 8L);
}
// Test SingleByteBuff To MultiByteBuff
doTestPositionalPutByteBuff(srcSingleByteBuff);
}
private void doTestPositionalPutByteBuff(ByteBuff srcByteBuff) throws Exception {
ByteBuffer bb3 = ByteBuffer.allocate(50);
ByteBuffer bb4 = ByteBuffer.allocate(50);
ByteBuffer bb5 = ByteBuffer.allocate(50);
ByteBuffer bb6 = ByteBuffer.allocate(50);
MultiByteBuff destMultiByteBuff = new MultiByteBuff(bb3, bb4, bb5, bb6);
// full copy
destMultiByteBuff.put(0, srcByteBuff, 0, 200);
int compareTo = ByteBuff.compareTo(srcByteBuff, 0, 200, destMultiByteBuff, 0, 200);
assertTrue(compareTo == 0);
// Test src to dest first ByteBuffer
destMultiByteBuff.put(0, srcByteBuff, 32, 63);
compareTo = ByteBuff.compareTo(srcByteBuff, 32, 63, destMultiByteBuff, 0, 63);
assertTrue(compareTo == 0);
// Test src to dest first and second ByteBuffer
destMultiByteBuff.put(0, srcByteBuff, 0, 63);
compareTo = ByteBuff.compareTo(srcByteBuff, 0, 63, destMultiByteBuff, 0, 63);
assertTrue(compareTo == 0);
// Test src to dest third ByteBuffer
destMultiByteBuff.put(100, srcByteBuff, 100, 50);
compareTo = ByteBuff.compareTo(srcByteBuff, 100, 50, destMultiByteBuff, 100, 50);
assertTrue(compareTo == 0);
// Test src to dest first,second and third ByteBuffer
destMultiByteBuff.put(48, srcByteBuff, 32, 63);
compareTo = ByteBuff.compareTo(srcByteBuff, 32, 63, destMultiByteBuff, 48, 63);
assertTrue(compareTo == 0);
// Test src to dest first,second,third and fourth ByteBuffer
destMultiByteBuff.put(48, srcByteBuff, 32, 120);
compareTo = ByteBuff.compareTo(srcByteBuff, 32, 120, destMultiByteBuff, 48, 120);
assertTrue(compareTo == 0);
// Test src to dest first and second ByteBuffer
destMultiByteBuff.put(0, srcByteBuff, 132, 63);
compareTo = ByteBuff.compareTo(srcByteBuff, 132, 63, destMultiByteBuff, 0, 63);
assertTrue(compareTo == 0);
// Test src to dest second,third and fourth ByteBuffer
destMultiByteBuff.put(95, srcByteBuff, 132, 67);
compareTo = ByteBuff.compareTo(srcByteBuff, 132, 67, destMultiByteBuff, 95, 67);
assertTrue(compareTo == 0);
// Test src to dest fourth ByteBuffer
destMultiByteBuff.put(162, srcByteBuff, 132, 24);
compareTo = ByteBuff.compareTo(srcByteBuff, 132, 24, destMultiByteBuff, 162, 24);
assertTrue(compareTo == 0);
// Test src BufferUnderflowException
try {
destMultiByteBuff.put(0, srcByteBuff, 0, 300);
fail();
} catch (BufferUnderflowException e) {
assertTrue(e != null);
}
try {
destMultiByteBuff.put(95, srcByteBuff, 132, 89);
fail();
} catch (BufferUnderflowException e) {
assertTrue(e != null);
}
// Test dest BufferOverflowException
try {
destMultiByteBuff.put(100, srcByteBuff, 0, 101);
fail();
} catch (BufferOverflowException e) {
assertTrue(e != null);
}
try {
destMultiByteBuff.put(151, srcByteBuff, 132, 68);
fail();
} catch (BufferOverflowException e) {
assertTrue(e != null);
}
destMultiByteBuff = new MultiByteBuff(bb3, bb4);
try {
destMultiByteBuff.put(0, srcByteBuff, 0, 101);
fail();
} catch (BufferOverflowException e) {
assertTrue(e != null);
}
}
}