Skip to content

Helper utility for defining the size of things in storage with syntax similar to kotlin Duration. Sample on kotlin playground:

License

nxoim/byteCount

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

This is a helper utility for defining the size of things in storage with syntax similar to kotlin Duration

Example:

val cacheSize = 2.petabytes

// somewhere in the codebase
Something.Builder()
    .setCacheSize(maxSizeMb = cacheSize.inWholeMebibytes)

Installation: copy the codeblock below lmao

import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.jvm.JvmInline

@JvmInline
value class ByteCount(val bytes: Long) {
    val inWholeKilobytes: Long get() = bytes / Kilobyte.bytes
    val inWholeMegabytes: Long get() = bytes / Megabyte.bytes
    val inWholeGigabytes: Long get() = bytes / Gigabyte.bytes
    val inWholeTerabytes: Long get() = bytes / Terabyte.bytes
    val inWholePetabytes: Long get() = bytes / Petabyte.bytes

    val inWholeKibibytes: Long get() = bytes / Kibibyte.bytes
    val inWholeMebibytes: Long get() = bytes / Mebibyte.bytes
    val inWholeGibibytes: Long get() = bytes / Gibibyte.bytes
    val inWholeTebibytes: Long get() = bytes / Tebibyte.bytes
    val inWholePebibytes: Long get() = bytes / Pebibyte.bytes

    override fun toString() = "$bytes bytes"

    operator fun plus(other: ByteCount): ByteCount {
        val result = this.bytes + other.bytes
        // if signs of operands are same but result's sign is different.
        return if ((this.bytes xor result) and (other.bytes xor result) < 0) {
            if (this.bytes > 0) ByteCount.Max else ByteCount.Min
        } else {
            ByteCount(result)
        }
    }

    operator fun minus(other: ByteCount): ByteCount {
        val result = this.bytes - other.bytes
        // true if x and y have different signs, AND x and result have different signs.
        return if (((this.bytes xor other.bytes) < 0) && ((this.bytes xor result) < 0)) {
            if (other.bytes < 0) Max else Min
        } else
            ByteCount(result)
    }

    operator fun times(scale: Long): ByteCount {
        if (bytes == 0L || scale == 0L) return Zero
        try {
            return ByteCount(multiplyExact(bytes, scale))
        } catch (e: ArithmeticException) {
            val positiveResultExpected = (bytes > 0) == (scale > 0)
            return if (positiveResultExpected) Max else Min
        }
    }
    operator fun times(other: ByteCount): ByteCount = times(other.bytes)
    operator fun times(scale: Int): ByteCount = times(scale.toLong())
    operator fun times(scale: Short): ByteCount = times(scale.toLong())

    operator fun rem(other: Long): ByteCount {
        if (other == 0L) {
            throw IllegalArgumentException("Modulo by zero is undefined.")
        }
        return ByteCount(this.bytes % other)
    }
    operator fun rem(other: ByteCount): ByteCount = rem(other.bytes)
    operator fun rem(other: Int): ByteCount = rem(other.toLong())
    operator fun rem(other: Short): ByteCount = rem(other.toLong())

    operator fun unaryPlus(): ByteCount = ByteCount(+this.bytes)
    operator fun unaryMinus(): ByteCount {
        if (bytes == Long.MIN_VALUE) return Max
        return ByteCount(-bytes)
    }
    operator fun compareTo(other: ByteCount) = this.bytes.compareTo(other.bytes)

    inline fun <T> toBinaryComponents(block: (kibibytes: Long, bytes: Long) -> T): T {
        contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }

        var remaining = this.bytes
        val kibibytes = remaining / Kibibyte.bytes
        remaining %= Kibibyte.bytes

        return block(kibibytes, remaining)
    }

    inline fun <T> toBinaryComponents(block: (mebibytes: Long, kibibytes: Long, bytes: Long) -> T): T {
        contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
        var remaining = this.bytes
        val mebibytes = remaining / Mebibyte.bytes
        remaining %= Mebibyte.bytes
        val kibibytes = remaining / Kibibyte.bytes
        remaining %= Kibibyte.bytes

        return block(mebibytes, kibibytes, remaining)
    }

    inline fun <T> toBinaryComponents(block: (gibibytes: Long, mebibytes: Long, kibibytes: Long, bytes: Long) -> T): T {
        contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }

        var remaining = this.bytes
        val gibibytes = remaining / Gibibyte.bytes
        remaining %= Gibibyte.bytes
        val mebibytes = remaining / Mebibyte.bytes
        remaining %= Mebibyte.bytes
        val kibibytes = remaining / Kibibyte.bytes
        remaining %= Kibibyte.bytes

        return block(gibibytes, mebibytes, kibibytes, remaining)
    }

    inline fun <T> toBinaryComponents(block: (tebibytes: Long, gibibytes: Long, mebibytes: Long, kibibytes: Long, bytes: Long) -> T): T {
        contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }

        var remaining = this.bytes
        val tebibytes = remaining / Tebibyte.bytes
        remaining %= Tebibyte.bytes
        val gibibytes = remaining / Gibibyte.bytes
        remaining %= Gibibyte.bytes
        val mebibytes = remaining / Mebibyte.bytes
        remaining %= Mebibyte.bytes
        val kibibytes = remaining / Kibibyte.bytes
        remaining %= Kibibyte.bytes

        return block(tebibytes, gibibytes, mebibytes, kibibytes, remaining)
    }

    inline fun <T> toBinaryComponents(block: (pebibytes: Long, tebibytes: Long, gibibytes: Long, mebibytes: Long, kibibytes: Long, bytes: Long) -> T): T {
        contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }

        var remaining = this.bytes
        val pebibytes = remaining / Pebibyte.bytes
        remaining %= Pebibyte.bytes
        val tebibytes = remaining / Tebibyte.bytes
        remaining %= Tebibyte.bytes
        val gibibytes = remaining / Gibibyte.bytes
        remaining %= Gibibyte.bytes
        val mebibytes = remaining / Mebibyte.bytes
        remaining %= Mebibyte.bytes
        val kibibytes = remaining / Kibibyte.bytes
        remaining %= Kibibyte.bytes

        return block(pebibytes, tebibytes, gibibytes, mebibytes, kibibytes, remaining)
    }

    inline fun <T> toDecimalComponents(block: (kilobytes: Long, bytes: Long) -> T): T {
        contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }

        var remaining = this.bytes
        val kilobytes = remaining / Kilobyte.bytes
        remaining %= Kilobyte.bytes

        return block(kilobytes, remaining)
    }

    inline fun <T> toDecimalComponents(block: (megabytes: Long, kilobytes: Long, bytes: Long) -> T): T {
        contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }

        var remaining = this.bytes
        val megabytes = remaining / Megabyte.bytes
        remaining %= Megabyte.bytes
        val kilobytes = remaining / Kilobyte.bytes
        remaining %= Kilobyte.bytes

        return block(megabytes, kilobytes, remaining)
    }

    inline fun <T> toDecimalComponents(block: (gigabytes: Long, megabytes: Long, kilobytes: Long, bytes: Long) -> T): T {
        contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }

        var remaining = this.bytes
        val gigabytes = remaining / Gigabyte.bytes
        remaining %= Gigabyte.bytes
        val megabytes = remaining / Megabyte.bytes
        remaining %= Megabyte.bytes
        val kilobytes = remaining / Kilobyte.bytes
        remaining %= Kilobyte.bytes

        return block(gigabytes, megabytes, kilobytes, remaining)
    }

    inline fun <T> toDecimalComponents(block: (terabytes: Long, gigabytes: Long, megabytes: Long, kilobytes: Long, bytes: Long) -> T): T {
        contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }

        var remaining = this.bytes
        val terabytes = remaining / Terabyte.bytes
        remaining %= Terabyte.bytes
        val gigabytes = remaining / Gigabyte.bytes
        remaining %= Gigabyte.bytes
        val megabytes = remaining / Megabyte.bytes
        remaining %= Megabyte.bytes
        val kilobytes = remaining / Kilobyte.bytes
        remaining %= Kilobyte.bytes

        return block(terabytes, gigabytes, megabytes, kilobytes, remaining)
    }

    inline fun <T> toDecimalComponents(block: (petabytes: Long, terabytes: Long, gigabytes: Long, megabytes: Long, kilobytes: Long, bytes: Long) -> T): T {
        contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }

        var remaining = this.bytes
        val petabytes = remaining / Petabyte.bytes
        remaining %= Petabyte.bytes
        val terabytes = remaining / Terabyte.bytes
        remaining %= Terabyte.bytes
        val gigabytes = remaining / Gigabyte.bytes
        remaining %= Gigabyte.bytes
        val megabytes = remaining / Megabyte.bytes
        remaining %= Megabyte.bytes
        val kilobytes = remaining / Kilobyte.bytes
        remaining %= Kilobyte.bytes

        return block(petabytes, terabytes, gigabytes, megabytes, kilobytes, remaining)
    }

    companion object {
        val Min = ByteCount(Long.MIN_VALUE)
        val Zero = ByteCount(0)
        val Max = ByteCount(Long.MAX_VALUE)

        val Kilobyte = ByteCount(1_000)
        val Megabyte = ByteCount(1_000_000)
        val Gigabyte = ByteCount(1_000_000_000)
        val Terabyte = ByteCount(1_000_000_000_000)
        val Petabyte = ByteCount(1_000_000_000_000_000)

        val Kibibyte = ByteCount(1024)
        val Mebibyte = ByteCount(1_048_576)
        val Gibibyte = ByteCount(1_073_741_824)
        val Tebibyte = ByteCount(1_099_511_627_776)
        val Pebibyte = ByteCount(1_125_899_906_842_624)
    }
}


