VarInt: fix long values accepted as an int value

`-1` and `Integer.MIN_VALUE` (and all values inbetween) are in fact
very large VarInts that can only be expressed as a long. So their
test data have been moved from `VarIntTest.integerTestVectors` to
`longTestVectors`.

`intValue()` is changed to guard against such cases, and a helper
`fitsInt()` is introduced.

JavaDocs are added to `intValue()` and `longValue()` to clarify the
expected value ranges.
This commit is contained in:
Andreas Schildbach 2023-04-13 11:33:25 +02:00
parent 5f5e7384a2
commit ba8849ca13
2 changed files with 31 additions and 6 deletions

View File

@ -107,12 +107,36 @@ public class VarInt {
originallyEncodedSize = copy.originallyEncodedSize; originallyEncodedSize = copy.originallyEncodedSize;
} }
/**
* Gets the value as a long. For values greater than {@link Long#MAX_VALUE} the returned long
* will be negative. It is still to be interpreted as an unsigned value.
*
* @return value as a long
*/
public long longValue() { public long longValue() {
return value; return value;
} }
public int intValue() { /**
return Math.toIntExact(value); * Determine if the value would fit an int, i.e. it is in the range of {@code 0} to {@link Integer#MAX_VALUE}.
* If this is true, it's safe to call {@link #intValue()}.
*
* @return true if the value fits an int, false otherwise
*/
public boolean fitsInt() {
return value >= 0 && value <= Integer.MAX_VALUE;
}
/**
* Gets the value as an unsigned int in the range of {@code 0} to {@link Integer#MAX_VALUE}.
*
* @return value as an unsigned int
* @throws ArithmeticException if the value doesn't fit an int
*/
public int intValue() throws ArithmeticException {
check(fitsInt(), () ->
new ArithmeticException("value too large for an int: " + Long.toUnsignedString(value)));
return (int) value;
} }
/** /**

View File

@ -26,6 +26,7 @@ import java.nio.ByteBuffer;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@RunWith(JUnitParamsRunner.class) @RunWith(JUnitParamsRunner.class)
public class VarIntTest { public class VarIntTest {
@ -34,6 +35,7 @@ public class VarIntTest {
@Parameters(method = "integerTestVectors") @Parameters(method = "integerTestVectors")
public void testIntCreation(int value, int size) { public void testIntCreation(int value, int size) {
VarInt a = VarInt.of(value); VarInt a = VarInt.of(value);
assertTrue(a.fitsInt());
assertEquals(value, a.intValue()); assertEquals(value, a.intValue());
assertEquals(size, a.getSizeInBytes()); assertEquals(size, a.getSizeInBytes());
assertEquals(size, a.getOriginalSizeInBytes()); assertEquals(size, a.getOriginalSizeInBytes());
@ -45,6 +47,7 @@ public class VarIntTest {
@Parameters(method = "longTestVectors") @Parameters(method = "longTestVectors")
public void testIntGetErr(int value, int size) { public void testIntGetErr(int value, int size) {
VarInt a = VarInt.of(value); VarInt a = VarInt.of(value);
assertFalse(a.fitsInt());
a.intValue(); a.intValue();
} }
@ -52,6 +55,7 @@ public class VarIntTest {
@Parameters(method = "longTestVectors") @Parameters(method = "longTestVectors")
public void testIntGetErr2(int value, int size) { public void testIntGetErr2(int value, int size) {
VarInt a = VarInt.of(value); VarInt a = VarInt.of(value);
assertFalse(a.fitsInt());
VarInt.ofBytes(a.serialize(), 0).intValue(); VarInt.ofBytes(a.serialize(), 0).intValue();
} }
@ -89,10 +93,7 @@ public class VarIntTest {
new Object[]{ 0x7FFF, 3}, new Object[]{ 0x7FFF, 3},
new Object[]{ 0x8000, 3}, new Object[]{ 0x8000, 3},
new Object[]{ 0x10000, 5}, new Object[]{ 0x10000, 5},
new Object[]{ Integer.MIN_VALUE, 9},
new Object[]{ Integer.MAX_VALUE, 5}, new Object[]{ Integer.MAX_VALUE, 5},
// -1 shouldn't normally be passed, but at least stay consistent (bug regression test)
new Object[]{ -1, 9}
}; };
} }
@ -105,9 +106,9 @@ public class VarIntTest {
new Object[]{ 0xAABBCCDDL, 5}, new Object[]{ 0xAABBCCDDL, 5},
new Object[]{ 0xFFFFFFFFL, 5}, new Object[]{ 0xFFFFFFFFL, 5},
new Object[]{ 0xCAFEBABEDEADBEEFL, 9}, new Object[]{ 0xCAFEBABEDEADBEEFL, 9},
new Object[]{ Integer.MIN_VALUE, 9},
new Object[]{ Long.MIN_VALUE, 9}, new Object[]{ Long.MIN_VALUE, 9},
new Object[]{ Long.MAX_VALUE, 9}, new Object[]{ Long.MAX_VALUE, 9},
// -1 shouldn't normally be passed, but at least stay consistent (bug regression test)
new Object[]{ -1L, 9} new Object[]{ -1L, 9}
}; };
} }