From 935b3f0e11e9e8684fa92a0c170a7cf4cbdb83b2 Mon Sep 17 00:00:00 2001 From: Egor Andreevich Date: Thu, 14 Nov 2024 12:26:22 -0500 Subject: [PATCH] Improve algorithm to count digits in Long (#1548) --- .../commonMain/kotlin/okio/internal/Buffer.kt | 86 +++++++------------ .../kotlin/okio/CommonBufferedSinkTest.kt | 36 ++++++++ 2 files changed, 65 insertions(+), 57 deletions(-) diff --git a/okio/src/commonMain/kotlin/okio/internal/Buffer.kt b/okio/src/commonMain/kotlin/okio/internal/Buffer.kt index 2270fc1dcc..13194403ee 100644 --- a/okio/src/commonMain/kotlin/okio/internal/Buffer.kt +++ b/okio/src/commonMain/kotlin/okio/internal/Buffer.kt @@ -461,63 +461,7 @@ internal inline fun Buffer.commonWriteDecimalLong(v: Long): Buffer { negative = true } - // Binary search for character width which favors matching lower numbers. - var width = - if (v < 100000000L) { - if (v < 10000L) { - if (v < 100L) { - if (v < 10L) { - 1 - } else { - 2 - } - } else if (v < 1000L) { - 3 - } else { - 4 - } - } else if (v < 1000000L) { - if (v < 100000L) { - 5 - } else { - 6 - } - } else if (v < 10000000L) { - 7 - } else { - 8 - } - } else if (v < 1000000000000L) { - if (v < 10000000000L) { - if (v < 1000000000L) { - 9 - } else { - 10 - } - } else if (v < 100000000000L) { - 11 - } else { - 12 - } - } else if (v < 1000000000000000L) { - if (v < 10000000000000L) { - 13 - } else if (v < 100000000000000L) { - 14 - } else { - 15 - } - } else if (v < 100000000000000000L) { - if (v < 10000000000000000L) { - 16 - } else { - 17 - } - } else if (v < 1000000000000000000L) { - 18 - } else { - 19 - } + var width = countDigitsIn(v) if (negative) { ++width } @@ -539,6 +483,34 @@ internal inline fun Buffer.commonWriteDecimalLong(v: Long): Buffer { return this } +private fun countDigitsIn(v: Long): Int { + val guess = ((64 - v.countLeadingZeroBits()) * 10) ushr 5 + return guess + (if (v > DigitCountToLargestValue[guess]) 1 else 0) +} + +private val DigitCountToLargestValue = longArrayOf( + -1, // Every value has more than 0 digits. + 9L, // For 1 digit (index 1), the largest value is 9. + 99L, + 999L, + 9999L, + 99999L, + 999999L, + 9999999L, + 99999999L, + 999999999L, + 9999999999L, + 99999999999L, + 999999999999L, + 9999999999999L, + 99999999999999L, + 999999999999999L, + 9999999999999999L, + 99999999999999999L, + 999999999999999999L, // For 18 digits (index 18), the largest value is 999999999999999999. + Long.MAX_VALUE, // For 19 digits (index 19), the largest value is MAX_VALUE. +) + internal inline fun Buffer.commonWriteHexadecimalUnsignedLong(v: Long): Buffer { var v = v if (v == 0L) { diff --git a/okio/src/commonTest/kotlin/okio/CommonBufferedSinkTest.kt b/okio/src/commonTest/kotlin/okio/CommonBufferedSinkTest.kt index 08c093a871..e48274c9b3 100644 --- a/okio/src/commonTest/kotlin/okio/CommonBufferedSinkTest.kt +++ b/okio/src/commonTest/kotlin/okio/CommonBufferedSinkTest.kt @@ -264,6 +264,42 @@ class CommonBufferedSinkTest( assertLongDecimalString("10000000000000000", 10000000000000000L) assertLongDecimalString("100000000000000000", 100000000000000000L) assertLongDecimalString("1000000000000000000", 1000000000000000000L) + assertLongDecimalString("-9", -9L) + assertLongDecimalString("-99", -99L) + assertLongDecimalString("-999", -999L) + assertLongDecimalString("-9999", -9999L) + assertLongDecimalString("-99999", -99999L) + assertLongDecimalString("-999999", -999999L) + assertLongDecimalString("-9999999", -9999999L) + assertLongDecimalString("-99999999", -99999999L) + assertLongDecimalString("-999999999", -999999999L) + assertLongDecimalString("-9999999999", -9999999999L) + assertLongDecimalString("-99999999999", -99999999999L) + assertLongDecimalString("-999999999999", -999999999999L) + assertLongDecimalString("-9999999999999", -9999999999999L) + assertLongDecimalString("-99999999999999", -99999999999999L) + assertLongDecimalString("-999999999999999", -999999999999999L) + assertLongDecimalString("-9999999999999999", -9999999999999999L) + assertLongDecimalString("-99999999999999999", -99999999999999999L) + assertLongDecimalString("-999999999999999999", -999999999999999999L) + assertLongDecimalString("-10", -10L) + assertLongDecimalString("-100", -100L) + assertLongDecimalString("-1000", -1000L) + assertLongDecimalString("-10000", -10000L) + assertLongDecimalString("-100000", -100000L) + assertLongDecimalString("-1000000", -1000000L) + assertLongDecimalString("-10000000", -10000000L) + assertLongDecimalString("-100000000", -100000000L) + assertLongDecimalString("-1000000000", -1000000000L) + assertLongDecimalString("-10000000000", -10000000000L) + assertLongDecimalString("-100000000000", -100000000000L) + assertLongDecimalString("-1000000000000", -1000000000000L) + assertLongDecimalString("-10000000000000", -10000000000000L) + assertLongDecimalString("-100000000000000", -100000000000000L) + assertLongDecimalString("-1000000000000000", -1000000000000000L) + assertLongDecimalString("-10000000000000000", -10000000000000000L) + assertLongDecimalString("-100000000000000000", -100000000000000000L) + assertLongDecimalString("-1000000000000000000", -1000000000000000000L) } private fun assertLongDecimalString(string: String, value: Long) {