Skip to content

Commit 148de67

Browse files
committed
add exact bitshifts
1 parent 8c27ba6 commit 148de67

File tree

2 files changed

+231
-0
lines changed

2 files changed

+231
-0
lines changed

core/src/num/int_macros.rs

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,6 +1413,66 @@ macro_rules! int_impl {
14131413
}
14141414
}
14151415

1416+
/// Exact shift left. Computes `self << rhs` as long as it can be reversed losslessly.
1417+
///
1418+
/// Returns `None` if any bits that would be shifted out differ from the resulting sign bit
1419+
/// or if `rhs` >=
1420+
#[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
1421+
/// Otherwise, returns `Some(self << rhs)`.
1422+
///
1423+
/// # Examples
1424+
///
1425+
/// ```
1426+
/// #![feature(exact_bitshifts)]
1427+
///
1428+
#[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(4), Some(0x10));")]
1429+
#[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(", stringify!($SelfT), "::BITS - 2), Some(1 << ", stringify!($SelfT), "::BITS - 2));")]
1430+
#[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(", stringify!($SelfT), "::BITS - 1), None);")]
1431+
#[doc = concat!("assert_eq!((-0x2", stringify!($SelfT), ").exact_shl(", stringify!($SelfT), "::BITS - 2), Some(-0x2 << ", stringify!($SelfT), "::BITS - 2));")]
1432+
#[doc = concat!("assert_eq!((-0x2", stringify!($SelfT), ").exact_shl(", stringify!($SelfT), "::BITS - 1), None);")]
1433+
/// ```
1434+
#[unstable(feature = "exact_bitshifts", issue = "144336")]
1435+
#[must_use = "this returns the result of the operation, \
1436+
without modifying the original"]
1437+
#[inline]
1438+
pub const fn exact_shl(self, rhs: u32) -> Option<$SelfT> {
1439+
if rhs < self.leading_zeros() || rhs < self.leading_ones() {
1440+
// SAFETY: rhs is checked above
1441+
Some(unsafe { self.unchecked_shl(rhs) })
1442+
} else {
1443+
None
1444+
}
1445+
}
1446+
1447+
/// Unchecked exact shift left. Computes `self << rhs`, assuming the operation can be
1448+
/// losslessly reversed and `rhs` cannot be larger than
1449+
#[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
1450+
///
1451+
/// # Safety
1452+
///
1453+
/// This results in undefined behavior when `rhs >= self.leading_zeros() && rhs >=
1454+
/// self.leading_ones()` i.e. when
1455+
#[doc = concat!("[`", stringify!($SelfT), "::exact_shl`]")]
1456+
/// would return `None`.
1457+
#[unstable(feature = "exact_bitshifts", issue = "144336")]
1458+
#[must_use = "this returns the result of the operation, \
1459+
without modifying the original"]
1460+
#[inline]
1461+
pub const unsafe fn unchecked_exact_shl(self, rhs: u32) -> $SelfT {
1462+
assert_unsafe_precondition!(
1463+
check_language_ub,
1464+
concat!(stringify!($SelfT), "::unchecked_exact_shl cannot shift out non-zero bits"),
1465+
(
1466+
zeros: u32 = self.leading_zeros(),
1467+
ones: u32 = self.leading_ones(),
1468+
rhs: u32 = rhs,
1469+
) => rhs < zeros || rhs < ones,
1470+
);
1471+
1472+
// SAFETY: this is guaranteed to be safe by the caller
1473+
unsafe { self.unchecked_shl(rhs) }
1474+
}
1475+
14161476
/// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is
14171477
/// larger than or equal to the number of bits in `self`.
14181478
///
@@ -1534,6 +1594,63 @@ macro_rules! int_impl {
15341594
}
15351595
}
15361596

