| 1 | //! `num_conv` is a crate to convert between integer types without using `as` casts. This provides |
| 2 | //! better certainty when refactoring, makes the exact behavior of code more explicit, and allows |
| 3 | //! using turbofish syntax. |
| 4 | |
| 5 | #![no_std ] |
| 6 | |
| 7 | /// Anonymously import all extension traits. |
| 8 | /// |
| 9 | /// This allows you to use the methods without worrying about polluting the namespace or importing |
| 10 | /// them individually. |
| 11 | /// |
| 12 | /// ```rust |
| 13 | /// use num_conv::prelude::*; |
| 14 | /// ``` |
| 15 | pub mod prelude { |
| 16 | pub use crate::{CastSigned as _, CastUnsigned as _, Extend as _, Truncate as _}; |
| 17 | } |
| 18 | |
| 19 | mod sealed { |
| 20 | pub trait Integer {} |
| 21 | |
| 22 | macro_rules! impl_integer { |
| 23 | ($($t:ty)*) => {$( |
| 24 | impl Integer for $t {} |
| 25 | )*}; |
| 26 | } |
| 27 | |
| 28 | impl_integer! { |
| 29 | u8 u16 u32 u64 u128 usize |
| 30 | i8 i16 i32 i64 i128 isize |
| 31 | } |
| 32 | |
| 33 | pub trait ExtendTargetSealed<T> { |
| 34 | fn extend(self) -> T; |
| 35 | } |
| 36 | |
| 37 | pub trait TruncateTargetSealed<T> { |
| 38 | fn truncate(self) -> T; |
| 39 | } |
| 40 | } |
| 41 | |
| 42 | /// Cast to a signed integer of the same size. |
| 43 | /// |
| 44 | /// This trait is implemented for all integers. Unsigned to signed casts are equivalent to |
| 45 | /// `0.wrapping_add_signed(value)`, while signed to signed casts are an identity conversion. |
| 46 | /// |
| 47 | /// ```rust |
| 48 | /// # use num_conv::CastSigned; |
| 49 | /// assert_eq!(u8::MAX.cast_signed(), -1_i8); |
| 50 | /// assert_eq!(u16::MAX.cast_signed(), -1_i16); |
| 51 | /// assert_eq!(u32::MAX.cast_signed(), -1_i32); |
| 52 | /// assert_eq!(u64::MAX.cast_signed(), -1_i64); |
| 53 | /// assert_eq!(u128::MAX.cast_signed(), -1_i128); |
| 54 | /// assert_eq!(usize::MAX.cast_signed(), -1_isize); |
| 55 | /// ``` |
| 56 | /// |
| 57 | /// ```rust |
| 58 | /// # use num_conv::CastSigned; |
| 59 | /// assert_eq!(0_i8.cast_signed(), 0_i8); |
| 60 | /// assert_eq!(0_i16.cast_signed(), 0_i16); |
| 61 | /// assert_eq!(0_i32.cast_signed(), 0_i32); |
| 62 | /// assert_eq!(0_i64.cast_signed(), 0_i64); |
| 63 | /// assert_eq!(0_i128.cast_signed(), 0_i128); |
| 64 | /// assert_eq!(0_isize.cast_signed(), 0_isize); |
| 65 | /// ``` |
| 66 | pub trait CastSigned: sealed::Integer { |
| 67 | /// The signed integer type with the same size as `Self`. |
| 68 | type Signed; |
| 69 | |
| 70 | /// Cast an integer to the signed integer of the same size. |
| 71 | fn cast_signed(self) -> Self::Signed; |
| 72 | } |
| 73 | |
| 74 | /// Cast to an unsigned integer of the same size. |
| 75 | /// |
| 76 | /// This trait is implemented for all integers. Signed to unsigned casts are equivalent to |
| 77 | /// `0.wrapping_add_unsigned(value)`, while unsigned to unsigned casts are an identity conversion. |
| 78 | /// |
| 79 | /// ```rust |
| 80 | /// # use num_conv::CastUnsigned; |
| 81 | /// assert_eq!((-1_i8).cast_unsigned(), u8::MAX); |
| 82 | /// assert_eq!((-1_i16).cast_unsigned(), u16::MAX); |
| 83 | /// assert_eq!((-1_i32).cast_unsigned(), u32::MAX); |
| 84 | /// assert_eq!((-1_i64).cast_unsigned(), u64::MAX); |
| 85 | /// assert_eq!((-1_i128).cast_unsigned(), u128::MAX); |
| 86 | /// assert_eq!((-1_isize).cast_unsigned(), usize::MAX); |
| 87 | /// ``` |
| 88 | /// |
| 89 | /// ```rust |
| 90 | /// # use num_conv::CastUnsigned; |
| 91 | /// assert_eq!(0_u8.cast_unsigned(), 0_u8); |
| 92 | /// assert_eq!(0_u16.cast_unsigned(), 0_u16); |
| 93 | /// assert_eq!(0_u32.cast_unsigned(), 0_u32); |
| 94 | /// assert_eq!(0_u64.cast_unsigned(), 0_u64); |
| 95 | /// assert_eq!(0_u128.cast_unsigned(), 0_u128); |
| 96 | /// assert_eq!(0_usize.cast_unsigned(), 0_usize); |
| 97 | /// ``` |
| 98 | pub trait CastUnsigned: sealed::Integer { |
| 99 | /// The unsigned integer type with the same size as `Self`. |
| 100 | type Unsigned; |
| 101 | |
| 102 | /// Cast an integer to the unsigned integer of the same size. |
| 103 | fn cast_unsigned(self) -> Self::Unsigned; |
| 104 | } |
| 105 | |
| 106 | /// A type that can be used with turbofish syntax in [`Extend::extend`]. |
| 107 | /// |
| 108 | /// It is unlikely that you will want to use this trait directly. You are probably looking for the |
| 109 | /// [`Extend`] trait. |
| 110 | pub trait ExtendTarget<T>: sealed::ExtendTargetSealed<T> {} |
| 111 | |
| 112 | /// A type that can be used with turbofish syntax in [`Truncate::truncate`]. |
| 113 | /// |
| 114 | /// It is unlikely that you will want to use this trait directly. You are probably looking for the |
| 115 | /// [`Truncate`] trait. |
| 116 | pub trait TruncateTarget<T>: sealed::TruncateTargetSealed<T> {} |
| 117 | |
| 118 | /// Extend to an integer of the same size or larger, preserving its value. |
| 119 | /// |
| 120 | /// ```rust |
| 121 | /// # use num_conv::Extend; |
| 122 | /// assert_eq!(0_u8.extend::<u16>(), 0_u16); |
| 123 | /// assert_eq!(0_u16.extend::<u32>(), 0_u32); |
| 124 | /// assert_eq!(0_u32.extend::<u64>(), 0_u64); |
| 125 | /// assert_eq!(0_u64.extend::<u128>(), 0_u128); |
| 126 | /// ``` |
| 127 | /// |
| 128 | /// ```rust |
| 129 | /// # use num_conv::Extend; |
| 130 | /// assert_eq!((-1_i8).extend::<i16>(), -1_i16); |
| 131 | /// assert_eq!((-1_i16).extend::<i32>(), -1_i32); |
| 132 | /// assert_eq!((-1_i32).extend::<i64>(), -1_i64); |
| 133 | /// assert_eq!((-1_i64).extend::<i128>(), -1_i128); |
| 134 | /// ``` |
| 135 | pub trait Extend: sealed::Integer { |
| 136 | /// Extend an integer to an integer of the same size or larger, preserving its value. |
| 137 | fn extend<T>(self) -> T |
| 138 | where |
| 139 | Self: ExtendTarget<T>; |
| 140 | } |
| 141 | |
| 142 | impl<T: sealed::Integer> Extend for T { |
| 143 | fn extend<U>(self) -> U |
| 144 | where |
| 145 | T: ExtendTarget<U>, |
| 146 | { |
| 147 | sealed::ExtendTargetSealed::extend(self) |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | /// Truncate to an integer of the same size or smaller, preserving the least significant bits. |
| 152 | /// |
| 153 | /// ```rust |
| 154 | /// # use num_conv::Truncate; |
| 155 | /// assert_eq!(u16::MAX.truncate::<u8>(), u8::MAX); |
| 156 | /// assert_eq!(u32::MAX.truncate::<u16>(), u16::MAX); |
| 157 | /// assert_eq!(u64::MAX.truncate::<u32>(), u32::MAX); |
| 158 | /// assert_eq!(u128::MAX.truncate::<u64>(), u64::MAX); |
| 159 | /// ``` |
| 160 | /// |
| 161 | /// ```rust |
| 162 | /// # use num_conv::Truncate; |
| 163 | /// assert_eq!((-1_i16).truncate::<i8>(), -1_i8); |
| 164 | /// assert_eq!((-1_i32).truncate::<i16>(), -1_i16); |
| 165 | /// assert_eq!((-1_i64).truncate::<i32>(), -1_i32); |
| 166 | /// assert_eq!((-1_i128).truncate::<i64>(), -1_i64); |
| 167 | /// ``` |
| 168 | pub trait Truncate: sealed::Integer { |
| 169 | /// Truncate an integer to an integer of the same size or smaller, preserving the least |
| 170 | /// significant bits. |
| 171 | fn truncate<T>(self) -> T |
| 172 | where |
| 173 | Self: TruncateTarget<T>; |
| 174 | } |
| 175 | |
| 176 | impl<T: sealed::Integer> Truncate for T { |
| 177 | fn truncate<U>(self) -> U |
| 178 | where |
| 179 | T: TruncateTarget<U>, |
| 180 | { |
| 181 | sealed::TruncateTargetSealed::truncate(self) |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | macro_rules! impl_cast_signed { |
| 186 | ($($($from:ty),+ => $to:ty;)*) => {$($( |
| 187 | const _: () = assert!( |
| 188 | core::mem::size_of::<$from>() == core::mem::size_of::<$to>(), |
| 189 | concat!( |
| 190 | "cannot cast " , |
| 191 | stringify!($from), |
| 192 | " to " , |
| 193 | stringify!($to), |
| 194 | " because they are different sizes" |
| 195 | ) |
| 196 | ); |
| 197 | |
| 198 | impl CastSigned for $from { |
| 199 | type Signed = $to; |
| 200 | fn cast_signed(self) -> Self::Signed { |
| 201 | self as _ |
| 202 | } |
| 203 | } |
| 204 | )+)*}; |
| 205 | } |
| 206 | |
| 207 | macro_rules! impl_cast_unsigned { |
| 208 | ($($($from:ty),+ => $to:ty;)*) => {$($( |
| 209 | const _: () = assert!( |
| 210 | core::mem::size_of::<$from>() == core::mem::size_of::<$to>(), |
| 211 | concat!( |
| 212 | "cannot cast " , |
| 213 | stringify!($from), |
| 214 | " to " , |
| 215 | stringify!($to), |
| 216 | " because they are different sizes" |
| 217 | ) |
| 218 | ); |
| 219 | |
| 220 | impl CastUnsigned for $from { |
| 221 | type Unsigned = $to; |
| 222 | fn cast_unsigned(self) -> Self::Unsigned { |
| 223 | self as _ |
| 224 | } |
| 225 | } |
| 226 | )+)*}; |
| 227 | } |
| 228 | |
| 229 | macro_rules! impl_extend { |
| 230 | ($($from:ty => $($to:ty),+;)*) => {$($( |
| 231 | const _: () = assert!( |
| 232 | core::mem::size_of::<$from>() <= core::mem::size_of::<$to>(), |
| 233 | concat!( |
| 234 | "cannot extend " , |
| 235 | stringify!($from), |
| 236 | " to " , |
| 237 | stringify!($to), |
| 238 | " because " , |
| 239 | stringify!($from), |
| 240 | " is larger than " , |
| 241 | stringify!($to) |
| 242 | ) |
| 243 | ); |
| 244 | |
| 245 | impl sealed::ExtendTargetSealed<$to> for $from { |
| 246 | fn extend(self) -> $to { |
| 247 | self as _ |
| 248 | } |
| 249 | } |
| 250 | |
| 251 | impl ExtendTarget<$to> for $from {} |
| 252 | )+)*}; |
| 253 | } |
| 254 | |
| 255 | macro_rules! impl_truncate { |
| 256 | ($($($from:ty),+ => $to:ty;)*) => {$($( |
| 257 | const _: () = assert!( |
| 258 | core::mem::size_of::<$from>() >= core::mem::size_of::<$to>(), |
| 259 | concat!( |
| 260 | "cannot truncate " , |
| 261 | stringify!($from), |
| 262 | " to " , |
| 263 | stringify!($to), |
| 264 | " because " , |
| 265 | stringify!($from), |
| 266 | " is smaller than " , |
| 267 | stringify!($to) |
| 268 | ) |
| 269 | ); |
| 270 | |
| 271 | impl sealed::TruncateTargetSealed<$to> for $from { |
| 272 | fn truncate(self) -> $to { |
| 273 | self as _ |
| 274 | } |
| 275 | } |
| 276 | |
| 277 | impl TruncateTarget<$to> for $from {} |
| 278 | )+)*}; |
| 279 | } |
| 280 | |
| 281 | impl_cast_signed! { |
| 282 | u8, i8 => i8; |
| 283 | u16, i16 => i16; |
| 284 | u32, i32 => i32; |
| 285 | u64, i64 => i64; |
| 286 | u128, i128 => i128; |
| 287 | usize, isize => isize; |
| 288 | } |
| 289 | |
| 290 | impl_cast_unsigned! { |
| 291 | u8, i8 => u8; |
| 292 | u16, i16 => u16; |
| 293 | u32, i32 => u32; |
| 294 | u64, i64 => u64; |
| 295 | u128, i128 => u128; |
| 296 | usize, isize => usize; |
| 297 | } |
| 298 | |
| 299 | impl_extend! { |
| 300 | u8 => u8, u16, u32, u64, u128, usize; |
| 301 | u16 => u16, u32, u64, u128, usize; |
| 302 | u32 => u32, u64, u128; |
| 303 | u64 => u64, u128; |
| 304 | u128 => u128; |
| 305 | usize => usize; |
| 306 | |
| 307 | i8 => i8, i16, i32, i64, i128, isize; |
| 308 | i16 => i16, i32, i64, i128, isize; |
| 309 | i32 => i32, i64, i128; |
| 310 | i64 => i64, i128; |
| 311 | i128 => i128; |
| 312 | isize => isize; |
| 313 | } |
| 314 | |
| 315 | impl_truncate! { |
| 316 | u8, u16, u32, u64, u128, usize => u8; |
| 317 | u16, u32, u64, u128, usize => u16; |
| 318 | u32, u64, u128 => u32; |
| 319 | u64, u128 => u64; |
| 320 | u128 => u128; |
| 321 | usize => usize; |
| 322 | |
| 323 | i8, i16, i32, i64, i128, isize => i8; |
| 324 | i16, i32, i64, i128, isize => i16; |
| 325 | i32, i64, i128 => i32; |
| 326 | i64, i128 => i64; |
| 327 | i128 => i128; |
| 328 | isize => isize; |
| 329 | } |
| 330 | |