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