From 02867c8aec9d69b392750601820513899e9a5e31 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Wed, 25 Jun 2014 16:52:14 -0400 Subject: [PATCH] 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 --- .../com/typesafe/config/impl/SimpleConfig.java | 14 +++----------- .../com/typesafe/config/impl/UnitParserTest.scala | 12 ++++++++++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java b/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java index 7cea76df..9f461e39 100644 --- a/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java +++ b/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java @@ -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, diff --git a/config/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala b/config/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala index 3106b55a..12a1ce52 100644 --- a/config/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala @@ -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()