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

Revise DataSize & DataRate #481

Merged
merged 19 commits into from
Jul 18, 2022
Merged
Show file tree
Hide file tree
Changes from 8 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
42 changes: 41 additions & 1 deletion app/src/main/java/org/astraea/app/common/DataRate.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,21 @@ public BigDecimal toBigDecimal(DataUnit dataUnit, Duration duration) {
return this.totalBitTransmitted
.measurement(dataUnit)
.multiply(fromDurationToBigDecimalSafely(duration))
.divide(durationInNanoSecond, MathContext.DECIMAL32);
.divide(durationInNanoSecond, MathContext.DECIMAL128);
}

/**
* @return the data rate with bytes/second unit as a double value. If the value is beyond the
* range of double, {@link Double#POSITIVE_INFINITY} will be returned.
*/
public double byteRate() {
return toBigDecimal(DataUnit.Byte, ChronoUnit.SECONDS).doubleValue();
}

/** @return the data rate per second as a {@link DataSize}. */
public DataSize dataSize() {
var bitsPerSecond = toBigDecimal(DataUnit.Bit, ChronoUnit.SECONDS).toBigInteger();
return new DataSize(bitsPerSecond);
}

/**
Expand Down Expand Up @@ -180,6 +194,32 @@ public static double ofDouble(DataSize size, DataUnit unit, Duration time) {
return ofBigDecimal(size, unit, time).doubleValue();
}

/**
* @param bytesPerSecond the double value that represent a data rate in bytes/second unit
* @return a {@link DataRate} converted from the given parameter.
*/
public static DataRate of(double bytesPerSecond) {
var bits =
BigDecimal.valueOf(bytesPerSecond)
.multiply(new BigDecimal(DataUnit.Byte.bits))
.toBigInteger();
var size = new DataSize(bits);
return new DataRate(size, Duration.ofSeconds(1));
}

/**
* @param bytesPerSecond the double value that represent a data rate in bytes/second unit
* @return a {@link DataRate} converted from the given parameter.
*/
public static DataRate of(long bytesPerSecond) {
var bits =
BigDecimal.valueOf(bytesPerSecond)
.multiply(new BigDecimal(DataUnit.Byte.bits))
.toBigInteger();
var size = new DataSize(bits);
return new DataRate(size, Duration.ofSeconds(1));
}

