1 use std::ffi::CStr;
2 use std::ffi::CString;
3 use std::mem::transmute;
4 use std::ops::Deref;
5 use std::os::raw::c_char;
6 use std::path::Path;
7 use std::ptr::NonNull;
8 use std::sync::OnceLock;
9 
10 use crate::Error;
11 use crate::Result;
12 
str_to_cstring(s: &str) -> Result<CString>13 pub fn str_to_cstring(s: &str) -> Result<CString> {
14     CString::new(s).map_err(|e| Error::with_invalid_data(e.to_string()))
15 }
16 
path_to_cstring<P: AsRef<Path>>(path: P) -> Result<CString>17 pub fn path_to_cstring<P: AsRef<Path>>(path: P) -> Result<CString> {
18     let path_str = path.as_ref().to_str().ok_or_else(|| {
19         Error::with_invalid_data(format!("{} is not valid unicode", path.as_ref().display()))
20     })?;
21 
22     str_to_cstring(path_str)
23 }
24 
c_ptr_to_string(p: *const c_char) -> Result<String>25 pub fn c_ptr_to_string(p: *const c_char) -> Result<String> {
26     if p.is_null() {
27         return Err(Error::with_invalid_data("Null string"));
28     }
29 
30     let c_str = unsafe { CStr::from_ptr(p) };
31     Ok(c_str
32         .to_str()
33         .map_err(|e| Error::with_invalid_data(e.to_string()))?
34         .to_owned())
35 }
36 
37 /// Convert a `[c_char]` into a `CStr`.
c_char_slice_to_cstr(s: &[c_char]) -> Option<&CStr>38 pub fn c_char_slice_to_cstr(s: &[c_char]) -> Option<&CStr> {
39     // TODO: Switch to using `CStr::from_bytes_until_nul` once we require
40     //       Rust 1.69.0.
41     let nul_idx = s
42         .iter()
43         .enumerate()
44         .find_map(|(idx, b)| (*b == 0).then_some(idx))?;
45     let cstr =
46         // SAFETY: `c_char` and `u8` are both just one byte plain old data
47         //         types.
48         CStr::from_bytes_with_nul(unsafe { transmute::<&[c_char], &[u8]>(&s[0..=nul_idx]) })
49             .unwrap();
50     Some(cstr)
51 }
52 
53 /// Round up a number to the next multiple of `r`
roundup(num: usize, r: usize) -> usize54 pub fn roundup(num: usize, r: usize) -> usize {
55     ((num + (r - 1)) / r) * r
56 }
57 
58 /// Get the number of CPUs in the system, e.g., to interact with per-cpu maps.
num_possible_cpus() -> Result<usize>59 pub fn num_possible_cpus() -> Result<usize> {
60     let ret = unsafe { libbpf_sys::libbpf_num_possible_cpus() };
61     parse_ret(ret).map(|()| ret as usize)
62 }
63 
parse_ret(ret: i32) -> Result<()>64 pub fn parse_ret(ret: i32) -> Result<()> {
65     if ret < 0 {
66         // Error code is returned negative, flip to positive to match errno
67         Err(Error::from_raw_os_error(-ret))
68     } else {
69         Ok(())
70     }
71 }
72 
parse_ret_i32(ret: i32) -> Result<i32>73 pub fn parse_ret_i32(ret: i32) -> Result<i32> {
74     parse_ret(ret).map(|()| ret)
75 }
76 
77 
78 /// Check the returned pointer of a `libbpf` call, extracting any
79 /// reported errors and converting them.
validate_bpf_ret<T>(ptr: *mut T) -> Result<NonNull<T>>80 pub fn validate_bpf_ret<T>(ptr: *mut T) -> Result<NonNull<T>> {
81     // SAFETY: `libbpf_get_error` is always safe to call.
82     match unsafe { libbpf_sys::libbpf_get_error(ptr as *const _) } {
83         0 => {
84             debug_assert!(!ptr.is_null());
85             // SAFETY: libbpf guarantees that if NULL is returned an
86             //         error it set, so we will always end up with a
87             //         valid pointer when `libbpf_get_error` returned 0.
88             let ptr = unsafe { NonNull::new_unchecked(ptr) };
89             Ok(ptr)
90         }
91         err => Err(Error::from_raw_os_error(-err as i32)),
92     }
93 }
94 
95 
96 // Fix me, If std::sync::LazyLock is stable(https://github.com/rust-lang/rust/issues/109736).
97 pub(crate) struct LazyLock<T> {
98     cell: OnceLock<T>,
99     init: fn() -> T,
100 }
101 
102 impl<T> LazyLock<T> {
new(f: fn() -> T) -> Self103     pub const fn new(f: fn() -> T) -> Self {
104         Self {
105             cell: OnceLock::new(),
106             init: f,
107         }
108     }
109 }
110 
111 impl<T> Deref for LazyLock<T> {
112     type Target = T;
113     #[inline]
deref(&self) -> &T114     fn deref(&self) -> &T {
115         self.cell.get_or_init(self.init)
116     }
117 }
118 
119 #[cfg(test)]
120 mod tests {
121     use super::*;
122 
123     #[test]
test_roundup()124     fn test_roundup() {
125         for i in 1..=256 {
126             let up = roundup(i, 8);
127             assert!(up % 8 == 0);
128             assert!(i <= up);
129             assert!(up - i < 8);
130         }
131     }
132 
133     #[test]
test_roundup_multiples()134     fn test_roundup_multiples() {
135         for i in (8..=256).step_by(8) {
136             assert_eq!(roundup(i, 8), i);
137         }
138     }
139 
140     #[test]
test_num_possible_cpus()141     fn test_num_possible_cpus() {
142         let num = num_possible_cpus().unwrap();
143         assert!(num > 0);
144     }
145 
146     /// Check that we can convert a `[c_char]` into a `CStr`.
147     #[test]
c_char_slice_conversion()148     fn c_char_slice_conversion() {
149         let slice = [];
150         assert_eq!(c_char_slice_to_cstr(&slice), None);
151 
152         let slice = [0];
153         assert_eq!(
154             c_char_slice_to_cstr(&slice).unwrap(),
155             CStr::from_bytes_with_nul(b"\0").unwrap()
156         );
157 
158         let slice = ['a' as _, 'b' as _, 'c' as _, 0 as _];
159         assert_eq!(
160             c_char_slice_to_cstr(&slice).unwrap(),
161             CStr::from_bytes_with_nul(b"abc\0").unwrap()
162         );
163 
164         // Missing terminating NUL byte.
165         let slice = ['a' as _, 'b' as _, 'c' as _];
166         assert_eq!(c_char_slice_to_cstr(&slice), None);
167     }
168 }
169