From eca2994310894a2d4cde95eacacda89e7464b627 Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Fri, 7 Jun 2024 16:02:52 +0200 Subject: [PATCH 1/2] Simplify evaluation of hex-encoded number's length --- core/common/src/-Util.kt | 7 ++++++ core/common/src/Sinks.kt | 22 +----------------- core/common/test/UtilsTest.kt | 42 +++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 21 deletions(-) create mode 100644 core/common/test/UtilsTest.kt diff --git a/core/common/src/-Util.kt b/core/common/src/-Util.kt index f7bbca2d1..eddccf90a 100644 --- a/core/common/src/-Util.kt +++ b/core/common/src/-Util.kt @@ -171,3 +171,10 @@ internal fun Long.toHexString(): String { return result.concatToString(i, result.size) } + +// v needs to be non zero! +internal inline fun hexNumberLength(v: Long): Int { + val exactWidth = (64 - v.countLeadingZeroBits()) + // Round up to the nearest full byte + return ((exactWidth + 3) / 4) +} diff --git a/core/common/src/Sinks.kt b/core/common/src/Sinks.kt index ac3d193f7..3bb82ff5d 100644 --- a/core/common/src/Sinks.kt +++ b/core/common/src/Sinks.kt @@ -149,27 +149,7 @@ public fun Sink.writeHexadecimalUnsignedLong(long: Long) { return } - // Mask every bit below the most significant bit to a 1 - // http://aggregate.org/MAGIC/#Most%20Significant%201%20Bit - var x = v - x = x or (x ushr 1) - x = x or (x ushr 2) - x = x or (x ushr 4) - x = x or (x ushr 8) - x = x or (x ushr 16) - x = x or (x ushr 32) - - // Count the number of 1s - // http://aggregate.org/MAGIC/#Population%20Count%20(Ones%20Count) - x -= x ushr 1 and 0x5555555555555555 - x = (x ushr 2 and 0x3333333333333333) + (x and 0x3333333333333333) - x = (x ushr 4) + x and 0x0f0f0f0f0f0f0f0f - x += x ushr 8 - x += x ushr 16 - x = (x and 0x3f) + ((x ushr 32) and 0x3f) - - // Round up to the nearest full byte - val width = ((x + 3) / 4).toInt() + val width = hexNumberLength(v) writeToInternalBuffer { buffer -> val tail = buffer.writableSegment(width) diff --git a/core/common/test/UtilsTest.kt b/core/common/test/UtilsTest.kt new file mode 100644 index 000000000..6f30a787d --- /dev/null +++ b/core/common/test/UtilsTest.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. + */ + +package kotlinx.io + +import kotlin.test.Test +import kotlin.test.assertEquals + +class UtilsTest { + @Test + fun hexNumberLength() { + val num2length: Map = mapOf( + 0x1L to 1, + 0x10L to 2, + 0x100L to 3, + 0x1000L to 4, + 0x10000L to 5, + 0x100000L to 6, + 0x1000000L to 7, + 0x10000000L to 8, + 0x100000000L to 9, + 0x1000000000L to 10, + 0x10000000000L to 11, + 0x100000000000L to 12, + 0x1000000000000L to 13, + 0x10000000000000L to 14, + 0x100000000000000L to 15, + 0x1000000000000000L to 16, + -1L to 16, + 0x3fL to 2, + 0x7fL to 2, + 0xffL to 2, + 0L to 0 // although, it's not how it should work + ) + + num2length.forEach { (num, length) -> + assertEquals(length, hexNumberLength(num), "Wrong length for 0x${num.toString(16)}") + } + } +} From c9a779a41771dc9854842441cfaef0f42d5debfe Mon Sep 17 00:00:00 2001 From: Filipp Zhinkin Date: Tue, 11 Jun 2024 16:25:15 +0200 Subject: [PATCH 2/2] Clean up the code, fixed the value returned for `0L` --- core/common/src/-Util.kt | 11 ++++++++--- core/common/test/UtilsTest.kt | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/core/common/src/-Util.kt b/core/common/src/-Util.kt index eddccf90a..01848f02e 100644 --- a/core/common/src/-Util.kt +++ b/core/common/src/-Util.kt @@ -172,9 +172,14 @@ internal fun Long.toHexString(): String { return result.concatToString(i, result.size) } -// v needs to be non zero! +/** + * Returns the number of characters required to encode [v] + * as a hexadecimal number without leading zeros (with `v == 0L` being the only exception, + * `hexNumberLength(0) == 1`). + */ internal inline fun hexNumberLength(v: Long): Int { - val exactWidth = (64 - v.countLeadingZeroBits()) - // Round up to the nearest full byte + if (v == 0L) return 1 + val exactWidth = (Long.SIZE_BITS - v.countLeadingZeroBits()) + // Round up to the nearest full nibble return ((exactWidth + 3) / 4) } diff --git a/core/common/test/UtilsTest.kt b/core/common/test/UtilsTest.kt index 6f30a787d..392dc7baf 100644 --- a/core/common/test/UtilsTest.kt +++ b/core/common/test/UtilsTest.kt @@ -32,7 +32,7 @@ class UtilsTest { 0x3fL to 2, 0x7fL to 2, 0xffL to 2, - 0L to 0 // although, it's not how it should work + 0L to 1 ) num2length.forEach { (num, length) ->