/rust/registry/src/index.crates.io-6f17d22bba15001f/powerfmt-0.2.0/src/buf.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! A buffer for constructing a string while avoiding heap allocation. |
2 | | |
3 | | use core::hash::{Hash, Hasher}; |
4 | | use core::mem::MaybeUninit; |
5 | | use core::{fmt, str}; |
6 | | |
7 | | use crate::smart_display::{FormatterOptions, Metadata, SmartDisplay}; |
8 | | |
9 | | /// A buffer for construct a string while avoiding heap allocation. |
10 | | /// |
11 | | /// The only requirement is that the buffer is large enough to hold the formatted string. |
12 | | pub struct WriteBuffer<const SIZE: usize> { |
13 | | buf: [MaybeUninit<u8>; SIZE], |
14 | | len: usize, |
15 | | } |
16 | | |
17 | | impl<const SIZE: usize> fmt::Debug for WriteBuffer<SIZE> { |
18 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
19 | 0 | f.debug_struct("DisplayBuffer") |
20 | 0 | .field("buf", &self.as_str()) |
21 | 0 | .field("remaining_capacity", &self.remaining_capacity()) |
22 | 0 | .finish() |
23 | 0 | } |
24 | | } |
25 | | |
26 | | impl<const SIZE: usize> WriteBuffer<SIZE> { |
27 | | /// Creates an empty buffer. |
28 | 0 | pub const fn new() -> Self { |
29 | 0 | Self { |
30 | 0 | buf: maybe_uninit_uninit_array::<_, SIZE>(), |
31 | 0 | len: 0, |
32 | 0 | } |
33 | 0 | } |
34 | | |
35 | | /// Obtain the contents of the buffer as a string. |
36 | 0 | pub fn as_str(&self) -> &str { |
37 | 0 | self |
38 | 0 | } |
39 | | |
40 | | /// Determine how many bytes are remaining in the buffer. |
41 | 0 | pub const fn remaining_capacity(&self) -> usize { |
42 | 0 | SIZE - self.len |
43 | 0 | } |
44 | | } |
45 | | |
46 | | impl<const SIZE: usize> Default for WriteBuffer<SIZE> { |
47 | 0 | fn default() -> Self { |
48 | 0 | Self::new() |
49 | 0 | } |
50 | | } |
51 | | |
52 | | impl<const LEFT_SIZE: usize, const RIGHT_SIZE: usize> PartialOrd<WriteBuffer<RIGHT_SIZE>> |
53 | | for WriteBuffer<LEFT_SIZE> |
54 | | { |
55 | 0 | fn partial_cmp(&self, other: &WriteBuffer<RIGHT_SIZE>) -> Option<core::cmp::Ordering> { |
56 | 0 | self.as_str().partial_cmp(other.as_str()) |
57 | 0 | } |
58 | | } |
59 | | |
60 | | impl<const LEFT_SIZE: usize, const RIGHT_SIZE: usize> PartialEq<WriteBuffer<RIGHT_SIZE>> |
61 | | for WriteBuffer<LEFT_SIZE> |
62 | | { |
63 | 0 | fn eq(&self, other: &WriteBuffer<RIGHT_SIZE>) -> bool { |
64 | 0 | self.as_str() == other.as_str() |
65 | 0 | } |
66 | | } |
67 | | |
68 | | impl<const SIZE: usize> Eq for WriteBuffer<SIZE> {} |
69 | | |
70 | | impl<const SIZE: usize> Ord for WriteBuffer<SIZE> { |
71 | 0 | fn cmp(&self, other: &Self) -> core::cmp::Ordering { |
72 | 0 | self.as_str().cmp(other.as_str()) |
73 | 0 | } |
74 | | } |
75 | | |
76 | | impl<const SIZE: usize> Hash for WriteBuffer<SIZE> { |
77 | 0 | fn hash<H: Hasher>(&self, state: &mut H) { |
78 | 0 | self.as_str().hash(state) |
79 | 0 | } |
80 | | } |
81 | | |
82 | | impl<const SIZE: usize> AsRef<str> for WriteBuffer<SIZE> { |
83 | 0 | fn as_ref(&self) -> &str { |
84 | 0 | self |
85 | 0 | } |
86 | | } |
87 | | |
88 | | impl<const SIZE: usize> AsRef<[u8]> for WriteBuffer<SIZE> { |
89 | 0 | fn as_ref(&self) -> &[u8] { |
90 | 0 | self.as_bytes() |
91 | 0 | } |
92 | | } |
93 | | |
94 | | impl<const SIZE: usize> core::borrow::Borrow<str> for WriteBuffer<SIZE> { |
95 | 0 | fn borrow(&self) -> &str { |
96 | 0 | self |
97 | 0 | } |
98 | | } |
99 | | |
100 | | impl<const SIZE: usize> core::ops::Deref for WriteBuffer<SIZE> { |
101 | | type Target = str; |
102 | | |
103 | 0 | fn deref(&self) -> &Self::Target { |
104 | 0 | // SAFETY: `buf` is only written to by the `fmt::Write::write_str` implementation which |
105 | 0 | // writes a valid UTF-8 string to `buf` and correctly sets `len`. |
106 | 0 | unsafe { |
107 | 0 | let s = maybe_uninit_slice_assume_init_ref(&self.buf[..self.len]); |
108 | 0 | str::from_utf8_unchecked(s) |
109 | 0 | } |
110 | 0 | } |
111 | | } |
112 | | |
113 | | impl<const SIZE: usize> fmt::Display for WriteBuffer<SIZE> { |
114 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
115 | 0 | f.write_str(self) |
116 | 0 | } |
117 | | } |
118 | | |
119 | | impl<const SIZE: usize> SmartDisplay for WriteBuffer<SIZE> { |
120 | | type Metadata = (); |
121 | | |
122 | 0 | fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { |
123 | 0 | Metadata::new(self.len, self, ()) |
124 | 0 | } |
125 | | |
126 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
127 | 0 | f.pad(self) |
128 | 0 | } |
129 | | } |
130 | | |
131 | | impl<const SIZE: usize> fmt::Write for WriteBuffer<SIZE> { |
132 | 0 | fn write_str(&mut self, s: &str) -> fmt::Result { |
133 | 0 | let bytes = s.as_bytes(); |
134 | | |
135 | 0 | if let Some(buf) = self.buf.get_mut(self.len..(self.len + bytes.len())) { |
136 | 0 | maybe_uninit_write_slice(buf, bytes); |
137 | 0 | self.len += bytes.len(); |
138 | 0 | Ok(()) |
139 | | } else { |
140 | 0 | Err(fmt::Error) |
141 | | } |
142 | 0 | } |
143 | | } |
144 | | |
145 | | /// Equivalent of [`MaybeUninit::uninit_array`] that compiles on stable. |
146 | | #[must_use] |
147 | | #[inline(always)] |
148 | 0 | const fn maybe_uninit_uninit_array<T, const N: usize>() -> [MaybeUninit<T>; N] { |
149 | 0 | // SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid. |
150 | 0 | unsafe { MaybeUninit::<[MaybeUninit<T>; N]>::uninit().assume_init() } |
151 | 0 | } |
152 | | |
153 | | /// Equivalent of [`MaybeUninit::write_slice`] that compiles on stable. |
154 | 0 | fn maybe_uninit_write_slice<'a, T>(this: &'a mut [MaybeUninit<T>], src: &[T]) -> &'a mut [T] |
155 | 0 | where |
156 | 0 | T: Copy, |
157 | 0 | { |
158 | 0 | #[allow(trivial_casts)] |
159 | 0 | // SAFETY: T and MaybeUninit<T> have the same layout |
160 | 0 | let uninit_src = unsafe { &*(src as *const [T] as *const [MaybeUninit<T>]) }; |
161 | 0 |
|
162 | 0 | this.copy_from_slice(uninit_src); |
163 | 0 |
|
164 | 0 | // SAFETY: Valid elements have just been copied into `this` so it is initialized |
165 | 0 | unsafe { maybe_uninit_slice_assume_init_mut(this) } |
166 | 0 | } |
167 | | |
168 | | /// Equivalent of [`MaybeUninit::slice_assume_init_mut`] that compiles on stable. |
169 | | /// |
170 | | /// # Safety |
171 | | /// |
172 | | /// See [`MaybeUninit::slice_assume_init_mut`](https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.slice_assume_init_mut). |
173 | | #[inline(always)] |
174 | 0 | unsafe fn maybe_uninit_slice_assume_init_mut<T, U>(slice: &mut [MaybeUninit<T>]) -> &mut [U] { |
175 | 0 | #[allow(trivial_casts)] |
176 | 0 | // SAFETY: similar to safety notes for `slice_get_ref`, but we have a mutable reference which is |
177 | 0 | // also guaranteed to be valid for writes. |
178 | 0 | unsafe { |
179 | 0 | &mut *(slice as *mut [MaybeUninit<T>] as *mut [U]) |
180 | 0 | } |
181 | 0 | } |
182 | | |
183 | | /// Equivalent of [`MaybeUninit::slice_assume_init_ref`] that compiles on stable. |
184 | | /// |
185 | | /// # Safety |
186 | | /// |
187 | | /// See [`MaybeUninit::slice_assume_init_ref`](https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.slice_assume_init_ref). |
188 | | #[inline(always)] |
189 | 0 | const unsafe fn maybe_uninit_slice_assume_init_ref<T>(slice: &[MaybeUninit<T>]) -> &[T] { |
190 | 0 | #[allow(trivial_casts)] |
191 | 0 | // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees that `slice` is |
192 | 0 | // initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`. The pointer |
193 | 0 | // obtained is valid since it refers to memory owned by `slice` which is a reference and thus |
194 | 0 | // guaranteed to be valid for reads. |
195 | 0 | unsafe { |
196 | 0 | &*(slice as *const [MaybeUninit<T>] as *const [T]) |
197 | 0 | } |
198 | 0 | } |