[#5718] Result of ByteBufUtil.compare(ByteBuf a, ByteBuf b) is dependent on ByteOrder of supplied ByteBufs

Motivation:

Result of ByteBufUtil.compare(ByteBuf a, ByteBuf b) is dependent on ByteOrder of supplied ByteBufs which should not be the case (as stated in the javadocs).

Modifications:

Ensure we get a consistent behavior when calling ByteBufUtil.compare(ByteBuf a, ByteBuf b) and not depend on ByteOrder.

Result:

ByteBufUtil.compare(ByteBuf a, ByteBuf b) and so AbstractByteBuf.compare(...) works correctly as stated in the javadocs.
This commit is contained in:
Norman Maurer 2016-08-19 11:19:30 +02:00
parent 5b46cf25c1
commit a01519e4f8
3 changed files with 88 additions and 36 deletions

View File

@ -229,54 +229,83 @@ public final class ByteBufUtil {
final int minLength = Math.min(aLen, bLen); final int minLength = Math.min(aLen, bLen);
final int uintCount = minLength >>> 2; final int uintCount = minLength >>> 2;
final int byteCount = minLength & 3; final int byteCount = minLength & 3;
int aIndex = bufferA.readerIndex(); int aIndex = bufferA.readerIndex();
int bIndex = bufferB.readerIndex(); int bIndex = bufferB.readerIndex();
if (uintCount > 0) {
boolean bufferAIsBigEndian = bufferA.order() == ByteOrder.BIG_ENDIAN;
final long res;
int uintCountIncrement = uintCount << 2;
if (bufferA.order() == bufferB.order()) { if (bufferA.order() == bufferB.order()) {
for (int i = uintCount; i > 0; i --) { res = bufferAIsBigEndian ? compareUintBigEndian(bufferA, bufferB, aIndex, bIndex, uintCountIncrement) :
long va = bufferA.getUnsignedInt(aIndex); compareUintLittleEndian(bufferA, bufferB, aIndex, bIndex, uintCountIncrement);
long vb = bufferB.getUnsignedInt(bIndex);
if (va > vb) {
return 1;
}
if (va < vb) {
return -1;
}
aIndex += 4;
bIndex += 4;
}
} else { } else {
for (int i = uintCount; i > 0; i --) { res = bufferAIsBigEndian ? compareUintBigEndianA(bufferA, bufferB, aIndex, bIndex, uintCountIncrement) :
long va = bufferA.getUnsignedInt(aIndex); compareUintBigEndianB(bufferA, bufferB, aIndex, bIndex, uintCountIncrement);
long vb = swapInt(bufferB.getInt(bIndex)) & 0xFFFFFFFFL;
if (va > vb) {
return 1;
} }
if (va < vb) { if (res != 0) {
return -1; // Ensure we not overflow when cast
} return (int) Math.min(Integer.MAX_VALUE, res);
aIndex += 4;
bIndex += 4;
} }
aIndex += uintCountIncrement;
bIndex += uintCountIncrement;
} }
for (int i = byteCount; i > 0; i --) { for (int aEnd = aIndex + byteCount; aIndex < aEnd; ++aIndex, ++bIndex) {
short va = bufferA.getUnsignedByte(aIndex); int comp = bufferA.getUnsignedByte(aIndex) - bufferB.getUnsignedByte(bIndex);
short vb = bufferB.getUnsignedByte(bIndex); if (comp != 0) {
if (va > vb) { return comp;
return 1;
} }
if (va < vb) {
return -1;
}
aIndex ++;
bIndex ++;
} }
return aLen - bLen; return aLen - bLen;
} }
private static long compareUintBigEndian(
ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
long comp = bufferA.getUnsignedInt(aIndex) - bufferB.getUnsignedInt(bIndex);
if (comp != 0) {
return comp;
}
}
return 0;
}
private static long compareUintLittleEndian(
ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
long comp = bufferA.getUnsignedIntLE(aIndex) - bufferB.getUnsignedIntLE(bIndex);
if (comp != 0) {
return comp;
}
}
return 0;
}
private static long compareUintBigEndianA(
ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
long comp = bufferA.getUnsignedInt(aIndex) - bufferB.getUnsignedIntLE(bIndex);
if (comp != 0) {
return comp;
}
}
return 0;
}
private static long compareUintBigEndianB(
ByteBuf bufferA, ByteBuf bufferB, int aIndex, int bIndex, int uintCountIncrement) {
for (int aEnd = aIndex + uintCountIncrement; aIndex < aEnd; aIndex += 4, bIndex += 4) {
long comp = bufferA.getUnsignedIntLE(aIndex) - bufferB.getUnsignedInt(bIndex);
if (comp != 0) {
return comp;
}
}
return 0;
}
/** /**
* The default implementation of {@link ByteBuf#indexOf(int, int, byte)}. * The default implementation of {@link ByteBuf#indexOf(int, int, byte)}.
* This method is useful when implementing a new buffer type. * This method is useful when implementing a new buffer type.

View File

@ -31,6 +31,7 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ReadOnlyBufferException; import java.nio.ReadOnlyBufferException;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
@ -1822,6 +1823,28 @@ public abstract class AbstractByteBufTest {
buffer.retainedSlice(0, 31).compareTo(wrappedBuffer(value).order(LITTLE_ENDIAN)) < 0); buffer.retainedSlice(0, 31).compareTo(wrappedBuffer(value).order(LITTLE_ENDIAN)) < 0);
} }
@Test
public void testCompareTo2() {
byte[] bytes = {1, 2, 3, 4};
byte[] bytesReversed = {4, 3, 2, 1};
ByteBuf buf1 = newBuffer(4).clear().writeBytes(bytes).order(ByteOrder.LITTLE_ENDIAN);
ByteBuf buf2 = newBuffer(4).clear().writeBytes(bytesReversed).order(ByteOrder.LITTLE_ENDIAN);
ByteBuf buf3 = newBuffer(4).clear().writeBytes(bytes).order(ByteOrder.BIG_ENDIAN);
ByteBuf buf4 = newBuffer(4).clear().writeBytes(bytesReversed).order(ByteOrder.BIG_ENDIAN);
try {
assertEquals(buf1.compareTo(buf2), buf3.compareTo(buf4));
assertEquals(buf2.compareTo(buf1), buf4.compareTo(buf3));
assertEquals(buf1.compareTo(buf3), buf2.compareTo(buf4));
assertEquals(buf3.compareTo(buf1), buf4.compareTo(buf2));
} finally {
buf1.release();
buf2.release();
buf3.release();
buf4.release();
}
}
@Test @Test
public void testToString() { public void testToString() {
buffer.clear(); buffer.clear();