hex_conservative/
buf_encoder.rs1use core::borrow::Borrow;
12
13use arrayvec::ArrayString;
14
15use super::Case;
16
17pub struct BufEncoder<const CAP: usize> {
23 buf: ArrayString<CAP>,
24}
25
26impl<const CAP: usize> BufEncoder<CAP> {
27 const _CHECK_EVEN_CAPACITY: () = [(); 1][CAP % 2];
28
29 #[inline]
31 pub fn new() -> Self { BufEncoder { buf: ArrayString::new() } }
32
33 #[inline]
39 #[track_caller]
40 pub fn put_byte(&mut self, byte: u8, case: Case) {
41 self.buf.push_str(&case.table().byte_to_hex(byte));
42 }
43
44 #[inline]
50 #[track_caller]
51 pub fn put_bytes<I>(&mut self, bytes: I, case: Case)
52 where
53 I: IntoIterator,
54 I::Item: Borrow<u8>,
55 {
56 self.put_bytes_inner(bytes.into_iter(), case)
57 }
58
59 #[inline]
60 #[track_caller]
61 fn put_bytes_inner<I>(&mut self, bytes: I, case: Case)
62 where
63 I: Iterator,
64 I::Item: Borrow<u8>,
65 {
66 if let Some(max) = bytes.size_hint().1 {
68 assert!(max <= self.space_remaining());
69 }
70 for byte in bytes {
71 self.put_byte(*byte.borrow(), case);
72 }
73 }
74
75 #[must_use = "this may write only part of the input buffer"]
80 #[inline]
81 #[track_caller]
82 pub fn put_bytes_min<'a>(&mut self, bytes: &'a [u8], case: Case) -> &'a [u8] {
83 let to_write = self.space_remaining().min(bytes.len());
84 self.put_bytes(&bytes[..to_write], case);
85 &bytes[to_write..]
86 }
87
88 #[inline]
90 pub fn is_full(&self) -> bool { self.space_remaining() == 0 }
91
92 #[inline]
94 pub fn as_str(&self) -> &str { &self.buf }
95
96 #[inline]
98 pub fn clear(&mut self) { self.buf.clear(); }
99
100 #[inline]
104 pub fn space_remaining(&self) -> usize { self.buf.remaining_capacity() / 2 }
105
106 pub(crate) fn put_filler(&mut self, filler: char, max_count: usize) -> usize {
107 let mut buf = [0; 4];
108 let filler = filler.encode_utf8(&mut buf);
109 let max_capacity = self.buf.remaining_capacity() / filler.len();
110 let to_write = max_capacity.min(max_count);
111
112 for _ in 0..to_write {
113 self.buf.push_str(filler);
114 }
115
116 to_write
117 }
118}
119
120impl<const CAP: usize> Default for BufEncoder<CAP> {
121 fn default() -> Self { Self::new() }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn empty() {
130 let encoder = BufEncoder::<2>::new();
131 assert_eq!(encoder.as_str(), "");
132 assert!(!encoder.is_full());
133 }
134
135 #[test]
136 fn single_byte_exact_buf() {
137 let mut encoder = BufEncoder::<2>::new();
138 assert_eq!(encoder.space_remaining(), 1);
139 encoder.put_byte(42, Case::Lower);
140 assert_eq!(encoder.as_str(), "2a");
141 assert_eq!(encoder.space_remaining(), 0);
142 assert!(encoder.is_full());
143 encoder.clear();
144 assert_eq!(encoder.space_remaining(), 1);
145 assert!(!encoder.is_full());
146 encoder.put_byte(42, Case::Upper);
147 assert_eq!(encoder.as_str(), "2A");
148 assert_eq!(encoder.space_remaining(), 0);
149 assert!(encoder.is_full());
150 }
151
152 #[test]
153 fn single_byte_oversized_buf() {
154 let mut encoder = BufEncoder::<4>::new();
155 assert_eq!(encoder.space_remaining(), 2);
156 encoder.put_byte(42, Case::Lower);
157 assert_eq!(encoder.space_remaining(), 1);
158 assert_eq!(encoder.as_str(), "2a");
159 assert!(!encoder.is_full());
160 encoder.clear();
161 assert_eq!(encoder.space_remaining(), 2);
162 encoder.put_byte(42, Case::Upper);
163 assert_eq!(encoder.as_str(), "2A");
164 assert_eq!(encoder.space_remaining(), 1);
165 assert!(!encoder.is_full());
166 }
167
168 #[test]
169 fn two_bytes() {
170 let mut encoder = BufEncoder::<4>::new();
171 encoder.put_byte(42, Case::Lower);
172 assert_eq!(encoder.space_remaining(), 1);
173 encoder.put_byte(255, Case::Lower);
174 assert_eq!(encoder.space_remaining(), 0);
175 assert_eq!(encoder.as_str(), "2aff");
176 assert!(encoder.is_full());
177 encoder.clear();
178 assert!(!encoder.is_full());
179 encoder.put_byte(42, Case::Upper);
180 encoder.put_byte(255, Case::Upper);
181 assert_eq!(encoder.as_str(), "2AFF");
182 assert!(encoder.is_full());
183 }
184
185 #[test]
186 fn put_bytes_min() {
187 let mut encoder = BufEncoder::<2>::new();
188 let remainder = encoder.put_bytes_min(b"", Case::Lower);
189 assert_eq!(remainder, b"");
190 assert_eq!(encoder.as_str(), "");
191 let remainder = encoder.put_bytes_min(b"*", Case::Lower);
192 assert_eq!(remainder, b"");
193 assert_eq!(encoder.as_str(), "2a");
194 encoder.clear();
195 let remainder = encoder.put_bytes_min(&[42, 255], Case::Lower);
196 assert_eq!(remainder, &[255]);
197 assert_eq!(encoder.as_str(), "2a");
198 }
199
200 #[test]
201 fn same_as_fmt() {
202 use core::fmt::{self, Write};
203
204 struct Writer {
205 buf: [u8; 2],
206 pos: usize,
207 }
208
209 impl Writer {
210 fn as_str(&self) -> &str { core::str::from_utf8(&self.buf[..self.pos]).unwrap() }
211 }
212
213 impl Write for Writer {
214 fn write_str(&mut self, s: &str) -> fmt::Result {
215 assert!(self.pos <= 2);
216 if s.len() > 2 - self.pos {
217 Err(fmt::Error)
218 } else {
219 self.buf[self.pos..(self.pos + s.len())].copy_from_slice(s.as_bytes());
220 self.pos += s.len();
221 Ok(())
222 }
223 }
224 }
225
226 let mut writer = Writer { buf: [0u8; 2], pos: 0 };
227 let mut encoder = BufEncoder::<2>::new();
228
229 for i in 0..=255 {
230 write!(writer, "{:02x}", i).unwrap();
231 encoder.put_byte(i, Case::Lower);
232 assert_eq!(encoder.as_str(), writer.as_str());
233 writer.pos = 0;
234 encoder.clear();
235 }
236 for i in 0..=255 {
237 write!(writer, "{:02X}", i).unwrap();
238 encoder.put_byte(i, Case::Upper);
239 assert_eq!(encoder.as_str(), writer.as_str());
240 writer.pos = 0;
241 encoder.clear();
242 }
243 }
244}