lowercase_hex/
buffer.rs

1use crate::{byte2hex, imp};
2use core::fmt;
3use core::slice;
4use core::str;
5
6#[cfg(feature = "alloc")]
7#[allow(unused_imports)]
8use alloc::{string::String, vec::Vec};
9
10/// A correctly sized stack allocation for the formatted bytes to be written
11/// into.
12///
13/// `N` is the amount of bytes of the input, while `PREFIX` specifies whether
14/// the "0x" prefix is prepended to the output.
15///
16/// Note that this buffer will contain only the prefix, if specified, and null
17/// ('\0') bytes before any formatting is done.
18///
19/// # Examples
20///
21/// ```
22/// let mut buffer = lowercase_hex::Buffer::<4>::new();
23/// let printed = buffer.format(b"1234");
24/// assert_eq!(printed, "31323334");
25/// ```
26#[must_use]
27#[repr(C)]
28#[derive(Clone)]
29pub struct Buffer<const N: usize, const PREFIX: bool = false> {
30    // Workaround for Rust issue #76560:
31    // https://github.com/rust-lang/rust/issues/76560
32    // This would ideally be `[u8; (N + PREFIX as usize) * 2]`
33    prefix: [u8; 2],
34    bytes: [[u8; 2]; N],
35}
36
37impl<const N: usize, const PREFIX: bool> Default for Buffer<N, PREFIX> {
38    #[inline]
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44impl<const N: usize, const PREFIX: bool> fmt::Debug for Buffer<N, PREFIX> {
45    #[inline]
46    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47        f.debug_tuple("Buffer").field(&self.as_str()).finish()
48    }
49}
50
51impl<const N: usize, const PREFIX: bool> Buffer<N, PREFIX> {
52    /// The length of the buffer in bytes.
53    pub const LEN: usize = (N + PREFIX as usize) * 2;
54
55    const ASSERT_SIZE: () = assert!(core::mem::size_of::<Self>() == 2 + N * 2, "invalid size");
56    const ASSERT_ALIGNMENT: () = assert!(core::mem::align_of::<Self>() == 1, "invalid alignment");
57
58    /// This is a cheap operation; you don't need to worry about reusing buffers
59    /// for efficiency.
60    #[inline]
61    pub const fn new() -> Self {
62        let () = Self::ASSERT_SIZE;
63        let () = Self::ASSERT_ALIGNMENT;
64        Self {
65            prefix: if PREFIX { [b'0', b'x'] } else { [0, 0] },
66            bytes: [[0; 2]; N],
67        }
68    }
69
70    /// Print an array of bytes into this buffer.
71    #[inline]
72    pub const fn const_format(self, array: &[u8; N]) -> Self {
73        self.const_format_inner(array)
74    }
75
76    /// Same as `encode_to_slice_inner`, but const-stable.
77    const fn const_format_inner(mut self, array: &[u8; N]) -> Self {
78        let mut i = 0;
79        while i < N {
80            let (high, low) = byte2hex(array[i]);
81            self.bytes[i][0] = high;
82            self.bytes[i][1] = low;
83            i += 1;
84        }
85        self
86    }
87
88    /// Print an array of bytes into this buffer and return a reference to its
89    /// hex string representation within the buffer.
90    #[inline]
91    pub fn format(&mut self, array: &[u8; N]) -> &mut str {
92        // length of array is guaranteed to be N.
93        self.format_inner(array)
94    }
95
96    /// Print a slice of bytes into this buffer and return a reference to its
97    /// hex string representation within the buffer.
98    ///
99    /// # Panics
100    ///
101    /// If the slice is not exactly `N` bytes long.
102    #[track_caller]
103    #[inline]
104    pub fn format_slice<T: AsRef<[u8]>>(&mut self, slice: T) -> &mut str {
105        self.format_slice_inner(slice.as_ref())
106    }
107
108    // Checks length
109    #[track_caller]
110    fn format_slice_inner(&mut self, slice: &[u8]) -> &mut str {
111        assert_eq!(slice.len(), N, "length mismatch");
112        self.format_inner(slice)
113    }
114
115    // Doesn't check length
116    #[inline]
117    fn format_inner(&mut self, input: &[u8]) -> &mut str {
118        // SAFETY: Length was checked previously;
119        // we only write only ASCII bytes.
120        unsafe {
121            let buf = self.as_mut_bytes();
122            let output = buf.as_mut_ptr().add(PREFIX as usize * 2);
123            imp::encode(input, output);
124            str::from_utf8_unchecked_mut(buf)
125        }
126    }
127
128    /// Copies `self` into a new owned `String`.
129    #[cfg(feature = "alloc")]
130    #[inline]
131    #[allow(clippy::inherent_to_string)] // this is intentional
132    pub fn to_string(&self) -> String {
133        // SAFETY: The buffer always contains valid UTF-8.
134        unsafe { String::from_utf8_unchecked(self.as_bytes().to_vec()) }
135    }
136
137    /// Returns a reference to the underlying bytes casted to a string slice.
138    #[inline]
139    pub const fn as_str(&self) -> &str {
140        // SAFETY: The buffer always contains valid UTF-8.
141        unsafe { str::from_utf8_unchecked(self.as_bytes()) }
142    }
143
144    /// Returns a mutable reference to the underlying bytes casted to a string
145    /// slice.
146    #[inline]
147    pub fn as_mut_str(&mut self) -> &mut str {
148        // SAFETY: The buffer always contains valid UTF-8.
149        unsafe { str::from_utf8_unchecked_mut(self.as_mut_bytes()) }
150    }
151
152    /// Copies `self` into a new `Vec`.
153    #[cfg(feature = "alloc")]
154    #[inline]
155    pub fn to_vec(&self) -> Vec<u8> {
156        self.as_bytes().to_vec()
157    }
158
159    /// Returns a reference the underlying stack-allocated byte array.
160    ///
161    /// # Panics
162    ///
163    /// If `LEN` does not equal `Self::LEN`.
164    ///
165    /// This is panic is evaluated at compile-time if the `nightly` feature
166    /// is enabled, as inline `const` blocks are currently unstable.
167    ///
168    /// See Rust tracking issue [#76001](https://github.com/rust-lang/rust/issues/76001).
169    #[inline]
170    pub const fn as_byte_array<const LEN: usize>(&self) -> &[u8; LEN] {
171        maybe_const_assert!(LEN == Self::LEN, "`LEN` must be equal to `Self::LEN`");
172        // SAFETY: [u16; N] is layout-compatible with [u8; N * 2].
173        unsafe { &*self.as_ptr().cast::<[u8; LEN]>() }
174    }
175
176    /// Returns a mutable reference the underlying stack-allocated byte array.
177    ///
178    /// # Panics
179    ///
180    /// If `LEN` does not equal `Self::LEN`.
181    ///
182    /// See [`as_byte_array`](Buffer::as_byte_array) for more information.
183    #[inline]
184    pub fn as_mut_byte_array<const LEN: usize>(&mut self) -> &mut [u8; LEN] {
185        maybe_const_assert!(LEN == Self::LEN, "`LEN` must be equal to `Self::LEN`");
186        // SAFETY: [u16; N] is layout-compatible with [u8; N * 2].
187        unsafe { &mut *self.as_mut_ptr().cast::<[u8; LEN]>() }
188    }
189
190    /// Returns a reference to the underlying bytes.
191    #[inline]
192    pub const fn as_bytes(&self) -> &[u8] {
193        // SAFETY: [u16; N] is layout-compatible with [u8; N * 2].
194        unsafe { slice::from_raw_parts(self.as_ptr(), Self::LEN) }
195    }
196
197    /// Returns a mutable reference to the underlying bytes.
198    ///
199    /// # Safety
200    ///
201    /// The caller must ensure that the content of the slice is valid UTF-8
202    /// before the borrow ends and the underlying `str` is used.
203    ///
204    /// Use of a `str` whose contents are not valid UTF-8 is undefined behavior.
205    #[inline]
206    pub unsafe fn as_mut_bytes(&mut self) -> &mut [u8] {
207        // SAFETY: [u16; N] is layout-compatible with [u8; N * 2].
208        unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), Self::LEN) }
209    }
210
211    /// Returns a mutable reference to the underlying buffer, excluding the prefix.
212    ///
213    /// # Safety
214    ///
215    /// See [`as_mut_bytes`](Buffer::as_mut_bytes).
216    #[inline]
217    pub unsafe fn buffer(&mut self) -> &mut [u8] {
218        unsafe { slice::from_raw_parts_mut(self.bytes.as_mut_ptr().cast(), N * 2) }
219    }
220
221    /// Returns a raw pointer to the buffer.
222    ///
223    /// The caller must ensure that the buffer outlives the pointer this
224    /// function returns, or else it will end up pointing to garbage.
225    #[inline]
226    pub const fn as_ptr(&self) -> *const u8 {
227        unsafe { (self as *const Self).cast::<u8>().add(!PREFIX as usize * 2) }
228    }
229
230    /// Returns an unsafe mutable pointer to the slice's buffer.
231    ///
232    /// The caller must ensure that the slice outlives the pointer this
233    /// function returns, or else it will end up pointing to garbage.
234    #[inline]
235    pub fn as_mut_ptr(&mut self) -> *mut u8 {
236        unsafe { (self as *mut Self).cast::<u8>().add(!PREFIX as usize * 2) }
237    }
238}