Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for huge memory units #663

Merged
merged 8 commits into from
Feb 17, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 37 additions & 7 deletions config/src/main/java/com/typesafe/config/ConfigMemorySize.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@
*/
package com.typesafe.config;

import java.math.BigInteger;

/**
* 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;
}
Expand All @@ -26,16 +29,43 @@ 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);
}

/**
* 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));
}

/**
* Gets the size in bytes.
*
* @since 1.3.0
* @return how many bytes
*/
public long toBytes() {
if (bytes.bitLength() < 64)
return bytes.longValue();
else
throw new IllegalArgumentException(
mpryahin marked this conversation as resolved.
Show resolved Hide resolved
"size-in-bytes value is out of range for a 64-bit long: '" + 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 toBytesBigInteger() {
return bytes;
}

Expand All @@ -47,7 +77,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;
}
Expand All @@ -56,7 +86,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();
}

}
Expand Down
65 changes: 35 additions & 30 deletions config/src/main/java/com/typesafe/config/impl/SimpleConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -282,20 +283,20 @@ 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();
mpryahin marked this conversation as resolved.
Show resolved Hide resolved
}

@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);
havocp marked this conversation as resolved.
Show resolved Hide resolved
}

@Deprecated
Expand Down Expand Up @@ -482,15 +483,22 @@ public List<? extends Object> getAnyRefList(String path) {

@Override
public List<Long> getBytesList(String path) {
List<Long> l = new ArrayList<Long>();
return getMemorySizeList(path).stream()
.map(ConfigMemorySize::toBytes)
mpryahin marked this conversation as resolved.
Show resolved Hide resolved
.collect(Collectors.toList());
}

@Override
public List<ConfigMemorySize> getMemorySizeList(String path) {
List<ConfigMemorySize> l = new ArrayList<>();
List<? extends ConfigValue> list = getList(path);
for (ConfigValue v : list) {
if (v.valueType() == ConfigValueType.NUMBER) {
l.add(((Number) v.unwrapped()).longValue());
l.add(ConfigMemorySize.ofBytes(((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);
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()
Expand All @@ -500,16 +508,6 @@ public List<Long> getBytesList(String path) {
return l;
havocp marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public List<ConfigMemorySize> getMemorySizeList(String path) {
List<Long> list = getBytesList(path);
List<ConfigMemorySize> builder = new ArrayList<ConfigMemorySize>();
for (Long v : list) {
builder.add(ConfigMemorySize.ofBytes(v));
}
return builder;
}

@Override
public List<Long> getDurationList(String path, TimeUnit unit) {
List<Long> l = new ArrayList<Long>();
Expand Down Expand Up @@ -850,10 +848,21 @@ static MemoryUnit parseUnit(String unit) {
*/
public static long parseBytes(String input, ConfigOrigin originForException,
String pathForException) {
BigInteger result = parseBytesAsBigInteger(input, originForException, pathForException);
mpryahin marked this conversation as resolved.
Show resolved Hide resolved
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,
mpryahin marked this conversation as resolved.
Show resolved Hide resolved
String pathForException) {
String s = ConfigImplUtil.unicodeTrim(input);
String unitString = getUnits(s);
String numberString = ConfigImplUtil.unicodeTrim(s.substring(0,
s.length() - unitString.length()));
s.length() - unitString.length()));

// this would be caught later anyway, but the error message
// is more helpful if we check it here.
Expand All @@ -880,11 +889,7 @@ public static long parseBytes(String input, ConfigOrigin originForException,
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 + "'");
return result;
} catch (NumberFormatException e) {
throw new ConfigException.BadValue(originForException, pathForException,
"Could not parse size-in-bytes number '" + numberString + "'");
Expand Down
2 changes: 2 additions & 0 deletions config/src/test/resources/test01.conf
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@
"megsList" : [1M, 1024K, 1048576],
"megAsNumber" : 1048576,
"halfMeg" : 0.5M
"yottabyte" : 1YB
"yottabyteList" : [1YB, 0.5YB]
},

"system" : {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.toBytesBigInteger)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
*/
package com.typesafe.config.impl

import java.math.BigInteger
import java.time.temporal.{ ChronoUnit, TemporalUnit }

import org.junit.Assert._
Expand Down Expand Up @@ -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").toBytesBigInteger)
assertEquals(Seq(new BigInteger("1000000000000000000000000"), new BigInteger("500000000000000000000000")),
conf.getMemorySizeList("memsizes.yottabyteList").asScala.map(_.toBytesBigInteger))
}

@Test
Expand Down