diff --git a/base/src/main/kotlin/de/staropensource/engine/base/utility/DataSize.kt b/base/src/main/kotlin/de/staropensource/engine/base/utility/DataSize.kt
new file mode 100644
index 0000000..78b9731
--- /dev/null
+++ b/base/src/main/kotlin/de/staropensource/engine/base/utility/DataSize.kt
@@ -0,0 +1,457 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Authors
+ * Licensed under the GNU General Public License v3.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.engine.base.utility
+
+import java.math.RoundingMode
+import java.text.DecimalFormat
+import java.text.DecimalFormatSymbols
+import java.util.Locale
+
+/**
+ * Converts between various data size units.
+ *
+ * @param bytes bytes to convert
+ * @since v1-alpha10
+ */
+data class DataSize(
+ val bytes: ULong
+) {
+ /**
+ * Companion object of [DataSize].
+ *
+ * @since v1-alpha10
+ */
+ companion object {
+ /**
+ * Converts the specified amount of
+ * kilobytes into a [DataSize] instance.
+ *
+ * @param kilobytes kilobytes to convert
+ * @since v1-alpha10
+ */
+ @JvmStatic
+ fun ofKilobytes(kilobytes: Double): DataSize = DataSize(formatOf(kilobytes.times(1000)))
+
+ /**
+ * Converts the specified amount of
+ * kibibytes into a [DataSize] instance.
+ *
+ * @param kibibytes kibibytes to convert
+ * @since v1-alpha10
+ */
+ @JvmStatic
+ fun ofKibibytes(kibibytes: Double): DataSize = DataSize(formatOf(kibibytes.times(1024)))
+
+ /**
+ * Converts the specified amount of
+ * megabytes into a [DataSize] instance.
+ *
+ * @param megabytes megabytes to convert
+ * @since v1-alpha10
+ */
+ @JvmStatic
+ fun ofMegabyte(megabytes: Double): DataSize = DataSize(formatOf(megabytes.times(1000000)))
+
+ /**
+ * Converts the specified amount of
+ * mebibytes into a [DataSize] instance.
+ *
+ * @param mebibytes mebibytes to convert
+ * @since v1-alpha10
+ */
+ @JvmStatic
+ fun ofMebibyte(mebibytes: Double): DataSize = DataSize(formatOf(mebibytes.times(1048576)))
+
+ /**
+ * Converts the specified amount of
+ * gigabytes into a [DataSize] instance.
+ *
+ * @param gigabytes gigabytes to convert
+ * @since v1-alpha10
+ */
+ @JvmStatic
+ fun ofGigabyte(gigabytes: Double): DataSize = DataSize(formatOf(gigabytes.times(1000000000)))
+
+ /**
+ * Converts the specified amount of
+ * gebibytes into a [DataSize] instance.
+ *
+ * @param gebibytes gebibytes to convert
+ * @since v1-alpha10
+ */
+ @JvmStatic
+ fun ofGibibyte(gebibytes: Double): DataSize = DataSize(formatOf(gebibytes.times(1073741824)))
+
+ /**
+ * Converts the specified amount of
+ * terabytes into a [DataSize] instance.
+ *
+ * @param terabytes terabytes to convert
+ * @since v1-alpha10
+ */
+ @JvmStatic
+ fun ofTerabyte(terabytes: Double): DataSize = DataSize(formatOf(terabytes.times(1000000000000)))
+
+ /**
+ * Converts the specified amount of
+ * tebibytes into a [DataSize] instance.
+ *
+ * @param tebibytes tebibytes to convert
+ * @since v1-alpha10
+ */
+ @JvmStatic
+ fun ofTebibyte(tebibytes: Double): DataSize = DataSize(formatOf(tebibytes.times(1099511627776)))
+
+ /**
+ * Converts the specified amount of
+ * petabytes into a [DataSize] instance.
+ *
+ * @param petabytes petabytes to convert
+ * @since v1-alpha10
+ */
+ @JvmStatic
+ fun ofPetabyte(petabytes: Double): DataSize = DataSize(formatOf(petabytes.times(1000000000000000)))
+
+ /**
+ * Converts the specified amount of
+ * pebibytes into a [DataSize] instance.
+ *
+ * @param pebibytes pebibytes to convert
+ * @since v1-alpha10
+ */
+ @JvmStatic
+ fun ofPebibyte(pebibytes: Double): DataSize = DataSize(formatOf(pebibytes.times(1125899906842624)))
+
+
+ // -----> Internal
+ /**
+ * Performs miscellaneous operations
+ * on the specified [Double] to
+ * "optimize" it's value.
+ *
+ * @param number [Double] to operate on
+ * @return modified [Double]
+ * @since v1-alpha10
+ */
+ fun formatTo(number: Double): Double {
+ val decimalFormat = DecimalFormat("#.####", DecimalFormatSymbols(Locale.ROOT))
+ decimalFormat.roundingMode = RoundingMode.HALF_UP
+ return decimalFormat.format(number).toDouble()
+ }
+
+ /**
+ * Performs miscellaneous operations
+ * on the specified [Double] and
+ * converts it into a [ULong]
+ *
+ * @param number [Double] to operate on
+ * @return modified [Double]
+ * @since v1-alpha10
+ */
+ fun formatOf(number: Double): ULong {
+ val decimalFormat = DecimalFormat("#", DecimalFormatSymbols(Locale.ROOT))
+ decimalFormat.roundingMode = RoundingMode.HALF_UP
+ return decimalFormat.format(number).toULong()
+ }
+ }
+
+
+ // -----> Conversion
+ /**
+ * Converts the specified amount
+ * of [bytes] into kilobytes.
+ *
+ * @return [bytes] in kilobytes
+ * @since v1-alpha10
+ */
+ fun toKilobyte(): Double = formatTo(bytes.toDouble().div(1000))
+
+ /**
+ * Converts the specified amount
+ * of [bytes] into kibibytes.
+ *
+ * @return [bytes] in kibibytes
+ * @since v1-alpha10
+ */
+ fun toKibibyte(): Double = formatTo(bytes.toDouble().div(1024))
+
+ /**
+ * Converts the specified amount
+ * of [bytes] into megabytes.
+ *
+ * @return [bytes] in megabytes
+ * @since v1-alpha10
+ */
+ fun toMegabyte(): Double = formatTo(bytes.toDouble().div(1000000))
+
+ /**
+ * Converts the specified amount
+ * of [bytes] into mebibytes.
+ *
+ * @return [bytes] in mebibytes
+ * @since v1-alpha10
+ */
+ fun toMebibyte(): Double = formatTo(bytes.toDouble().div(1048576))
+
+ /**
+ * Converts the specified amount
+ * of [bytes] into gigabytes.
+ *
+ * @return [bytes] in gigabytes
+ * @since v1-alpha10
+ */
+ fun toGigabyte(): Double = formatTo(bytes.toDouble().div(1000000000))
+
+ /**
+ * Converts the specified amount
+ * of [bytes] into gibibytes.
+ *
+ * @return [bytes] in gibibytes
+ * @since v1-alpha10
+ */
+ fun toGibibyte(): Double = formatTo(bytes.toDouble().div(1073741824))
+
+ /**
+ * Converts the specified amount
+ * of [bytes] into terabytes.
+ *
+ * @return [bytes] in terabytes
+ * @since v1-alpha10
+ */
+ fun toTerabyte(): Double = formatTo(bytes.toDouble().div(1000000000000))
+
+ /**
+ * Converts the specified amount
+ * of [bytes] into tebibytes.
+ *
+ * @return [bytes] in tebibytes
+ * @since v1-alpha10
+ */
+ fun toTebibyte(): Double = formatTo(bytes.toDouble().div(1099511627776))
+
+ /**
+ * Converts the specified amount
+ * of [bytes] into petabytes.
+ *
+ * @return [bytes] in petabytes
+ * @since v1-alpha10
+ */
+ fun toPetabyte(): Double = formatTo(bytes.toDouble().div(1000000000000000))
+
+ /**
+ * Converts the specified amount
+ * of [bytes] into pebibytes.
+ *
+ * @return [bytes] in pebibytes
+ * @since v1-alpha10
+ */
+ fun toPebibyte(): Double = formatTo(bytes.toDouble().div(1125899906842624))
+
+
+ // -----> Miscellaneous
+ /**
+ * Returns the perfect representation
+ * of the specified data size.
+ *
+ * @return perfect representation
+ * @since v1-alpha10
+ */
+ override fun toString(): String = bytes.toString()
+
+ /**
+ * Returns the perfect representation
+ * of the specified data size.
+ *
+ * @param binary uses a factor of `1024` (giving [Unit.MEBIBYTE], [Unit.GIBIBYTE], ...) instead of `1000` (giving [Unit.MEGABYTE], [Unit.GIGABYTE], ...)
+ * @return pair of the converted amount and the [Unit]
+ * @since v1-alpha10
+ */
+ fun toPerfectRepresentation(binary: Boolean = true): Pair {
+ if (binary) {
+ return if (toPebibyte() >= 1)
+ Pair(toPebibyte(), Unit.PEBIBYTE)
+ else if (toTebibyte() >= 1)
+ Pair(toTebibyte(), Unit.TEBIBYTE)
+ else if (toGibibyte() >= 1)
+ Pair(toGibibyte(), Unit.GIBIBYTE)
+ else if (toMebibyte() >= 1)
+ Pair(toMebibyte(), Unit.MEBIBYTE)
+ else
+ Pair(toKibibyte(), Unit.KIBIBYTE)
+ } else {
+ return if (toPetabyte() >= 1)
+ Pair(toPetabyte(), Unit.PETABYTE)
+ else if (toTerabyte() >= 1)
+ Pair(toTerabyte(), Unit.TERABYTE)
+ else if (toGigabyte() >= 1)
+ Pair(toGigabyte(), Unit.GIGABYTE)
+ else if (toMegabyte() >= 1)
+ Pair(toMegabyte(), Unit.MEGABYTE)
+ else
+ Pair(toKilobyte(), Unit.KILOBYTE)
+ }
+ }
+
+
+ // -----> Inner classes
+ /**
+ * Represents all unit types [DataSize] supports.
+ *
+ * @since v1-alpha10
+ */
+ enum class Unit {
+ // -----> by 1024
+ /**
+ * The kibibyte (KiB) unit.
+ *
+ * Next unit after bytes.
+ *
+ * @since v1-alpha10
+ */
+ KIBIBYTE,
+
+ /**
+ * The mibibyte (MiB) unit.
+ *
+ * Next unit after [KIBIBYTE].
+ *
+ * @since v1-alpha10
+ */
+ MEBIBYTE,
+
+ /**
+ * The gibibyte (GiB) unit.
+ *
+ * Next unit after [MEBIBYTE].
+ *
+ * @since v1-alpha10
+ */
+ GIBIBYTE,
+
+ /**
+ * The tebibyte (TiB) unit.
+ *
+ * Next unit after [GIBIBYTE].
+ *
+ * @since v1-alpha10
+ */
+ TEBIBYTE,
+
+ /**
+ * The pebibyte (PiB) unit.
+ *
+ * Next unit after [TEBIBYTE].
+ *
+ * @since v1-alpha10
+ */
+ PEBIBYTE,
+
+ // -----> by 1000
+ /**
+ * The kilobyte (KB) unit.
+ *
+ * Next unit after bytes.
+ *
+ * @since v1-alpha10
+ */
+ KILOBYTE,
+
+ /**
+ * The megabyte (MB) unit.
+ *
+ * Next unit after [KILOBYTE].
+ *
+ * @since v1-alpha10
+ */
+ MEGABYTE,
+
+ /**
+ * The gigabyte (GB) unit.
+ *
+ * Next unit after [MEGABYTE].
+ *
+ * @since v1-alpha10
+ */
+ GIGABYTE,
+
+ /**
+ * The terabyte (TB) unit.
+ *
+ * Next unit after [GIGABYTE].
+ *
+ * @since v1-alpha10
+ */
+ TERABYTE,
+
+ /**
+ * The petabytes (PB) unit.
+ *
+ * Next unit after [TERABYTE].
+ *
+ * @since v1-alpha10
+ */
+ PETABYTE;
+
+
+ /**
+ * Returns the name of this unit.
+ *
+ * @return name
+ * @since v1-alpha10
+ */
+ fun getName(): String = when (this) {
+ // by 1024
+ KIBIBYTE -> "Kibibyte"
+ MEBIBYTE -> "Mebibyte"
+ GIBIBYTE -> "Gibibyte"
+ TEBIBYTE -> "Tebibyte"
+ PEBIBYTE -> "Pebibyte"
+
+ // by 1000
+ KILOBYTE -> "Kilobyte"
+ MEGABYTE -> "Megabyte"
+ GIGABYTE -> "Gigabyte"
+ TERABYTE -> "Terabyte"
+ PETABYTE -> "Petabyte"
+ }
+
+ /**
+ * Returns the short name of this unit.
+ *
+ * @return short name
+ * @since v1-alpha10
+ */
+ fun getShortName(): String = when (this) {
+ // by 1024
+ KIBIBYTE -> "KiB"
+ MEBIBYTE -> "MiB"
+ GIBIBYTE -> "GiB"
+ TEBIBYTE -> "TiB"
+ PEBIBYTE -> "PiB"
+
+ // by 1000
+ KILOBYTE -> "KB"
+ MEGABYTE -> "MB"
+ GIGABYTE -> "GB"
+ TERABYTE -> "TB"
+ PETABYTE -> "PB"
+ }
+ }
+}
diff --git a/base/src/test/kotlin/de/staropensource/engine/base/utility/DataSizeTest.kt b/base/src/test/kotlin/de/staropensource/engine/base/utility/DataSizeTest.kt
new file mode 100644
index 0000000..e3ededc
--- /dev/null
+++ b/base/src/test/kotlin/de/staropensource/engine/base/utility/DataSizeTest.kt
@@ -0,0 +1,260 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Authors
+ * Licensed under the GNU General Public License v3.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.staropensource.engine.base.utility
+
+import de.staropensource.engine.testing.TestBase
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.DisplayName
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.CsvSource
+
+class DataSizeTest : TestBase() {
+ @ParameterizedTest
+ @CsvSource(
+ delimiter = ',',
+ quoteCharacter = '"',
+ textBlock = """
+ "4.4", "4400"
+ "6.1", "6100"
+ "0.9", "900"
+ "691.131", "691131""""
+ )
+ fun kilobytes(kilobytes: Double, bytes: ULong) {
+ val dataSize: DataSize = DataSize.ofKilobytes(kilobytes)
+ assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}")
+ assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}")
+ assertTrue(dataSize.toKilobyte() > kilobytes.minus(5), ".toKilobyte = ${dataSize.toKilobyte()} is smaller than range floor ${kilobytes.minus(5)}")
+ assertTrue(dataSize.toKilobyte() < kilobytes.plus(5), ".toKilobyte = ${dataSize.toKilobyte()} is bigger than range ceiling ${kilobytes.plus(5)}")
+ }
+
+ @ParameterizedTest
+ @CsvSource(
+ delimiter = ',',
+ quoteCharacter = '"',
+ textBlock = """
+ "4.4", "4505"
+ "6.1", "6246"
+ "0.9", "921"
+ "691.131", "707718""""
+ )
+ fun kibibytes(kibibytes: Double, bytes: ULong) {
+ val dataSize: DataSize = DataSize.ofKibibytes(kibibytes)
+ assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}")
+ assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}")
+ assertTrue(dataSize.toKibibyte() > kibibytes.minus(5), ".toKibibyte = ${dataSize.toKibibyte()} is smaller than range floor ${kibibytes.minus(5)}")
+ assertTrue(dataSize.toKibibyte() < kibibytes.plus(5), ".toKibibyte = ${dataSize.toKibibyte()} is bigger than range ceiling ${kibibytes.plus(5)}")
+ }
+
+ @ParameterizedTest
+ @CsvSource(
+ delimiter = ',',
+ quoteCharacter = '"',
+ textBlock = """
+ "4.4", "4400000"
+ "6.1", "6100000"
+ "0.9", "900000"
+ "691.131", "691131000""""
+ )
+ fun megabyte(megabytes: Double, bytes: ULong) {
+ val dataSize: DataSize = DataSize.ofMegabyte(megabytes)
+ assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}")
+ assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}")
+ assertTrue(dataSize.toMegabyte() > megabytes.minus(5), ".toMegabyte = ${dataSize.toMegabyte()} is smaller than range floor ${megabytes.minus(5)}")
+ assertTrue(dataSize.toMegabyte() < megabytes.plus(5), ".toMegabyte = ${dataSize.toMegabyte()} is bigger than range ceiling ${megabytes.plus(5)}")
+ }
+
+ @ParameterizedTest
+ @CsvSource(
+ delimiter = ',',
+ quoteCharacter = '"',
+ textBlock = """
+ "4.4", "4613734"
+ "6.1", "6396313"
+ "0.9", "943718"
+ "691.131", "724703379""""
+ )
+ fun mebibyte(mebibytes: Double, bytes: ULong) {
+ val dataSize: DataSize = DataSize.ofMebibyte(mebibytes)
+ assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}")
+ assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}")
+ assertTrue(dataSize.toMebibyte() > mebibytes.minus(5), ".toMebibyte = ${dataSize.toMebibyte()} is smaller than range floor ${mebibytes.minus(5)}")
+ assertTrue(dataSize.toMebibyte() < mebibytes.plus(5), ".toMebibyte = ${dataSize.toMebibyte()} is bigger than range ceiling ${mebibytes.plus(5)}")
+ }
+
+ @ParameterizedTest
+ @CsvSource(
+ delimiter = ',',
+ quoteCharacter = '"',
+ textBlock = """
+ "4.4", "4400000000"
+ "6.1", "6100000000"
+ "0.9", "900000000"
+ "691.131", "691131000000""""
+ )
+ fun gigabyte(gigabytes: Double, bytes: ULong) {
+ val dataSize: DataSize = DataSize.ofGigabyte(gigabytes)
+ assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}")
+ assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}")
+ assertTrue(dataSize.toGigabyte() > gigabytes.minus(5), ".toGigabyte = ${dataSize.toGigabyte()} is smaller than range floor ${gigabytes.minus(5)}")
+ assertTrue(dataSize.toGigabyte() < gigabytes.plus(5), ".toGigabyte = ${dataSize.toGigabyte()} is bigger than range ceiling ${gigabytes.plus(5)}")
+ }
+
+ @ParameterizedTest
+ @CsvSource(
+ delimiter = ',',
+ quoteCharacter = '"',
+ textBlock = """
+ "4.4", "4724464025"
+ "6.1", "6549825126"
+ "0.9", "966367641"
+ "691.131", "742096260562""""
+ )
+ fun gibibyte(gibibytes: Double, bytes: ULong) {
+ val dataSize: DataSize = DataSize.ofGibibyte(gibibytes)
+ assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}")
+ assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}")
+ assertTrue(dataSize.toGibibyte() > gibibytes.minus(5), ".toGibibyte = ${dataSize.toGibibyte()} is smaller than range floor ${gibibytes.minus(5)}")
+ assertTrue(dataSize.toGibibyte() < gibibytes.plus(5), ".toGibibyte = ${dataSize.toGibibyte()} is bigger than range ceiling ${gibibytes.plus(5)}")
+ }
+
+ @ParameterizedTest
+ @CsvSource(
+ delimiter = ',',
+ quoteCharacter = '"',
+ textBlock = """
+ "4.4", "4400000000000"
+ "6.1", "6100000000000"
+ "0.9", "900000000000"
+ "691.131", "691131000000000""""
+ )
+ fun terabyte(terabytes: Double, bytes: ULong) {
+ val dataSize: DataSize = DataSize.ofTerabyte(terabytes)
+ assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}")
+ assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}")
+ assertTrue(dataSize.toTerabyte() > terabytes.minus(5), ".toTerabyte = ${dataSize.toTerabyte()} is smaller than range floor ${terabytes.minus(5)}")
+ assertTrue(dataSize.toTerabyte() < terabytes.plus(5), ".toTerabyte = ${dataSize.toTerabyte()} is bigger than range ceiling ${terabytes.plus(5)}")
+ }
+
+ @ParameterizedTest
+ @CsvSource(
+ delimiter = ',',
+ quoteCharacter = '"',
+ textBlock = """
+ "4.4", "4837851162214"
+ "6.1", "6707020929433"
+ "0.9", "989560464998"
+ "691.131", "759906570816454""""
+ )
+ fun tebibyte(tebibytes: Double, bytes: ULong) {
+ val dataSize: DataSize = DataSize.ofTebibyte(tebibytes)
+ assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}")
+ assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}")
+ assertTrue(dataSize.toTebibyte() > tebibytes.minus(5), ".toTebibyte = ${dataSize.toTebibyte()} is smaller than range floor ${tebibytes.minus(5)}")
+ assertTrue(dataSize.toTebibyte() < tebibytes.plus(5), ".toTebibyte = ${dataSize.toTebibyte()} is bigger than range ceiling ${tebibytes.plus(5)}")
+ }
+
+ @ParameterizedTest
+ @CsvSource(
+ delimiter = ',',
+ quoteCharacter = '"',
+ textBlock = """
+ "4.4", "4400000000000000"
+ "6.1", "6100000000000000"
+ "0.9", "900000000000000""""
+ )
+ fun petabyte(petabytes: Double, bytes: ULong) {
+ val dataSize: DataSize = DataSize.ofPetabyte(petabytes)
+ assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}")
+ assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}")
+ assertTrue(dataSize.toPetabyte() > petabytes.minus(5), ".toPetabyte = ${dataSize.toPetabyte()} is smaller than range floor ${petabytes.minus(5)}")
+ assertTrue(dataSize.toPetabyte() < petabytes.plus(5), ".toPetabyte = ${dataSize.toPetabyte()} is bigger than range ceiling ${petabytes.plus(5)}")
+ }
+
+ @ParameterizedTest
+ @CsvSource(
+ delimiter = ',',
+ quoteCharacter = '"',
+ textBlock = """
+ "4.4", "4953959590107546"
+ "6.1", "6867989431740006"
+ "0.9", "1013309916158361""""
+ )
+ fun pebibyte(pebibytes: Double, bytes: ULong) {
+ val dataSize: DataSize = DataSize.ofPebibyte(pebibytes)
+ assertTrue(dataSize.bytes > bytes.minus(5UL), ".bytes = ${dataSize.bytes} is smaller than range floor ${bytes.minus(5UL)}")
+ assertTrue(dataSize.bytes < bytes.plus(5UL), ".bytes = ${dataSize.bytes} is bigger than range ceiling ${bytes.plus(5UL)}")
+ assertTrue(dataSize.toPebibyte() > pebibytes.minus(5), ".toPebibyte = ${dataSize.toPebibyte()} is smaller than range floor ${pebibytes.minus(5)}")
+ assertTrue(dataSize.toPebibyte() < pebibytes.plus(5), ".toPebibyte = ${dataSize.toPebibyte()} is bigger than range ceiling ${pebibytes.plus(5)}")
+ }
+
+ @Test
+ @DisplayName("toString")
+ fun toStringTest() {
+ val dataSize: DataSize = DataSize.ofMebibyte(410465.451)
+ assertEquals(dataSize.bytes, dataSize.toString().toULong())
+ }
+
+ @ParameterizedTest
+ @CsvSource(
+ delimiter = ',',
+ quoteCharacter = '"',
+ textBlock = """
+ "591", "KIBIBYTE"
+ "1023", "KIBIBYTE"
+ "1024", "KIBIBYTE"
+ "1047552", "KIBIBYTE"
+ "1048576", "MEBIBYTE"
+ "1072693248", "MEBIBYTE"
+ "1073741824", "GIBIBYTE"
+ "1098437885952", "GIBIBYTE"
+ "1099511627776", "TEBIBYTE"
+ "1124800395214848", "TEBIBYTE"
+ "1125899906842624", "PEBIBYTE"
+ "1151795604700004400", "PEBIBYTE""""
+ )
+ @DisplayName("toPerfectRepresentation (by 1024)")
+ fun toPerfectRepresentationBy1024(bytes: ULong, unit: DataSize.Unit) {
+ assertEquals(unit, DataSize(bytes).toPerfectRepresentation(binary = true).second, "Bytes ${bytes} did not match Unit.${unit.name}")
+ }
+
+ @ParameterizedTest
+ @CsvSource(
+ delimiter = ',',
+ quoteCharacter = '"',
+ textBlock = """
+ "591", "KILOBYTE"
+ "999", "KILOBYTE"
+ "1000", "KILOBYTE"
+ "999000", "KILOBYTE"
+ "1000000", "MEGABYTE"
+ "999000000", "MEGABYTE"
+ "1000000000", "GIGABYTE"
+ "999000000000", "GIGABYTE"
+ "1000000000000", "TERABYTE"
+ "999000000000000", "TERABYTE"
+ "1000000000000000", "PETABYTE"
+ "999000000000000000", "PETABYTE""""
+ )
+ @DisplayName("toPerfectRepresentation (by 1000)")
+ fun toPerfectRepresentationBy1000(bytes: ULong, unit: DataSize.Unit) {
+ assertEquals(unit, DataSize(bytes).toPerfectRepresentation(binary = false).second, "Bytes ${bytes} did not match Unit.${unit.name}")
+ }
+}