@@ -623,7 +623,9 @@ mod cast_from_raw {
623
623
/// [cast_from_raw]: crate::pointer::SizeCompat::cast_from_raw
624
624
//
625
625
// 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 >
627
629
where
628
630
Src : KnownLayout < PointerMetadata = usize > + ?Sized ,
629
631
Dst : KnownLayout < PointerMetadata = usize > + ?Sized ,
@@ -694,12 +696,19 @@ mod cast_from_raw {
694
696
/// `Src`'s alignment must not be smaller than `Dst`'s alignment.
695
697
#[ derive( Copy , Clone ) ]
696
698
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 ,
699
704
}
700
705
701
706
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 > {
703
712
if src. align . get ( ) < dst. align . get ( ) {
704
713
return None ;
705
714
}
@@ -724,33 +733,60 @@ mod cast_from_raw {
724
733
return None ;
725
734
} ;
726
735
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
+ }
730
744
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;
732
751
#[ 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" ) ;
734
757
735
- if delta_mod_other_elem != 0 || src. elem_size < dst. elem_size || elem_remainder != 0
736
- {
758
+ if denom. get ( ) != 1 && !allow_shrink {
737
759
return None ;
738
760
}
739
761
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();
743
765
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();
747
782
748
783
// SAFETY: We checked above that `src.align >= dst.align`.
749
784
Some ( CastParams {
750
785
// SAFETY: We checked above that this is an exact ratio.
751
- offset_delta_elems ,
786
+ offset_delta_elems_num ,
752
787
// SAFETY: We checked above that this is an exact ratio.
753
- elem_multiple,
788
+ elem_multiple_num,
789
+ denom,
754
790
} )
755
791
}
756
792
@@ -774,24 +810,25 @@ mod cast_from_raw {
774
810
// metadata, this math will not overflow, and the returned value
775
811
// will describe a `Dst` of the same size.
776
812
#[ 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 ( )
781
818
}
782
819
}
783
820
784
- trait Params < Src : ?Sized > {
821
+ trait Params < const ALLOW_SHRINK : bool , Src : ?Sized > {
785
822
const CAST_PARAMS : CastParams ;
786
823
}
787
824
788
- impl < Src , Dst > Params < Src > for Dst
825
+ impl < const ALLOW_SHRINK : bool , Src , Dst > Params < ALLOW_SHRINK , Src > for Dst
789
826
where
790
827
Src : KnownLayout + ?Sized ,
791
828
Dst : KnownLayout < PointerMetadata = usize > + ?Sized ,
792
829
{
793
830
const CAST_PARAMS : CastParams =
794
- match CastParams :: try_compute ( & Src :: LAYOUT , & Dst :: LAYOUT ) {
831
+ match CastParams :: try_compute ( & Src :: LAYOUT , & Dst :: LAYOUT , ALLOW_SHRINK ) {
795
832
Some ( params) => params,
796
833
None => const_panic ! (
797
834
"cannot `transmute_ref!` or `transmute_mut!` between incompatible types"
@@ -800,7 +837,7 @@ mod cast_from_raw {
800
837
}
801
838
802
839
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 ;
804
841
805
842
// SAFETY: `src: PtrInner`, and so by invariant on `PtrInner`, `src`'s
806
843
// referent is no larger than `isize::MAX`.
0 commit comments