1597+
/// Exact shift right. Computes `self >> rhs` as long as it can be reversed losslessly.
1598+
///
1599+
/// Returns `None` if any non-zero bits would be shifted out or if `rhs` >=
1600+
#[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
1601+
/// Otherwise, returns `Some(self >> rhs)`.
1602+
///
1603+
/// # Examples
1604+
///
1605+
/// ```
1606+
/// #![feature(exact_bitshifts)]
1607+
///
1608+
#[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(4), Some(0x1));")]
1609+
#[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(5), None);")]
1610+
/// ```
1611+
#[unstable(feature = "exact_bitshifts", issue = "144336")]
1612+
#[must_use = "this returns the result of the operation, \
1613+
without modifying the original"]
1614+
#[inline]
1615+
pub const fn exact_shr(self, rhs: u32) -> Option<$SelfT> {
1616+
if rhs <= self.trailing_zeros() && rhs < <$SelfT>::BITS {
1617+
// SAFETY: rhs is checked above
1618+
Some(unsafe { self.unchecked_shr(rhs) })
1619+
} else {
1620+
None
1621+
}
1622+
}
1623+
1624+
/// Unchecked exact shift right. Computes `self >> rhs`, assuming the operation can be
1625+
/// losslessly reversed and `rhs` cannot be larger than
1626+
#[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
1627+
///
1628+
/// # Safety
1629+
///
1630+
/// This results in undefined behavior when `rhs > self.trailing_zeros() || rhs >=
1631+
#[doc = concat!(stringify!($SelfT), "::BITS`")]
1632+
/// i.e. when
1633+
#[doc = concat!("[`", stringify!($SelfT), "::exact_shr`]")]
1634+
/// would return `None`.
1635+
#[unstable(feature = "exact_bitshifts", issue = "144336")]
1636+
#[must_use = "this returns the result of the operation, \
1637+
without modifying the original"]
1638+
#[inline]
1639+
pub const unsafe fn unchecked_exact_shr(self, rhs: u32) -> $SelfT {
1640+
assert_unsafe_precondition!(
1641+
check_language_ub,
1642+
concat!(stringify!($SelfT), "::unchecked_exact_shr cannot shift out non-zero bits"),
1643+
(
1644+
zeros: u32 = self.trailing_zeros(),
1645+
bits: u32 = <$SelfT>::BITS,
1646+
rhs: u32 = rhs,
1647+
) => rhs <= zeros && rhs < bits,
1648+
);
1649+
1650+
// SAFETY: this is guaranteed to be safe by the caller
1651+
unsafe { self.unchecked_shr(rhs) }
1652+
}
1653+
15371654
/// Checked absolute value. Computes `self.abs()`, returning `None` if
15381655
/// `self == MIN`.
15391656
///

core/src/num/uint_macros.rs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1734,6 +1734,63 @@ macro_rules! uint_impl {
17341734
}
17351735
}
17361736

1737+
/// Exact shift left. Computes `self << rhs` as long as it can be reversed losslessly.
1738+
///
1739+
/// Returns `None` if any non-zero bits would be shifted out or if `rhs` >=
1740+
#[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
1741+
/// Otherwise, returns `Some(self << rhs)`.
1742+
///
1743+
/// # Examples
1744+
///
1745+
/// ```
1746+
/// #![feature(exact_bitshifts)]
1747+
///
1748+
#[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(4), Some(0x10));")]
1749+
#[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".exact_shl(129), None);")]
1750+
/// ```
1751+
#[unstable(feature = "exact_bitshifts", issue = "144336")]
1752+
#[must_use = "this returns the result of the operation, \
1753+
without modifying the original"]
1754+
#[inline]
1755+
pub const fn exact_shl(self, rhs: u32) -> Option<$SelfT> {
1756+
if rhs <= self.leading_zeros() && rhs < <$SelfT>::BITS {
1757+
// SAFETY: rhs is checked above
1758+
Some(unsafe { self.unchecked_shl(rhs) })
1759+
} else {
1760+
None
1761+
}
1762+
}
1763+
1764+
/// Unchecked exact shift left. Computes `self << rhs`, assuming the operation can be
1765+
/// losslessly reversed `rhs` cannot be larger than
1766+
#[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
1767+
///
1768+
/// # Safety
1769+
///
1770+
/// This results in undefined behavior when `rhs > self.leading_zeros() || rhs >=
1771+
#[doc = concat!(stringify!($SelfT), "::BITS`")]
1772+
/// i.e. when
1773+
#[doc = concat!("[`", stringify!($SelfT), "::exact_shl`]")]
1774+
/// would return `None`.
1775+
#[unstable(feature = "exact_bitshifts", issue = "144336")]
1776+
#[must_use = "this returns the result of the operation, \
1777+
without modifying the original"]
1778+
#[inline]
1779+
pub const unsafe fn unchecked_exact_shl(self, rhs: u32) -> $SelfT {
1780+
assert_unsafe_precondition!(
1781+
check_language_ub,
1782+
concat!(stringify!($SelfT), "::exact_shl_unchecked cannot shift out non-zero bits"),
1783+
(
1784+
zeros: u32 = self.leading_zeros(),
1785+
bits: u32 = <$SelfT>::BITS,
1786+
rhs: u32 = rhs,
1787+
) => rhs <= zeros && rhs < bits,
1788+
);
1789+
1790+
// SAFETY: this is guaranteed to be safe by the caller
1791+
unsafe { self.unchecked_shl(rhs) }
1792+
}
1793+
17371794
/// Checked shift right. Computes `self >> rhs`, returning `None`
17381795
/// if `rhs` is larger than or equal to the number of bits in `self`.
17391796
///
@@ -1849,6 +1906,63 @@ macro_rules! uint_impl {
18491906
}
18501907
}
18511908

