1#![cfg_attr(not(any(test, doctest)), no_std)]
8#![forbid(unsafe_code, unused_must_use)]
9
10use core::error::Error;
11use core::fmt::Display;
12
13#[derive(Debug)]
20#[derive(Clone)]
21pub struct Bytes<I: Iterator<Item = u8>> {
22 netascii: core::iter::Peekable<I>,
23}
24
25impl<I: Iterator<Item = u8>> Bytes<I> {
26 pub fn from_netascii(netascii: impl IntoIterator<IntoIter = I>) -> Self {
27 Self {
28 netascii: netascii.into_iter().peekable(),
29 }
30 }
31}
32
33impl<I: Iterator<Item = u8>> Iterator for Bytes<I> {
34 type Item = Result<u8, CrError>;
35
36 fn next(&mut self) -> Option<Self::Item> {
37 Some(Ok(match self.netascii.next()? {
38 | b'\r' => match self.netascii.peek().copied() {
39 | Some(b'\n') => {
40 self.netascii.next();
41 b'\n'
42 }
43 | Some(b'\0') => {
44 self.netascii.next();
45 b'\r'
46 }
47 | _ => return Some(Err(CrError)),
48 },
49 | x => x,
50 }))
51 }
52
53 fn size_hint(&self) -> (usize, Option<usize>) {
54 let (netascii_lower, netascii_upper) = self.netascii.size_hint();
55 let lower = netascii_lower / 2;
56 let upper = netascii_upper;
57
58 (lower, upper)
59 }
60}
61
62#[derive(Debug)]
67#[derive(Clone, Copy)]
68#[derive(PartialEq, Eq)]
69pub struct CrError;
70
71impl Display for CrError {
72 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
73 write!(
74 f,
75 "lone CR encountered (must be followed by either LF or NUL)"
76 )
77 }
78}
79
80impl Error for CrError {}
81
82#[derive(Debug)]
86#[derive(Clone)]
87pub struct Netascii<I: Iterator> {
88 bytes: I,
89 next: Option<u8>,
90}
91
92impl<I: Iterator<Item = u8>> Netascii<I> {
93 pub fn from_bytes(bytes: impl IntoIterator<IntoIter = I>) -> Self {
94 Self {
95 bytes: bytes.into_iter(),
96 next: None,
97 }
98 }
99}
100
101impl<I: Iterator<Item = u8>> Iterator for Netascii<I> {
102 type Item = u8;
103
104 fn next(&mut self) -> Option<Self::Item> {
105 match self.next.take() {
106 | Some(c) => Some(c),
107 | None => Some(match self.bytes.next()? {
108 | b'\r' => {
109 debug_assert_eq!(self.next, None);
111 self.next = Some(b'\0');
112 b'\r'
113 }
114 | b'\n' => {
115 debug_assert_eq!(self.next, None);
117 self.next = Some(b'\n');
118 b'\r'
119 }
120 | c => c,
121 }),
122 }
123 }
124
125 fn size_hint(&self) -> (usize, Option<usize>) {
126 let (bytes_lower, bytes_upper) = self.bytes.size_hint();
127 let inserting = if self.next.is_some() { 1 } else { 0 };
128 let lower = bytes_lower.saturating_add(inserting);
129 let upper = bytes_upper
130 .map(|bytes_upper| bytes_upper.saturating_mul(2).saturating_add(inserting));
131 (lower, upper)
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_inverse() {
141 let lorem = *br"
142 Lorem ipsum dolor sit amet,
143 consectetur adipiscing elit.
144 ";
145
146 let netascii = Netascii::from_bytes(lorem);
147 let bytes =
148 Bytes::from_netascii(netascii).collect::<Result<Vec<u8>, CrError>>().unwrap();
149
150 assert_eq!(&bytes, &lorem);
151 }
152
153 #[test]
154 fn test_cr_decode() {
155 let netascii = *b"lorem ipsum\r\n dolor sit\r\0 amet";
156 let expected = *b"lorem ipsum\n dolor sit\r amet";
157
158 let bytes =
159 Bytes::from_netascii(netascii).collect::<Result<Vec<u8>, CrError>>().unwrap();
160
161 assert_eq!(&bytes, &expected);
162 }
163
164 #[test]
165 fn test_cr_encode() {
166 let lorem = *b"lorem ipsum\n dolor sit\r amet";
167 let expected = *b"lorem ipsum\r\n dolor sit\r\0 amet";
168
169 let netascii = Netascii::from_bytes(lorem).collect::<Vec<u8>>();
170
171 assert_eq!(&netascii, &expected);
172 }
173
174 #[test]
175 fn test_no_same_repr_encode() {
176 let lorem = *b"Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
177 let netascii = Netascii::from_bytes(lorem).collect::<Vec<u8>>();
178
179 assert_eq!(&netascii, &lorem);
180 }
181
182 #[test]
183 fn test_no_same_repr_decode() {
184 let netascii = *b"Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
185 let decoded =
186 Bytes::from_netascii(netascii).collect::<Result<Vec<u8>, CrError>>().unwrap();
187
188 assert_eq!(&decoded, &netascii);
189 }
190
191 #[test]
192 fn test_illegal_cr() {
193 let netascii = *b"Lorem ipsum dolor sit\r amet, consectetur adipiscing elit.";
194 let decoded = Bytes::from_netascii(netascii).collect::<Result<Vec<u8>, _>>();
195 assert_eq!(Err(CrError), decoded);
196
197 let netascii = *b"Lorem ipsum dolor sit\r\r amet, consectetur adipiscing elit.";
198 let decoded = Bytes::from_netascii(netascii).collect::<Result<Vec<u8>, _>>();
199 assert_eq!(Err(CrError), decoded);
200
201 let netascii = *b"Lorem ipsum dolor sit\n\r amet, consectetur adipiscing elit.";
202 let decoded = Bytes::from_netascii(netascii).collect::<Result<Vec<u8>, _>>();
203 assert_eq!(Err(CrError), decoded);
204 }
205
206 #[test]
207 fn test_encode_size_hint() {
208 let lorem = *b"Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
209 let netascii = Netascii::from_bytes(lorem);
210
211 assert!(netascii.size_hint().0 <= lorem.len());
212 assert!(netascii.size_hint().1 >= Some(lorem.len() * 2));
213 }
214
215 #[test]
216 fn test_decode_size_hint() {
217 let netascii = *b"Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
218 let bytes = Bytes::from_netascii(netascii);
219
220 assert!(bytes.size_hint().0 <= netascii.len() / 2);
221 assert!(bytes.size_hint().1 >= Some(netascii.len()));
222 }
223}