1use core::hash::{Hash, Hasher};
4use core::mem::MaybeUninit;
5use core::{fmt, str};
6
7use crate::smart_display::{FormatterOptions, Metadata, SmartDisplay};
8
9pub struct WriteBuffer<const SIZE: usize> {
13 buf: [MaybeUninit<u8>; SIZE],
14 len: usize,
15}
16
17impl<const SIZE: usize> fmt::Debug for WriteBuffer<SIZE> {
18 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
19 f.debug_struct("DisplayBuffer")
20 .field("buf", &self.as_str())
21 .field("remaining_capacity", &self.remaining_capacity())
22 .finish()
23 }
24}
25
26impl<const SIZE: usize> WriteBuffer<SIZE> {
27 pub const fn new() -> Self {
29 Self {
30 buf: maybe_uninit_uninit_array::<_, SIZE>(),
31 len: 0,
32 }
33 }
34
35 pub fn as_str(&self) -> &str {
37 self
38 }
39
40 pub const fn remaining_capacity(&self) -> usize {
42 SIZE - self.len
43 }
44}
45
46impl<const SIZE: usize> Default for WriteBuffer<SIZE> {
47 fn default() -> Self {
48 Self::new()
49 }
50}
51
52impl<const LEFT_SIZE: usize, const RIGHT_SIZE: usize> PartialOrd<WriteBuffer<RIGHT_SIZE>>
53 for WriteBuffer<LEFT_SIZE>
54{
55 fn partial_cmp(&self, other: &WriteBuffer<RIGHT_SIZE>) -> Option<core::cmp::Ordering> {
56 self.as_str().partial_cmp(other.as_str())
57 }
58}
59
60impl<const LEFT_SIZE: usize, const RIGHT_SIZE: usize> PartialEq<WriteBuffer<RIGHT_SIZE>>
61 for WriteBuffer<LEFT_SIZE>
62{
63 fn eq(&self, other: &WriteBuffer<RIGHT_SIZE>) -> bool {
64 self.as_str() == other.as_str()
65 }
66}
67
68impl<const SIZE: usize> Eq for WriteBuffer<SIZE> {}
69
70impl<const SIZE: usize> Ord for WriteBuffer<SIZE> {
71 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
72 self.as_str().cmp(other.as_str())
73 }
74}
75
76impl<const SIZE: usize> Hash for WriteBuffer<SIZE> {
77 fn hash<H: Hasher>(&self, state: &mut H) {
78 self.as_str().hash(state)
79 }
80}
81
82impl<const SIZE: usize> AsRef<str> for WriteBuffer<SIZE> {
83 fn as_ref(&self) -> &str {
84 self
85 }
86}
87
88impl<const SIZE: usize> AsRef<[u8]> for WriteBuffer<SIZE> {
89 fn as_ref(&self) -> &[u8] {
90 self.as_bytes()
91 }
92}
93
94impl<const SIZE: usize> core::borrow::Borrow<str> for WriteBuffer<SIZE> {
95 fn borrow(&self) -> &str {
96 self
97 }
98}
99
100impl<const SIZE: usize> core::ops::Deref for WriteBuffer<SIZE> {
101 type Target = str;
102
103 fn deref(&self) -> &Self::Target {
104 unsafe {
107 let s = maybe_uninit_slice_assume_init_ref(&self.buf[..self.len]);
108 str::from_utf8_unchecked(s)
109 }
110 }
111}
112
113impl<const SIZE: usize> fmt::Display for WriteBuffer<SIZE> {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 f.write_str(self)
116 }
117}
118
119impl<const SIZE: usize> SmartDisplay for WriteBuffer<SIZE> {
120 type Metadata = ();
121
122 fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> {
123 Metadata::new(self.len, self, ())
124 }
125
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 f.pad(self)
128 }
129}
130
131impl<const SIZE: usize> fmt::Write for WriteBuffer<SIZE> {
132 fn write_str(&mut self, s: &str) -> fmt::Result {
133 let bytes = s.as_bytes();
134
135 if let Some(buf) = self.buf.get_mut(self.len..(self.len + bytes.len())) {
136 maybe_uninit_write_slice(buf, bytes);
137 self.len += bytes.len();
138 Ok(())
139 } else {
140 Err(fmt::Error)
141 }
142 }
143}
144
145#[must_use]
147#[inline(always)]
148const fn maybe_uninit_uninit_array<T, const N: usize>() -> [MaybeUninit<T>; N] {
149 unsafe { MaybeUninit::<[MaybeUninit<T>; N]>::uninit().assume_init() }
151}
152
153fn maybe_uninit_write_slice<'a, T>(this: &'a mut [MaybeUninit<T>], src: &[T]) -> &'a mut [T]
155where
156 T: Copy,
157{
158 #[allow(trivial_casts)]
159 let uninit_src = unsafe { &*(src as *const [T] as *const [MaybeUninit<T>]) };
161
162 this.copy_from_slice(uninit_src);
163
164 unsafe { maybe_uninit_slice_assume_init_mut(this) }
166}
167
168#[inline(always)]
174unsafe fn maybe_uninit_slice_assume_init_mut<T, U>(slice: &mut [MaybeUninit<T>]) -> &mut [U] {
175 #[allow(trivial_casts)]
176 unsafe {
179 &mut *(slice as *mut [MaybeUninit<T>] as *mut [U])
180 }
181}
182
183#[inline(always)]
189const unsafe fn maybe_uninit_slice_assume_init_ref<T>(slice: &[MaybeUninit<T>]) -> &[T] {
190 #[allow(trivial_casts)]
191 unsafe {
196 &*(slice as *const [MaybeUninit<T>] as *const [T])
197 }
198}