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