Skip to main content

libdd_common/
cstr.rs

1// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/
2// SPDX-License-Identifier: Apache-2.0
3
4#[doc(hidden)]
5#[allow(clippy::panic)]
6pub const fn validate_cstr_contents(bytes: &[u8]) {
7    // `str_len` is the length excluding the null terminator.
8    let str_len = bytes.len() - 1usize;
9    if bytes[str_len] != b'\0' {
10        panic!("cstr must be null terminated");
11    }
12
13    // Search for a null byte, safe due to above guard.
14    let mut i = 0;
15    while bytes[i] != b'\0' {
16        i += 1;
17    }
18
19    // The only null byte should have been the last byte of the slice.
20    if i != str_len {
21        panic!("cstr string cannot contain null character outside of last element");
22    }
23}
24
25#[macro_export]
26macro_rules! cstr {
27    ($s:expr) => {{
28        let mut bytes = $s.as_bytes();
29        if bytes[bytes.len() - 1usize] != b'\0' {
30            bytes = concat!($s, "\0").as_bytes();
31        }
32
33        $crate::cstr::validate_cstr_contents(bytes);
34        unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(bytes) }
35    }};
36}
37
38#[macro_export]
39macro_rules! cstr_u8 {
40    ($s:literal) => {{
41        $crate::cstr::validate_cstr_contents($s);
42        unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked($s as &[u8]) }
43    }};
44}
45
46#[cfg(test)]
47mod tests {
48    #[test]
49    fn test_cstr() {
50        assert_eq!(b"/dev/null", cstr!("/dev/null").to_bytes());
51        assert_eq!(b"/dev/null", cstr!("/dev/null\0").to_bytes());
52        assert_eq!(b"/dev/null", cstr_u8!(b"/dev/null\0").to_bytes());
53    }
54
55    #[test]
56    #[should_panic]
57    fn test_invalid_cstr_with_extra_null_character() {
58        _ = cstr!("/dev/null\0\0");
59    }
60
61    #[test]
62    #[should_panic]
63    fn test_invalid_cstr_u8_without_terminatid_nul() {
64        _ = cstr_u8!(b"/dev/null");
65    }
66
67    #[test]
68    #[should_panic]
69    fn test_invalid_cstr_with_nul_character() {
70        _ = cstr!("/dev/\0null");
71    }
72}