val Short.bytes: ByteCount get() = ByteCount(this.toLong())
val Int.bytes: ByteCount get() = ByteCount(this.toLong())
val Long.bytes: ByteCount get() = ByteCount(this)

val Short.kilobytes: ByteCount get() = ByteCount.Kilobyte * this
val Int.kilobytes: ByteCount get() = ByteCount.Kilobyte * this
val Long.kilobytes: ByteCount get() = ByteCount.Kilobyte * this

val Short.megabytes: ByteCount get() = ByteCount.Megabyte * this
val Int.megabytes: ByteCount get() = ByteCount.Megabyte * this
val Long.megabytes: ByteCount get() = ByteCount.Megabyte * this

val Short.gigabytes: ByteCount get() = ByteCount.Gigabyte * this
val Int.gigabytes: ByteCount get() = ByteCount.Gigabyte * this
val Long.gigabytes: ByteCount get() = ByteCount.Gigabyte * this

val Short.terabytes: ByteCount get() = ByteCount.Terabyte * this
val Int.terabytes: ByteCount get() = ByteCount.Terabyte * this
val Long.terabytes: ByteCount get() = ByteCount.Terabyte * this

val Short.petabytes: ByteCount get() = ByteCount.Petabyte * this
val Int.petabytes: ByteCount get() = ByteCount.Petabyte * this
val Long.petabytes: ByteCount get() = ByteCount.Petabyte * this


