Skip to content

Commit e1b4e8a

Browse files
committed
Add is_aligned{,_to} convenience methods to NonNull
1 parent 2bcaa97 commit e1b4e8a

File tree

1 file changed

+234
-0
lines changed

1 file changed

+234
-0
lines changed

Diff for: library/core/src/ptr/non_null.rs

+234
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,240 @@ impl<T: ?Sized> NonNull<T> {
12821282
unsafe { ptr::align_offset(self.pointer, align) }
12831283
}
12841284
}
1285+
1286+
/// Returns whether the pointer is properly aligned for `T`.
1287+
///
1288+
/// # Examples
1289+
///
1290+
/// ```
1291+
/// #![feature(pointer_is_aligned)]
1292+
/// use std::ptr::NonNull;
1293+
///
1294+
/// // On some platforms, the alignment of i32 is less than 4.
1295+
/// #[repr(align(4))]
1296+
/// struct AlignedI32(i32);
1297+
///
1298+
/// let data = AlignedI32(42);
1299+
/// let ptr = NonNull::<AlignedI32>::from(&data);
1300+
///
1301+
/// assert!(ptr.is_aligned());
1302+
/// assert!(!NonNull::new(ptr.as_ptr().wrapping_byte_add(1)).unwrap().is_aligned());
1303+
/// ```
1304+
///
1305+
/// # At compiletime
1306+
/// **Note: Alignment at compiletime is experimental and subject to change. See the
1307+
/// [tracking issue] for details.**
1308+
///
1309+
/// At compiletime, the compiler may not know where a value will end up in memory.
1310+
/// Calling this function on a pointer created from a reference at compiletime will only
1311+
/// return `true` if the pointer is guaranteed to be aligned. This means that the pointer
1312+
/// is never aligned if cast to a type with a stricter alignment than the reference's
1313+
/// underlying allocation.
1314+
///
1315+
/// ```
1316+
/// #![feature(pointer_is_aligned)]
1317+
/// #![feature(const_pointer_is_aligned)]
1318+
/// #![feature(non_null_convenience)]
1319+
/// #![feature(const_option)]
1320+
/// #![feature(const_nonnull_new)]
1321+
/// use std::ptr::NonNull;
1322+
///
1323+
/// // On some platforms, the alignment of primitives is less than their size.
1324+
/// #[repr(align(4))]
1325+
/// struct AlignedI32(i32);
1326+
/// #[repr(align(8))]
1327+
/// struct AlignedI64(i64);
1328+
///
1329+
/// const _: () = {
1330+
/// let data = [AlignedI32(42), AlignedI32(42)];
1331+
/// let ptr = NonNull::<AlignedI32>::new(&data[0] as *const _ as *mut _).unwrap();
1332+
/// assert!(ptr.is_aligned());
1333+
///
1334+
/// // At runtime either `ptr1` or `ptr2` would be aligned, but at compiletime neither is aligned.
1335+
/// let ptr1 = ptr.cast::<AlignedI64>();
1336+
/// let ptr2 = unsafe { ptr.add(1).cast::<AlignedI64>() };
1337+
/// assert!(!ptr1.is_aligned());
1338+
/// assert!(!ptr2.is_aligned());
1339+
/// };
1340+
/// ```
1341+
///
1342+
/// Due to this behavior, it is possible that a runtime pointer derived from a compiletime
1343+
/// pointer is aligned, even if the compiletime pointer wasn't aligned.
1344+
///
1345+
/// ```
1346+
/// #![feature(pointer_is_aligned)]
1347+
/// #![feature(const_pointer_is_aligned)]
1348+
///
1349+
/// // On some platforms, the alignment of primitives is less than their size.
1350+
/// #[repr(align(4))]
1351+
/// struct AlignedI32(i32);
1352+
/// #[repr(align(8))]
1353+
/// struct AlignedI64(i64);
1354+
///
1355+
/// // At compiletime, neither `COMPTIME_PTR` nor `COMPTIME_PTR + 1` is aligned.
1356+
/// const COMPTIME_PTR: *const AlignedI32 = &AlignedI32(42);
1357+
/// const _: () = assert!(!COMPTIME_PTR.cast::<AlignedI64>().is_aligned());
1358+
/// const _: () = assert!(!COMPTIME_PTR.wrapping_add(1).cast::<AlignedI64>().is_aligned());
1359+
///
1360+
/// // At runtime, either `runtime_ptr` or `runtime_ptr + 1` is aligned.
1361+
/// let runtime_ptr = COMPTIME_PTR;
1362+
/// assert_ne!(
1363+
/// runtime_ptr.cast::<AlignedI64>().is_aligned(),
1364+
/// runtime_ptr.wrapping_add(1).cast::<AlignedI64>().is_aligned(),
1365+
/// );
1366+
/// ```
1367+
///
1368+
/// If a pointer is created from a fixed address, this function behaves the same during
1369+
/// runtime and compiletime.
1370+
///
1371+
/// ```
1372+
/// #![feature(pointer_is_aligned)]
1373+
/// #![feature(const_pointer_is_aligned)]
1374+
/// #![feature(const_option)]
1375+
/// #![feature(const_nonnull_new)]
1376+
/// use std::ptr::NonNull;
1377+
///
1378+
/// // On some platforms, the alignment of primitives is less than their size.
1379+
/// #[repr(align(4))]
1380+
/// struct AlignedI32(i32);
1381+
/// #[repr(align(8))]
1382+
/// struct AlignedI64(i64);
1383+
///
1384+
/// const _: () = {
1385+
/// let ptr = NonNull::new(40 as *mut AlignedI32).unwrap();
1386+
/// assert!(ptr.is_aligned());
1387+
///
1388+
/// // For pointers with a known address, runtime and compiletime behavior are identical.
1389+
/// let ptr1 = ptr.cast::<AlignedI64>();
1390+
/// let ptr2 = NonNull::new(ptr.as_ptr().wrapping_add(1)).unwrap().cast::<AlignedI64>();
1391+
/// assert!(ptr1.is_aligned());
1392+
/// assert!(!ptr2.is_aligned());
1393+
/// };
1394+
/// ```
1395+
///
1396+
/// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
1397+
#[unstable(feature = "pointer_is_aligned", issue = "96284")]
1398+
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
1399+
#[must_use]
1400+
#[inline]
1401+
pub const fn is_aligned(self) -> bool
1402+
where
1403+
T: Sized,
1404+
{
1405+
self.pointer.is_aligned()
1406+
}
1407+
1408+
/// Returns whether the pointer is aligned to `align`.
1409+
///
1410+
/// For non-`Sized` pointees this operation considers only the data pointer,
1411+
/// ignoring the metadata.
1412+
///
1413+
/// # Panics
1414+
///
1415+
/// The function panics if `align` is not a power-of-two (this includes 0).
1416+
///
1417+
/// # Examples
1418+
///
1419+
/// ```
1420+
/// #![feature(pointer_is_aligned)]
1421+
///
1422+
/// // On some platforms, the alignment of i32 is less than 4.
1423+
/// #[repr(align(4))]
1424+
/// struct AlignedI32(i32);
1425+
///
1426+
/// let data = AlignedI32(42);
1427+
/// let ptr = &data as *const AlignedI32;
1428+
///
1429+
/// assert!(ptr.is_aligned_to(1));
1430+
/// assert!(ptr.is_aligned_to(2));
1431+
/// assert!(ptr.is_aligned_to(4));
1432+
///
1433+
/// assert!(ptr.wrapping_byte_add(2).is_aligned_to(2));
1434+
/// assert!(!ptr.wrapping_byte_add(2).is_aligned_to(4));
1435+
///
1436+
/// assert_ne!(ptr.is_aligned_to(8), ptr.wrapping_add(1).is_aligned_to(8));
1437+
/// ```
1438+
///
1439+
/// # At compiletime
1440+
/// **Note: Alignment at compiletime is experimental and subject to change. See the
1441+
/// [tracking issue] for details.**
1442+
///
1443+
/// At compiletime, the compiler may not know where a value will end up in memory.
1444+
/// Calling this function on a pointer created from a reference at compiletime will only
1445+
/// return `true` if the pointer is guaranteed to be aligned. This means that the pointer
1446+
/// cannot be stricter aligned than the reference's underlying allocation.
1447+
///
1448+
/// ```
1449+
/// #![feature(pointer_is_aligned)]
1450+
/// #![feature(const_pointer_is_aligned)]
1451+
///
1452+
/// // On some platforms, the alignment of i32 is less than 4.
1453+
/// #[repr(align(4))]
1454+
/// struct AlignedI32(i32);
1455+
///
1456+
/// const _: () = {
1457+
/// let data = AlignedI32(42);
1458+
/// let ptr = &data as *const AlignedI32;
1459+
///
1460+
/// assert!(ptr.is_aligned_to(1));
1461+
/// assert!(ptr.is_aligned_to(2));
1462+
/// assert!(ptr.is_aligned_to(4));
1463+
///
1464+
/// // At compiletime, we know for sure that the pointer isn't aligned to 8.
1465+
/// assert!(!ptr.is_aligned_to(8));
1466+
/// assert!(!ptr.wrapping_add(1).is_aligned_to(8));
1467+
/// };
1468+
/// ```
1469+
///
1470+
/// Due to this behavior, it is possible that a runtime pointer derived from a compiletime
1471+
/// pointer is aligned, even if the compiletime pointer wasn't aligned.
1472+
///
1473+
/// ```
1474+
/// #![feature(pointer_is_aligned)]
1475+
/// #![feature(const_pointer_is_aligned)]
1476+
///
1477+
/// // On some platforms, the alignment of i32 is less than 4.
1478+
/// #[repr(align(4))]
1479+
/// struct AlignedI32(i32);
1480+
///
1481+
/// // At compiletime, neither `COMPTIME_PTR` nor `COMPTIME_PTR + 1` is aligned.
1482+
/// const COMPTIME_PTR: *const AlignedI32 = &AlignedI32(42);
1483+
/// const _: () = assert!(!COMPTIME_PTR.is_aligned_to(8));
1484+
/// const _: () = assert!(!COMPTIME_PTR.wrapping_add(1).is_aligned_to(8));
1485+
///
1486+
/// // At runtime, either `runtime_ptr` or `runtime_ptr + 1` is aligned.
1487+
/// let runtime_ptr = COMPTIME_PTR;
1488+
/// assert_ne!(
1489+
/// runtime_ptr.is_aligned_to(8),
1490+
/// runtime_ptr.wrapping_add(1).is_aligned_to(8),
1491+
/// );
1492+
/// ```
1493+
///
1494+
/// If a pointer is created from a fixed address, this function behaves the same during
1495+
/// runtime and compiletime.
1496+
///
1497+
/// ```
1498+
/// #![feature(pointer_is_aligned)]
1499+
/// #![feature(const_pointer_is_aligned)]
1500+
///
1501+
/// const _: () = {
1502+
/// let ptr = 40 as *const u8;
1503+
/// assert!(ptr.is_aligned_to(1));
1504+
/// assert!(ptr.is_aligned_to(2));
1505+
/// assert!(ptr.is_aligned_to(4));
1506+
/// assert!(ptr.is_aligned_to(8));
1507+
/// assert!(!ptr.is_aligned_to(16));
1508+
/// };
1509+
/// ```
1510+
///
1511+
/// [tracking issue]: https://github.com/rust-lang/rust/issues/104203
1512+
#[unstable(feature = "pointer_is_aligned", issue = "96284")]
1513+
#[rustc_const_unstable(feature = "const_pointer_is_aligned", issue = "104203")]
1514+
#[must_use]
1515+
#[inline]
1516+
pub const fn is_aligned_to(self, align: usize) -> bool {
1517+
self.pointer.is_aligned_to(align)
1518+
}
12851519
}
12861520

12871521
impl<T> NonNull<[T]> {

0 commit comments

Comments
 (0)