sentinel/
sentinel.rs

1/// Types which have a sentinel value.
2///
3/// # Safety
4///
5/// The associated [`is_sentinel`] method must be pure. For any given input, it must either always
6/// return `true`, or always return `false`.
7///
8/// The associated [`find_sentinel`] method must be coherent with the [`is_sentinel`] method. It
9/// must return the smallest index such that evaluating [`is_sentinel`] on the value returns
10/// `true`. Same for [`find_sentinel_infinite`].
11///
12/// [`SENTINEL`] must be a sentinel value. It must consistently return `true` when passed to
13/// [`is_sentinel`].
14///
15/// [`is_sentinel`]: Sentinel::is_sentinel
16/// [`find_sentinel`]: Sentinel::find_sentinel
17/// [`find_sentinel_infinite`]: Sentinel::find_sentinel_infinite
18/// [`SENTINEL`]: Sentinel::SENTINEL
19pub unsafe trait Sentinel: Sized {
20    /// A sentinel value of this type.
21    const SENTINEL: Self = Self::SENTINEL;
22
23    /// If the type has a special "unwrapped" version which does not include the sentinel value as
24    /// a valid value, it should be defined here.
25    type Unwrapped;
26
27    /// Unwraps a non-sentinel value.
28    ///
29    /// If the value is not a sentinel, [`Some(_)`] is returned.
30    #[inline]
31    fn unwrap_sentinel(this: Self) -> Option<Self::Unwrapped> {
32        if Self::is_sentinel(&this) {
33            None
34        } else {
35            // SAFETY:
36            //  We just made sure that `this` is not a sentinel.
37            Some(unsafe { Self::unwrap_sentinel_unchecked(this) })
38        }
39    }
40
41    /// Unwraps a non-sentinel value without checking whether it is actually a sentinel or not.
42    ///
43    /// # Safety
44    ///
45    /// The provided `this` must not be a sentinel value.
46    #[inline]
47    unsafe fn unwrap_sentinel_unchecked(this: Self) -> Self::Unwrapped {
48        unsafe { Self::unwrap_sentinel(this).unwrap_unchecked() }
49    }
50
51    /// Determines whether `value` is a sentinel value.
52    fn is_sentinel(this: &Self) -> bool;
53
54    /// Returns the index of the first sentinel found starting at `start`.
55    ///
56    /// # Safety
57    ///
58    /// A sentinel value must exist in the allocated object referenced by the pointer. Every
59    /// element up to (and including) the sentinel, must be initialized and valid for reads.
60    unsafe fn find_sentinel_infinite(start: *const Self) -> usize {
61        let mut index = 0;
62        while !Self::is_sentinel(unsafe { &*start.add(index) }) {
63            index = index.wrapping_add(1);
64        }
65        index
66    }
67
68    /// Returns the index of the first sentinel value of the provied slice.
69    ///
70    /// If the sentinel is not found, [`None`] is returned.
71    #[inline]
72    fn find_sentinel(slice: &[Self]) -> Option<usize> {
73        slice.iter().position(Self::is_sentinel)
74    }
75}
76
77unsafe impl Sentinel for u8 {
78    const SENTINEL: Self = 0u8;
79
80    type Unwrapped = core::num::NonZeroU8;
81
82    #[inline(always)]
83    fn unwrap_sentinel(this: Self) -> Option<Self::Unwrapped> {
84        core::num::NonZeroU8::new(this)
85    }
86
87    #[inline(always)]
88    unsafe fn unwrap_sentinel_unchecked(this: Self) -> Self::Unwrapped {
89        unsafe { core::num::NonZeroU8::new_unchecked(this) }
90    }
91
92    #[inline(always)]
93    fn is_sentinel(value: &u8) -> bool {
94        *value == 0
95    }
96
97    #[inline(always)]
98    #[cfg(all(feature = "memchr", not(feature = "libc")))]
99    fn find_sentinel(slice: &[u8]) -> Option<usize> {
100        memchr::memchr(0, slice)
101    }
102
103    #[inline(always)]
104    #[cfg(feature = "libc")]
105    fn find_sentinel(slice: &[u8]) -> Option<usize> {
106        let ret =
107            unsafe { libc::memchr(slice.as_ptr() as _, b'\0' as _, slice.len()) as *const u8 };
108        if ret.is_null() {
109            None
110        } else {
111            Some(unsafe { ret.offset_from(slice.as_ptr()) } as usize)
112        }
113    }
114
115    #[cfg(feature = "libc")]
116    #[inline(always)]
117    unsafe fn find_sentinel_infinite(slice: *const u8) -> usize {
118        unsafe { libc::strlen(slice as _) }
119    }
120}
121
122unsafe impl Sentinel for i8 {
123    const SENTINEL: Self = 0i8;
124
125    type Unwrapped = core::num::NonZeroI8;
126
127    #[inline(always)]
128    fn unwrap_sentinel(this: Self) -> Option<Self::Unwrapped> {
129        core::num::NonZeroI8::new(this)
130    }
131
132    #[inline(always)]
133    unsafe fn unwrap_sentinel_unchecked(this: Self) -> Self::Unwrapped {
134        unsafe { core::num::NonZeroI8::new_unchecked(this) }
135    }
136
137    #[inline(always)]
138    fn is_sentinel(value: &i8) -> bool {
139        *value == 0
140    }
141
142    #[inline(always)]
143    #[cfg(all(feature = "memchr", not(feature = "libc")))]
144    fn find_sentinel(slice: &[i8]) -> Option<usize> {
145        unsafe { memchr::memchr(0, &*(slice as *const [i8] as *const [u8])) }
146    }
147
148    #[inline(always)]
149    #[cfg(feature = "libc")]
150    fn find_sentinel(slice: &[i8]) -> Option<usize> {
151        let ret =
152            unsafe { libc::memchr(slice.as_ptr() as _, b'\0' as _, slice.len()) as *const i8 };
153        if ret.is_null() {
154            None
155        } else {
156            Some(unsafe { ret.offset_from(slice.as_ptr()) } as usize)
157        }
158    }
159
160    #[cfg(feature = "libc")]
161    #[inline(always)]
162    unsafe fn find_sentinel_infinite(slice: *const i8) -> usize {
163        unsafe { libc::strlen(slice as _) }
164    }
165}
166
167unsafe impl Sentinel for bool {
168    const SENTINEL: Self = false;
169
170    type Unwrapped = bool;
171
172    #[inline(always)]
173    fn unwrap_sentinel(this: Self) -> Option<Self::Unwrapped> {
174        Some(this)
175    }
176
177    #[inline(always)]
178    unsafe fn unwrap_sentinel_unchecked(this: Self) -> Self::Unwrapped {
179        this
180    }
181
182    #[inline(always)]
183    fn is_sentinel(value: &bool) -> bool {
184        !*value
185    }
186
187    #[inline(always)]
188    #[cfg(all(feature = "memchr", not(feature = "libc")))]
189    fn find_sentinel(slice: &[bool]) -> Option<usize> {
190        unsafe { memchr::memchr(0, &*(slice as *const [bool] as *const [u8])) }
191    }
192
193    #[inline(always)]
194    #[cfg(feature = "libc")]
195    fn find_sentinel(slice: &[bool]) -> Option<usize> {
196        let ret =
197            unsafe { libc::memchr(slice.as_ptr() as _, false as _, slice.len()) as *const bool };
198        if ret.is_null() {
199            None
200        } else {
201            Some(unsafe { ret.offset_from(slice.as_ptr()) } as usize)
202        }
203    }
204
205    #[cfg(feature = "libc")]
206    #[inline(always)]
207    unsafe fn find_sentinel_infinite(slice: *const bool) -> usize {
208        unsafe { libc::strlen(slice as _) }
209    }
210}
211
212macro_rules! impl_Sentinel_zero {
213    ($($t:ty),* $(,)?) => {
214        $(
215            unsafe impl Sentinel for $t {
216                const SENTINEL: Self = 0;
217
218                type Unwrapped = $t;
219
220                #[inline(always)]
221                fn unwrap_sentinel(this: Self) -> Option<Self::Unwrapped> {
222                    if this == 0 {
223                        None
224                    } else {
225                        Some(this)
226                    }
227                }
228
229                #[inline(always)]
230                unsafe fn unwrap_sentinel_unchecked(this: Self) -> Self::Unwrapped {
231                    this
232                }
233
234                #[inline(always)]
235                fn is_sentinel(value: &$t) -> bool {
236                    *value == 0
237                }
238            }
239        )*
240    };
241}
242
243impl_Sentinel_zero!(u16, i16, u32, i32, u64, i64, u128, i128, usize, isize,);
244
245unsafe impl<T> Sentinel for *const T {
246    const SENTINEL: Self = core::ptr::null();
247
248    type Unwrapped = Self;
249
250    #[inline(always)]
251    fn unwrap_sentinel(this: Self) -> Option<Self::Unwrapped> {
252        if this.is_null() {
253            None
254        } else {
255            Some(this)
256        }
257    }
258
259    #[inline(always)]
260    unsafe fn unwrap_sentinel_unchecked(this: Self) -> Self::Unwrapped {
261        this
262    }
263
264    #[inline(always)]
265    fn is_sentinel(value: &*const T) -> bool {
266        value.is_null()
267    }
268}
269
270unsafe impl<T> Sentinel for *mut T {
271    const SENTINEL: Self = core::ptr::null_mut();
272
273    type Unwrapped = Self;
274
275    #[inline(always)]
276    fn unwrap_sentinel(this: Self) -> Option<Self::Unwrapped> {
277        if this.is_null() {
278            None
279        } else {
280            Some(this)
281        }
282    }
283
284    #[inline(always)]
285    unsafe fn unwrap_sentinel_unchecked(this: Self) -> Self::Unwrapped {
286        this
287    }
288
289    #[inline(always)]
290    fn is_sentinel(value: &*mut T) -> bool {
291        value.is_null()
292    }
293}
294
295unsafe impl<T> Sentinel for Option<T> {
296    const SENTINEL: Self = None;
297
298    type Unwrapped = T;
299
300    #[inline(always)]
301    fn unwrap_sentinel(this: Self) -> Option<Self::Unwrapped> {
302        this
303    }
304
305    #[inline(always)]
306    unsafe fn unwrap_sentinel_unchecked(this: Self) -> Self::Unwrapped {
307        unsafe { this.unwrap_unchecked() }
308    }
309
310    #[inline(always)]
311    fn is_sentinel(value: &Option<T>) -> bool {
312        value.is_none()
313    }
314}