const_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 = const_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> AsRef<str> for Buffer<N, PREFIX> {
52 #[inline]
53 fn as_ref(&self) -> &str {
54 self.as_str()
55 }
56}
57
58impl<const N: usize, const PREFIX: bool> Buffer<N, PREFIX> {
59 /// The length of the buffer in bytes.
60 pub const LEN: usize = (N + PREFIX as usize) * 2;
61
62 const ASSERT_SIZE: () = assert!(core::mem::size_of::<Self>() == 2 + N * 2, "invalid size");
63 const ASSERT_ALIGNMENT: () = assert!(core::mem::align_of::<Self>() == 1, "invalid alignment");
64
65 /// This is a cheap operation; you don't need to worry about reusing buffers
66 /// for efficiency.
67 #[inline]
68 pub const fn new() -> Self {
69 let () = Self::ASSERT_SIZE;
70 let () = Self::ASSERT_ALIGNMENT;
71 Self {
72 prefix: if PREFIX { [b'0', b'x'] } else { [0, 0] },
73 bytes: [[0; 2]; N],
74 }
75 }
76
77 /// Print an array of bytes into this buffer.
78 #[inline]
79 pub const fn const_format(self, array: &[u8; N]) -> Self {
80 self.const_format_inner::<false>(array)
81 }
82
83 /// Print an array of bytes into this buffer.
84 #[inline]
85 pub const fn const_format_upper(self, array: &[u8; N]) -> Self {
86 self.const_format_inner::<true>(array)
87 }
88
89 /// Same as `encode_to_slice_inner`, but const-stable.
90 const fn const_format_inner<const UPPER: bool>(mut self, array: &[u8; N]) -> Self {
91 let mut i = 0;
92 while i < N {
93 let (high, low) = byte2hex::<UPPER>(array[i]);
94 self.bytes[i][0] = high;
95 self.bytes[i][1] = low;
96 i += 1;
97 }
98 self
99 }
100
101 /// Print an array of bytes into this buffer and return a reference to its
102 /// *lower* hex string representation within the buffer.
103 #[inline]
104 pub fn format(&mut self, array: &[u8; N]) -> &mut str {
105 // length of array is guaranteed to be N.
106 self.format_inner::<false>(array)
107 }
108
109 /// Print an array of bytes into this buffer and return a reference to its
110 /// *upper* hex string representation within the buffer.
111 #[inline]
112 pub fn format_upper(&mut self, array: &[u8; N]) -> &mut str {
113 // length of array is guaranteed to be N.
114 self.format_inner::<true>(array)
115 }
116
117 /// Print a slice of bytes into this buffer and return a reference to its
118 /// *lower* hex string representation within the buffer.
119 ///
120 /// # Panics
121 ///
122 /// If the slice is not exactly `N` bytes long.
123 #[track_caller]
124 #[inline]
125 pub fn format_slice<T: AsRef<[u8]>>(&mut self, slice: T) -> &mut str {
126 self.format_slice_inner::<false>(slice.as_ref())
127 }
128
129 /// Print a slice of bytes into this buffer and return a reference to its
130 /// *upper* hex string representation within the buffer.
131 ///
132 /// # Panics
133 ///
134 /// If the slice is not exactly `N` bytes long.
135 #[track_caller]
136 #[inline]
137 pub fn format_slice_upper<T: AsRef<[u8]>>(&mut self, slice: T) -> &mut str {
138 self.format_slice_inner::<true>(slice.as_ref())
139 }
140
141 // Checks length
142 #[track_caller]
143 fn format_slice_inner<const UPPER: bool>(&mut self, slice: &[u8]) -> &mut str {
144 assert_eq!(slice.len(), N, "length mismatch");
145 self.format_inner::<UPPER>(slice)
146 }
147
148 // Doesn't check length
149 #[inline]
150 fn format_inner<const UPPER: bool>(&mut self, input: &[u8]) -> &mut str {
151 // SAFETY: Length was checked previously;
152 // we only write only ASCII bytes.
153 unsafe {
154 let buf = self.as_mut_bytes();
155 let output = buf.get_unchecked_mut(PREFIX as usize * 2..);
156 imp::encode::<UPPER>(input, output);
157 str::from_utf8_unchecked_mut(buf)
158 }
159 }
160
161 /// Copies `self` into a new owned `String`.
162 #[cfg(feature = "alloc")]
163 #[inline]
164 #[allow(clippy::inherent_to_string)] // this is intentional
165 pub fn to_string(&self) -> String {
166 // SAFETY: The buffer always contains valid UTF-8.
167 unsafe { String::from_utf8_unchecked(self.as_bytes().to_vec()) }
168 }
169
170 /// Returns a reference to the underlying bytes casted to a string slice.
171 #[inline]
172 pub const fn as_str(&self) -> &str {
173 // SAFETY: The buffer always contains valid UTF-8.
174 unsafe { str::from_utf8_unchecked(self.as_bytes()) }
175 }
176
177 /// Returns a mutable reference to the underlying bytes casted to a string
178 /// slice.
179 #[inline]
180 pub fn as_mut_str(&mut self) -> &mut str {
181 // SAFETY: The buffer always contains valid UTF-8.
182 unsafe { str::from_utf8_unchecked_mut(self.as_mut_bytes()) }
183 }
184
185 /// Copies `self` into a new `Vec`.
186 #[cfg(feature = "alloc")]
187 #[inline]
188 pub fn to_vec(&self) -> Vec<u8> {
189 self.as_bytes().to_vec()
190 }
191
192 /// Returns a reference the underlying stack-allocated byte array.
193 ///
194 /// # Panics
195 ///
196 /// If `LEN` does not equal `Self::LEN`.
197 ///
198 /// This is panic is evaluated at compile-time if the `nightly` feature
199 /// is enabled, as inline `const` blocks are currently unstable.
200 ///
201 /// See Rust tracking issue [#76001](https://github.com/rust-lang/rust/issues/76001).
202 #[inline]
203 pub const fn as_byte_array<const LEN: usize>(&self) -> &[u8; LEN] {
204 maybe_const_assert!(LEN == Self::LEN, "`LEN` must be equal to `Self::LEN`");
205 // SAFETY: [u16; N] is layout-compatible with [u8; N * 2].
206 unsafe { &*self.as_ptr().cast::<[u8; LEN]>() }
207 }
208
209 /// Returns a mutable reference the underlying stack-allocated byte array.
210 ///
211 /// # Panics
212 ///
213 /// If `LEN` does not equal `Self::LEN`.
214 ///
215 /// See [`as_byte_array`](Buffer::as_byte_array) for more information.
216 #[inline]
217 pub fn as_mut_byte_array<const LEN: usize>(&mut self) -> &mut [u8; LEN] {
218 maybe_const_assert!(LEN == Self::LEN, "`LEN` must be equal to `Self::LEN`");
219 // SAFETY: [u16; N] is layout-compatible with [u8; N * 2].
220 unsafe { &mut *self.as_mut_ptr().cast::<[u8; LEN]>() }
221 }
222
223 /// Returns a reference to the underlying bytes.
224 #[inline]
225 pub const fn as_bytes(&self) -> &[u8] {
226 // SAFETY: [u16; N] is layout-compatible with [u8; N * 2].
227 unsafe { slice::from_raw_parts(self.as_ptr(), Self::LEN) }
228 }
229
230 /// Returns a mutable reference to the underlying bytes.
231 ///
232 /// # Safety
233 ///
234 /// The caller must ensure that the content of the slice is valid UTF-8
235 /// before the borrow ends and the underlying `str` is used.
236 ///
237 /// Use of a `str` whose contents are not valid UTF-8 is undefined behavior.
238 #[inline]
239 pub unsafe fn as_mut_bytes(&mut self) -> &mut [u8] {
240 // SAFETY: [u16; N] is layout-compatible with [u8; N * 2].
241 unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), Self::LEN) }
242 }
243
244 /// Returns a mutable reference to the underlying buffer, excluding the prefix.
245 ///
246 /// # Safety
247 ///
248 /// See [`as_mut_bytes`](Buffer::as_mut_bytes).
249 #[inline]
250 pub unsafe fn buffer(&mut self) -> &mut [u8] {
251 unsafe { slice::from_raw_parts_mut(self.bytes.as_mut_ptr().cast(), N * 2) }
252 }
253
254 /// Returns a raw pointer to the buffer.
255 ///
256 /// The caller must ensure that the buffer outlives the pointer this
257 /// function returns, or else it will end up pointing to garbage.
258 #[inline]
259 pub const fn as_ptr(&self) -> *const u8 {
260 unsafe { (self as *const Self).cast::<u8>().add(!PREFIX as usize * 2) }
261 }
262
263 /// Returns an unsafe mutable pointer to the slice's buffer.
264 ///
265 /// The caller must ensure that the slice outlives the pointer this
266 /// function returns, or else it will end up pointing to garbage.
267 #[inline]
268 pub fn as_mut_ptr(&mut self) -> *mut u8 {
269 unsafe { (self as *mut Self).cast::<u8>().add(!PREFIX as usize * 2) }
270 }
271}