@@ -623,7 +623,9 @@ mod cast_from_raw {
623
623
/// [cast_from_raw]: crate::pointer::SizeCompat::cast_from_raw
624
624
//
625
625
// 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 >
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,62 @@ 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 = 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
+ } ;
734
759
735
- if delta_mod_other_elem != 0 || src. elem_size < dst. elem_size || elem_remainder != 0
736
- {
760
+ if denom. get ( ) != 1 && !allow_shrink {
737
761
return None ;
738
762
}
739
763
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();
743
767
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();
747
784
748
785
// SAFETY: We checked above that `src.align >= dst.align`.
749
786
Some ( CastParams {
750
787
// SAFETY: We checked above that this is an exact ratio.
751
- offset_delta_elems ,
788
+ offset_delta_elems_num ,
752
789
// SAFETY: We checked above that this is an exact ratio.
753
- elem_multiple,
790
+ elem_multiple_num,
791
+ denom,
754
792
} )
755
793
}
756
794
@@ -774,24 +812,25 @@ mod cast_from_raw {
774
812
// metadata, this math will not overflow, and the returned value
775
813
// will describe a `Dst` of the same size.
776
814
#[ 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 ( )
781
820
}
782
821
}
783
822
784
- trait Params < Src : ?Sized > {
823
+ trait Params < Src : ?Sized , const ALLOW_SHRINK : bool > {
785
824
const CAST_PARAMS : CastParams ;
786
825
}
787
826
788
- impl < Src , Dst > Params < Src > for Dst
827
+ impl < Src , Dst , const ALLOW_SHRINK : bool > Params < Src , ALLOW_SHRINK > for Dst
789
828
where
790
829
Src : KnownLayout + ?Sized ,
791
830
Dst : KnownLayout < PointerMetadata = usize > + ?Sized ,
792
831
{
793
832
const CAST_PARAMS : CastParams =
794
- match CastParams :: try_compute ( & Src :: LAYOUT , & Dst :: LAYOUT ) {
833
+ match CastParams :: try_compute ( & Src :: LAYOUT , & Dst :: LAYOUT , ALLOW_SHRINK ) {
795
834
Some ( params) => params,
796
835
None => const_panic ! (
797
836
"cannot `transmute_ref!` or `transmute_mut!` between incompatible types"
@@ -800,7 +839,7 @@ mod cast_from_raw {
800
839
}
801
840
802
841
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 ;
804
843
805
844
// SAFETY: `src: PtrInner`, and so by invariant on `PtrInner`, `src`'s
806
845
// referent is no larger than `isize::MAX`.
0 commit comments