@@ -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