Coverage Report

Created: 2025-07-11 07:02

/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
}