private static String chronoName(ChronoUnit chronoUnit) {
return chronoUnit.name().toLowerCase(Locale.ROOT);
}
Expand Down
41 changes: 38 additions & 3 deletions app/src/main/java/org/astraea/app/common/DataSize.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,35 @@
/** Data size class */
public class DataSize implements Comparable<DataSize> {

public static final DataSize ZERO = DataUnit.Byte.of(0);
private final BigInteger bits;

DataSize(long volume, DataUnit dataUnit) {
this(BigInteger.valueOf(volume).multiply(dataUnit.bits));
}

DataSize(BigInteger bigInteger) {
this.bits = bigInteger;
DataSize(BigInteger bits) {
this.bits = bits;
}

/**
* Add data volume.
*
* @param bytes value to add
* @return a new {@link DataSize} that have applied the math operation.
*/
public DataSize add(long bytes) {
return add(bytes, DataUnit.Byte);
}

/**
* Subtract data volume.
*
* @param bytes value to subtract
* @return a new {@link DataSize} that have applied the math operation.
*/
public DataSize subtract(long bytes) {
return subtract(bytes, DataUnit.Byte);
}

/**
Expand Down Expand Up @@ -139,14 +160,23 @@ public BigInteger bits() {
return bits;
}

/**
* @return the current bytes in long primitive type. All the remaining bits that can't become a
* byte are discarded from the return value.
* @throws ArithmeticException if the value will not exactly fit into a long.
*/
public long bytes() {
return bits().divide(BigInteger.valueOf(8)).longValueExact();
}

/**
* The measurement value in term of specific data unit.
*
* @param dataUnit data unit to describe current size.
* @return a {@link BigDecimal} describe current data size in term of specific data unit.
*/
public BigDecimal measurement(DataUnit dataUnit) {
return new BigDecimal(this.bits).divide(new BigDecimal(dataUnit.bits), MathContext.DECIMAL32);
return new BigDecimal(this.bits).divide(new BigDecimal(dataUnit.bits), MathContext.DECIMAL128);
}

/**
Expand Down Expand Up @@ -197,6 +227,11 @@ public DataRate dataRate(Duration timePassed) {
return DataRate.of(this, timePassed);
}

/** @return a {@link DataRate} based on current data size over one second. */
public DataRate perSecond() {
return dataRate(ChronoUnit.SECONDS);
}

/** Return a string represent current size in given data unit. */
public String toString(DataUnit unit) {
var divide =
Expand Down
42 changes: 42 additions & 0 deletions app/src/test/java/org/astraea/app/common/DataRateTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.function.BiConsumer;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
Expand Down Expand Up @@ -100,4 +102,44 @@ void testDataRate(
expectedIdealDataRate, sut.idealDataRate(ChronoUnit.SECONDS).doubleValue());
assertEquals(expectedDataUnit, sut.idealDataUnit(ChronoUnit.SECONDS));
}

@Test
void testToDataSize() {
Assertions.assertEquals(DataUnit.Byte.of(1024), DataUnit.Byte.of(1024).perSecond().dataSize());
Assertions.assertEquals(DataUnit.KiB.of(1024), DataUnit.KiB.of(1024).perSecond().dataSize());
Assertions.assertEquals(DataUnit.EiB.of(24), DataUnit.EiB.of(24).perSecond().dataSize());
}

@Test
void testDoubleByteRate() {
BiConsumer<Double, Double> assertDoubleEqual =
(a, b) -> {
Assertions.assertTrue(
Math.abs(a - b) < 1e-8,
"The value " + a + " and " + b + " should have no difference above 1e-8");
};

assertDoubleEqual.accept(1024.0, DataUnit.Byte.of(1024).perSecond().byteRate());
assertDoubleEqual.accept(1024.0 * 1024, DataUnit.KiB.of(1024).perSecond().byteRate());
}

@Test
void testLongByteRate() {
Assertions.assertEquals(1024L, DataUnit.Byte.of(1024).perSecond().byteRate());
Assertions.assertEquals(1024L * 1024, DataUnit.KiB.of(1024).perSecond().byteRate());
}

@Test
void testFromLong() {
Assertions.assertEquals(
DataRate.of(1024, DataUnit.Byte, ChronoUnit.SECONDS).byteRate(),
DataRate.of(1024).byteRate());
}

@Test
void testFromDouble() {
Assertions.assertEquals(
DataRate.of(1024, DataUnit.Byte, ChronoUnit.SECONDS).byteRate(),
DataRate.of(1024.0).byteRate());
}
}
87 changes: 87 additions & 0 deletions app/src/test/java/org/astraea/app/common/DataSizeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.stream.IntStream;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
Expand Down Expand Up @@ -85,6 +87,33 @@ void typicalUsageOfDataUnit() {
double double2 = DataRate.ofDouble(randomSize, DataUnit.Byte, ChronoUnit.SECONDS);
double double3 = DataRate.ofDouble(randomSize, DataUnit.Byte, Duration.ofSeconds(1));

// sum all data size
var sumAll =
IntStream.range(0, 100).mapToObj(DataUnit.Byte::of).reduce(DataSize.ZERO, DataSize::add);
Assertions.assertEquals(4950, sumAll.bytes());

// fast way to get bytes, be aware of exception caused by overflow.
long bytesInLong = randomSize.bytes();
long bytes10Gib = DataUnit.Gib.of(10).bytes();

// fast way & normal way to get second rate
DataRate faster = DataUnit.Byte.of(1000).perSecond();
DataRate normal = DataRate.of(1000, DataUnit.Byte, ChronoUnit.SECONDS);

// data rate to other types
double dataRateDouble = DataUnit.Byte.of(1000).perSecond().byteRate();
DataSize dataRateSize = DataUnit.Byte.of(1000).perSecond().dataSize();

// fast way to get DataSize & DataRate from primitive type
DataSize primitive0 = DataUnit.Byte.of(1000);
DataSize primitive1 = DataUnit.Byte.of((long) 1000.0);
DataRate primitive2 = DataRate.of(123.0);
DataRate primitive3 = DataRate.of(1024);

// fast way to add/subtract data from primitive type
DataUnit.Byte.of(1000).subtract(500);
DataUnit.Byte.of(1024).add(1024);

// solve the above problem
var dataVolume = DataUnit.YB.of(1);
var dataVolumeOver1000Years = dataVolume.dataRate(ChronoUnit.MILLENNIA);
Expand Down Expand Up @@ -315,4 +344,62 @@ void compare() {
assertFalse(DataUnit.MB.of(0).greaterEqualTo(DataUnit.KB.of(1000)));
assertFalse(DataUnit.MB.of(1).greaterThan(DataUnit.KB.of(1001)));
}

@ParameterizedTest
@CsvSource(
delimiterString = " ",
value = {"1000 KiB", "1234 Kib", "5566 GB", "42 Byte"})
void perSecond(long measurement, DataUnit unit) {
var dataSize = unit.of(measurement);
var dataRate = dataSize.perSecond();

Assertions.assertEquals(
measurement, dataRate.toBigDecimal(unit, ChronoUnit.SECONDS).longValueExact());
Assertions.assertEquals(
measurement * 60, dataRate.toBigDecimal(unit, ChronoUnit.MINUTES).longValueExact());
}

@ParameterizedTest
@CsvSource(
delimiterString = ",",
value = {
// measurement, unit, expected-value
" 1, KiB, 1024",
" 1000, GB, 1000000000000",
" 8, Bit, 1",
" 7, Bit, 0",
" 0, Bit, 0",
" 9, Bit, 1",
" 15, Bit, 1",
" 16, Bit, 2",
" 800, Bit, 100"
})
void bytes(long measurement, DataUnit unit, long expected) {
var dataSize = unit.of(measurement);

Assertions.assertEquals(expected, dataSize.bytes());

// overflow cases
Assertions.assertThrows(ArithmeticException.class, () -> DataUnit.PiB.of(8192).bytes());
Assertions.assertDoesNotThrow(() -> DataUnit.PiB.of(8191).bytes());
Assertions.assertDoesNotThrow(() -> DataUnit.Byte.of(Long.MAX_VALUE).bytes());
}

@Test
void addBytes() {
Assertions.assertEquals(1100, DataUnit.Byte.of(1000).add(100).bytes());
Assertions.assertEquals(1124, DataUnit.KiB.of(1).add(100).bytes());
}

@Test
void subtractBytes() {
Assertions.assertEquals(900, DataUnit.Byte.of(1000).subtract(100).bytes());
Assertions.assertEquals(924, DataUnit.KiB.of(1).subtract(100).bytes());
}

@Test
void zero() {
Assertions.assertEquals(DataUnit.Bit.of(0), DataSize.ZERO);
Assertions.assertEquals(0, DataSize.ZERO.bits().longValue());
}
}