diff --git a/CHANGELOG.md b/CHANGELOG.md index b260f267..a56912b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ **Added** - Add `--summary-only` flag. +**Changed** +- Replace `com.jakewharton.diffuse.io.Size` with `me.saket.bytesize.ByteSize` in the APIs. + **Fixed** - Significantly improve `.jar` diff performance. diff --git a/formats/api/formats.api b/formats/api/formats.api index 9d248958..2c42f0f7 100644 --- a/formats/api/formats.api +++ b/formats/api/formats.api @@ -97,19 +97,19 @@ public final class com/jakewharton/diffuse/format/Apk$Companion { } public final class com/jakewharton/diffuse/format/ArchiveFile { - public synthetic fun (Ljava/lang/String;Lcom/jakewharton/diffuse/format/ArchiveFile$Type;JJZLkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Lcom/jakewharton/diffuse/format/ArchiveFile$Type;Lme/saket/bytesize/ByteSize;Lme/saket/bytesize/ByteSize;Z)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Lcom/jakewharton/diffuse/format/ArchiveFile$Type; - public final fun component3-kab5oJg ()J - public final fun component4-kab5oJg ()J + public final fun component3 ()Lme/saket/bytesize/ByteSize; + public final fun component4 ()Lme/saket/bytesize/ByteSize; public final fun component5 ()Z - public final fun copy-Ws8QFX4 (Ljava/lang/String;Lcom/jakewharton/diffuse/format/ArchiveFile$Type;JJZ)Lcom/jakewharton/diffuse/format/ArchiveFile; - public static synthetic fun copy-Ws8QFX4$default (Lcom/jakewharton/diffuse/format/ArchiveFile;Ljava/lang/String;Lcom/jakewharton/diffuse/format/ArchiveFile$Type;JJZILjava/lang/Object;)Lcom/jakewharton/diffuse/format/ArchiveFile; + public final fun copy (Ljava/lang/String;Lcom/jakewharton/diffuse/format/ArchiveFile$Type;Lme/saket/bytesize/ByteSize;Lme/saket/bytesize/ByteSize;Z)Lcom/jakewharton/diffuse/format/ArchiveFile; + public static synthetic fun copy$default (Lcom/jakewharton/diffuse/format/ArchiveFile;Ljava/lang/String;Lcom/jakewharton/diffuse/format/ArchiveFile$Type;Lme/saket/bytesize/ByteSize;Lme/saket/bytesize/ByteSize;ZILjava/lang/Object;)Lcom/jakewharton/diffuse/format/ArchiveFile; public fun equals (Ljava/lang/Object;)Z public final fun getPath ()Ljava/lang/String; - public final fun getSize-kab5oJg ()J + public final fun getSize ()Lme/saket/bytesize/ByteSize; public final fun getType ()Lcom/jakewharton/diffuse/format/ArchiveFile$Type; - public final fun getUncompressedSize-kab5oJg ()J + public final fun getUncompressedSize ()Lme/saket/bytesize/ByteSize; public fun hashCode ()I public final fun isCompressed ()Z public fun toString ()Ljava/lang/String; diff --git a/formats/src/main/kotlin/com/jakewharton/diffuse/format/ArchiveFile.kt b/formats/src/main/kotlin/com/jakewharton/diffuse/format/ArchiveFile.kt index 2c8a7594..f3888c04 100644 --- a/formats/src/main/kotlin/com/jakewharton/diffuse/format/ArchiveFile.kt +++ b/formats/src/main/kotlin/com/jakewharton/diffuse/format/ArchiveFile.kt @@ -1,14 +1,14 @@ package com.jakewharton.diffuse.format import com.jakewharton.diffuse.format.Arsc as ArscFormat -import com.jakewharton.diffuse.io.Size import java.util.Locale +import me.saket.bytesize.ByteSize data class ArchiveFile( val path: String, val type: Type, - val size: Size, - val uncompressedSize: Size, + val size: ByteSize, + val uncompressedSize: ByteSize, val isCompressed: Boolean, ) { enum class Type { diff --git a/formats/src/main/kotlin/com/jakewharton/diffuse/format/ArchiveFiles.kt b/formats/src/main/kotlin/com/jakewharton/diffuse/format/ArchiveFiles.kt index 9b96f73d..e6977fdf 100644 --- a/formats/src/main/kotlin/com/jakewharton/diffuse/format/ArchiveFiles.kt +++ b/formats/src/main/kotlin/com/jakewharton/diffuse/format/ArchiveFiles.kt @@ -2,8 +2,8 @@ package com.jakewharton.diffuse.format import com.jakewharton.diffuse.format.ArchiveFile.Type import com.jakewharton.diffuse.format.ArchiveFile.Type.Other -import com.jakewharton.diffuse.io.Size import com.jakewharton.diffuse.io.Zip +import me.saket.bytesize.binaryBytes class ArchiveFiles internal constructor(private val files: Map) : Map by files { @@ -26,7 +26,7 @@ class ArchiveFiles internal constructor(private val files: Map { - override fun toString(): String = bytes.binaryBytes.toString() - - override fun compareTo(other: Size) = bytes.compareTo(other.bytes) - - operator fun plus(other: Size) = Size(bytes + other.bytes) - - operator fun minus(other: Size) = Size(bytes - other.bytes) - - operator fun unaryMinus() = Size(-bytes) - - val absoluteValue - get() = Size(bytes.absoluteValue) - - companion object { - val ZERO = Size(0) - } -} diff --git a/io/src/main/kotlin/com/jakewharton/diffuse/io/Zip.kt b/io/src/main/kotlin/com/jakewharton/diffuse/io/Zip.kt index 38c8be56..ff93ecc3 100644 --- a/io/src/main/kotlin/com/jakewharton/diffuse/io/Zip.kt +++ b/io/src/main/kotlin/com/jakewharton/diffuse/io/Zip.kt @@ -9,6 +9,8 @@ import java.util.zip.ZipEntry import java.util.zip.ZipInputStream import kotlin.io.path.exists import kotlin.io.path.inputStream +import me.saket.bytesize.BinaryByteSize +import me.saket.bytesize.ByteSize import okio.ByteString import okio.utf8Size @@ -47,13 +49,13 @@ interface Zip : Closeable { val path: String /** The uncompressed size of the entry contents */ - val uncompressedSize: Size + val uncompressedSize: ByteSize /** The compressed size of the entry contents. */ - val compressedSize: Size + val compressedSize: ByteSize /** The impact on the overall zip size. This is [compressedSize] plus metadata. */ - val zipSize: Size + val zipSize: ByteSize /** Returns true if the entry is compressed using any method other than 'store'. */ val isCompressed: Boolean @@ -64,7 +66,13 @@ interface Zip : Closeable { private fun ZipInputStream.mapEntries( entryFactory: - (name: String, size: Size, compressedSize: Size, zipSize: Size, isCompressed: Boolean) -> T + ( + name: String, + size: ByteSize, + compressedSize: ByteSize, + zipSize: ByteSize, + isCompressed: Boolean, + ) -> T ): List { return entries().map { it.toZipEntry(this, entryFactory) }.toList() } @@ -72,7 +80,13 @@ private fun ZipInputStream.mapEntries( private fun ZipEntry.toZipEntry( zipStream: ZipInputStream, entryFactory: - (name: String, size: Size, compressedSize: Size, zipSize: Size, isCompressed: Boolean) -> T, + ( + name: String, + size: ByteSize, + compressedSize: ByteSize, + zipSize: ByteSize, + isCompressed: Boolean, + ) -> T, ): T { val isCompressed = method != ZipEntry.STORED val nameSize = name.utf8Size() @@ -113,7 +127,13 @@ private fun ZipEntry.toZipEntry( extraSize + commentSize - return entryFactory(name, Size(size), Size(compressedSize), Size(zipSize), isCompressed) + return entryFactory( + name, + BinaryByteSize(size), + BinaryByteSize(compressedSize), + BinaryByteSize(zipSize), + isCompressed, + ) } internal fun Path.toZip(): Zip { @@ -133,9 +153,9 @@ internal class PathZip(fs: FileSystem, override val entries: List) : class Entry( private val root: Path, override val path: String, - override val uncompressedSize: Size, - override val compressedSize: Size, - override val zipSize: Size, + override val uncompressedSize: ByteSize, + override val compressedSize: ByteSize, + override val zipSize: ByteSize, override val isCompressed: Boolean, ) : Zip.Entry { override fun asInput(): Input { @@ -160,9 +180,9 @@ internal class BytesZip(override val entries: List) : Zip { class Entry( private val bytes: ByteString, override val path: String, - override val uncompressedSize: Size, - override val compressedSize: Size, - override val zipSize: Size, + override val uncompressedSize: ByteSize, + override val compressedSize: ByteSize, + override val zipSize: ByteSize, override val isCompressed: Boolean, ) : Zip.Entry { override fun asInput(): Input { diff --git a/io/src/test/kotlin/com/jakewharton/diffuse/io/SizeTest.kt b/io/src/test/kotlin/com/jakewharton/diffuse/io/SizeTest.kt deleted file mode 100644 index a55d8d88..00000000 --- a/io/src/test/kotlin/com/jakewharton/diffuse/io/SizeTest.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.jakewharton.diffuse.io - -import assertk.assertThat -import assertk.assertions.hasToString -import org.junit.Test - -class SizeTest { - @Test - fun toStringFormatsBytes() { - assertThat(Size(0)).hasToString("0 B") - assertThat(Size(1)).hasToString("1 B") - assertThat(Size(-1)).hasToString("-1 B") - assertThat(Size(1024)).hasToString("1 KiB") - assertThat(Size(-1024)).hasToString("-1 KiB") - assertThat(Size(1024L * 1024)).hasToString("1 MiB") - assertThat(Size(-(1024L * 1024))).hasToString("-1 MiB") - assertThat(Size(1024L * 1024 * 1024)).hasToString("1 GiB") - assertThat(Size(-(1024L * 1024 * 1024))).hasToString("-1 GiB") - } -} diff --git a/io/src/test/kotlin/com/jakewharton/diffuse/io/ZipTest.kt b/io/src/test/kotlin/com/jakewharton/diffuse/io/ZipTest.kt index 8d0fad5c..ecc3dba8 100644 --- a/io/src/test/kotlin/com/jakewharton/diffuse/io/ZipTest.kt +++ b/io/src/test/kotlin/com/jakewharton/diffuse/io/ZipTest.kt @@ -3,6 +3,7 @@ package com.jakewharton.diffuse.io import assertk.assertThat import assertk.assertions.isEqualTo import com.jakewharton.diffuse.testing.decodeHexWithWhitespace +import me.saket.bytesize.binaryBytes import org.junit.Test class ZipTest { @@ -23,7 +24,7 @@ class ZipTest { .toZip() val entry = zip.entries.single() - assertThat(entry.compressedSize).isEqualTo(Size(7)) - assertThat(entry.uncompressedSize).isEqualTo(Size(5)) + assertThat(entry.compressedSize).isEqualTo(7.binaryBytes) + assertThat(entry.uncompressedSize).isEqualTo(5.binaryBytes) } } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArchiveFilesDiff.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArchiveFilesDiff.kt index 85f963dd..98b3ef1e 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArchiveFilesDiff.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/ArchiveFilesDiff.kt @@ -4,7 +4,6 @@ import com.jakewharton.diffuse.diff.ArchiveFilesDiff.Change import com.jakewharton.diffuse.diffuseTable import com.jakewharton.diffuse.format.ArchiveFile.Type import com.jakewharton.diffuse.format.ArchiveFiles -import com.jakewharton.diffuse.io.Size import com.jakewharton.diffuse.report.toDiffString import com.jakewharton.picnic.TableSectionDsl import com.jakewharton.picnic.TextAlignment.BottomCenter @@ -12,6 +11,8 @@ import com.jakewharton.picnic.TextAlignment.BottomLeft import com.jakewharton.picnic.TextAlignment.MiddleCenter import com.jakewharton.picnic.TextAlignment.MiddleRight import com.jakewharton.picnic.renderText +import me.saket.bytesize.ByteSize +import me.saket.bytesize.binaryBytes internal class ArchiveFilesDiff( val oldFiles: ArchiveFiles, @@ -20,10 +21,10 @@ internal class ArchiveFilesDiff( ) { data class Change( val path: String, - val size: Size, - val sizeDiff: Size, - val uncompressedSize: Size, - val uncompressedSizeDiff: Size, + val size: ByteSize, + val sizeDiff: ByteSize, + val uncompressedSize: ByteSize, + val uncompressedSizeDiff: ByteSize, val type: Type, ) { enum class Type { @@ -54,9 +55,9 @@ internal class ArchiveFilesDiff( if (path !in newFiles) { Change( path, - Size.ZERO, + 0.binaryBytes, -oldFile.size, - Size.ZERO, + 0.binaryBytes, -oldFile.uncompressedSize, Change.Type.Removed, ) @@ -130,13 +131,13 @@ internal fun ArchiveFilesDiff.toSummaryTable( fun TableSectionDsl.addApkRow(name: String, type: Type? = null) { val old = if (type != null) oldFiles.filterValues { it.type == type } else oldFiles val new = if (type != null) newFiles.filterValues { it.type == type } else newFiles - val oldSize = old.values.fold(Size.ZERO) { acc, file -> acc + file.size } - val newSize = new.values.fold(Size.ZERO) { acc, file -> acc + file.size } + val oldSize = old.values.fold(0.binaryBytes) { acc, file -> acc + file.size } + val newSize = new.values.fold(0.binaryBytes) { acc, file -> acc + file.size } val oldUncompressedSize = - old.values.fold(Size.ZERO) { acc, file -> acc + file.uncompressedSize } + old.values.fold(0.binaryBytes) { acc, file -> acc + file.uncompressedSize } val newUncompressedSize = - new.values.fold(Size.ZERO) { acc, file -> acc + file.uncompressedSize } - if (oldSize != Size.ZERO || newSize != Size.ZERO || type !in skipIfEmptyTypes) { + new.values.fold(0.binaryBytes) { acc, file -> acc + file.uncompressedSize } + if (oldSize != 0.binaryBytes || newSize != 0.binaryBytes || type !in skipIfEmptyTypes) { val uncompressedDiff = (newUncompressedSize - oldUncompressedSize).toDiffString() if (includeCompressed) { row( @@ -200,15 +201,15 @@ internal fun ArchiveFilesDiff.toDetailReport() = buildString { footer { row { if (includeCompressed) { - val totalSize = changes.fold(Size.ZERO) { acc, change -> acc + change.size } - val totalDiff = changes.fold(Size.ZERO) { acc, change -> acc + change.sizeDiff } + val totalSize = changes.fold(0.binaryBytes) { acc, change -> acc + change.size } + val totalDiff = changes.fold(0.binaryBytes) { acc, change -> acc + change.sizeDiff } cell(totalSize) { alignment = MiddleRight } cell(totalDiff.toDiffString()) { alignment = MiddleRight } } val totalUncompressedSize = - changes.fold(Size.ZERO) { acc, change -> acc + change.uncompressedSize } + changes.fold(0.binaryBytes) { acc, change -> acc + change.uncompressedSize } val totalUncompressedDiff = - changes.fold(Size.ZERO) { acc, change -> acc + change.uncompressedSizeDiff } + changes.fold(0.binaryBytes) { acc, change -> acc + change.uncompressedSizeDiff } cell(totalUncompressedSize) { alignment = MiddleRight } cell(totalUncompressedDiff.toDiffString()) { alignment = MiddleRight } cell("(total)") diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/diff/SizeExtensions.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/SizeExtensions.kt new file mode 100644 index 00000000..36c74402 --- /dev/null +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/diff/SizeExtensions.kt @@ -0,0 +1,25 @@ +package com.jakewharton.diffuse.diff + +import kotlin.math.absoluteValue +import me.saket.bytesize.BinaryByteSize +import me.saket.bytesize.ByteSize +import me.saket.bytesize.DecimalBitSize +import me.saket.bytesize.DecimalByteSize + +/** TODO: https://github.com/saket/byte-size/pull/21 */ +internal val ByteSize.absoluteValue: ByteSize + get() = + when (this) { + is DecimalBitSize -> DecimalBitSize(inWholeBits.absoluteValue) + is BinaryByteSize -> BinaryByteSize(inWholeBytes.absoluteValue) + is DecimalByteSize -> DecimalByteSize(inWholeBytes.absoluteValue) + } + +/** TODO: https://github.com/saket/byte-size/pull/20 */ +@Suppress("NOTHING_TO_INLINE") +internal inline operator fun ByteSize.unaryMinus(): ByteSize = + when (this) { + is DecimalBitSize -> DecimalBitSize(-inWholeBits) + is BinaryByteSize -> BinaryByteSize(-inWholeBytes) + is DecimalByteSize -> DecimalByteSize(-inWholeBytes) + } diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArchiveFilesInfo.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArchiveFilesInfo.kt index cce1a076..21b76875 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArchiveFilesInfo.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/info/ArchiveFilesInfo.kt @@ -3,10 +3,10 @@ package com.jakewharton.diffuse.info import com.jakewharton.diffuse.diffuseTable import com.jakewharton.diffuse.format.ArchiveFile import com.jakewharton.diffuse.format.ArchiveFiles -import com.jakewharton.diffuse.io.Size import com.jakewharton.picnic.TableSectionDsl import com.jakewharton.picnic.TextAlignment import com.jakewharton.picnic.renderText +import me.saket.bytesize.binaryBytes internal fun ArchiveFiles.toSummaryTable( name: String, @@ -32,10 +32,10 @@ internal fun ArchiveFiles.toSummaryTable( fun TableSectionDsl.addApkRow(name: String, type: ArchiveFile.Type? = null) { val old = if (type != null) filterValues { it.type == type } else this@toSummaryTable - val oldSize = old.values.fold(Size.ZERO) { acc, file -> acc + file.size } + val oldSize = old.values.fold(0.binaryBytes) { acc, file -> acc + file.size } val oldUncompressedSize = - old.values.fold(Size.ZERO) { acc, file -> acc + file.uncompressedSize } - if (oldSize != Size.ZERO || type !in skipIfEmptyTypes) { + old.values.fold(0.binaryBytes) { acc, file -> acc + file.uncompressedSize } + if (oldSize != 0.binaryBytes || type !in skipIfEmptyTypes) { if (includeCompressed) { row(name, oldSize, oldUncompressedSize) } else { diff --git a/reports/src/main/kotlin/com/jakewharton/diffuse/report/strings.kt b/reports/src/main/kotlin/com/jakewharton/diffuse/report/strings.kt index a7834bb8..34121128 100644 --- a/reports/src/main/kotlin/com/jakewharton/diffuse/report/strings.kt +++ b/reports/src/main/kotlin/com/jakewharton/diffuse/report/strings.kt @@ -1,6 +1,6 @@ package com.jakewharton.diffuse.report -import com.jakewharton.diffuse.io.Size +import me.saket.bytesize.ByteSize internal fun Int.toUnitString(unit: String, vararg specializations: Pair): String { return buildString { @@ -19,8 +19,8 @@ internal fun Int.toDiffString(zeroSign: Char? = null) = buildString { append(this@toDiffString) } -internal fun Size.toDiffString() = buildString { - if (bytes > 0L) { +internal fun ByteSize.toDiffString() = buildString { + if (inWholeBytes > 0L) { append('+') } append(this@toDiffString)