diff --git a/library/api/api.txt b/library/api/api.txt index 37699ef..15f32c0 100644 --- a/library/api/api.txt +++ b/library/api/api.txt @@ -25,6 +25,22 @@ package me.saket.bytesize { method @kotlin.jvm.JvmSynthetic public static inline operator long times(Number, long other); } + @kotlin.jvm.JvmInline public final value class Bitrate { + ctor public Bitrate(@kotlin.PublishedApi me.saket.bytesize.ByteSize bytesPerSecond); + method public inline operator int compareTo(me.saket.bytesize.ByteSize other); + method public inline operator me.saket.bytesize.ByteSize div(double divisor); + method public inline operator me.saket.bytesize.ByteSize minus(me.saket.bytesize.ByteSize other); + method public inline operator me.saket.bytesize.ByteSize plus(me.saket.bytesize.ByteSize other); + method public inline operator me.saket.bytesize.ByteSize times(double multiplier); + method public inline String toString(); + } + + public final class BitrateKt { + method @kotlin.jvm.JvmSynthetic public static inline operator me.saket.bytesize.ByteSize div(me.saket.bytesize.ByteSize, long duration); + method @kotlin.jvm.JvmSynthetic public static inline operator me.saket.bytesize.ByteSize times(long, me.saket.bytesize.ByteSize bitrate); + method @kotlin.jvm.JvmSynthetic public static inline operator me.saket.bytesize.ByteSize times(me.saket.bytesize.ByteSize, long duration); + } + public sealed interface ByteSize { method public operator me.saket.bytesize.ByteSize div(Number other); method public operator double div(me.saket.bytesize.ByteSize other); @@ -44,7 +60,7 @@ package me.saket.bytesize { @kotlin.jvm.JvmInline public final value class DecimalBitSize implements me.saket.bytesize.ByteSize { ctor public DecimalBitSize(Number bits); - ctor public DecimalBitSize(@kotlin.PublishedApi inline long bits); + ctor public DecimalBitSize(@kotlin.PublishedApi long bits); method public inline int compareTo(me.saket.bytesize.ByteSize other); method public inline long div(Number other); method public inline double div(me.saket.bytesize.ByteSize other); @@ -94,6 +110,17 @@ package me.saket.bytesize { public final class ErrorsKt { method @Deprecated @kotlin.jvm.JvmSynthetic public static me.saket.bytesize.ByteSize ByteSize(long bytes); + method @kotlin.jvm.JvmSynthetic public long getBinaryBytes(); + method @kotlin.jvm.JvmSynthetic public long getBinaryBytes(); + method @kotlin.jvm.JvmSynthetic public me.saket.bytesize.ByteSize getBytes(); + method @kotlin.jvm.JvmSynthetic public long getDecimalBits(); + method @kotlin.jvm.JvmSynthetic public long getDecimalBits(); + method @kotlin.jvm.JvmSynthetic public long getDecimalBytes(); + method @kotlin.jvm.JvmSynthetic public long getDecimalBytes(); + property @kotlin.jvm.JvmSynthetic public long binaryBytes; + property @kotlin.jvm.JvmSynthetic public me.saket.bytesize.ByteSize bytes; + property @kotlin.jvm.JvmSynthetic public long decimalBits; + property @kotlin.jvm.JvmSynthetic public long decimalBytes; } } diff --git a/library/src/commonMain/kotlin/me/saket/bytesize/Bitrate.kt b/library/src/commonMain/kotlin/me/saket/bytesize/Bitrate.kt new file mode 100644 index 0000000..ae0d1a9 --- /dev/null +++ b/library/src/commonMain/kotlin/me/saket/bytesize/Bitrate.kt @@ -0,0 +1,43 @@ +@file:Suppress("INAPPLICABLE_JVM_NAME", "OVERRIDE_BY_INLINE", "NOTHING_TO_INLINE") + +package me.saket.bytesize + +import kotlin.jvm.JvmInline +import kotlin.jvm.JvmSynthetic +import kotlin.time.Duration +import kotlin.time.DurationUnit + +/** Represents a byte or bit size per second. */ +@JvmInline +value class Bitrate( + @PublishedApi + @get:JvmSynthetic + internal val bytesPerSecond: ByteSize, +) : Comparable { + override inline operator fun compareTo(other: Bitrate): Int = bytesPerSecond.compareTo(other.bytesPerSecond) + + inline operator fun plus(other: Bitrate): Bitrate = Bitrate(bytesPerSecond + other.bytesPerSecond) + + inline operator fun minus(other: Bitrate): Bitrate = Bitrate(bytesPerSecond - other.bytesPerSecond) + + inline operator fun times(multiplier: Double): Bitrate = Bitrate(bytesPerSecond * multiplier) + + inline operator fun div(divisor: Double): Bitrate = Bitrate(bytesPerSecond / divisor) + + override inline fun toString(): String = "$bytesPerSecond/s" +} + +@JvmSynthetic +inline operator fun ByteSize.div(duration: Duration): Bitrate { + return Bitrate(this / duration.toDouble(DurationUnit.SECONDS)) +} + +@JvmSynthetic +inline operator fun Bitrate.times(duration: Duration): ByteSize { + return bytesPerSecond * duration.toDouble(DurationUnit.SECONDS) +} + +@JvmSynthetic +inline operator fun Duration.times(bitrate: Bitrate): ByteSize { + return bitrate * this +} diff --git a/library/src/commonTest/kotlin/me/saket/bytesize/BitrateTest.kt b/library/src/commonTest/kotlin/me/saket/bytesize/BitrateTest.kt new file mode 100644 index 0000000..4435fcb --- /dev/null +++ b/library/src/commonTest/kotlin/me/saket/bytesize/BitrateTest.kt @@ -0,0 +1,80 @@ +package me.saket.bytesize + +import assertk.assertThat +import assertk.assertions.hasToString +import assertk.assertions.isEqualTo +import assertk.assertions.isGreaterThan +import assertk.assertions.isLessThan +import kotlin.test.Test +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds + +class BitrateTest { + @Test fun plus() { + val rate1 = 10.megabytes / 1.seconds + val rate2 = 5.megabytes / 1.seconds + assertThat(rate1 + rate2).isEqualTo(15.megabytes / 1.seconds) + } + + @Test fun minus() { + val rate1 = 10.megabytes / 1.seconds + val rate2 = 5.megabytes / 1.seconds + assertThat(rate1 - rate2).isEqualTo(5.megabytes / 1.seconds) + } + + @Test fun times_double() { + val rate = 10.megabytes / 1.seconds + assertThat(rate * 2.5).isEqualTo(25.megabytes / 1.seconds) + } + + @Test fun div_double() { + val rate = 10.megabytes / 1.seconds + assertThat(rate / 2.5).isEqualTo(4.megabytes / 1.seconds) + } + + @Test fun binary_byte_size_div_duration() { + val rate = 100.mebibytes / 3.2.seconds + assertThat(rate).hasToString("31.25 MiB/s") + } + + @Test fun decimal_byte_size_div_duration() { + val rate = 100.megabytes / 4.seconds + assertThat(rate).hasToString("25 MB/s") + } + + @Test fun decimal_bit_size_div_duration() { + val rate = 100.megabits / 2.seconds + assertThat(rate).hasToString("50 Mb/s") + } + + @Test fun bitrate_times_duration() { + val rate = 10.megabytes / 1.seconds + val size = rate * 5.seconds + assertThat(size).isEqualTo(50.megabytes) + } + + @Test fun duration_times_bitrate() { + val rate = 10.megabytes / 1.seconds + val size = 5.seconds * rate + assertThat(size).isEqualTo(50.megabytes) + } + + @Test fun bitrate_times_duration_with_minutes() { + val rate = 1.megabytes / 1.seconds + val size = rate * 1.minutes + assertThat(size).isEqualTo(60.megabytes) + } + + @Test fun comparison() { + val slow = 10.megabytes / 1.seconds + val fast = 100.megabytes / 1.seconds + assertThat(slow).isLessThan(fast) + assertThat(fast).isGreaterThan(slow) + } + + @Test fun comparison_across_types() { + val binary = 1.gibibytes / 1.seconds + val decimal = 1.gigabytes / 1.seconds + assertThat(binary).isGreaterThan(decimal) + } +}