Skip to main content

fbl/
sentinel.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub const CONTAINER_SENTINEL_BIT: usize = 1;
6
7/// Create a sentinel pointer from a raw pointer.
8#[inline]
9pub fn make_sentinel<T, U>(ptr: *mut U) -> *mut T {
10    const {
11        assert!(
12            core::mem::align_of::<T>() > 1,
13            "Type T must have alignment > 1 to be used with sentinel pointers"
14        )
15    };
16    ptr.map_addr(|addr| addr | CONTAINER_SENTINEL_BIT).cast()
17}
18
19/// Create a sentinel pointer from null.
20#[inline]
21pub const fn make_sentinel_null<T>() -> *mut T {
22    // In const fn, we can assert directly since it is evaluated in const context
23    assert!(
24        core::mem::align_of::<T>() > 1,
25        "Type T must have alignment > 1 to be used with sentinel pointers"
26    );
27    core::ptr::without_provenance_mut(CONTAINER_SENTINEL_BIT)
28}
29
30/// Turn a sentinel pointer back into a normal pointer.
31#[inline]
32pub fn unmake_sentinel<T, U>(sentinel: *mut U) -> *mut T {
33    const {
34        assert!(
35            core::mem::align_of::<T>() > 1,
36            "Type T must have alignment > 1 to be used with sentinel pointers"
37        )
38    };
39    sentinel.map_addr(|addr| addr & !CONTAINER_SENTINEL_BIT).cast()
40}
41
42/// Test to see if a pointer is a sentinel pointer.
43#[inline]
44pub fn is_sentinel_ptr<T>(ptr: *const T) -> bool {
45    const {
46        assert!(
47            core::mem::align_of::<T>() > 1,
48            "Type T must have alignment > 1 to be used with sentinel pointers"
49        )
50    };
51    (ptr.addr() & CONTAINER_SENTINEL_BIT) != 0
52}
53
54/// Test to see if a pointer (which may be a sentinel) is valid.
55/// Valid means it is not null and not a sentinel.
56#[inline]
57pub fn valid_sentinel_ptr<T>(ptr: *const T) -> bool {
58    const {
59        assert!(
60            core::mem::align_of::<T>() > 1,
61            "Type T must have alignment > 1 to be used with sentinel pointers"
62        )
63    };
64    !ptr.is_null() && !is_sentinel_ptr(ptr)
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_sentinel_machinery() {
73        let mut value = 42;
74        let ptr = &mut value as *mut i32;
75
76        assert!(!is_sentinel_ptr(ptr));
77        assert!(valid_sentinel_ptr(ptr));
78
79        let sent = make_sentinel::<i32, i32>(ptr);
80        assert!(is_sentinel_ptr(sent));
81        assert!(!valid_sentinel_ptr(sent));
82
83        let unmasked = unmake_sentinel::<i32, i32>(sent);
84        assert_eq!(unmasked, ptr);
85        assert!(!is_sentinel_ptr(unmasked));
86        assert!(valid_sentinel_ptr(unmasked));
87
88        let null_sent = make_sentinel_null::<i32>();
89        assert!(is_sentinel_ptr(null_sent));
90        assert!(!valid_sentinel_ptr(null_sent));
91        assert_eq!(null_sent as usize, 1);
92
93        let unmasked_null = unmake_sentinel::<i32, i32>(null_sent);
94        assert!(unmasked_null.is_null());
95    }
96}