Skip to content

Commit

Permalink
Update Xof.Utils with ability to pass 32 bit numbers (#88)
Browse files Browse the repository at this point in the history
  • Loading branch information
05nelsonm authored Jan 2, 2025
1 parent 7eeb576 commit c17f0e9
Show file tree
Hide file tree
Showing 12 changed files with 159 additions and 41 deletions.
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
[![badge-latest-release]][url-latest-release]

[![badge-kotlin]][url-kotlin]
[![badge-endians]][url-endians]

![badge-platform-android]
![badge-platform-jvm]
Expand Down Expand Up @@ -242,7 +241,6 @@ dependencies {

<!-- TAG_DEPENDENCIES -->
[badge-kotlin]: https://img.shields.io/badge/kotlin-1.9.24-blue.svg?logo=kotlin
[badge-endians]: https://img.shields.io/badge/kotlincrypto.endians-0.3.1-blue.svg

<!-- TAG_PLATFORMS -->
[badge-platform-android]: http://img.shields.io/badge/-android-6EDB8D.svg?style=flat
Expand All @@ -266,7 +264,6 @@ dependencies {
[url-license]: https://www.apache.org/licenses/LICENSE-2.0.txt
[url-kotlin]: https://kotlinlang.org
[url-kotlin-crypto]: https://github.com/KotlinCrypto
[url-endians]: https://github.com/KotlinCrypto/endians
[url-hash]: https://github.com/KotlinCrypto/hash
[url-macs]: https://github.com/KotlinCrypto/MACs
[url-version-catalog]: https://github.com/KotlinCrypto/version-catalog
Expand Down
3 changes: 1 addition & 2 deletions benchmarks/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# benchmarks

Benchmarks for tracking performance of `core` implementation. Currently, the only
thing worth benchmarking is the `Digest` implementation.
Benchmarks for tracking performance of `core` implementation.

**NOTE:** Benchmarking is run on every Pull Request. Results can be viewed for each
workflow run on the [GitHub Actions][url-actions] tab of the repository.
Expand Down
1 change: 1 addition & 0 deletions benchmarks/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ kmpConfiguration {
dependencies {
implementation(libs.benchmark.runtime)
implementation(project(":library:digest"))
implementation(project(":library:xof"))
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2025 Matthew Nelson
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package org.kotlincrypto.core.benchmarks

import kotlinx.benchmark.*
import org.kotlincrypto.core.InternalKotlinCryptoApi
import org.kotlincrypto.core.xof.Xof
import kotlin.random.Random

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(BenchmarkTimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 2)
@Measurement(iterations = 5, time = 3)
@OptIn(InternalKotlinCryptoApi::class)
open class XofUtilsBenchmark {

private val longs: LongArray = LongArray(10) { Random.Default.nextLong() }
private val loHi = Array(longs.size) { longs[it].let { l -> l.toInt() to l.rotateLeft(32).toInt() } }

@Benchmark
fun leftEncodeLongs() {
val longs = longs
longs.forEach { long -> Xof.Utils.leftEncode(long) }
}

@Benchmark
fun leftEncodeLoHi() {
val loHi = loHi
loHi.forEach { Xof.Utils.leftEncode(it.first, it.second) }
}
}
4 changes: 0 additions & 4 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,11 @@ gradle-kmp-configuration = "0.3.2"
gradle-kotlin = "1.9.24"
gradle-publish-maven = "0.29.0"

kotlincrypto-endians = "0.3.1"

[libraries]
gradle-kmp-configuration = { module = "io.matthewnelson:gradle-kmp-configuration-plugin", version.ref = "gradle-kmp-configuration" }
gradle-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "gradle-kotlin" }
gradle-publish-maven = { module = "com.vanniktech:gradle-maven-publish-plugin", version.ref = "gradle-publish-maven" }

kotlincrypto-endians-endians = { module = "org.kotlincrypto.endians:endians", version.ref = "kotlincrypto-endians" }

# tests & tools
androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidx-test-runner" }
benchmark-runtime = { module = "org.jetbrains.kotlinx:kotlinx-benchmark-runtime", version.ref = "gradle-benchmark" }
Expand Down
4 changes: 4 additions & 0 deletions library/xof/api/xof.api
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ public abstract class org/kotlincrypto/core/xof/Xof$Reader {

public final class org/kotlincrypto/core/xof/Xof$Utils {
public static final field INSTANCE Lorg/kotlincrypto/core/xof/Xof$Utils;
public static final fun leftEncode (I)[B
public static final fun leftEncode (II)[B
public static final fun leftEncode (J)[B
public static final fun rightEncode (I)[B
public static final fun rightEncode (II)[B
public static final fun rightEncode (J)[B
}

Expand Down
4 changes: 4 additions & 0 deletions library/xof/api/xof.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ sealed class <#A: org.kotlincrypto.core.xof/XofAlgorithm> org.kotlincrypto.core.
}

final object Utils { // org.kotlincrypto.core.xof/Xof.Utils|null[0]
final fun leftEncode(kotlin/Int): kotlin/ByteArray // org.kotlincrypto.core.xof/Xof.Utils.leftEncode|leftEncode(kotlin.Int){}[0]
final fun leftEncode(kotlin/Int, kotlin/Int): kotlin/ByteArray // org.kotlincrypto.core.xof/Xof.Utils.leftEncode|leftEncode(kotlin.Int;kotlin.Int){}[0]
final fun leftEncode(kotlin/Long): kotlin/ByteArray // org.kotlincrypto.core.xof/Xof.Utils.leftEncode|leftEncode(kotlin.Long){}[0]
final fun rightEncode(kotlin/Int): kotlin/ByteArray // org.kotlincrypto.core.xof/Xof.Utils.rightEncode|rightEncode(kotlin.Int){}[0]
final fun rightEncode(kotlin/Int, kotlin/Int): kotlin/ByteArray // org.kotlincrypto.core.xof/Xof.Utils.rightEncode|rightEncode(kotlin.Int;kotlin.Int){}[0]
final fun rightEncode(kotlin/Long): kotlin/ByteArray // org.kotlincrypto.core.xof/Xof.Utils.rightEncode|rightEncode(kotlin.Long){}[0]
}
}
1 change: 0 additions & 1 deletion library/xof/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ kmpConfiguration {
common {
sourceSetMain {
dependencies {
implementation(libs.kotlincrypto.endians.endians)
api(project(":library:core"))
}
}
Expand Down
83 changes: 57 additions & 26 deletions library/xof/src/commonMain/kotlin/org/kotlincrypto/core/xof/Xof.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package org.kotlincrypto.core.xof

import org.kotlincrypto.core.*
import org.kotlincrypto.endians.BigEndian.Companion.toBigEndian
import kotlin.jvm.JvmName
import kotlin.jvm.JvmOverloads
import kotlin.jvm.JvmStatic
Expand Down Expand Up @@ -204,50 +203,82 @@ public sealed class Xof<A: XofAlgorithm>: Algorithm, Copyable<Xof<A>>, Resettabl
public object Utils {

@JvmStatic
public fun leftEncode(value: Long): ByteArray {
// If it's zero, return early with [1, 0]
if (value == 0L) return ByteArray(2).apply { this[0] = 1 }

val be = value.toBigEndian()
public fun leftEncode(value: Int): ByteArray {
return encode(lo = value, hi = 0, left = true)
}

// Find index of first non-zero byte
var i = 0
while (i < be.size && be[i] == 0.toByte()) {
i++
}
@JvmStatic
public fun leftEncode(value: Long): ByteArray {
val lo = value.toInt()
val hi = value.rotateLeft(32).toInt()
return encode(lo = lo, hi = hi, left = true)
}

val b = ByteArray(be.size - i + 1)
@JvmStatic
public fun leftEncode(lo: Int, hi: Int): ByteArray {
return encode(lo = lo, hi = hi, left = true)
}

// Prepend with number of non-zero bytes
b[0] = (be.size - i).toByte()
@JvmStatic
public fun rightEncode(value: Int): ByteArray {
return encode(lo = value, hi = 0, left = false)
}

be.copyInto(b, 1, i)
@JvmStatic
public fun rightEncode(value: Long): ByteArray {
val lo = value.toInt()
val hi = value.rotateLeft(32).toInt()
return encode(lo = lo, hi = hi, left = false)
}

return b
@JvmStatic
public fun rightEncode(lo: Int, hi: Int): ByteArray {
return encode(lo = lo, hi = hi, left = false)
}

@JvmStatic
public fun rightEncode(value: Long): ByteArray {
// If it's zero, return early with [0, 1]
if (value == 0L) return ByteArray(2).apply { this[1] = 1 }
private fun encode(lo: Int, hi: Int, left: Boolean): ByteArray {
if (lo == 0 && hi == 0) {
// If it's zero, return early
return if (left) byteArrayOf(1, 0) else byteArrayOf(0, 1)
}

val be = value.toBigEndian()
val a = byteArrayOf(
(hi ushr 24).toByte(),
(hi ushr 16).toByte(),
(hi ushr 8).toByte(),
(hi ).toByte(),
(lo ushr 24).toByte(),
(lo ushr 16).toByte(),
(lo ushr 8).toByte(),
(lo ).toByte(),
)

// Find index of first non-zero byte
var i = 0
while (i < be.size && be[i] == 0.toByte()) {
while (i < a.size && a[i] == ZERO) {
i++
}

val b = ByteArray(be.size - i + 1)

// Append with number of non-zero bytes
b[b.lastIndex] = (be.size - i).toByte()
val b = ByteArray(a.size - i + 1)
val num = (a.size - i).toByte()

val offset = if (left) {
// Prepend with number of non-zero bytes
b[0] = num
1
} else {
// Append with number of non-zero bytes
b[b.lastIndex] = num
0
}

be.copyInto(b, 0, i)
a.copyInto(b, offset, i)

return b
}

private const val ZERO: Byte = 0
}

protected abstract fun newReader(): Reader
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,60 @@ import kotlin.test.assertContentEquals
@OptIn(InternalKotlinCryptoApi::class)
class XofUtilsUnitTest {

private sealed class Data private constructor() {
class L(val value: Long): Data() {
override fun leftEncode(): ByteArray = Xof.Utils.leftEncode(value)
override fun rightEncode(): ByteArray = Xof.Utils.rightEncode(value)
}
class I(val value: Int): Data() {
override fun leftEncode(): ByteArray = Xof.Utils.leftEncode(value)
override fun rightEncode(): ByteArray = Xof.Utils.rightEncode(value)
}
class LoHi(val lo: Int, val hi: Int): Data() {
override fun leftEncode(): ByteArray = Xof.Utils.leftEncode(lo = lo, hi = hi)
override fun rightEncode(): ByteArray = Xof.Utils.rightEncode(lo = lo, hi = hi)
}

abstract fun leftEncode(): ByteArray
abstract fun rightEncode(): ByteArray
}

@Test
fun givenValue_whenEncoded_thenIsAsExpected() {
listOf(
Data.L(777711L) to byteArrayOf(3, 11, -35, -17),
Data.L(-777711L) to byteArrayOf(8, -1, -1, -1, -1, -1, -12, 34, 17),
Data.LoHi(-777711, -1) to byteArrayOf(8, -1, -1, -1, -1, -1, -12, 34, 17),
Data.L(555L) to byteArrayOf(2, 2, 43),
Data.I(555) to byteArrayOf(2, 2, 43),
Data.L(Long.MIN_VALUE) to byteArrayOf(8, -128, 0, 0, 0, 0, 0, 0, 0),
Data.L(Long.MAX_VALUE) to byteArrayOf(8, 127, -1, -1, -1, -1, -1, -1, -1),
).forEach { (data, expected) ->
assertContentEquals(expected, data.leftEncode())

// Shift expected for right encoding
var i = 0
while (i < expected.lastIndex) {
val old = expected[i]
val new = expected[i + 1]
expected[i] = new
expected[i + 1] = old
i++
}

assertContentEquals(expected, data.rightEncode())
}
}

@Test
fun givenLeftEncoding_whenValueZero_thenResultIsAsExpected() {
val expected = ByteArray(2).apply { this[0] = 1 }
val actual = Xof.Utils.leftEncode(0L)
assertContentEquals(expected, actual)
assertContentEquals(expected, Xof.Utils.leftEncode(0L))
}

@Test
fun givenRightEncoding_whenValueZero_thenResultIsAsExpected() {
val expected = ByteArray(2).apply { this[1] = 1 }
val actual = Xof.Utils.rightEncode(0L)
assertContentEquals(expected, actual)
assertContentEquals(expected, Xof.Utils.rightEncode(0L))
}
}
1 change: 0 additions & 1 deletion library/xof/src/jvmMain/java9/module-info.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
module org.kotlincrypto.core.xof {
requires kotlin.stdlib;
requires transitive org.kotlincrypto.core;
requires org.kotlincrypto.endians;

exports org.kotlincrypto.core.xof;
}

0 comments on commit c17f0e9

Please sign in to comment.