@@ -95,6 +95,38 @@ pub fn sha256_hash(data: &[u8]) -> String {
95
95
}
96
96
}
97
97
98
+ /// Hex-encode a byte slice into a lowercase ASCII string.
99
+ ///
100
+ /// # Safety
101
+ /// This implementation uses `unsafe` code for performance reasons:
102
+ /// - We call [`String::as_mut_vec`] to get direct access to the
103
+ /// underlying `Vec<u8>` backing the `String`.
104
+ /// - We then use [`set_len`] to pre-allocate the final length without
105
+ /// initializing the contents first.
106
+ /// - Finally, we use [`get_unchecked`] and [`get_unchecked_mut`] to
107
+ /// avoid bounds checking inside the tight encoding loop.
108
+ ///
109
+ /// # Why unsafe is needed
110
+ /// Normally, writing this function with safe Rust requires:
111
+ /// - Pushing each hex digit one-by-one into the string (extra bounds checks).
112
+ /// - Or allocating and copying temporary buffers.
113
+ /// Using `unsafe` avoids redundant checks and makes this implementation
114
+ /// significantly faster, especially for large inputs.
115
+ ///
116
+ /// # Why this is correct
117
+ /// - `s` is allocated with exactly `len * 2` capacity, and we immediately
118
+ /// set its length to that value. Every byte in the string buffer will be
119
+ /// initialized before being read or used.
120
+ /// - The loop index `i` is always in `0..len`, so `bytes.get_unchecked(i)`
121
+ /// is safe.
122
+ /// - Each write goes to positions `j` and `j + 1`, where `j = i * 2`.
123
+ /// Since `i < len`, the maximum write index is `2*len - 1`, which is
124
+ /// within the allocated range.
125
+ /// - All written bytes come from the `LUT` table, which has exactly 16
126
+ /// elements, and indices are masked into the 0–15 range.
127
+ ///
128
+ /// Therefore, although `unsafe` is used to skip bounds checking,
129
+ /// the logic ensures all memory accesses remain in-bounds and initialized.
98
130
pub fn hex_encode ( bytes : & [ u8 ] ) -> String {
99
131
const LUT : & [ u8 ; 16 ] = b"0123456789abcdef" ;
100
132
let len = bytes. len ( ) ;
0 commit comments