@@ -623,7 +623,9 @@ mod cast_from_raw {
623623 /// [cast_from_raw]: crate::pointer::SizeCompat::cast_from_raw
624624 //
625625 // FIXME(#1817): Support Sized->Unsized and Unsized->Sized casts
626- pub ( crate ) fn cast_from_raw < Src , Dst > ( src : PtrInner < ' _ , Src > ) -> PtrInner < ' _ , Dst >
626+ pub ( crate ) fn cast_from_raw < Src , Dst , const ALLOW_SHRINK : bool > (
627+ src : PtrInner < ' _ , Src > ,
628+ ) -> PtrInner < ' _ , Dst >
627629 where
628630 Src : KnownLayout < PointerMetadata = usize > + ?Sized ,
629631 Dst : KnownLayout < PointerMetadata = usize > + ?Sized ,
@@ -694,12 +696,19 @@ mod cast_from_raw {
694696 /// `Src`'s alignment must not be smaller than `Dst`'s alignment.
695697 #[ derive( Copy , Clone ) ]
696698 struct CastParams {
697- offset_delta_elems : usize ,
698- elem_multiple : usize ,
699+ // `offset_delta / dst.elem_size = offset_delta_elems_num / denom`
700+ offset_delta_elems_num : usize ,
701+ // `src.elem_size / dst.elem_size = elem_multiple_num / denom`
702+ elem_multiple_num : usize ,
703+ denom : NonZeroUsize ,
699704 }
700705
701706 impl CastParams {
702- const fn try_compute ( src : & DstLayout , dst : & DstLayout ) -> Option < CastParams > {
707+ const fn try_compute (
708+ src : & DstLayout ,
709+ dst : & DstLayout ,
710+ allow_shrink : bool ,
711+ ) -> Option < CastParams > {
703712 if src. align . get ( ) < dst. align . get ( ) {
704713 return None ;
705714 }
@@ -724,33 +733,62 @@ mod cast_from_raw {
724733 return None ;
725734 } ;
726735
727- // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
728- #[ allow( clippy:: arithmetic_side_effects) ]
729- let delta_mod_other_elem = offset_delta % dst_elem_size. get ( ) ;
736+ const fn gcd ( a : usize , b : usize ) -> usize {
737+ if a == 0 {
738+ b
739+ } else {
740+ #[ allow( clippy:: arithmetic_side_effects) ]
741+ gcd ( b % a, a)
742+ }
743+ }
730744
731- // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
745+ let gcd = gcd ( gcd ( offset_delta, src. elem_size ) , dst_elem_size. get ( ) ) ;
746+ // PANICS: `dst_elem_size.get()` is non-zero, and so `denom`
747+ // will be non-zero.
748+
749+ #[ allow( clippy:: arithmetic_side_effects) ]
750+ let offset_delta_elems_num = offset_delta / gcd;
732751 #[ allow( clippy:: arithmetic_side_effects) ]
733- let elem_remainder = src. elem_size % dst_elem_size. get ( ) ;
752+ let elem_multiple_num = src. elem_size / gcd;
753+ // PANICS: `dst_elem_size` is non-zero, and `gcd` is no greater
754+ // than it by construction. Thus, this should be at least 1.
755+ let denom = match NonZeroUsize :: new ( dst_elem_size. get ( ) / gcd) {
756+ Some ( d) => d,
757+ None => const_panic ! ( "CastParams::try_compute: denom should be non-zero" ) ,
758+ } ;
734759
735- if delta_mod_other_elem != 0 || src. elem_size < dst. elem_size || elem_remainder != 0
736- {
760+ if denom. get ( ) != 1 && !allow_shrink {
737761 return None ;
738762 }
739763
740- // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
741- #[ allow( clippy:: arithmetic_side_effects) ]
742- let offset_delta_elems = offset_delta / dst_elem_size. get ( ) ;
764+ // // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
765+ // #[allow(clippy::arithmetic_side_effects)]
766+ // let delta_mod_other_elem = offset_delta % dst_elem_size.get();
743767
744- // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
745- #[ allow( clippy:: arithmetic_side_effects) ]
746- let elem_multiple = src. elem_size / dst_elem_size. get ( ) ;
768+ // // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
769+ // #[allow(clippy::arithmetic_side_effects)]
770+ // let elem_remainder = src.elem_size % dst_elem_size.get();
771+
772+ // if delta_mod_other_elem != 0 || src.elem_size < dst.elem_size || elem_remainder != 0
773+ // {
774+ // return None;
775+ // }
776+
777+ // // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
778+ // #[allow(clippy::arithmetic_side_effects)]
779+ // let offset_delta_elems = offset_delta / dst_elem_size.get();
780+
781+ // // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
782+ // #[allow(clippy::arithmetic_side_effects)]
783+ // let elem_multiple = src.elem_size / dst_elem_size.get();
747784
748785 // SAFETY: We checked above that `src.align >= dst.align`.
749786 Some ( CastParams {
750787 // SAFETY: We checked above that this is an exact ratio.
751- offset_delta_elems ,
788+ offset_delta_elems_num ,
752789 // SAFETY: We checked above that this is an exact ratio.
753- elem_multiple,
790+ elem_multiple_num,
791+ denom,
754792 } )
755793 }
756794
@@ -774,24 +812,25 @@ mod cast_from_raw {
774812 // metadata, this math will not overflow, and the returned value
775813 // will describe a `Dst` of the same size.
776814 #[ allow( unstable_name_collisions) ]
777- unsafe {
778- self . offset_delta_elems
779- . unchecked_add ( src_meta. unchecked_mul ( self . elem_multiple ) )
780- }
815+ let num = unsafe {
816+ self . offset_delta_elems_num
817+ . unchecked_add ( src_meta. unchecked_mul ( self . elem_multiple_num ) )
818+ } ;
819+ num / self . denom . get ( )
781820 }
782821 }
783822
784- trait Params < Src : ?Sized > {
823+ trait Params < Src : ?Sized , const ALLOW_SHRINK : bool > {
785824 const CAST_PARAMS : CastParams ;
786825 }
787826
788- impl < Src , Dst > Params < Src > for Dst
827+ impl < Src , Dst , const ALLOW_SHRINK : bool > Params < Src , ALLOW_SHRINK > for Dst
789828 where
790829 Src : KnownLayout + ?Sized ,
791830 Dst : KnownLayout < PointerMetadata = usize > + ?Sized ,
792831 {
793832 const CAST_PARAMS : CastParams =
794- match CastParams :: try_compute ( & Src :: LAYOUT , & Dst :: LAYOUT ) {
833+ match CastParams :: try_compute ( & Src :: LAYOUT , & Dst :: LAYOUT , ALLOW_SHRINK ) {
795834 Some ( params) => params,
796835 None => const_panic ! (
797836 "cannot `transmute_ref!` or `transmute_mut!` between incompatible types"
@@ -800,7 +839,7 @@ mod cast_from_raw {
800839 }
801840
802841 let src_meta = <Src as KnownLayout >:: pointer_to_metadata ( src. as_non_null ( ) . as_ptr ( ) ) ;
803- let params = <Dst as Params < Src > >:: CAST_PARAMS ;
842+ let params = <Dst as Params < Src , ALLOW_SHRINK > >:: CAST_PARAMS ;
804843
805844 // SAFETY: `src: PtrInner`, and so by invariant on `PtrInner`, `src`'s
806845 // referent is no larger than `isize::MAX`.
0 commit comments