@@ -623,7 +623,9 @@ mod cast_from_raw {
623623 /// [cast_from_raw]: crate::pointer::SizeCompat::cast_from_raw
624624 //
625625 // TODO(#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 < const ALLOW_SHRINK : bool , Src , Dst > (
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,60 @@ 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 = NonZeroUsize :: new ( dst_elem_size. get ( ) / gcd)
756+ . expect ( "CastParams::try_compute: denom should be non-zero" ) ;
734757
735- if delta_mod_other_elem != 0 || src. elem_size < dst. elem_size || elem_remainder != 0
736- {
758+ if denom. get ( ) != 1 && !allow_shrink {
737759 return None ;
738760 }
739761
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 ( ) ;
762+ // // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
763+ // #[allow(clippy::arithmetic_side_effects)]
764+ // let delta_mod_other_elem = offset_delta % dst_elem_size.get();
743765
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 ( ) ;
766+ // // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
767+ // #[allow(clippy::arithmetic_side_effects)]
768+ // let elem_remainder = src.elem_size % dst_elem_size.get();
769+
770+ // if delta_mod_other_elem != 0 || src.elem_size < dst.elem_size || elem_remainder != 0
771+ // {
772+ // return None;
773+ // }
774+
775+ // // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
776+ // #[allow(clippy::arithmetic_side_effects)]
777+ // let offset_delta_elems = offset_delta / dst_elem_size.get();
778+
779+ // // PANICS: `dst_elem_size: NonZeroUsize`, so this won't div by zero.
780+ // #[allow(clippy::arithmetic_side_effects)]
781+ // let elem_multiple = src.elem_size / dst_elem_size.get();
747782
748783 // SAFETY: We checked above that `src.align >= dst.align`.
749784 Some ( CastParams {
750785 // SAFETY: We checked above that this is an exact ratio.
751- offset_delta_elems ,
786+ offset_delta_elems_num ,
752787 // SAFETY: We checked above that this is an exact ratio.
753- elem_multiple,
788+ elem_multiple_num,
789+ denom,
754790 } )
755791 }
756792
@@ -774,24 +810,25 @@ mod cast_from_raw {
774810 // metadata, this math will not overflow, and the returned value
775811 // will describe a `Dst` of the same size.
776812 #[ allow( unstable_name_collisions) ]
777- unsafe {
778- self . offset_delta_elems
779- . unchecked_add ( src_meta. unchecked_mul ( self . elem_multiple ) )
780- }
813+ let num = unsafe {
814+ self . offset_delta_elems_num
815+ . unchecked_add ( src_meta. unchecked_mul ( self . elem_multiple_num ) )
816+ } ;
817+ num / self . denom . get ( )
781818 }
782819 }
783820
784- trait Params < Src : ?Sized > {
821+ trait Params < const ALLOW_SHRINK : bool , Src : ?Sized > {
785822 const CAST_PARAMS : CastParams ;
786823 }
787824
788- impl < Src , Dst > Params < Src > for Dst
825+ impl < const ALLOW_SHRINK : bool , Src , Dst > Params < ALLOW_SHRINK , Src > for Dst
789826 where
790827 Src : KnownLayout + ?Sized ,
791828 Dst : KnownLayout < PointerMetadata = usize > + ?Sized ,
792829 {
793830 const CAST_PARAMS : CastParams =
794- match CastParams :: try_compute ( & Src :: LAYOUT , & Dst :: LAYOUT ) {
831+ match CastParams :: try_compute ( & Src :: LAYOUT , & Dst :: LAYOUT , ALLOW_SHRINK ) {
795832 Some ( params) => params,
796833 None => const_panic ! (
797834 "cannot `transmute_ref!` or `transmute_mut!` between incompatible types"
@@ -800,7 +837,7 @@ mod cast_from_raw {
800837 }
801838
802839 let src_meta = <Src as KnownLayout >:: pointer_to_metadata ( src. as_non_null ( ) . as_ptr ( ) ) ;
803- let params = <Dst as Params < Src > >:: CAST_PARAMS ;
840+ let params = <Dst as Params < ALLOW_SHRINK , Src > >:: CAST_PARAMS ;
804841
805842 // SAFETY: `src: PtrInner`, and so by invariant on `PtrInner`, `src`'s
806843 // referent is no larger than `isize::MAX`.
0 commit comments