1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! Common utility functions for CUID generation

// =============================================================================
// UTILITY FUNCTIONS
// =============================================================================

// Construcing Base36 Values
// =========================

/// Converts any number representable as a u128 into a base36 String.
///
/// Benchmarking has shown this function to be faster than anything I've been
/// able to find in a library.
pub fn to_base_36<N: Into<u128>>(number: N) -> String {
    const RADIX: u32 = 36;
    let mut number = number.into();

    // If the number is less than the radix, it can be represented by a single
    // char. Just push that char and return.
    if number < RADIX as u128 {
        return char::from_digit(number as u32, RADIX)
            // Panic safety: we just checked that `number < RADIX`. RADIX is a
            // constant (36). `char::from_digit()` only fails if the number is
            // not a valid digit in the specified radix (and it panics if the
            // radix is greater than 36). Since we're working with base 36, we
            // know that 0-35 are valid numbers, and we know that `number` is
            // from 0-35.
            .expect("35 and under is always valid")
            .to_string();
    }

    // Allocate a string with the appropriate length for the result.
    //
    // Number of digits from n in base10 to base36 is log36(n) + 1.
    //
    // u128::MAX.log(36).trunc() is ~24, so allocating for 25 chars should always
    // be enough to avoid reallocation.
    let mut buffer = String::with_capacity(25);

    while number > 0 {
        buffer.push(
            char::from_digit((number % RADIX as u128) as u32, RADIX)
                // Panic safety: the result of the modulo operation x % y on two
                // numbers x and y must always be a number in the range [0, y).
                // `char::from_digit()` here is therefore safe for the same
                // reasons it is above: we know that any number modulo RADIX
                // must be a number from 0 through (RADIX - 1). We know that
                // RADIX is 36 (a constant). And so we know that
                // `char::from_digit()` will not return an error.
                .expect("Modulo radix always yields a valid number"),
        );
        number /= RADIX as u128;
    }

    // SAFETY: .as_mut_vec() is unsafe because it allows inserting bytes that
    // are not valid UTF-8. We are not inserting any bytes, just reversing the
    // string, so this is safe.
    unsafe {
        // We reverse here so that we can push to the back of the string
        // prior to this, which is faster than pushing to the front of the string
        buffer.as_mut_vec().reverse();
    }

    buffer
}

/// Trait for types that can be converted to base 36.
pub trait ToBase36 {
    fn to_base_36(self) -> String;
}

/// Blanket impl for ToBase36 for anything that can be converted to a u128.
impl<N> ToBase36 for N
where
    N: Into<u128>,
{
    fn to_base_36(self) -> String {
        to_base_36(self)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use proptest::prelude::*;

    proptest! {
        #[test]
        fn doesnt_panic(n: u128) {
            to_base_36(n);
        }

        #[test]
        fn expected_output(n: u128) {
            let val = to_base_36(n);
            assert_eq!(
                &format!("{}", radix_fmt::radix_36(n)),
                &val,
            );
            assert_eq!(
                &num::bigint::BigUint::from_bytes_be(&n.to_be_bytes()).to_str_radix(36),
                &val
            )
        }
    }
}