| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System.Diagnostics; |
| | | 5 | | using System.Numerics; |
| | | 6 | | using System.Runtime.CompilerServices; |
| | | 7 | | using System.Runtime.InteropServices; |
| | | 8 | | |
| | | 9 | | namespace System.Buffers.Text |
| | | 10 | | { |
| | | 11 | | internal static partial class FormattingHelpers |
| | | 12 | | { |
| | | 13 | | // Based on do_count_digits from https://github.com/fmtlib/fmt/blob/662adf4f33346ba9aba8b072194e319869ede54a/inc |
| | | 14 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 15 | | public static int CountDigits(ulong value) |
| | | 16 | | { |
| | | 17 | | // Map the log2(value) to a power of 10. |
| | 0 | 18 | | ReadOnlySpan<byte> log2ToPow10 = |
| | 0 | 19 | | [ |
| | 0 | 20 | | 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, |
| | 0 | 21 | | 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, |
| | 0 | 22 | | 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15, |
| | 0 | 23 | | 15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20 |
| | 0 | 24 | | ]; |
| | 0 | 25 | | Debug.Assert(log2ToPow10.Length == 64); |
| | | 26 | | |
| | 0 | 27 | | nint elementOffset = log2ToPow10[(int)ulong.Log2(value)]; |
| | | 28 | | |
| | | 29 | | // Read the associated power of 10. |
| | 0 | 30 | | ReadOnlySpan<ulong> powersOf10 = |
| | 0 | 31 | | [ |
| | 0 | 32 | | 0, // unused entry to avoid needing to subtract |
| | 0 | 33 | | 0, |
| | 0 | 34 | | 10, |
| | 0 | 35 | | 100, |
| | 0 | 36 | | 1000, |
| | 0 | 37 | | 10000, |
| | 0 | 38 | | 100000, |
| | 0 | 39 | | 1000000, |
| | 0 | 40 | | 10000000, |
| | 0 | 41 | | 100000000, |
| | 0 | 42 | | 1000000000, |
| | 0 | 43 | | 10000000000, |
| | 0 | 44 | | 100000000000, |
| | 0 | 45 | | 1000000000000, |
| | 0 | 46 | | 10000000000000, |
| | 0 | 47 | | 100000000000000, |
| | 0 | 48 | | 1000000000000000, |
| | 0 | 49 | | 10000000000000000, |
| | 0 | 50 | | 100000000000000000, |
| | 0 | 51 | | 1000000000000000000, |
| | 0 | 52 | | 10000000000000000000, |
| | 0 | 53 | | ]; |
| | 0 | 54 | | Debug.Assert((elementOffset + 1) <= powersOf10.Length); |
| | 0 | 55 | | ulong powerOf10 = Unsafe.Add(ref MemoryMarshal.GetReference(powersOf10), elementOffset); |
| | | 56 | | |
| | | 57 | | // Return the number of digits based on the power of 10, shifted by 1 |
| | | 58 | | // if it falls below the threshold. |
| | 0 | 59 | | int index = (int)elementOffset; |
| | 0 | 60 | | return index - (value < powerOf10 ? 1 : 0); |
| | | 61 | | } |
| | | 62 | | |
| | | 63 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 64 | | public static int CountDigits(uint value) |
| | | 65 | | { |
| | | 66 | | // Algorithm based on https://lemire.me/blog/2021/06/03/computing-the-number-of-digits-of-an-integer-even-fa |
| | 0 | 67 | | ReadOnlySpan<long> table = |
| | 0 | 68 | | [ |
| | 0 | 69 | | 4294967296, |
| | 0 | 70 | | 8589934582, |
| | 0 | 71 | | 8589934582, |
| | 0 | 72 | | 8589934582, |
| | 0 | 73 | | 12884901788, |
| | 0 | 74 | | 12884901788, |
| | 0 | 75 | | 12884901788, |
| | 0 | 76 | | 17179868184, |
| | 0 | 77 | | 17179868184, |
| | 0 | 78 | | 17179868184, |
| | 0 | 79 | | 21474826480, |
| | 0 | 80 | | 21474826480, |
| | 0 | 81 | | 21474826480, |
| | 0 | 82 | | 21474826480, |
| | 0 | 83 | | 25769703776, |
| | 0 | 84 | | 25769703776, |
| | 0 | 85 | | 25769703776, |
| | 0 | 86 | | 30063771072, |
| | 0 | 87 | | 30063771072, |
| | 0 | 88 | | 30063771072, |
| | 0 | 89 | | 34349738368, |
| | 0 | 90 | | 34349738368, |
| | 0 | 91 | | 34349738368, |
| | 0 | 92 | | 34349738368, |
| | 0 | 93 | | 38554705664, |
| | 0 | 94 | | 38554705664, |
| | 0 | 95 | | 38554705664, |
| | 0 | 96 | | 41949672960, |
| | 0 | 97 | | 41949672960, |
| | 0 | 98 | | 41949672960, |
| | 0 | 99 | | 42949672960, |
| | 0 | 100 | | 42949672960, |
| | 0 | 101 | | ]; |
| | 0 | 102 | | Debug.Assert(table.Length == 32, "Every result of uint.Log2(value) needs a long entry in the table."); |
| | | 103 | | |
| | 0 | 104 | | long tableValue = table[(int)uint.Log2(value)]; |
| | 0 | 105 | | return (int)((value + tableValue) >> 32); |
| | | 106 | | } |
| | | 107 | | |
| | | 108 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 109 | | public static int CountHexDigits(ulong value) |
| | | 110 | | { |
| | | 111 | | // The number of hex digits is log16(value) + 1, or log2(value) / 4 + 1 |
| | 0 | 112 | | return (BitOperations.Log2(value) >> 2) + 1; |
| | | 113 | | } |
| | | 114 | | |
| | | 115 | | // Counts the number of trailing '0' digits in a decimal number. |
| | | 116 | | // e.g., value = 0 => retVal = 0, valueWithoutTrailingZeros = 0 |
| | | 117 | | // value = 1234 => retVal = 0, valueWithoutTrailingZeros = 1234 |
| | | 118 | | // value = 320900 => retVal = 2, valueWithoutTrailingZeros = 3209 |
| | | 119 | | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| | | 120 | | public static int CountDecimalTrailingZeros(uint value, out uint valueWithoutTrailingZeros) |
| | | 121 | | { |
| | 0 | 122 | | int zeroCount = 0; |
| | | 123 | | |
| | 0 | 124 | | if (value != 0) |
| | | 125 | | { |
| | 0 | 126 | | while (true) |
| | | 127 | | { |
| | 0 | 128 | | uint temp = value / 10; |
| | 0 | 129 | | if (value != (temp * 10)) |
| | | 130 | | { |
| | | 131 | | break; |
| | | 132 | | } |
| | | 133 | | |
| | 0 | 134 | | value = temp; |
| | 0 | 135 | | zeroCount++; |
| | | 136 | | } |
| | | 137 | | } |
| | | 138 | | |
| | 0 | 139 | | valueWithoutTrailingZeros = value; |
| | 0 | 140 | | return zeroCount; |
| | | 141 | | } |
| | | 142 | | } |
| | | 143 | | } |