Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
kapacity = "0.9.9-beta06"
kapacity = "0.9.9-beta07"
agp = "8.11.2"
android-compileSdk = "36"
android-minSdk = "24"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,19 @@ value class Kapacity private constructor(val rawBytes: Long) : Comparable<Kapaci
unit: KapacityUnit? = null,
useMetric: Boolean = true,
useUnitSuffix: Boolean = true,
useAbbreviation: Boolean = true,
): String {
val resolvedUnit = unit ?: determineKapacityUnit(useMetric = useMetric)
if (resolvedUnit == KapacityUnit.Byte) {
return formatByteCount(byteCount = this.rawBytes).let { byteCountStr ->
if (useUnitSuffix) "$byteCountStr bytes" else byteCountStr
if (useUnitSuffix) {
if (useAbbreviation) {
"$byteCountStr ${resolvedUnit.abbreviationMetric}"
} else {
if (this.rawBytes != 1L) "$byteCountStr ${resolvedUnit}s"
else "$byteCountStr $resolvedUnit"
}
} else byteCountStr
}
}
// Double division is perfectly safe here, even for Exabytes.
Expand All @@ -37,11 +45,19 @@ value class Kapacity private constructor(val rawBytes: Long) : Comparable<Kapaci
val formattedSize = formatSize(size = size)

return if (useUnitSuffix) {
if (size != 1.0) "$formattedSize ${resolvedUnit}s" else "$formattedSize $resolvedUnit"
if (useAbbreviation) {
if (useMetric) "$formattedSize ${resolvedUnit.abbreviationMetric}"
else "$formattedSize ${resolvedUnit.abbreviationBinary}"
} else if (size != 1.0) "$formattedSize ${resolvedUnit}s" else "$formattedSize $resolvedUnit"
} else formattedSize
}

override fun toString(): String = toString(unit = null, useMetric = true, useUnitSuffix = true)
override fun toString(): String = toString(
unit = null,
useMetric = true,
useUnitSuffix = true,
useAbbreviation = true,
)


//region `Long` operator overloads
Expand Down Expand Up @@ -206,16 +222,48 @@ value class Kapacity private constructor(val rawBytes: Long) : Comparable<Kapaci
* @property metric The multiplier for the base-10 standard (powers of 1,000).
* @property binary The multiplier for the base-2 standard (powers of 1,024).
*/
enum class KapacityUnit(internal val metric: Long, internal val binary: Long) {
Byte(metric = 1, binary = 1), // 1000^0 or 1024^0
Kilobyte(metric = 1_000, binary = 1_024), // 1000^1 or 1024^1
Megabyte(metric = 1_000_000, binary = 1_048_576), // 1000^2 or 1024^2
Gigabyte(metric = 1_000_000_000, binary = 1_073_741_824), // 1000^3 or 1024^3
Terabyte(metric = 1_000_000_000_000, binary = 1_099_511_627_776), // 1000^4 or 1024^4
Petabyte(metric = 1_000_000_000_000_000, binary = 1_125_899_906_842_624), // 1000^5 or 1024^5
enum class KapacityUnit(
internal val metric: Long,
internal val binary: Long,
internal val abbreviationMetric: String,
internal val abbreviationBinary: String = abbreviationMetric,
) {
Byte(metric = 1, binary = 1, abbreviationMetric = "B"), // 1000^0 or 1024^0
Kilobyte(
metric = 1_000,
binary = 1_024,
abbreviationMetric = "KB",
abbreviationBinary = "KiB"
), // 1000^1 or 1024^1
Megabyte(
metric = 1_000_000,
binary = 1_048_576,
abbreviationMetric = "MB",
abbreviationBinary = "MiB"
), // 1000^2 or 1024^2
Gigabyte(
metric = 1_000_000_000,
binary = 1_073_741_824,
abbreviationMetric = "GB",
abbreviationBinary = "GiB"
), // 1000^3 or 1024^3
Terabyte(
metric = 1_000_000_000_000,
binary = 1_099_511_627_776,
abbreviationMetric = "TB",
abbreviationBinary = "TiB"
), // 1000^4 or 1024^4
Petabyte(
metric = 1_000_000_000_000_000,
binary = 1_125_899_906_842_624,
abbreviationMetric = "PB",
abbreviationBinary = "PiB"
), // 1000^5 or 1024^5
Exabyte(
metric = 1_000_000_000_000_000_000,
binary = 1_152_921_504_606_846_976
binary = 1_152_921_504_606_846_976,
abbreviationMetric = "EB",
abbreviationBinary = "EiB"
), // 1000^6 or 1024^6
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package io.github.developrofthings.kapacity

import kotlin.test.Test
import kotlin.test.assertEquals

class KapacityToStringTest {

//region Byte Edge Cases
@Test
fun testByteFormattingWithAbbreviation() {
val singleByte = 1.byte
val multipleBytes = 15.byte

// Abbreviation should always be "B" regardless of pluralization
assertEquals("1 B", singleByte.toString(useAbbreviation = true))
assertEquals("15 B", multipleBytes.toString(useAbbreviation = true))
}

@Test
fun testByteFormattingWithFullNames() {
val singleByte = 1.byte
val multipleBytes = 15.byte

// Full names must correctly pluralize "Byte" vs "Bytes"
assertEquals("1 Byte", singleByte.toString(useAbbreviation = false))
assertEquals("15 Bytes", multipleBytes.toString(useAbbreviation = false))
}

@Test
fun testByteFormattingWithoutSuffix() {
val capacity = 42.byte

// Should completely drop the suffix and the space
assertEquals("42", capacity.toString(useUnitSuffix = false))
}
//endregion

//region Metric Formatting Tests
@Test
fun testMetricFormattingWithAbbreviation() {
val capacity = 1.5.megabyte // 1,500,000 bytes

assertEquals("1.5 MB", capacity.toString(useMetric = true, useAbbreviation = true))
}

@Test
fun testMetricFormattingWithFullNames() {
val exactlyOne = 1.kilobyte // 1,000 bytes
val multiple = 1.5.kilobyte // 1,500 bytes

assertEquals("1 Kilobyte", exactlyOne.toString(useMetric = true, useAbbreviation = false))
assertEquals("1.5 Kilobytes", multiple.toString(useMetric = true, useAbbreviation = false))
}
//endregion

//region Binary (IEC) Formatting Tests
@Test
fun testBinaryFormattingWithAbbreviation() {
val capacity = 1.5.mebibyte // 1.5 * 1,048,576 bytes

// Binary should use the "iB" abbreviations
assertEquals("1.5 MiB", capacity.toString(useMetric = false, useAbbreviation = true))
}

@Test
fun testBinaryFormattingWithFullNames() {
val exactlyOne = 1.kibibyte // 1,024 bytes
val multiple = 1.5.kibibyte // 1,536 bytes

// Since the enum name is still Kilobyte, it should print the enum's name
assertEquals("1 Kilobyte", exactlyOne.toString(useMetric = false, useAbbreviation = false))
assertEquals("1.5 Kilobytes", multiple.toString(useMetric = false, useAbbreviation = false))
}
//endregion

//region Forced Unit Tests
@Test
fun testForcedUnitFormatting() {
val capacity = 1.megabyte

// Forcing a 1 MB capacity to print in Kilobytes
assertEquals(
"1,000 KB",
capacity.toString(unit = KapacityUnit.Kilobyte, useMetric = true, useAbbreviation = true)
)

// No suffix forced unit
assertEquals(
"1,000",
capacity.toString(unit = KapacityUnit.Kilobyte, useUnitSuffix = false)
)
}
//endregion
}
Loading