1#![deny(
120 rustdoc::broken_intra_doc_links,
121 rustdoc::missing_crate_level_docs,
122 missing_docs,
123 rust_2018_compatibility,
124 rust_2018_idioms,
125 future_incompatible,
126 nonstandard_style,
127 unused,
128 clippy::all
129)]
130#![cfg_attr(not(feature = "std"), no_std)]
131
132#[cfg(not(feature = "std"))]
133extern crate alloc;
134
135#[cfg(feature = "uuid")]
143pub mod uuid;
144
145#[cfg(not(feature = "simdutf8"))]
146use core::str::{from_utf8, Utf8Error};
147#[cfg(has_atomics)]
148use core::sync::atomic::{
149 AtomicBool, AtomicI16, AtomicI32, AtomicI8, AtomicU16, AtomicU32, AtomicU8,
150};
151#[cfg(has_atomics_64)]
152use core::sync::atomic::{AtomicI64, AtomicU64};
153use core::{
154 convert::{Infallible, TryFrom},
155 fmt,
156 marker::{PhantomData, PhantomPinned},
157 mem::ManuallyDrop,
158 num::{
159 NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU128, NonZeroU16,
160 NonZeroU32, NonZeroU64, NonZeroU8,
161 },
162 ops, ptr, slice,
163};
164use ptr_meta::PtrExt;
165#[cfg(all(feature = "simdutf8", not(feature = "verbose")))]
166use simdutf8::basic::{from_utf8, Utf8Error};
167#[cfg(all(feature = "simdutf8", feature = "verbose"))]
168use simdutf8::compat::{from_utf8, Utf8Error};
169
170pub use bytecheck_derive::CheckBytes;
171
172#[cfg(not(feature = "std"))]
176pub trait Error: fmt::Debug + fmt::Display + 'static {}
177
178#[cfg(not(feature = "std"))]
179impl<T: fmt::Debug + fmt::Display + 'static> Error for T {}
180
181#[cfg(feature = "std")]
185pub trait Error: std::error::Error + 'static {
186 fn as_error(&self) -> &(dyn std::error::Error + 'static);
188}
189
190#[cfg(feature = "std")]
191impl<T: std::error::Error + 'static> Error for T {
192 fn as_error(&self) -> &(dyn std::error::Error + 'static) {
193 self
194 }
195}
196
197#[cfg(not(feature = "std"))]
199pub type ErrorBox<E> = alloc::boxed::Box<E>;
200
201#[cfg(feature = "std")]
203pub type ErrorBox<E> = std::boxed::Box<E>;
204
205pub trait CheckBytes<C: ?Sized> {
210 type Error: Error + 'static;
212
213 unsafe fn check_bytes<'a>(value: *const Self, context: &mut C)
221 -> Result<&'a Self, Self::Error>;
222}
223
224macro_rules! impl_primitive {
225 ($type:ty) => {
226 impl<C: ?Sized> CheckBytes<C> for $type {
227 type Error = Infallible;
228
229 #[inline]
230 unsafe fn check_bytes<'a>(
231 value: *const Self,
232 _: &mut C,
233 ) -> Result<&'a Self, Self::Error> {
234 Ok(&*value)
235 }
236 }
237 };
238}
239
240impl_primitive!(());
241impl_primitive!(i8);
242impl_primitive!(i16);
243impl_primitive!(i32);
244impl_primitive!(i64);
245impl_primitive!(i128);
246impl_primitive!(u8);
247impl_primitive!(u16);
248impl_primitive!(u32);
249impl_primitive!(u64);
250impl_primitive!(u128);
251impl_primitive!(f32);
252impl_primitive!(f64);
253#[cfg(has_atomics)]
254impl_primitive!(AtomicI8);
255#[cfg(has_atomics)]
256impl_primitive!(AtomicI16);
257#[cfg(has_atomics)]
258impl_primitive!(AtomicI32);
259#[cfg(has_atomics_64)]
260impl_primitive!(AtomicI64);
261#[cfg(has_atomics)]
262impl_primitive!(AtomicU8);
263#[cfg(has_atomics)]
264impl_primitive!(AtomicU16);
265#[cfg(has_atomics)]
266impl_primitive!(AtomicU32);
267#[cfg(has_atomics_64)]
268impl_primitive!(AtomicU64);
269
270impl<T: ?Sized, C: ?Sized> CheckBytes<C> for PhantomData<T> {
271 type Error = Infallible;
272
273 #[inline]
274 unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> {
275 Ok(&*value)
276 }
277}
278
279impl<C: ?Sized> CheckBytes<C> for PhantomPinned {
280 type Error = Infallible;
281
282 #[inline]
283 unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> {
284 Ok(&*value)
285 }
286}
287
288impl<C: ?Sized, T: CheckBytes<C> + ?Sized> CheckBytes<C> for ManuallyDrop<T> {
289 type Error = T::Error;
290
291 #[inline]
292 unsafe fn check_bytes<'a>(value: *const Self, c: &mut C) -> Result<&'a Self, Self::Error> {
293 let _ = T::check_bytes(value as *const T, c)?;
294 Ok(&*value)
295 }
296}
297
298#[derive(Debug)]
302pub struct BoolCheckError {
303 pub invalid_value: u8,
305}
306
307impl fmt::Display for BoolCheckError {
308 #[inline]
309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310 write!(
311 f,
312 "check failed for bool: expected 0 or 1, found {}",
313 self.invalid_value
314 )
315 }
316}
317
318#[cfg(feature = "std")]
319impl std::error::Error for BoolCheckError {}
320
321impl<C: ?Sized> CheckBytes<C> for bool {
322 type Error = BoolCheckError;
323
324 #[inline]
325 unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> {
326 let byte = *value.cast::<u8>();
327 match byte {
328 0 | 1 => Ok(&*value),
329 _ => Err(BoolCheckError {
330 invalid_value: byte,
331 }),
332 }
333 }
334}
335
336#[cfg(has_atomics)]
337impl<C: ?Sized> CheckBytes<C> for AtomicBool {
338 type Error = BoolCheckError;
339
340 #[inline]
341 unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> {
342 let byte = *value.cast::<u8>();
343 match byte {
344 0 | 1 => Ok(&*value),
345 _ => Err(BoolCheckError {
346 invalid_value: byte,
347 }),
348 }
349 }
350}
351
352#[derive(Debug)]
357pub struct CharCheckError {
358 pub invalid_value: u32,
360}
361
362impl From<Infallible> for CharCheckError {
363 #[inline]
364 fn from(_: Infallible) -> Self {
365 unsafe { core::hint::unreachable_unchecked() }
366 }
367}
368
369impl fmt::Display for CharCheckError {
370 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
371 write!(
372 f,
373 "check failed for char: invalid value {}",
374 self.invalid_value
375 )
376 }
377}
378
379#[cfg(feature = "std")]
380impl std::error::Error for CharCheckError {}
381
382impl<C: ?Sized> CheckBytes<C> for char {
383 type Error = CharCheckError;
384
385 #[inline]
386 unsafe fn check_bytes<'a>(
387 value: *const Self,
388 context: &mut C,
389 ) -> Result<&'a Self, Self::Error> {
390 let c = *u32::check_bytes(value.cast(), context)?;
391 char::try_from(c).map_err(|_| CharCheckError { invalid_value: c })?;
392 Ok(&*value)
393 }
394}
395
396macro_rules! peel_tuple {
397 ([$($error_rest:ident,)*], [$type:ident $index:tt, $($type_rest:ident $index_rest:tt,)*]) => { impl_tuple! { [$($error_rest,)*], [$($type_rest $index_rest,)*] } };
398}
399
400macro_rules! impl_tuple {
401 ([], []) => {};
402 ([$error:ident, $($error_rest:ident,)*], [$($type:ident $index:tt,)+]) => {
403 #[derive(Debug)]
405 pub enum $error<$($type),+> {
406 $(
407 $type($type),
409 )+
410 }
411
412 impl<$($type: fmt::Display),*> fmt::Display for $error<$($type),+> {
413 #[inline]
414 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
415 const SIZE: usize = [$($index,)+].len();
416 match self {
417 $($error::$type(e) => write!(f, "check failed for {}-tuple index {}: {}", SIZE, SIZE - $index - 1, e),)+
418 }
419 }
420 }
421
422 #[cfg(feature = "std")]
423 impl<$($type: fmt::Display + fmt::Debug),*> std::error::Error for $error<$($type),+> {}
424
425 impl<$($type: CheckBytes<C>,)+ C: ?Sized> CheckBytes<C> for ($($type,)+) {
426 type Error = $error<$($type::Error),+>;
427
428 #[inline]
429 #[allow(clippy::unneeded_wildcard_pattern)]
430 unsafe fn check_bytes<'a>(value: *const Self, context: &mut C) -> Result<&'a Self, Self::Error> {
431 let field_bytes = ($(ptr::addr_of!((*value).$index),)+);
432 impl_tuple!(@check_fields field_bytes, $error, context, $($type $index,)*);
433 Ok(&*value)
434 }
435 }
436
437 peel_tuple! {
438 [$($error_rest,)*],
439 [$($type $index,)+]
440 }
441 };
442 (@check_fields $field_bytes:ident, $error:ident, $context:ident,) => {};
443 (@check_fields $field_bytes:ident, $error:ident, $context:ident, $type:ident $index:tt, $($type_rest:ident $index_rest:tt,)*) => {
444 impl_tuple!(@check_fields $field_bytes, $error, $context, $($type_rest $index_rest,)*);
445 $type::check_bytes($field_bytes.$index, $context).map_err($error::$type)?;
446 };
447}
448
449impl_tuple! {
450 [Tuple12CheckError, Tuple11CheckError, Tuple10CheckError, Tuple9CheckError, Tuple8CheckError, Tuple7CheckError, Tuple6CheckError, Tuple5CheckError, Tuple4CheckError, Tuple3CheckError, Tuple2CheckError, Tuple1CheckError, ],
451 [T11 11, T10 10, T9 9, T8 8, T7 7, T6 6, T5 5, T4 4, T3 3, T2 2, T1 1, T0 0, ]
452}
453
454#[derive(Debug)]
456pub struct ArrayCheckError<T> {
457 pub index: usize,
459 pub error: T,
461}
462
463impl<T: fmt::Display> fmt::Display for ArrayCheckError<T> {
464 #[inline]
465 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
466 write!(
467 f,
468 "check failed for array index {}: {}",
469 self.index, self.error
470 )
471 }
472}
473
474#[cfg(feature = "std")]
475impl<T: fmt::Debug + fmt::Display> std::error::Error for ArrayCheckError<T> {}
476
477impl<T: CheckBytes<C>, C: ?Sized, const N: usize> CheckBytes<C> for [T; N] {
478 type Error = ArrayCheckError<T::Error>;
479
480 #[inline]
481 unsafe fn check_bytes<'a>(
482 value: *const Self,
483 context: &mut C,
484 ) -> Result<&'a Self, Self::Error> {
485 for index in 0..N {
486 let el = value.cast::<T>().add(index);
487 T::check_bytes(el, context).map_err(|error| ArrayCheckError { index, error })?;
488 }
489 Ok(&*value)
490 }
491}
492
493#[derive(Debug)]
495pub enum SliceCheckError<T> {
496 CheckBytes {
498 index: usize,
500 error: T,
502 },
503}
504
505impl<T: fmt::Display> fmt::Display for SliceCheckError<T> {
506 #[inline]
507 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508 match self {
509 SliceCheckError::CheckBytes { index, error } => {
510 write!(f, "check failed for slice index {}: {}", index, error)
511 }
512 }
513 }
514}
515
516#[cfg(feature = "std")]
517impl<T: fmt::Debug + fmt::Display> std::error::Error for SliceCheckError<T> {}
518
519impl<T: CheckBytes<C>, C: ?Sized> CheckBytes<C> for [T] {
520 type Error = SliceCheckError<T::Error>;
521
522 #[inline]
523 unsafe fn check_bytes<'a>(
524 value: *const Self,
525 context: &mut C,
526 ) -> Result<&'a Self, Self::Error> {
527 let (data, len) = PtrExt::to_raw_parts(value);
528 for index in 0..len {
529 let el = data.cast::<T>().add(index);
530 T::check_bytes(el, context)
531 .map_err(|error| SliceCheckError::CheckBytes { index, error })?;
532 }
533 Ok(&*value)
534 }
535}
536
537#[derive(Debug)]
539pub enum StrCheckError {
540 Utf8Error(Utf8Error),
542}
543
544impl From<Utf8Error> for StrCheckError {
545 fn from(e: Utf8Error) -> Self {
546 Self::Utf8Error(e)
547 }
548}
549
550impl fmt::Display for StrCheckError {
551 #[inline]
552 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553 match self {
554 StrCheckError::Utf8Error(e) => write!(f, "utf8 error: {}", e),
555 }
556 }
557}
558
559#[cfg(feature = "std")]
560impl std::error::Error for StrCheckError {
561 #[inline]
562 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
563 match self {
564 StrCheckError::Utf8Error(e) => Some(e),
565 }
566 }
567}
568
569impl<C: ?Sized> CheckBytes<C> for str {
570 type Error = StrCheckError;
571
572 #[inline]
573 unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> {
574 let (data, len) = PtrExt::to_raw_parts(value);
575 from_utf8(slice::from_raw_parts(data.cast(), len))?;
576 Ok(&*value)
577 }
578}
579
580#[cfg(feature = "std")]
582#[derive(Debug)]
583pub enum CStrCheckError {
584 Utf8Error(Utf8Error),
586 MissingNullTerminator,
588}
589
590#[cfg(feature = "std")]
591impl From<Utf8Error> for CStrCheckError {
592 fn from(e: Utf8Error) -> Self {
593 Self::Utf8Error(e)
594 }
595}
596
597#[cfg(feature = "std")]
598impl fmt::Display for CStrCheckError {
599 #[inline]
600 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
601 match self {
602 CStrCheckError::Utf8Error(e) => write!(f, "utf8 error: {}", e),
603 CStrCheckError::MissingNullTerminator => write!(f, "missing null terminator"),
604 }
605 }
606}
607
608#[cfg(feature = "std")]
609impl std::error::Error for CStrCheckError {
610 #[inline]
611 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
612 match self {
613 CStrCheckError::Utf8Error(e) => Some(e),
614 CStrCheckError::MissingNullTerminator => None,
615 }
616 }
617}
618
619#[cfg(feature = "std")]
620impl<C: ?Sized> CheckBytes<C> for ::std::ffi::CStr {
621 type Error = CStrCheckError;
622
623 #[inline]
624 unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> {
625 let (data, len) = PtrExt::to_raw_parts(value);
626 if len == 0 {
627 Err(CStrCheckError::MissingNullTerminator)
628 } else {
629 from_utf8(slice::from_raw_parts(data.cast(), len - 1))?;
630 if *data.cast::<u8>().add(len - 1) != 0 {
631 Err(CStrCheckError::MissingNullTerminator)
632 } else {
633 Ok(&*value)
634 }
635 }
636 }
637}
638
639#[derive(Debug)]
641pub struct StructCheckError {
642 pub field_name: &'static str,
644 pub inner: ErrorBox<dyn Error>,
646}
647
648impl fmt::Display for StructCheckError {
649 #[inline]
650 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
651 write!(
652 f,
653 "check failed for struct member {}: {}",
654 self.field_name, self.inner
655 )
656 }
657}
658
659#[cfg(feature = "std")]
660impl std::error::Error for StructCheckError {}
661
662#[derive(Debug)]
664pub struct TupleStructCheckError {
665 pub field_index: usize,
667 pub inner: ErrorBox<dyn Error>,
669}
670
671impl fmt::Display for TupleStructCheckError {
672 #[inline]
673 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
674 write!(
675 f,
676 "check failed for tuple struct member {}: {}",
677 self.field_index, self.inner
678 )
679 }
680}
681
682#[cfg(feature = "std")]
683impl std::error::Error for TupleStructCheckError {}
684
685#[derive(Debug)]
687pub enum EnumCheckError<T> {
688 InvalidStruct {
690 variant_name: &'static str,
692 inner: StructCheckError,
694 },
695 InvalidTuple {
697 variant_name: &'static str,
699 inner: TupleStructCheckError,
701 },
702 InvalidTag(
704 T,
706 ),
707}
708
709impl<T: fmt::Display> fmt::Display for EnumCheckError<T> {
710 #[inline]
711 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
712 match self {
713 EnumCheckError::InvalidStruct {
714 variant_name,
715 inner,
716 } => write!(
717 f,
718 "check failed for enum struct variant {}: {}",
719 variant_name, inner
720 ),
721 EnumCheckError::InvalidTuple {
722 variant_name,
723 inner,
724 } => write!(
725 f,
726 "check failed for enum tuple variant {}: {}",
727 variant_name, inner
728 ),
729 EnumCheckError::InvalidTag(tag) => write!(f, "invalid tag for enum: {}", tag),
730 }
731 }
732}
733
734#[cfg(feature = "std")]
735impl<T: fmt::Debug + fmt::Display> std::error::Error for EnumCheckError<T> {}
736
737impl<T: CheckBytes<C>, C: ?Sized> CheckBytes<C> for ops::Range<T> {
739 type Error = StructCheckError;
740
741 #[inline]
742 unsafe fn check_bytes<'a>(
743 value: *const Self,
744 context: &mut C,
745 ) -> Result<&'a Self, Self::Error> {
746 T::check_bytes(ptr::addr_of!((*value).start), context).map_err(|error| {
747 StructCheckError {
748 field_name: "start",
749 inner: ErrorBox::new(error),
750 }
751 })?;
752 T::check_bytes(ptr::addr_of!((*value).end), context).map_err(|error| StructCheckError {
753 field_name: "end",
754 inner: ErrorBox::new(error),
755 })?;
756 Ok(&*value)
757 }
758}
759
760impl<T: CheckBytes<C>, C: ?Sized> CheckBytes<C> for ops::RangeFrom<T> {
761 type Error = StructCheckError;
762
763 #[inline]
764 unsafe fn check_bytes<'a>(
765 value: *const Self,
766 context: &mut C,
767 ) -> Result<&'a Self, Self::Error> {
768 let bytes = value.cast::<u8>();
769 T::check_bytes(ptr::addr_of!((*value).start), context).map_err(|error| {
770 StructCheckError {
771 field_name: "start",
772 inner: ErrorBox::new(error),
773 }
774 })?;
775 Ok(&*bytes.cast())
776 }
777}
778
779impl<C: ?Sized> CheckBytes<C> for ops::RangeFull {
780 type Error = Infallible;
781
782 #[inline]
783 unsafe fn check_bytes<'a>(value: *const Self, _: &mut C) -> Result<&'a Self, Self::Error> {
784 Ok(&*value)
785 }
786}
787
788impl<T: CheckBytes<C>, C: ?Sized> CheckBytes<C> for ops::RangeTo<T> {
789 type Error = StructCheckError;
790
791 #[inline]
792 unsafe fn check_bytes<'a>(
793 value: *const Self,
794 context: &mut C,
795 ) -> Result<&'a Self, Self::Error> {
796 T::check_bytes(ptr::addr_of!((*value).end), context).map_err(|error| StructCheckError {
797 field_name: "end",
798 inner: ErrorBox::new(error),
799 })?;
800 Ok(&*value)
801 }
802}
803
804impl<T: CheckBytes<C>, C: ?Sized> CheckBytes<C> for ops::RangeToInclusive<T> {
805 type Error = StructCheckError;
806
807 #[inline]
808 unsafe fn check_bytes<'a>(
809 value: *const Self,
810 context: &mut C,
811 ) -> Result<&'a Self, Self::Error> {
812 T::check_bytes(ptr::addr_of!((*value).end), context).map_err(|error| StructCheckError {
813 field_name: "end",
814 inner: ErrorBox::new(error),
815 })?;
816 Ok(&*value)
817 }
818}
819
820#[derive(Debug)]
822pub enum NonZeroCheckError {
823 IsZero,
825}
826
827impl From<Infallible> for NonZeroCheckError {
828 #[inline]
829 fn from(_: Infallible) -> Self {
830 unsafe { core::hint::unreachable_unchecked() }
831 }
832}
833
834impl fmt::Display for NonZeroCheckError {
835 #[inline]
836 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
837 match self {
838 NonZeroCheckError::IsZero => write!(f, "nonzero integer is zero"),
839 }
840 }
841}
842
843#[cfg(feature = "std")]
844impl std::error::Error for NonZeroCheckError {}
845
846macro_rules! impl_nonzero {
847 ($nonzero:ident, $underlying:ident) => {
848 impl<C: ?Sized> CheckBytes<C> for $nonzero {
849 type Error = NonZeroCheckError;
850
851 #[inline]
852 unsafe fn check_bytes<'a>(
853 value: *const Self,
854 context: &mut C,
855 ) -> Result<&'a Self, Self::Error> {
856 if *$underlying::check_bytes(value.cast(), context)? == 0 {
857 Err(NonZeroCheckError::IsZero)
858 } else {
859 Ok(&*value)
860 }
861 }
862 }
863 };
864}
865
866impl_nonzero!(NonZeroI8, i8);
867impl_nonzero!(NonZeroI16, i16);
868impl_nonzero!(NonZeroI32, i32);
869impl_nonzero!(NonZeroI64, i64);
870impl_nonzero!(NonZeroI128, i128);
871impl_nonzero!(NonZeroU8, u8);
872impl_nonzero!(NonZeroU16, u16);
873impl_nonzero!(NonZeroU32, u32);
874impl_nonzero!(NonZeroU64, u64);
875impl_nonzero!(NonZeroU128, u128);