Improve size-in-bytes parser's use of BigInteger/BigDecimal

- use bitLength to see if we overflow Long
 - use BigInteger and BigDecimal to parse, instead of parseLong/parseDouble
This commit is contained in:
Havoc Pennington 2014-06-25 16:52:14 -04:00
parent 6311ef8d4d
commit 02867c8aec
2 changed files with 13 additions and 13 deletions

View File

@ -624,12 +624,6 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
}
}
private static boolean isValidLong(BigInteger v) {
BigInteger max = BigInteger.valueOf(Long.MAX_VALUE);
BigInteger min = BigInteger.valueOf(Long.MIN_VALUE);
return max.compareTo(v) >= 0 && min.compareTo(v) <= 0;
}
/**
* Parses a size-in-bytes string. If no units are specified in the string,
* it is assumed to be in bytes. The returned value is in bytes. The purpose
@ -673,14 +667,12 @@ final class SimpleConfig implements Config, MergeableValue, Serializable {
// if the string is purely digits, parse as an integer to avoid
// possible precision loss; otherwise as a double.
if (numberString.matches("[0-9]+")) {
Long v = Long.parseLong(numberString);
result = units.bytes.multiply(BigInteger.valueOf(v));
result = units.bytes.multiply(new BigInteger(numberString));
} else {
Double v = Double.parseDouble(numberString);
BigDecimal resultDecimal = (new BigDecimal(units.bytes)).multiply(BigDecimal.valueOf(v));
BigDecimal resultDecimal = (new BigDecimal(units.bytes)).multiply(new BigDecimal(numberString));
result = resultDecimal.toBigInteger();
}
if (isValidLong(result))
if (result.bitLength() < 64)
return result.longValue();
else
throw new ConfigException.BadValue(originForException, pathForException,

View File

@ -59,7 +59,10 @@ class UnitParserTest extends TestUtils {
@Test
def parseMemorySizeInBytes(): Unit = {
def parseMem(s: String) = SimpleConfig.parseBytes(s, fakeOrigin(), "test")
def parseMem(s: String): Long = SimpleConfig.parseBytes(s, fakeOrigin(), "test")
assertEquals(Long.MaxValue, parseMem(s"${Long.MaxValue} bytes"))
assertEquals(Long.MinValue, parseMem(s"${Long.MinValue} bytes"))
val oneMebis = List("1048576", "1048576b", "1048576bytes", "1048576byte",
"1048576 b", "1048576 bytes",
@ -123,13 +126,18 @@ class UnitParserTest extends TestUtils {
// later on we'll want to check this with BigInteger version of getBytes
@Test
def parseHugeMemorySizes(): Unit = {
def parseMem(s: String) = SimpleConfig.parseBytes(s, fakeOrigin(), "test")
def parseMem(s: String): Long = SimpleConfig.parseBytes(s, fakeOrigin(), "test")
def assertOutOfRange(s: String) = {
val fail = intercept[ConfigException.BadValue] {
parseMem(s)
}
assertTrue("number was too big", fail.getMessage.contains("out of range"))
}
import java.math.BigInteger
assertOutOfRange(s"${BigInteger.valueOf(Long.MaxValue).add(BigInteger.valueOf(1)).toString} bytes")
assertOutOfRange(s"${BigInteger.valueOf(Long.MinValue).subtract(BigInteger.valueOf(1)).toString} bytes")
var result = 1024L * 1024 * 1024
for (unit <- Seq("zebi", "yobi")) {
val first = unit.substring(0, 1).toUpperCase()