1#[cfg(feature = "alloc")]
5use alloc::collections::TryReserveError;
6#[cfg(feature = "unstable_ascii_char")]
7use core::ascii;
8#[cfg(feature = "utf8")]
9pub use simdutf8::compat::Utf8Error as SimdUtf8Error;
10use core::fmt::{Display, Formatter, Result as FmtResult};
11#[cfg(feature = "utf8")]
12use core::num::NonZeroU8;
13
14#[derive(Debug)]
16#[non_exhaustive]
17pub enum Error {
18 #[cfg(feature = "std")]
20 Io(std::io::Error),
21 #[cfg(feature = "unstable_ascii_char")]
23 Ascii(AsciiError),
24 #[cfg(feature = "utf8")]
26 Utf8(Utf8Error),
27 #[cfg(feature = "alloc")]
29 Allocation(TryReserveError),
30 Overflow {
33 remaining: usize
35 },
36 End {
38 required_count: usize
40 },
41 NoEnd,
43 InsufficientBuffer {
45 spare_capacity: usize,
47 required_count: usize
49 },
50}
51
52impl Error {
53 #[allow(clippy::missing_panics_doc)]
55 #[inline]
56 #[cfg(feature = "unstable_ascii_char")]
57 pub const fn invalid_ascii(invalid_byte: u8, valid_up_to: usize, consumed_count: usize) -> Self {
58 assert!(consumed_count >= valid_up_to, "at least `valid_up_to` bytes must be consumed");
59 Self::Ascii(AsciiError { invalid_byte, valid_up_to, consumed_count })
60 }
61 #[inline]
63 pub const fn overflow(remaining: usize) -> Self {
64 Self::Overflow { remaining }
65 }
66 #[inline]
68 pub const fn end(required_count: usize) -> Self {
69 Self::End { required_count }
70 }
71 #[inline]
73 pub const fn insufficient_buffer(spare_capacity: usize, required_count: usize) -> Self {
74 Self::InsufficientBuffer { spare_capacity, required_count }
75 }
76}
77
78#[allow(clippy::std_instead_of_core, reason = "Error trait in core is not yet stable")]
79#[cfg(feature = "std")]
80impl std::error::Error for Error {
81 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
82 match self {
83 Self::Io(error) => Some(error),
84 #[cfg(feature = "unstable_ascii_char")]
85 Self::Ascii(_) => None,
86 #[cfg(feature = "utf8")]
87 Self::Utf8(error) => error.source(),
88 #[cfg(feature = "alloc")]
89 Self::Allocation(error) => Some(error),
90 Self::Overflow { .. } |
91 Self::End { .. } |
92 Self::NoEnd |
93 Self::InsufficientBuffer { .. } => None,
94 }
95 }
96}
97
98impl Display for Error {
99 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
100 match self {
101 #[cfg(feature = "std")]
102 Self::Io(error) => Display::fmt(error, f),
103 #[cfg(feature = "unstable_ascii_char")]
104 Self::Ascii(error) => Display::fmt(error, f),
105 #[cfg(feature = "utf8")]
106 Self::Utf8(error) => Display::fmt(error, f),
107 #[cfg(feature = "alloc")]
108 Self::Allocation(error) => Display::fmt(error, f),
109 Self::Overflow { remaining } => write!(f, "sink overflowed with {remaining} bytes remaining to write"),
110 Self::End { required_count } => write!(f, "premature end-of-stream when reading {required_count} bytes"),
111 Self::NoEnd => write!(f, "cannot read to end of infinite source"),
112 Self::InsufficientBuffer {
113 spare_capacity, required_count
114 } => write!(f, "insufficient buffer capacity ({spare_capacity}) to read {required_count} bytes"),
115 }
116 }
117}
118
119#[cfg(feature = "std")]
120impl From<std::io::Error> for Error {
121 #[inline]
122 fn from(value: std::io::Error) -> Self {
123 Self::Io(value)
124 }
125}
126
127#[cfg(feature = "utf8")]
128impl From<SimdUtf8Error> for Error {
129 #[inline]
130 fn from(value: SimdUtf8Error) -> Self {
131 Self::Utf8(value.into())
132 }
133}
134
135#[cfg(feature = "utf8")]
136impl From<Utf8Error> for Error {
137 #[inline]
138 fn from(value: Utf8Error) -> Self {
139 Self::Utf8(value)
140 }
141}
142
143#[cfg(feature = "unstable_ascii_char")]
144impl From<AsciiError> for Error {
145 #[inline]
146 fn from(value: AsciiError) -> Self {
147 Self::Ascii(value)
148 }
149}
150
151#[cfg(feature = "alloc")]
152impl From<TryReserveError> for Error {
153 #[inline]
154 fn from(value: TryReserveError) -> Self {
155 Self::Allocation(value)
156 }
157}
158
159#[cfg(feature = "utf8")]
160#[derive(Copy, Clone, Debug, Eq, PartialEq)]
161pub struct Utf8Error {
162 offset: usize,
163 inner: SimdUtf8Error,
164}
165
166#[allow(clippy::exhaustive_enums)]
168#[cfg(feature = "utf8")]
169#[derive(Copy, Clone, Debug, Eq, PartialEq)]
170pub enum Utf8ErrorKind {
171 IncompleteChar,
173 InvalidBytes(NonZeroU8),
175}
176
177#[cfg(feature = "utf8")]
178impl Utf8Error {
179 #[inline]
182 #[must_use]
183 pub const fn offset(&self) -> usize { self.offset }
184 #[inline]
186 #[must_use]
187 pub fn valid_up_to(&self) -> usize {
188 self.offset + self.inner.valid_up_to()
189 }
190 #[inline]
195 #[must_use]
196 pub fn error_len(&self) -> Option<usize> {
197 self.inner.error_len()
198 }
199 #[inline]
205 #[must_use]
206 pub const fn last_error(&self) -> SimdUtf8Error { self.inner }
207 #[inline]
209 #[must_use]
210 pub fn error_kind(&self) -> Utf8ErrorKind {
211 match self.inner.error_len() {
212 Some(len) => Utf8ErrorKind::InvalidBytes(
213 unsafe {
217 NonZeroU8::new_unchecked(len as u8)
218 }
219 ),
220 None => Utf8ErrorKind::IncompleteChar
221 }
222 }
223 #[must_use]
234 pub unsafe fn valid_slice_unchecked<'a>(&self, bytes: &'a [u8]) -> &'a str {
235 core::str::from_utf8_unchecked(bytes.get_unchecked(..self.valid_up_to()))
236 }
237 pub unsafe fn split_valid<'a>(&self, bytes: &'a [u8]) -> (&'a str, &'a [u8]) {
248 let (valid, invalid) = bytes.split_at_unchecked(self.valid_up_to());
249 (core::str::from_utf8_unchecked(valid), invalid)
250 }
251 pub unsafe fn split_valid_mut<'a>(&self, bytes: &'a mut [u8]) -> (&'a mut str, &'a mut [u8]) {
262 let (valid, invalid) = bytes.split_at_mut_unchecked(self.valid_up_to());
263 (core::str::from_utf8_unchecked_mut(valid), invalid)
264 }
265}
266
267#[cfg(feature = "utf8")]
268impl Utf8Error {
269 #[cfg(any(feature = "unstable_specialization", feature = "alloc"))]
270 pub(crate) fn set_offset(&mut self, offset: usize) {
271 self.offset += offset;
272 }
273 #[cfg(feature = "unstable_specialization")]
274 pub(crate) fn with_offset(mut self, offset: usize) -> Self {
275 self.set_offset(offset);
276 self
277 }
278}
279
280#[allow(clippy::std_instead_of_core, reason = "Error trait in core is not yet stable")]
281#[cfg(all(feature = "std", feature = "utf8"))]
282impl std::error::Error for Utf8Error {
283 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
284 Some(&self.inner)
285 }
286}
287
288#[cfg(feature = "utf8")]
289impl Display for Utf8Error {
290 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
291 let valid_up_to = self.valid_up_to();
292 match self.error_len() {
293 Some(len) => write!(f, "invalid UTF-8 sequence of {len} bytes from index {valid_up_to}"),
294 None => write!(f, "incomplete UTF-8 byte sequence from index {valid_up_to}")
295 }
296 }
297}
298
299#[cfg(feature = "utf8")]
300impl From<SimdUtf8Error> for Utf8Error {
301 #[inline]
302 fn from(inner: SimdUtf8Error) -> Self {
303 Self { offset: 0, inner }
304 }
305}
306
307#[cfg(feature = "unstable_ascii_char")]
308#[derive(Copy, Clone, Debug, Eq, PartialEq)]
309pub struct AsciiError {
310 pub invalid_byte: u8,
312 pub valid_up_to: usize,
314 pub consumed_count: usize,
317}
318
319#[cfg(feature = "unstable_ascii_char")]
320impl AsciiError {
321 #[inline]
323 #[must_use]
324 pub const fn invalid_byte(&self) -> u8 { self.invalid_byte }
325 #[inline]
327 #[must_use]
328 pub const fn valid_up_to(&self) -> usize { self.valid_up_to }
329 #[inline]
332 #[must_use]
333 pub const fn consumed_count(&self) -> usize { self.consumed_count }
334 #[inline]
337 #[must_use]
338 pub const fn unchecked_count(&self) -> usize { self.consumed_count.saturating_sub(1 + self.valid_up_to) }
339 #[must_use]
346 pub fn valid_slice<'a>(&self, bytes: &'a [u8]) -> &'a [ascii::Char] {
347 assert!(bytes.len() >= self.valid_up_to, "the slice must contain at least `valid_up_to` bytes");
348 assert!(bytes[..self.valid_up_to].is_ascii(), "the valid slice must be ASCII");
349 unsafe {
351 self.valid_slice_unchecked(bytes)
352 }
353 }
354 #[must_use]
365 pub unsafe fn valid_slice_unchecked<'a>(&self, bytes: &'a [u8]) -> &'a [ascii::Char] {
366 bytes.get_unchecked(..self.valid_up_to).as_ascii_unchecked()
367 }
368 #[must_use]
376 pub fn split_valid<'a>(&self, bytes: &'a [u8]) -> (&'a [ascii::Char], &'a [u8]) {
377 assert!(self.consumed_count >= self.valid_up_to, "at least `valid_up_to` bytes must be consumed");
378 assert!(bytes.len() >= self.consumed_count, "the slice length must be longer than the consumed count");
379 assert!(bytes[..self.valid_up_to].is_ascii(), "the valid slice must be ASCII");
380 unsafe {
382 self.split_valid_unchecked(bytes)
383 }
384 }
385 #[must_use]
397 pub unsafe fn split_valid_unchecked<'a>(&self, bytes: &'a [u8]) -> (&'a [ascii::Char], &'a [u8]) {
398 (self.valid_slice_unchecked(bytes),
399 bytes.get_unchecked(self.valid_up_to..self.consumed_count))
400 }
401}
402
403#[allow(clippy::std_instead_of_core, reason = "Error trait in core is not yet stable")]
404#[cfg(all(feature = "std", feature = "unstable_ascii_char"))]
405impl std::error::Error for AsciiError { }
406
407#[cfg(feature = "unstable_ascii_char")]
408impl Display for AsciiError {
409 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
410 let Self { invalid_byte, valid_up_to, .. } = self;
411 write!(f, "non-ASCII byte {invalid_byte:#X} at index {valid_up_to}")
412 }
413}