val Short.kibibytes: ByteCount get() = ByteCount.Kibibyte * this
val Int.kibibytes: ByteCount get() = ByteCount.Kibibyte * this
val Long.kibibytes: ByteCount get() = ByteCount.Kibibyte * this

val Short.mebibytes: ByteCount get() = ByteCount.Mebibyte * this
val Int.mebibytes: ByteCount get() = ByteCount.Mebibyte * this
val Long.mebibytes: ByteCount get() = ByteCount.Mebibyte * this


val Short.gibibytes: ByteCount get() = ByteCount.Gibibyte * this
val Int.gibibytes: ByteCount get() = ByteCount.Gibibyte * this
val Long.gibibytes: ByteCount get() = ByteCount.Gibibyte * this


val Short.tebibytes: ByteCount get() = ByteCount.Tebibyte * this
val Int.tebibytes: ByteCount get() = ByteCount.Tebibyte * this
val Long.tebibytes: ByteCount get() = ByteCount.Tebibyte * this

val Short.pebibytes: ByteCount get() = ByteCount.Pebibyte * this
val Int.pebibytes: ByteCount get() = ByteCount.Pebibyte * this
val Long.pebibytes: ByteCount get() = ByteCount.Pebibyte * this

private fun multiplyExact(a: Long, b: Long): Long {
    if ((a == Long.MIN_VALUE && b == -1L) || b == Long.MIN_VALUE && a == -1L)
        throw ArithmeticException()

    val result = a * b
    if (a != 0L && result / a != b) throw ArithmeticException()

    return result
}

About

Helper utility for defining the size of things in storage with syntax similar to kotlin Duration. Sample on kotlin playground:

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages