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