1909+
/// Exact shift right. Computes `self >> rhs` as long as it can be reversed losslessly.
1910+
///
1911+
/// Returns `None` if any non-zero bits would be shifted out or if `rhs` >=
1912+
#[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
1913+
/// Otherwise, returns `Some(self >> rhs)`.
1914+
///
1915+
/// # Examples
1916+
///
1917+
/// ```
1918+
/// #![feature(exact_bitshifts)]
1919+
///
1920+
#[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(4), Some(0x1));")]
1921+
#[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".exact_shr(5), None);")]
1922+
/// ```
1923+
#[unstable(feature = "exact_bitshifts", issue = "144336")]
1924+
#[must_use = "this returns the result of the operation, \
1925+
without modifying the original"]
1926+
#[inline]
1927+
pub const fn exact_shr(self, rhs: u32) -> Option<$SelfT> {
1928+
if rhs <= self.trailing_zeros() && rhs < <$SelfT>::BITS {
1929+
// SAFETY: rhs is checked above
1930+
Some(unsafe { self.unchecked_shr(rhs) })
1931+
} else {
1932+
None
1933+
}
1934+
}
1935+
1936+
/// Unchecked exact shift right. Computes `self >> rhs`, assuming the operation can be
1937+
/// losslessly reversed and `rhs` cannot be larger than
1938+
#[doc = concat!("`", stringify!($SelfT), "::BITS`.")]
1939+
///
1940+
/// # Safety
1941+
///
1942+
/// This results in undefined behavior when `rhs > self.trailing_zeros() || rhs >=
1943+
#[doc = concat!(stringify!($SelfT), "::BITS`")]
1944+
/// i.e. when
1945+
#[doc = concat!("[`", stringify!($SelfT), "::exact_shr`]")]
1946+
/// would return `None`.
1947+
#[unstable(feature = "exact_bitshifts", issue = "144336")]
1948+
#[must_use = "this returns the result of the operation, \
1949+
without modifying the original"]
1950+
#[inline]
1951+
pub const unsafe fn unchecked_exact_shr(self, rhs: u32) -> $SelfT {
1952+
assert_unsafe_precondition!(
1953+
check_language_ub,
1954+
concat!(stringify!($SelfT), "::exact_shr_unchecked cannot shift out non-zero bits"),
1955+
(
1956+
zeros: u32 = self.trailing_zeros(),
1957+
bits: u32 = <$SelfT>::BITS,
1958+
rhs: u32 = rhs,
1959+
) => rhs <= zeros && rhs < bits,
1960+
);
1961+
1962+
// SAFETY: this is guaranteed to be safe by the caller
1963+
unsafe { self.unchecked_shr(rhs) }
1964+
}
1965+
18521966
/// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if
18531967
/// overflow occurred.
18541968
///

0 commit comments

Comments
 (0)