From 9f29aa5cb0ebe5d820664258bd1fe53e97e6f3e1 Mon Sep 17 00:00:00 2001 From: mpryahin Date: Thu, 26 Dec 2019 22:46:08 +0300 Subject: [PATCH 1/8] added support for memory units which don't fit in a long when transformed to bytes --- .../com/typesafe/config/ConfigMemorySize.java | 32 +++++-- .../typesafe/config/impl/SimpleConfig.java | 92 +++++++++++++++++-- config/src/test/resources/test01.conf | 2 + .../config/impl/ConfigMemorySizeTest.scala | 8 ++ .../com/typesafe/config/impl/ConfigTest.scala | 5 + 5 files changed, 124 insertions(+), 15 deletions(-) diff --git a/config/src/main/java/com/typesafe/config/ConfigMemorySize.java b/config/src/main/java/com/typesafe/config/ConfigMemorySize.java index 322b2c82f..5b1b1c678 100644 --- a/config/src/main/java/com/typesafe/config/ConfigMemorySize.java +++ b/config/src/main/java/com/typesafe/config/ConfigMemorySize.java @@ -1,21 +1,26 @@ /** - * Copyright (C) 2015 Typesafe Inc. + * Copyright (C) 2015 Typesafe Inc. */ package com.typesafe.config; +import java.math.BigInteger; +import java.util.concurrent.TimeUnit; + /** * An immutable class representing an amount of memory. Use * static factory methods such as {@link - * ConfigMemorySize#ofBytes(long)} to create instances. + * ConfigMemorySize#ofBytes(BigInteger)} to create instances. * * @since 1.3.0 */ public final class ConfigMemorySize { - private final long bytes; - private ConfigMemorySize(long bytes) { - if (bytes < 0) + private BigInteger bytes; + + private ConfigMemorySize(BigInteger bytes) { + if (bytes.compareTo(BigInteger.ZERO) < 0) { throw new IllegalArgumentException("Attempt to construct ConfigMemorySize with negative number: " + bytes); + } this.bytes = bytes; } @@ -26,16 +31,27 @@ private ConfigMemorySize(long bytes) { * @param bytes a number of bytes * @return an instance representing the number of bytes */ - public static ConfigMemorySize ofBytes(long bytes) { + public static ConfigMemorySize ofBytes(BigInteger bytes) { return new ConfigMemorySize(bytes); } + public static ConfigMemorySize ofBytes(long bytes) { + return new ConfigMemorySize(BigInteger.valueOf(bytes)); + } + /** + * * Gets the size in bytes. + * + * @deprecated use {@link #getBytes()} to handle to bytes conversion of huge memory units. * @since 1.3.0 * @return how many bytes */ public long toBytes() { + return bytes.longValueExact(); + } + + public BigInteger getBytes() { return bytes; } @@ -47,7 +63,7 @@ public String toString() { @Override public boolean equals(Object other) { if (other instanceof ConfigMemorySize) { - return ((ConfigMemorySize)other).bytes == this.bytes; + return ((ConfigMemorySize) other).bytes.equals(this.bytes); } else { return false; } @@ -56,7 +72,7 @@ public boolean equals(Object other) { @Override public int hashCode() { // in Java 8 this can become Long.hashCode(bytes) - return Long.valueOf(bytes).hashCode(); + return bytes.hashCode(); } } 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 c0d6b65c7..b9f47aaed 100644 --- a/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java +++ b/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java @@ -295,7 +295,15 @@ public Long getBytes(String path) { @Override public ConfigMemorySize getMemorySize(String path) { - return ConfigMemorySize.ofBytes(getBytes(path)); + BigInteger size; + try { + size = BigInteger.valueOf(getLong(path)); + } catch (ConfigException.WrongType e) { + ConfigValue v = find(path, ConfigValueType.STRING); + size = parseBytesAsBigInteger((String) v.unwrapped(), + v.origin(), path); + } + return ConfigMemorySize.ofBytes(size); } @Deprecated @@ -500,14 +508,43 @@ public List getBytesList(String path) { return l; } + public List getBytesListAsBigInteger(String path) { + List l = new ArrayList<>(); + List list = getList(path); + for (ConfigValue v : list) { + if (v.valueType() == ConfigValueType.NUMBER) { + l.add(BigInteger.valueOf(((Number) v.unwrapped()).longValue())); + } else if (v.valueType() == ConfigValueType.STRING) { + String s = (String) v.unwrapped(); + BigInteger n = parseBytesAsBigInteger(s, v.origin(), path); + l.add(n); + } else { + throw new ConfigException.WrongType(v.origin(), path, + "memory size string or number of bytes", v.valueType() + .name()); + } + } + return l; + } + @Override public List getMemorySizeList(String path) { - List list = getBytesList(path); - List builder = new ArrayList(); - for (Long v : list) { - builder.add(ConfigMemorySize.ofBytes(v)); + List l = new ArrayList<>(); + List list = getList(path); + for (ConfigValue v : list) { + if (v.valueType() == ConfigValueType.NUMBER) { + l.add(ConfigMemorySize.ofBytes(((Number) v.unwrapped()).longValue())); + } else if (v.valueType() == ConfigValueType.STRING) { + String s = (String) v.unwrapped(); + BigInteger n = parseBytesAsBigInteger(s, v.origin(), path); + l.add(ConfigMemorySize.ofBytes(n)); + } else { + throw new ConfigException.WrongType(v.origin(), path, + "memory size string or number of bytes", v.valueType() + .name()); + } } - return builder; + return l; } @Override @@ -884,7 +921,48 @@ public static long parseBytes(String input, ConfigOrigin originForException, return result.longValue(); else throw new ConfigException.BadValue(originForException, pathForException, - "size-in-bytes value is out of range for a 64-bit long: '" + input + "'"); + "size-in-bytes value is out of range for a 64-bit long: '" + input + "'"); + } catch (NumberFormatException e) { + throw new ConfigException.BadValue(originForException, pathForException, + "Could not parse size-in-bytes number '" + numberString + "'"); + } + } + + + public static BigInteger parseBytesAsBigInteger(String input, ConfigOrigin originForException, + String pathForException) { + String s = ConfigImplUtil.unicodeTrim(input); + String unitString = getUnits(s); + String numberString = ConfigImplUtil.unicodeTrim(s.substring(0, + s.length() - unitString.length())); + + // this would be caught later anyway, but the error message + // is more helpful if we check it here. + if (numberString.length() == 0) { + throw new ConfigException.BadValue(originForException, + pathForException, "No number in size-in-bytes value '" + + input + "'"); + } + + MemoryUnit units = MemoryUnit.parseUnit(unitString); + + if (units == null) { + throw new ConfigException.BadValue(originForException, pathForException, + "Could not parse size-in-bytes unit '" + unitString + + "' (try k, K, kB, KiB, kilobytes, kibibytes)"); + } + + try { + BigInteger result; + // if the string is purely digits, parse as an integer to avoid + // possible precision loss; otherwise as a double. + if (numberString.matches("[0-9]+")) { + result = units.bytes.multiply(new BigInteger(numberString)); + } else { + BigDecimal resultDecimal = (new BigDecimal(units.bytes)).multiply(new BigDecimal(numberString)); + result = resultDecimal.toBigInteger(); + } + return result; } catch (NumberFormatException e) { throw new ConfigException.BadValue(originForException, pathForException, "Could not parse size-in-bytes number '" + numberString + "'"); diff --git a/config/src/test/resources/test01.conf b/config/src/test/resources/test01.conf index e40eb7f5d..a8389fd68 100644 --- a/config/src/test/resources/test01.conf +++ b/config/src/test/resources/test01.conf @@ -78,6 +78,8 @@ "megsList" : [1M, 1024K, 1048576], "megAsNumber" : 1048576, "halfMeg" : 0.5M + "yottabyte" : 1YB + "yottabyteList" : [1YB, 0.5YB] }, "system" : { diff --git a/config/src/test/scala/com/typesafe/config/impl/ConfigMemorySizeTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigMemorySizeTest.scala index f98e6d4f8..e635e1d50 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigMemorySizeTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigMemorySizeTest.scala @@ -3,6 +3,8 @@ */ package com.typesafe.config.impl +import java.math.BigInteger + import org.junit.Assert._ import org.junit._ import com.typesafe.config.ConfigMemorySize @@ -22,4 +24,10 @@ class ConfigMemorySizeTest extends TestUtils { val kilobyte = ConfigMemorySize.ofBytes(1024) assertEquals(1024, kilobyte.toBytes) } + + @Test + def testGetBytes() { + val yottabyte = ConfigMemorySize.ofBytes(new BigInteger("1000000000000000000000000")) + assertEquals(new BigInteger("1000000000000000000000000"), yottabyte.getBytes) + } } diff --git a/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala index eeda74e70..7f1668154 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala @@ -3,6 +3,7 @@ */ package com.typesafe.config.impl +import java.math.BigInteger import java.time.temporal.{ ChronoUnit, TemporalUnit } import org.junit.Assert._ @@ -831,6 +832,10 @@ class ConfigTest extends TestUtils { assertEquals(Seq(1024 * 1024L, 1024 * 1024L, 1024L * 1024L), conf.getMemorySizeList("memsizes.megsList").asScala.map(_.toBytes)) assertEquals(512 * 1024L, conf.getMemorySize("memsizes.halfMeg").toBytes) + + assertEquals(new BigInteger("1000000000000000000000000"), conf.getMemorySize("memsizes.yottabyte").getBytes) + assertEquals(Seq(new BigInteger("1000000000000000000000000"), new BigInteger("500000000000000000000000")), + conf.getMemorySizeList("memsizes.yottabyteList").asScala.map(_.getBytes)) } @Test From 50c86cf3098f9354fbd4099856fde16f1af66c76 Mon Sep 17 00:00:00 2001 From: mpryahin Date: Thu, 26 Dec 2019 22:52:48 +0300 Subject: [PATCH 2/8] fixed code format --- .../main/java/com/typesafe/config/ConfigMemorySize.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/config/src/main/java/com/typesafe/config/ConfigMemorySize.java b/config/src/main/java/com/typesafe/config/ConfigMemorySize.java index 5b1b1c678..c4a5e1598 100644 --- a/config/src/main/java/com/typesafe/config/ConfigMemorySize.java +++ b/config/src/main/java/com/typesafe/config/ConfigMemorySize.java @@ -1,10 +1,9 @@ /** - * Copyright (C) 2015 Typesafe Inc. + * Copyright (C) 2015 Typesafe Inc. */ package com.typesafe.config; import java.math.BigInteger; -import java.util.concurrent.TimeUnit; /** * An immutable class representing an amount of memory. Use @@ -18,9 +17,8 @@ public final class ConfigMemorySize { private BigInteger bytes; private ConfigMemorySize(BigInteger bytes) { - if (bytes.compareTo(BigInteger.ZERO) < 0) { + if (bytes.compareTo(BigInteger.ZERO) < 0) throw new IllegalArgumentException("Attempt to construct ConfigMemorySize with negative number: " + bytes); - } this.bytes = bytes; } @@ -40,7 +38,6 @@ public static ConfigMemorySize ofBytes(long bytes) { } /** - * * Gets the size in bytes. * * @deprecated use {@link #getBytes()} to handle to bytes conversion of huge memory units. @@ -63,7 +60,7 @@ public String toString() { @Override public boolean equals(Object other) { if (other instanceof ConfigMemorySize) { - return ((ConfigMemorySize) other).bytes.equals(this.bytes); + return ((ConfigMemorySize)other).bytes.equals(this.bytes); } else { return false; } From a789d3884c2c31bef3e389f9e4dbdb6991a950e1 Mon Sep 17 00:00:00 2001 From: mpryahin Date: Thu, 26 Dec 2019 23:09:05 +0300 Subject: [PATCH 3/8] added Javadocs --- .../java/com/typesafe/config/ConfigMemorySize.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/config/src/main/java/com/typesafe/config/ConfigMemorySize.java b/config/src/main/java/com/typesafe/config/ConfigMemorySize.java index c4a5e1598..2040c34c3 100644 --- a/config/src/main/java/com/typesafe/config/ConfigMemorySize.java +++ b/config/src/main/java/com/typesafe/config/ConfigMemorySize.java @@ -33,6 +33,12 @@ public static ConfigMemorySize ofBytes(BigInteger bytes) { return new ConfigMemorySize(bytes); } + /** + * Constructs a ConfigMemorySize representing the given + * number of bytes. + * @param bytes a number of bytes + * @return an instance representing the number of bytes + */ public static ConfigMemorySize ofBytes(long bytes) { return new ConfigMemorySize(BigInteger.valueOf(bytes)); } @@ -48,6 +54,10 @@ public long toBytes() { return bytes.longValueExact(); } + /** + * Gets the size in bytes. + * @return how many bytes + */ public BigInteger getBytes() { return bytes; } From db6b6dcaf6c177eb00204a85d49aec8c596375b4 Mon Sep 17 00:00:00 2001 From: mpryahin Date: Thu, 26 Dec 2019 23:18:48 +0300 Subject: [PATCH 4/8] removed a redundant method --- .../typesafe/config/impl/SimpleConfig.java | 19 ------------------- 1 file changed, 19 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 b9f47aaed..36cd7182b 100644 --- a/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java +++ b/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java @@ -508,25 +508,6 @@ public List getBytesList(String path) { return l; } - public List getBytesListAsBigInteger(String path) { - List l = new ArrayList<>(); - List list = getList(path); - for (ConfigValue v : list) { - if (v.valueType() == ConfigValueType.NUMBER) { - l.add(BigInteger.valueOf(((Number) v.unwrapped()).longValue())); - } else if (v.valueType() == ConfigValueType.STRING) { - String s = (String) v.unwrapped(); - BigInteger n = parseBytesAsBigInteger(s, v.origin(), path); - l.add(n); - } else { - throw new ConfigException.WrongType(v.origin(), path, - "memory size string or number of bytes", v.valueType() - .name()); - } - } - return l; - } - @Override public List getMemorySizeList(String path) { List l = new ArrayList<>(); From 179e6d647a617c261ca841bff42c42b87e761a66 Mon Sep 17 00:00:00 2001 From: mpryahin Date: Thu, 9 Jan 2020 12:03:24 +0300 Subject: [PATCH 5/8] Code review improvements --- .../com/typesafe/config/ConfigMemorySize.java | 15 +++- .../typesafe/config/impl/SimpleConfig.java | 73 +++---------------- .../config/impl/ConfigMemorySizeTest.scala | 2 +- .../com/typesafe/config/impl/ConfigTest.scala | 4 +- 4 files changed, 24 insertions(+), 70 deletions(-) diff --git a/config/src/main/java/com/typesafe/config/ConfigMemorySize.java b/config/src/main/java/com/typesafe/config/ConfigMemorySize.java index 2040c34c3..4b8ae9800 100644 --- a/config/src/main/java/com/typesafe/config/ConfigMemorySize.java +++ b/config/src/main/java/com/typesafe/config/ConfigMemorySize.java @@ -46,19 +46,26 @@ public static ConfigMemorySize ofBytes(long bytes) { /** * Gets the size in bytes. * - * @deprecated use {@link #getBytes()} to handle to bytes conversion of huge memory units. * @since 1.3.0 * @return how many bytes */ public long toBytes() { - return bytes.longValueExact(); + if (bytes.bitLength() < 64) + return bytes.longValue(); + else + throw new IllegalArgumentException( + "size-in-bytes value is out of range for a 64-bit long: '" + bytes + "'"); } /** - * Gets the size in bytes. + * Gets the size in bytes. The behavior of this method + * is the same as that of the {@link #toBytes()} method, + * except that the number of bytes returned as a + * BigInteger value. Use it when memory value in bytes + * doesn't fit in a long value. * @return how many bytes */ - public BigInteger getBytes() { + public BigInteger toBytesBigInteger() { return bytes; } 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 36cd7182b..f8c4d0771 100644 --- a/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java +++ b/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import com.typesafe.config.Config; import com.typesafe.config.ConfigException; @@ -282,15 +283,7 @@ public Object getAnyRef(String path) { @Override public Long getBytes(String path) { - Long size = null; - try { - size = getLong(path); - } catch (ConfigException.WrongType e) { - ConfigValue v = find(path, ConfigValueType.STRING); - size = parseBytes((String) v.unwrapped(), - v.origin(), path); - } - return size; + return getMemorySize(path).toBytes(); } @Override @@ -490,22 +483,9 @@ public List getAnyRefList(String path) { @Override public List getBytesList(String path) { - List l = new ArrayList(); - List list = getList(path); - for (ConfigValue v : list) { - if (v.valueType() == ConfigValueType.NUMBER) { - l.add(((Number) v.unwrapped()).longValue()); - } else if (v.valueType() == ConfigValueType.STRING) { - String s = (String) v.unwrapped(); - Long n = parseBytes(s, v.origin(), path); - l.add(n); - } else { - throw new ConfigException.WrongType(v.origin(), path, - "memory size string or number of bytes", v.valueType() - .name()); - } - } - return l; + return getMemorySizeList(path).stream() + .map(ConfigMemorySize::toBytes) + .collect(Collectors.toList()); } @Override @@ -868,45 +848,12 @@ static MemoryUnit parseUnit(String unit) { */ public static long parseBytes(String input, ConfigOrigin originForException, String pathForException) { - String s = ConfigImplUtil.unicodeTrim(input); - String unitString = getUnits(s); - String numberString = ConfigImplUtil.unicodeTrim(s.substring(0, - s.length() - unitString.length())); - - // this would be caught later anyway, but the error message - // is more helpful if we check it here. - if (numberString.length() == 0) - throw new ConfigException.BadValue(originForException, - pathForException, "No number in size-in-bytes value '" - + input + "'"); - - MemoryUnit units = MemoryUnit.parseUnit(unitString); - - if (units == null) { - throw new ConfigException.BadValue(originForException, pathForException, - "Could not parse size-in-bytes unit '" + unitString - + "' (try k, K, kB, KiB, kilobytes, kibibytes)"); - } - - try { - BigInteger result; - // if the string is purely digits, parse as an integer to avoid - // possible precision loss; otherwise as a double. - if (numberString.matches("[0-9]+")) { - result = units.bytes.multiply(new BigInteger(numberString)); - } else { - BigDecimal resultDecimal = (new BigDecimal(units.bytes)).multiply(new BigDecimal(numberString)); - result = resultDecimal.toBigInteger(); - } - if (result.bitLength() < 64) - return result.longValue(); - else - throw new ConfigException.BadValue(originForException, pathForException, - "size-in-bytes value is out of range for a 64-bit long: '" + input + "'"); - } catch (NumberFormatException e) { + BigInteger result = parseBytesAsBigInteger(input, originForException, pathForException); + if (result.bitLength() < 64) + return result.longValue(); + else throw new ConfigException.BadValue(originForException, pathForException, - "Could not parse size-in-bytes number '" + numberString + "'"); - } + "size-in-bytes value is out of range for a 64-bit long: '" + input + "'"); } diff --git a/config/src/test/scala/com/typesafe/config/impl/ConfigMemorySizeTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigMemorySizeTest.scala index e635e1d50..1aab3bf75 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigMemorySizeTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigMemorySizeTest.scala @@ -28,6 +28,6 @@ class ConfigMemorySizeTest extends TestUtils { @Test def testGetBytes() { val yottabyte = ConfigMemorySize.ofBytes(new BigInteger("1000000000000000000000000")) - assertEquals(new BigInteger("1000000000000000000000000"), yottabyte.getBytes) + assertEquals(new BigInteger("1000000000000000000000000"), yottabyte.toBytesBigInteger) } } diff --git a/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala b/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala index 7f1668154..604aaa644 100644 --- a/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/ConfigTest.scala @@ -833,9 +833,9 @@ class ConfigTest extends TestUtils { conf.getMemorySizeList("memsizes.megsList").asScala.map(_.toBytes)) assertEquals(512 * 1024L, conf.getMemorySize("memsizes.halfMeg").toBytes) - assertEquals(new BigInteger("1000000000000000000000000"), conf.getMemorySize("memsizes.yottabyte").getBytes) + assertEquals(new BigInteger("1000000000000000000000000"), conf.getMemorySize("memsizes.yottabyte").toBytesBigInteger) assertEquals(Seq(new BigInteger("1000000000000000000000000"), new BigInteger("500000000000000000000000")), - conf.getMemorySizeList("memsizes.yottabyteList").asScala.map(_.getBytes)) + conf.getMemorySizeList("memsizes.yottabyteList").asScala.map(_.toBytesBigInteger)) } @Test From 0b4c9e81abc2b32c74ff7984e08d3aeab90e9893 Mon Sep 17 00:00:00 2001 From: mpryahin Date: Thu, 9 Jan 2020 12:22:19 +0300 Subject: [PATCH 6/8] fixed code formating to reduce diffs --- .../com/typesafe/config/impl/SimpleConfig.java | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 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 f8c4d0771..965c2cdde 100644 --- a/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java +++ b/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java @@ -501,8 +501,8 @@ public List getMemorySizeList(String path) { l.add(ConfigMemorySize.ofBytes(n)); } else { throw new ConfigException.WrongType(v.origin(), path, - "memory size string or number of bytes", v.valueType() - .name()); + "memory size string or number of bytes", v.valueType() + .name()); } } return l; @@ -866,18 +866,17 @@ public static BigInteger parseBytesAsBigInteger(String input, ConfigOrigin origi // this would be caught later anyway, but the error message // is more helpful if we check it here. - if (numberString.length() == 0) { + if (numberString.length() == 0) throw new ConfigException.BadValue(originForException, - pathForException, "No number in size-in-bytes value '" - + input + "'"); - } + pathForException, "No number in size-in-bytes value '" + + input + "'"); MemoryUnit units = MemoryUnit.parseUnit(unitString); if (units == null) { throw new ConfigException.BadValue(originForException, pathForException, - "Could not parse size-in-bytes unit '" + unitString - + "' (try k, K, kB, KiB, kilobytes, kibibytes)"); + "Could not parse size-in-bytes unit '" + unitString + + "' (try k, K, kB, KiB, kilobytes, kibibytes)"); } try { From b29fdd4a8b9a6367fb2a108e22a498f9ba5fb179 Mon Sep 17 00:00:00 2001 From: mpryahin Date: Sat, 11 Jan 2020 21:59:42 +0300 Subject: [PATCH 7/8] fixed contract violation which stems from incorect exception thrown when reading bytes and memorysize values --- .../com/typesafe/config/ConfigMemorySize.java | 5 +- .../typesafe/config/impl/SimpleConfig.java | 97 +++++++++++-------- .../typesafe/config/impl/UnitParserTest.scala | 40 ++++---- 3 files changed, 86 insertions(+), 56 deletions(-) diff --git a/config/src/main/java/com/typesafe/config/ConfigMemorySize.java b/config/src/main/java/com/typesafe/config/ConfigMemorySize.java index 4b8ae9800..305bd34ed 100644 --- a/config/src/main/java/com/typesafe/config/ConfigMemorySize.java +++ b/config/src/main/java/com/typesafe/config/ConfigMemorySize.java @@ -17,7 +17,7 @@ public final class ConfigMemorySize { private BigInteger bytes; private ConfigMemorySize(BigInteger bytes) { - if (bytes.compareTo(BigInteger.ZERO) < 0) + if (bytes.signum() < 0) throw new IllegalArgumentException("Attempt to construct ConfigMemorySize with negative number: " + bytes); this.bytes = bytes; } @@ -48,6 +48,9 @@ public static ConfigMemorySize ofBytes(long bytes) { * * @since 1.3.0 * @return how many bytes + * @exception IllegalArgumentException when memory value + * in bytes doesn't fit in a long value. Consider using + * {@link #toBytesBigInteger} in this case. */ public long toBytes() { if (bytes.bitLength() < 64) 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 965c2cdde..2a906d2f8 100644 --- a/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java +++ b/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java @@ -283,20 +283,54 @@ public Object getAnyRef(String path) { @Override public Long getBytes(String path) { - return getMemorySize(path).toBytes(); + BigInteger bytes = getBytesBigInteger(path); + ConfigValue v = find(path, ConfigValueType.STRING); + return toLong(bytes,v.origin(), path); } - @Override - public ConfigMemorySize getMemorySize(String path) { - BigInteger size; + private BigInteger getBytesBigInteger(String path) { + BigInteger bytes; + ConfigValue v = find(path, ConfigValueType.STRING); try { - size = BigInteger.valueOf(getLong(path)); + bytes = BigInteger.valueOf(getLong(path)); } catch (ConfigException.WrongType e) { - ConfigValue v = find(path, ConfigValueType.STRING); - size = parseBytesAsBigInteger((String) v.unwrapped(), + bytes = parseBytes((String) v.unwrapped(), v.origin(), path); } - return ConfigMemorySize.ofBytes(size); + if (bytes.signum() < 0) + throw new ConfigException.BadValue(v.origin(), path, + "Attempt to construct memory size with negative number: " + bytes); + return bytes; + } + + private List getBytesListBigInteger(String path){ + List result = new ArrayList<>(); + List list = getList(path); + + for (ConfigValue v : list) { + BigInteger bytes; + if (v.valueType() == ConfigValueType.NUMBER) { + bytes = BigInteger.valueOf(((Number) v.unwrapped()).longValue()); + } else if (v.valueType() == ConfigValueType.STRING) { + String s = (String) v.unwrapped(); + bytes = parseBytes(s, v.origin(), path); + } else { + throw new ConfigException.WrongType(v.origin(), path, + "memory size string or number of bytes", v.valueType() + .name()); + } + if (bytes.signum() < 0) + throw new ConfigException.BadValue(v.origin(), path, + "Attempt to construct ConfigMemorySize with negative number: " + bytes); + + result.add(bytes); + } + return result; + } + + @Override + public ConfigMemorySize getMemorySize(String path) { + return ConfigMemorySize.ofBytes(getBytesBigInteger(path)); } @Deprecated @@ -483,29 +517,27 @@ public List getAnyRefList(String path) { @Override public List getBytesList(String path) { - return getMemorySizeList(path).stream() - .map(ConfigMemorySize::toBytes) + ConfigValue v = find(path, ConfigValueType.LIST); + return getBytesListBigInteger(path).stream() + .map(bytes -> toLong(bytes, v.origin(), path)) .collect(Collectors.toList()); } + private Long toLong(BigInteger value, ConfigOrigin originForException, + String pathForException){ + if (value.bitLength() < 64) { + return value.longValue(); + } else { + throw new ConfigException.BadValue(originForException, pathForException, + "size-in-bytes value is out of range for a 64-bit long: '" + value + "'"); + } + } + @Override public List getMemorySizeList(String path) { - List l = new ArrayList<>(); - List list = getList(path); - for (ConfigValue v : list) { - if (v.valueType() == ConfigValueType.NUMBER) { - l.add(ConfigMemorySize.ofBytes(((Number) v.unwrapped()).longValue())); - } else if (v.valueType() == ConfigValueType.STRING) { - String s = (String) v.unwrapped(); - BigInteger n = parseBytesAsBigInteger(s, v.origin(), path); - l.add(ConfigMemorySize.ofBytes(n)); - } else { - throw new ConfigException.WrongType(v.origin(), path, - "memory size string or number of bytes", v.valueType() - .name()); - } - } - return l; + return getBytesListBigInteger(path).stream() + .map(ConfigMemorySize::ofBytes) + .collect(Collectors.toList()); } @Override @@ -846,18 +878,7 @@ static MemoryUnit parseUnit(String unit) { * @throws ConfigException * if string is invalid */ - public static long parseBytes(String input, ConfigOrigin originForException, - String pathForException) { - BigInteger result = parseBytesAsBigInteger(input, originForException, pathForException); - if (result.bitLength() < 64) - return result.longValue(); - else - throw new ConfigException.BadValue(originForException, pathForException, - "size-in-bytes value is out of range for a 64-bit long: '" + input + "'"); - } - - - public static BigInteger parseBytesAsBigInteger(String input, ConfigOrigin originForException, + public static BigInteger parseBytes(String input, ConfigOrigin originForException, String pathForException) { String s = ConfigImplUtil.unicodeTrim(input); String unitString = getUnits(s); 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 0a7f7e3e3..52b467452 100644 --- a/config/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala +++ b/config/src/test/scala/com/typesafe/config/impl/UnitParserTest.scala @@ -3,6 +3,7 @@ */ package com.typesafe.config.impl +import java.math.BigInteger import java.time.{ LocalDate, Period } import java.time.temporal.ChronoUnit @@ -89,10 +90,10 @@ class UnitParserTest extends TestUtils { @Test def parseMemorySizeInBytes(): Unit = { - def parseMem(s: String): Long = SimpleConfig.parseBytes(s, fakeOrigin(), "test") + def parseMem(s: String): BigInteger = SimpleConfig.parseBytes(s, fakeOrigin(), "test") - assertEquals(Long.MaxValue, parseMem(s"${Long.MaxValue} bytes")) - assertEquals(Long.MinValue, parseMem(s"${Long.MinValue} bytes")) + assertEquals(BigInteger.valueOf(Long.MaxValue), parseMem(s"${Long.MaxValue} bytes")) + assertEquals(BigInteger.valueOf(Long.MinValue), parseMem(s"${Long.MinValue} bytes")) val oneMebis = List("1048576", "1048576b", "1048576bytes", "1048576byte", "1048576 b", "1048576 bytes", @@ -104,7 +105,7 @@ class UnitParserTest extends TestUtils { for (s <- oneMebis) { val result = parseMem(s) - assertEquals(1024 * 1024, result) + assertEquals(BigInteger.valueOf(1024 * 1024), result) } val oneMegas = List("1000000", "1000000b", "1000000bytes", "1000000byte", @@ -117,13 +118,13 @@ class UnitParserTest extends TestUtils { for (s <- oneMegas) { val result = parseMem(s) - assertEquals(1000 * 1000, result) + assertEquals(BigInteger.valueOf(1000 * 1000), result) } - var result = 1024L * 1024 * 1024 - for (unit <- Seq("tebi", "pebi", "exbi")) { + var result = BigInteger.valueOf(1024L * 1024 * 1024) + for (unit <- Seq("tebi", "pebi", "exbi", "zebi", "yobi")) { val first = unit.substring(0, 1).toUpperCase() - result = result * 1024 + result = result.multiply(BigInteger.valueOf(1024)) assertEquals(result, parseMem("1" + first)) assertEquals(result, parseMem("1" + first + "i")) assertEquals(result, parseMem("1" + first + "iB")) @@ -131,10 +132,10 @@ class UnitParserTest extends TestUtils { assertEquals(result, parseMem("1" + unit + "bytes")) } - result = 1000L * 1000 * 1000 - for (unit <- Seq("tera", "peta", "exa")) { + result = BigInteger.valueOf(1000L * 1000 * 1000) + for (unit <- Seq("tera", "peta", "exa", "zetta", "yotta")) { val first = unit.substring(0, 1).toUpperCase() - result = result * 1000 + result = result.multiply(BigInteger.valueOf(1000)) assertEquals(result, parseMem("1" + first + "B")) assertEquals(result, parseMem("1" + unit + "byte")) assertEquals(result, parseMem("1" + unit + "bytes")) @@ -156,7 +157,7 @@ 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): Long = SimpleConfig.parseBytes(s, fakeOrigin(), "test") + def parseMem(s: String): Long = ConfigFactory.parseString(s"v = $s").getBytes("v") def assertOutOfRange(s: String): Unit = { val fail = intercept[ConfigException.BadValue] { parseMem(s) @@ -164,11 +165,17 @@ class UnitParserTest extends TestUtils { assertTrue("number was too big", fail.getMessage.contains("out of range")) } + def assertNegativeNumber(s: String): Unit = { + val fail = intercept[ConfigException.BadValue] { + parseMem(s) + } + assertTrue("number was negative", fail.getMessage.contains("negative number")) + } + 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") + assertNegativeNumber(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() assertOutOfRange("1" + first) @@ -177,17 +184,16 @@ class UnitParserTest extends TestUtils { assertOutOfRange("1" + unit + "byte") assertOutOfRange("1" + unit + "bytes") assertOutOfRange("1.1" + first) - assertOutOfRange("-1" + first) + assertNegativeNumber("-1" + first) } - result = 1000L * 1000 * 1000 for (unit <- Seq("zetta", "yotta")) { val first = unit.substring(0, 1).toUpperCase() assertOutOfRange("1" + first + "B") assertOutOfRange("1" + unit + "byte") assertOutOfRange("1" + unit + "bytes") assertOutOfRange("1.1" + first + "B") - assertOutOfRange("-1" + first + "B") + assertNegativeNumber("-1" + first + "B") } assertOutOfRange("1000 exabytes") From 89afa7757402f34f81a214db0ff71eae435ee919 Mon Sep 17 00:00:00 2001 From: Mike Date: Fri, 17 Jan 2020 23:29:29 +0300 Subject: [PATCH 8/8] Update config/src/main/java/com/typesafe/config/impl/SimpleConfig.java Co-Authored-By: Havoc Pennington --- config/src/main/java/com/typesafe/config/impl/SimpleConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2a906d2f8..12c0d3f7a 100644 --- a/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java +++ b/config/src/main/java/com/typesafe/config/impl/SimpleConfig.java @@ -285,7 +285,7 @@ public Object getAnyRef(String path) { public Long getBytes(String path) { BigInteger bytes = getBytesBigInteger(path); ConfigValue v = find(path, ConfigValueType.STRING); - return toLong(bytes,v.origin(), path); + return toLong(bytes, v.origin(), path); } private BigInteger getBytesBigInteger(String path) {