Skip to content

Commit

Permalink
Improve size-in-bytes parser's use of BigInteger/BigDecimal
Browse files Browse the repository at this point in the history
 - use bitLength to see if we overflow Long
 - use BigInteger and BigDecimal to parse, instead of parseLong/parseDouble
  • Loading branch information
havocp committed Jun 25, 2014
1 parent 6311ef8 commit 02867c8
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 13 deletions.
14 changes: 3 additions & 11 deletions config/src/main/java/com/typesafe/config/impl/SimpleConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -624,12 +624,6 @@ static MemoryUnit parseUnit(String unit) {
}
}

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
Expand Down Expand Up @@ -673,14 +667,12 @@ public static long parseBytes(String input, ConfigOrigin originForException,
// 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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()
Expand Down

0 comments on commit 02867c8

Please sign in to comment.