diff --git a/README.md b/README.md index eca9198..192443c 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ Stop guessing if a variable named `fileSize` is in Bytes, Kilobytes, or Megabyte ## ✨ Key Features * **Zero Allocation:** Built entirely on `@JvmInline value class` for zero garbage collection overhead at runtime. * **Strict Standards:** Explicit support for both Metric/SI (powers of 1,000) and Binary/IEC (powers of 1,024) capacities. -* **Safe Math:** Add, subtract, multiply, and divide capacities with built-in zero-coercion to prevent negative byte bounds. +* **Safe Dimensional Math:** Add, subtract, multiply, and divide capacities safely. Dividing two capacities correctly yields a scalar ratio. Zero-coercion prevents negative byte bounds. +* **Memory Safe:** Safely allocate `ByteArray` and `UByteArray` buffers with built-in protections against `Int.MAX_VALUE` overflows. * **Platform Native:** Read sizes instantly from Android `File`/`Path` and iOS `NSURL`/`NSData`. * **Thread-Safe UI Formatting:** Localized, multiplatform string formatting using thread-safe `DecimalFormat` (Android) and `NSNumberFormatter` (iOS). @@ -22,17 +23,17 @@ Kapacity is published to Maven Central. Add the following to your `build.gradle. ```kotlin dependencies { // Core library - implementation("io.github.developrofthings:kapacity:0.9.9-beta04") + implementation("io.github.developrofthings:kapacity:0.9.9-beta06") // Optional: kotlinx-io extensions (Buffer and ByteString interoperability) - implementation("io.github.developrofthings:kapacity-io:0.9.9-beta04") + implementation("io.github.developrofthings:kapacity-io:0.9.9-beta06") } ``` ## 📖 Usage ### 1. Creating Capacities -Kapacity provides fluent extension properties for `Long`, `ULong`, `Double`, and `Float`. +Kapacity provides fluent extension properties for `Long`, `Int`, `Double`, `Float`, and their unsigned counterparts. By default, standard properties (`.kilobyte`, `.megabyte`) use the **Metric (base-10)** standard. If you need exact memory measurements, use the **Binary (base-2)** equivalents. ``` kotlin @@ -43,11 +44,15 @@ val downloadSize = 5.megabyte val fractionalSize = 1.5.gigabyte // Binary (1 MiB = 1,048,576 Bytes) -val ramCache = 512.binaryMegabyte -val bufferSize = 16.binaryKilobyte +val ramCache = 512.mebibyte +val bufferSize = 16.kibibyte // Unsigned support val rawBytes = 1024uL.byte + +// Measure arrays and collections instantly +val myBuffer = ByteArray(2048) +val bufferCap = myBuffer.kapacity // 2.048 KB ``` ### 2. Math & Operations @@ -57,18 +62,36 @@ Because Kapacity is strictly typed, you can perform math operations directly on val total = 1.gigabyte val downloaded = 250.megabyte +// Standard math operators val remaining = total - downloaded val twiceAsLarge = total * 2 -// Zero-coercion prevents negative data bounds natively! +// Dimensional Division: Dividing a capacity by a capacity returns a Long ratio! +val chunkCount = 10.megabyte / 2.megabyte // Returns 5L + +// Zero-coercion prevents negative data bounds natively val overSubtracted = 10.megabyte - 50.megabyte // Returns 0 Bytes ``` -### 3. File System Integration (Android & iOS) +### 3. Safe Memory Allocation +Need to allocate a buffer based on a capacity? Kapacity provides safe builders that automatically truncate allocations to `Int.MAX_VALUE` (≈ 2.14 GB) to prevent fatal `OutOfMemoryError` or `IllegalArgumentException` crashes on the JVM. + +```kotlin +val targetSize = 16.kilobyte + +// Allocates an exactly sized, zero-initialized ByteArray or UByteArray +val primitiveBuffer = targetSize.toByteArray() +val unsignedBuffer = targetSize.toUByteArray() + +// Safely caps out at Int.MAX_VALUE under the hood +val massiveBuffer = 5.gigabyte.toByteArray() +``` + +### 4. File System Integration (Android & iOS) Kapacity includes platform-specific extensions to instantly measure file sizes directly from the disk or memory. **On Android:** -``` kotlin +```kotlin val myFile = File("/path/to/video.mp4") val capacity = myFile.kapacity @@ -85,7 +108,7 @@ val buffer = NSData.dataWithBytes(...) val bufferCapacity = buffer.kapacity ``` -### 4. Human-Readable UI Formatting +### 5. Human-Readable UI Formatting Kapacity handles localized formatting out of the box. The `toString()` function safely formats the underlying bytes into a readable string (e.g., "1.5 MB"), utilizing localized decimal separators. ``` kotlin diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 3903395..9bcebe7 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -38,8 +38,7 @@ kotlin { implementation(libs.compose.uiToolingPreview) implementation(libs.androidx.lifecycle.viewmodelCompose) implementation(libs.androidx.lifecycle.runtimeCompose) -// implementation(libs.kapacity.io) - implementation(project(":kapacity")) + implementation(libs.kapacity.io) } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1d88812..7c94d3f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kapacity = "0.9.9-beta05" +kapacity = "0.9.9-beta06" agp = "8.11.2" android-compileSdk = "36" android-minSdk = "24" diff --git a/kapacity/src/commonMain/kotlin/io/github/developrofthings/kapacity/Kapacity.kt b/kapacity/src/commonMain/kotlin/io/github/developrofthings/kapacity/Kapacity.kt index 8cb335b..c207bb9 100644 --- a/kapacity/src/commonMain/kotlin/io/github/developrofthings/kapacity/Kapacity.kt +++ b/kapacity/src/commonMain/kotlin/io/github/developrofthings/kapacity/Kapacity.kt @@ -43,6 +43,42 @@ value class Kapacity private constructor(val rawBytes: Long) : Comparable { + capacity / 0.0 + } + } + + @Test + fun testDoubleDivisionByNaNThrowsException() { + val capacity = 10.megabyte + + assertFailsWith { + capacity / Double.NaN + } + } + + @Test + fun testDoubleDivisionByInfinityThrowsException() { + val capacity = 10.megabyte + + assertFailsWith { + capacity / Double.POSITIVE_INFINITY + } + + assertFailsWith { + capacity / Double.NEGATIVE_INFINITY + } + } + //endregion + + //region Float Division Tests + @Test + fun testFloatDivisionHappyPath() { + val capacity = 10.megabyte + val result = capacity / 2.0f + + assertEquals(5.megabyte.rawBytes, result.rawBytes) + } + + @Test + fun testFloatDivisionByZeroThrowsException() { + val capacity = 10.megabyte + + assertFailsWith { + capacity / 0.0f + } + } + + @Test + fun testFloatDivisionByNaNThrowsException() { + val capacity = 10.megabyte + + assertFailsWith { + capacity / Float.NaN + } + } + //endregion +} \ No newline at end of file