//! This module contains kernel helper functions that may be exposed to specific
//! BPF program types.
//!
//! These helpers can be used to perform common tasks, query and operate on data
//! exposed by the kernel, and perform some operations that would normally be
//! denied by the BPF verifier.
//!
//! Here, we provide some higher-level wrappers around the underlying kernel
//! helpers, but also expose bindings to the underlying helpers as a fall-back
//! in case of a missing implementation.

use core::{
    cmp::Ordering,
    ffi::CStr,
    mem::{self, MaybeUninit},
};

pub use aya_ebpf_bindings::helpers as generated;
#[doc(hidden)]
pub use generated::*;

use crate::{
    check_bounds_signed,
    cty::{c_char, c_long, c_void},
};

/// Read bytes stored at `src` and store them as a `T`.
///
/// Generally speaking, the more specific [`bpf_probe_read_user`] and
/// [`bpf_probe_read_kernel`] should be preferred over this function.
///
/// Returns a bitwise copy of `mem::size_of::<T>()` bytes stored at the user space address
/// `src`. See `bpf_probe_read_kernel` for  reading kernel space memory.
///
/// # Examples
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::{cty::{c_int, c_long}, helpers::bpf_probe_read};
/// # fn try_test() -> Result<(), c_long> {
/// # let kernel_ptr: *const c_int = 0 as _;
/// let my_int: c_int = unsafe { bpf_probe_read(kernel_ptr)? };
///
/// // Do something with my_int
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// # Errors
///
/// On failure, this function returns a negative value wrapped in an `Err`.
#[inline]
pub unsafe fn bpf_probe_read<T>(src: *const T) -> Result<T, c_long> {
    let mut v: MaybeUninit<T> = MaybeUninit::uninit();
    let ret = generated::bpf_probe_read(
        v.as_mut_ptr() as *mut c_void,
        mem::size_of::<T>() as u32,
        src as *const c_void,
    );
    if ret == 0 {
        Ok(v.assume_init())
    } else {
        Err(ret)
    }
}

/// Read bytes from the pointer `src` into the provided destination buffer.
///
/// Generally speaking, the more specific [`bpf_probe_read_user_buf`] and
/// [`bpf_probe_read_kernel_buf`] should be preferred over this function.
///
/// # Examples
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::{cty::{c_int, c_long}, helpers::bpf_probe_read_buf};
/// # fn try_test() -> Result<(), c_long> {
/// # let ptr: *const u8 = 0 as _;
/// let mut buf = [0u8; 16];
/// unsafe { bpf_probe_read_buf(ptr, &mut buf)? };
///
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// # Errors
///
/// On failure, this function returns a negative value wrapped in an `Err`.
#[inline]
pub unsafe fn bpf_probe_read_buf(src: *const u8, dst: &mut [u8]) -> Result<(), c_long> {
    let ret = generated::bpf_probe_read(
        dst.as_mut_ptr() as *mut c_void,
        dst.len() as u32,
        src as *const c_void,
    );
    if ret == 0 { Ok(()) } else { Err(ret) }
}

/// Read bytes stored at the _user space_ pointer `src` and store them as a `T`.
///
/// Returns a bitwise copy of `mem::size_of::<T>()` bytes stored at the user space address
/// `src`. See `bpf_probe_read_kernel` for  reading kernel space memory.
///
/// # Examples
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::{cty::{c_int, c_long}, helpers::bpf_probe_read_user};
/// # fn try_test() -> Result<(), c_long> {
/// # let user_ptr: *const c_int = 0 as _;
/// let my_int: c_int = unsafe { bpf_probe_read_user(user_ptr)? };
///
/// // Do something with my_int
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// # Errors
///
/// On failure, this function returns a negative value wrapped in an `Err`.
#[inline]
pub unsafe fn bpf_probe_read_user<T>(src: *const T) -> Result<T, c_long> {
    let mut v: MaybeUninit<T> = MaybeUninit::uninit();
    let ret = generated::bpf_probe_read_user(
        v.as_mut_ptr() as *mut c_void,
        mem::size_of::<T>() as u32,
        src as *const c_void,
    );
    if ret == 0 {
        Ok(v.assume_init())
    } else {
        Err(ret)
    }
}

/// Read bytes from the _user space_ pointer `src` into the provided destination
/// buffer.
///
/// # Examples
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::{cty::{c_int, c_long}, helpers::bpf_probe_read_user_buf};
/// # fn try_test() -> Result<(), c_long> {
/// # let user_ptr: *const u8 = 0 as _;
/// let mut buf = [0u8; 16];
/// unsafe { bpf_probe_read_user_buf(user_ptr, &mut buf)? };
///
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// # Errors
///
/// On failure, this function returns a negative value wrapped in an `Err`.
#[inline]
pub unsafe fn bpf_probe_read_user_buf(src: *const u8, dst: &mut [u8]) -> Result<(), c_long> {
    let ret = generated::bpf_probe_read_user(
        dst.as_mut_ptr() as *mut c_void,
        dst.len() as u32,
        src as *const c_void,
    );
    if ret == 0 { Ok(()) } else { Err(ret) }
}

/// Read bytes stored at the _kernel space_ pointer `src` and store them as a `T`.
///
/// Returns a bitwise copy of `mem::size_of::<T>()` bytes stored at the kernel space address
/// `src`. See `bpf_probe_read_user` for  reading user space memory.
///
/// # Examples
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::{cty::{c_int, c_long}, helpers::bpf_probe_read_kernel};
/// # fn try_test() -> Result<(), c_long> {
/// # let kernel_ptr: *const c_int = 0 as _;
/// let my_int: c_int = unsafe { bpf_probe_read_kernel(kernel_ptr)? };
///
/// // Do something with my_int
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// # Errors
///
/// On failure, this function returns a negative value wrapped in an `Err`.
#[inline]
pub unsafe fn bpf_probe_read_kernel<T>(src: *const T) -> Result<T, c_long> {
    let mut v: MaybeUninit<T> = MaybeUninit::uninit();
    let ret = generated::bpf_probe_read_kernel(
        v.as_mut_ptr() as *mut c_void,
        mem::size_of::<T>() as u32,
        src as *const c_void,
    );
    if ret == 0 {
        Ok(v.assume_init())
    } else {
        Err(ret)
    }
}

/// Read bytes from the _kernel space_ pointer `src` into the provided destination
/// buffer.
///
/// # Examples
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::{cty::{c_int, c_long}, helpers::bpf_probe_read_kernel_buf};
/// # fn try_test() -> Result<(), c_long> {
/// # let kernel_ptr: *const u8 = 0 as _;
/// let mut buf = [0u8; 16];
/// unsafe { bpf_probe_read_kernel_buf(kernel_ptr, &mut buf)? };
///
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// # Errors
///
/// On failure, this function returns a negative value wrapped in an `Err`.
#[inline]
pub unsafe fn bpf_probe_read_kernel_buf(src: *const u8, dst: &mut [u8]) -> Result<(), c_long> {
    let ret = generated::bpf_probe_read_kernel(
        dst.as_mut_ptr() as *mut c_void,
        dst.len() as u32,
        src as *const c_void,
    );
    if ret == 0 { Ok(()) } else { Err(ret) }
}

/// Read a null-terminated string stored at `src` into `dest`.
///
/// Generally speaking, the more specific [`bpf_probe_read_user_str`] and
/// [`bpf_probe_read_kernel_str`] should be preferred over this function.
///
/// In case the length of `dest` is smaller then the length of `src`, the read bytes will
/// be truncated to the size of `dest`.
///
/// # Examples
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::{cty::c_long, helpers::bpf_probe_read_str};
/// # fn try_test() -> Result<(), c_long> {
/// # let kernel_ptr: *const u8 = 0 as _;
/// let mut my_str = [0u8; 16];
/// let num_read = unsafe { bpf_probe_read_str(kernel_ptr, &mut my_str)? };
///
/// // Do something with num_read and my_str
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// # Errors
///
/// On failure, this function returns Err(-1).
#[deprecated(
    note = "Use `bpf_probe_read_user_str_bytes` or `bpf_probe_read_kernel_str_bytes` instead"
)]
#[inline]
pub unsafe fn bpf_probe_read_str(src: *const u8, dest: &mut [u8]) -> Result<usize, c_long> {
    let len = generated::bpf_probe_read_str(
        dest.as_mut_ptr() as *mut c_void,
        dest.len() as u32,
        src as *const c_void,
    );
    let len = usize::try_from(len).map_err(|core::num::TryFromIntError { .. }| -1)?;
    // this can never happen, it's needed to tell the verifier that len is bounded.
    Ok(len.min(dest.len()))
}

/// Read a null-terminated string from _user space_ stored at `src` into `dest`.
///
/// In case the length of `dest` is smaller then the length of `src`, the read bytes will
/// be truncated to the size of `dest`.
///
/// # Examples
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::{cty::c_long, helpers::bpf_probe_read_user_str};
/// # fn try_test() -> Result<(), c_long> {
/// # let user_ptr: *const u8 = 0 as _;
/// let mut my_str = [0u8; 16];
/// let num_read = unsafe { bpf_probe_read_user_str(user_ptr, &mut my_str)? };
///
/// // Do something with num_read and my_str
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// # Errors
///
/// On failure, this function returns Err(-1).
#[deprecated(note = "Use `bpf_probe_read_user_str_bytes` instead")]
#[inline]
pub unsafe fn bpf_probe_read_user_str(src: *const u8, dest: &mut [u8]) -> Result<usize, c_long> {
    let len = generated::bpf_probe_read_user_str(
        dest.as_mut_ptr() as *mut c_void,
        dest.len() as u32,
        src as *const c_void,
    );
    let len = usize::try_from(len).map_err(|core::num::TryFromIntError { .. }| -1)?;
    // this can never happen, it's needed to tell the verifier that len is bounded.
    Ok(len.min(dest.len()))
}

/// Returns a byte slice read from _user space_ address `src`.
///
/// Reads at most `dest.len()` bytes from the `src` address, truncating if the
/// length of the source string is larger than `dest`. On success, the
/// destination buffer is always null terminated, and the returned slice
/// includes the bytes up to and not including NULL.
///
/// # Examples
///
/// With an array allocated on the stack (not recommended for bigger strings,
/// eBPF stack limit is 512 bytes):
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::{cty::c_long, helpers::bpf_probe_read_user_str_bytes};
/// # fn try_test() -> Result<(), c_long> {
/// # let user_ptr: *const u8 = 0 as _;
/// let mut buf = [0u8; 16];
/// let my_str_bytes = unsafe { bpf_probe_read_user_str_bytes(user_ptr, &mut buf)? };
///
/// // Do something with my_str_bytes
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// With a `PerCpuArray` (with size defined by us):
///
/// ```no_run
/// # use aya_ebpf::{cty::c_long, helpers::bpf_probe_read_user_str_bytes};
/// use aya_ebpf::{macros::map, maps::PerCpuArray};
///
/// #[repr(C)]
/// pub struct Buf {
///     pub buf: [u8; 4096],
/// }
///
/// #[map]
/// pub static BUF: PerCpuArray<Buf> = PerCpuArray::with_max_entries(1, 0);
///
/// # fn try_test() -> Result<(), c_long> {
/// # let user_ptr: *const u8 = 0 as _;
/// let buf = unsafe {
///     let ptr = BUF.get_ptr_mut(0).ok_or(0)?;
///     &mut *ptr
/// };
/// let my_str_bytes = unsafe { bpf_probe_read_user_str_bytes(user_ptr, &mut buf.buf)? };
///
/// // Do something with my_str_bytes
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// You can also convert the resulted bytes slice into `&str` using
/// [core::str::from_utf8_unchecked]:
///
/// ```no_run
/// # use aya_ebpf::{cty::c_long, helpers::bpf_probe_read_user_str_bytes};
/// # use aya_ebpf::{macros::map, maps::PerCpuArray};
/// # #[repr(C)]
/// # pub struct Buf {
/// #     pub buf: [u8; 4096],
/// # }
/// # #[map]
/// # pub static BUF: PerCpuArray<Buf> = PerCpuArray::with_max_entries(1, 0);
/// # fn try_test() -> Result<(), c_long> {
/// # let user_ptr: *const u8 = 0 as _;
/// # let buf = unsafe {
/// #     let ptr = BUF.get_ptr_mut(0).ok_or(0)?;
/// #     &mut *ptr
/// # };
/// let my_str = unsafe {
///     core::str::from_utf8_unchecked(bpf_probe_read_user_str_bytes(user_ptr, &mut buf.buf)?)
/// };
///
/// // Do something with my_str
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// # Errors
///
/// On failure, this function returns Err(-1).
#[inline]
pub unsafe fn bpf_probe_read_user_str_bytes(
    src: *const u8,
    dest: &mut [u8],
) -> Result<&[u8], c_long> {
    let len = generated::bpf_probe_read_user_str(
        dest.as_mut_ptr() as *mut c_void,
        dest.len() as u32,
        src as *const c_void,
    );

    read_str_bytes(len, dest)
}

fn read_str_bytes(len: i64, dest: &[u8]) -> Result<&[u8], c_long> {
    // The lower bound is 0, since it's what is returned for b"\0". See the
    // bpf_probe_read_user_[user|kernel]_bytes_empty integration tests.  The upper bound
    // check is not needed since the helper truncates, but the verifier doesn't
    // know that so we show it the upper bound.
    if !check_bounds_signed(len, 0, dest.len() as i64) {
        return Err(-1);
    }

    // len includes the NULL terminator but not for b"\0" for which the kernel
    // returns len=0. So we do a saturating sub and for b"\0" we return the
    // empty slice, for all other cases we omit the terminator.
    let len = usize::try_from(len).map_err(|core::num::TryFromIntError { .. }| -1)?;
    let len = len.saturating_sub(1);
    dest.get(..len).ok_or(-1)
}

/// Read a null-terminated string from _kernel space_ stored at `src` into `dest`.
///
/// In case the length of `dest` is smaller then the length of `src`, the read bytes will
/// be truncated to the size of `dest`.
///
/// # Examples
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::{cty::c_long, helpers::bpf_probe_read_kernel_str};
/// # fn try_test() -> Result<(), c_long> {
/// # let kernel_ptr: *const u8 = 0 as _;
/// let mut my_str = [0u8; 16];
/// let num_read = unsafe { bpf_probe_read_kernel_str(kernel_ptr, &mut my_str)? };
///
/// // Do something with num_read and my_str
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// # Errors
///
/// On failure, this function returns Err(-1).
#[deprecated(note = "Use bpf_probe_read_kernel_str_bytes instead")]
#[inline]
pub unsafe fn bpf_probe_read_kernel_str(src: *const u8, dest: &mut [u8]) -> Result<usize, c_long> {
    let len = generated::bpf_probe_read_kernel_str(
        dest.as_mut_ptr() as *mut c_void,
        dest.len() as u32,
        src as *const c_void,
    );
    let len = usize::try_from(len).map_err(|core::num::TryFromIntError { .. }| -1)?;
    // this can never happen, it's needed to tell the verifier that len is bounded.
    Ok(len.min(dest.len()))
}

/// Returns a byte slice read from _kernel space_ address `src`.
///
/// Reads at most `dest.len()` bytes from the `src` address, truncating if the
/// length of the source string is larger than `dest`. On success, the
/// destination buffer is always null terminated, and the returned slice
/// includes the bytes up to and not including NULL.
///
/// # Examples
///
/// With an array allocated on the stack (not recommended for bigger strings,
/// eBPF stack limit is 512 bytes):
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::{cty::c_long, helpers::bpf_probe_read_kernel_str_bytes};
/// # fn try_test() -> Result<(), c_long> {
/// # let kernel_ptr: *const u8 = 0 as _;
/// let mut buf = [0u8; 16];
/// let my_str_bytes = unsafe { bpf_probe_read_kernel_str_bytes(kernel_ptr, &mut buf)? };
///
/// // Do something with my_str_bytes
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// With a `PerCpuArray` (with size defined by us):
///
/// ```no_run
/// # use aya_ebpf::{cty::c_long, helpers::bpf_probe_read_kernel_str_bytes};
/// use aya_ebpf::{macros::map, maps::PerCpuArray};
///
/// #[repr(C)]
/// pub struct Buf {
///     pub buf: [u8; 4096],
/// }
///
/// #[map]
/// pub static BUF: PerCpuArray<Buf> = PerCpuArray::with_max_entries(1, 0);
///
/// # fn try_test() -> Result<(), c_long> {
/// # let kernel_ptr: *const u8 = 0 as _;
/// let buf = unsafe {
///     let ptr = BUF.get_ptr_mut(0).ok_or(0)?;
///     &mut *ptr
/// };
/// let my_str_bytes = unsafe { bpf_probe_read_kernel_str_bytes(kernel_ptr, &mut buf.buf)? };
///
/// // Do something with my_str_bytes
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// You can also convert the resulted bytes slice into `&str` using
/// [core::str::from_utf8_unchecked]:
///
/// ```no_run
/// # use aya_ebpf::{cty::c_long, helpers::bpf_probe_read_kernel_str_bytes};
/// # use aya_ebpf::{macros::map, maps::PerCpuArray};
/// # #[repr(C)]
/// # pub struct Buf {
/// #     pub buf: [u8; 4096],
/// # }
/// # #[map]
/// # pub static BUF: PerCpuArray<Buf> = PerCpuArray::with_max_entries(1, 0);
/// # fn try_test() -> Result<(), c_long> {
/// # let kernel_ptr: *const u8 = 0 as _;
/// # let buf = unsafe {
/// #     let ptr = BUF.get_ptr_mut(0).ok_or(0)?;
/// #     &mut *ptr
/// # };
/// let my_str = unsafe {
///     core::str::from_utf8_unchecked(bpf_probe_read_kernel_str_bytes(kernel_ptr, &mut buf.buf)?)
/// };
///
/// // Do something with my_str
/// # Ok::<(), c_long>(())
/// # }
/// ```
///
/// # Errors
///
/// On failure, this function returns Err(-1).
#[inline]
pub unsafe fn bpf_probe_read_kernel_str_bytes(
    src: *const u8,
    dest: &mut [u8],
) -> Result<&[u8], c_long> {
    let len = generated::bpf_probe_read_kernel_str(
        dest.as_mut_ptr() as *mut c_void,
        dest.len() as u32,
        src as *const c_void,
    );

    read_str_bytes(len, dest)
}

/// Write bytes to the _user space_ pointer `src` and store them as a `T`.
///
/// # Examples
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::{
/// #     cty::{c_int, c_long},
/// #     helpers::bpf_probe_write_user,
/// #     programs::ProbeContext,
/// # };
/// fn try_test(ctx: ProbeContext) -> Result<(), c_long> {
///     let retp: *mut c_int = ctx.arg(0).ok_or(1)?;
///     let val: i32 = 1;
///     // Write the value to the userspace pointer.
///     unsafe { bpf_probe_write_user(retp, &val as *const i32)? };
///
///     Ok::<(), c_long>(())
/// }
/// ```
///
/// # Errors
///
/// On failure, this function returns a negative value wrapped in an `Err`.
#[inline]
pub unsafe fn bpf_probe_write_user<T>(dst: *mut T, src: *const T) -> Result<(), c_long> {
    let ret = generated::bpf_probe_write_user(
        dst as *mut c_void,
        src as *const c_void,
        mem::size_of::<T>() as u32,
    );
    if ret == 0 { Ok(()) } else { Err(ret) }
}

/// Read the `comm` field associated with the current task struct
/// as a `[u8; 16]`.
///
/// # Examples
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::helpers::bpf_get_current_comm;
/// let comm = bpf_get_current_comm();
///
/// // Do something with comm
/// ```
///
/// # Errors
///
/// On failure, this function returns a negative value wrapped in an `Err`.
#[inline]
pub fn bpf_get_current_comm() -> Result<[u8; 16], c_long> {
    let mut comm: [u8; 16usize] = [0; 16];
    let ret = unsafe { generated::bpf_get_current_comm(&mut comm as *mut _ as *mut c_void, 16u32) };
    if ret == 0 { Ok(comm) } else { Err(ret) }
}

/// Read the process id and thread group id associated with the current task struct as
/// a `u64`.
///
/// In the return value, the upper 32 bits are the `tgid`, and the lower 32 bits are the
/// `pid`. That is, the returned value is equal to: `(tgid << 32) | pid`. A caller may
/// access the individual fields by either casting to a `u32` or performing a `>> 32` bit
/// shift and casting to a `u32`.
///
/// Note that the naming conventions used in the kernel differ from user space. From the
/// perspective of user space, `pid` may be thought of as the thread id, and `tgid` may be
/// thought of as the process id. For single-threaded processes, these values are
/// typically the same.
///
/// # Examples
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::helpers::bpf_get_current_pid_tgid;
/// let tgid = (bpf_get_current_pid_tgid() >> 32) as u32;
/// let pid = bpf_get_current_pid_tgid() as u32;
///
/// // Do something with pid and tgid
/// ```
#[inline]
pub fn bpf_get_current_pid_tgid() -> u64 {
    unsafe { generated::bpf_get_current_pid_tgid() }
}

/// Read the user id and group id associated with the current task struct as
/// a `u64`.
///
/// In the return value, the upper 32 bits are the `gid`, and the lower 32 bits are the
/// `uid`. That is, the returned value is equal to: `(gid << 32) | uid`. A caller may
/// access the individual fields by either casting to a `u32` or performing a `>> 32` bit
/// shift and casting to a `u32`.
///
/// # Examples
///
/// ```no_run
/// # #![expect(dead_code)]
/// # use aya_ebpf::helpers::bpf_get_current_uid_gid;
/// let gid = (bpf_get_current_uid_gid() >> 32) as u32;
/// let uid = bpf_get_current_uid_gid() as u32;
///
/// // Do something with uid and gid
/// ```
#[inline]
pub fn bpf_get_current_uid_gid() -> u64 {
    unsafe { generated::bpf_get_current_uid_gid() }
}

/// Prints a debug message to the BPF debugging pipe.
///
/// The [format string syntax][fmt] is the same as that of the `printk` kernel
/// function. It is passed in as a fixed-size byte array (`&[u8; N]`), so you
/// will have to prefix your string literal with a `b`. A terminating zero byte
/// is appended automatically.
///
/// The macro can read from arbitrary pointers, so it must be used in an `unsafe`
/// scope in order to compile.
///
/// Invocations with less than 4 arguments call the `bpf_trace_printk` helper,
/// otherwise the `bpf_trace_vprintk` function is called. The latter function
/// requires a kernel version >= 5.16 whereas the former has been around since
/// the dawn of eBPF. The return value of the BPF helper is also returned from
/// this macro.
///
/// Messages can be read by executing the following command in a second terminal:
///
/// ```bash
/// sudo cat /sys/kernel/debug/tracing/trace_pipe
/// ```
///
/// If no messages are printed when calling [`bpf_printk!`] after executing the
/// above command, it is possible that tracing must first be enabled by running:
///
/// ```bash
/// echo 1 | sudo tee /sys/kernel/debug/tracing/tracing_on
/// ```
///
/// # Example
///
/// ```no_run
/// # use aya_ebpf::helpers::bpf_printk;
/// unsafe {
///   bpf_printk!(b"hi there! dec: %d, hex: 0x%08X", 42, 0x1234);
/// }
/// ```
///
/// [fmt]: https://www.kernel.org/doc/html/latest/core-api/printk-formats.html#printk-specifiers
#[macro_export]
macro_rules! bpf_printk {
    ($fmt:literal $(,)? $($arg:expr),* $(,)?) => {{
        use $crate::helpers::PrintkArg;
        const FMT: [u8; { $fmt.len() + 1 }] = $crate::helpers::zero_pad_array::<
            { $fmt.len() }, { $fmt.len() + 1 }>(*$fmt);
        let data = [$(PrintkArg::from($arg)),*];
        $crate::helpers::bpf_printk_impl(&FMT, &data)
    }};
}

// Macros are always exported from the crate root. Also export it from `helpers`.
#[doc(inline)]
pub use bpf_printk;

/// Argument ready to be passed to `printk` BPF helper.
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct PrintkArg([u8; 8]);

impl PrintkArg {
    /// Manually construct a `printk` BPF helper argument.
    #[inline]
    pub fn from_raw(x: u64) -> Self {
        Self(x.to_ne_bytes())
    }
}

macro_rules! impl_integer_promotion {
    ($($ty:ty : via $via:ty),* $(,)?) => {$(
        /// Create `printk` arguments from integer types.
        impl From<$ty> for PrintkArg {
            #[inline]
            fn from(x: $ty) -> PrintkArg {
                PrintkArg((x as $via).to_ne_bytes())
            }
        }
    )*}
}

impl_integer_promotion!(
  char:  via usize,
  u8:    via usize,
  u16:   via usize,
  u32:   via usize,
  u64:   via usize,
  usize: via usize,
  i8:    via isize,
  i16:   via isize,
  i32:   via isize,
  i64:   via isize,
  isize: via isize,
);

/// Construct `printk` BPF helper arguments from constant pointers.
impl<T> From<*const T> for PrintkArg {
    #[inline]
    fn from(x: *const T) -> Self {
        PrintkArg((x as usize).to_ne_bytes())
    }
}

/// Construct `printk` BPF helper arguments from mutable pointers.
impl<T> From<*mut T> for PrintkArg {
    #[inline]
    fn from(x: *mut T) -> Self {
        PrintkArg((x as usize).to_ne_bytes())
    }
}

/// Expands the given byte array to `DST_LEN`, right-padding it with zeros. If
/// `DST_LEN` is smaller than `SRC_LEN`, the array is instead truncated.
///
/// This function serves as a helper for the [`bpf_printk!`] macro.
#[doc(hidden)]
pub const fn zero_pad_array<const SRC_LEN: usize, const DST_LEN: usize>(
    src: [u8; SRC_LEN],
) -> [u8; DST_LEN] {
    let mut out: [u8; DST_LEN] = [0u8; DST_LEN];

    // The `min` function is not `const`. Hand-roll it.
    let mut i = if DST_LEN > SRC_LEN { SRC_LEN } else { DST_LEN };

    while i > 0 {
        i -= 1;
        out[i] = src[i];
    }

    out
}

/// Internal helper function for the [`bpf_printk!`] macro.
///
/// The BPF helpers require the length for both the format string as well as
/// the argument array to be of constant size to pass the verifier. Const
/// generics are used to represent this requirement in Rust.
#[inline]
#[doc(hidden)]
pub unsafe fn bpf_printk_impl<const FMT_LEN: usize, const NUM_ARGS: usize>(
    fmt: &[u8; FMT_LEN],
    args: &[PrintkArg; NUM_ARGS],
) -> i64 {
    // This function can't be wrapped in `helpers.rs` because it has variadic
    // arguments. We also can't turn the definitions in `helpers.rs` into
    // `const`s because MIRI believes casting arbitrary integers to function
    // pointers to be an error.
    let printk: unsafe extern "C" fn(fmt: *const c_char, fmt_size: u32, ...) -> c_long =
        mem::transmute(6usize);

    let fmt_ptr = fmt.as_ptr() as *const c_char;
    let fmt_size = fmt.len() as u32;

    match NUM_ARGS {
        0 => printk(fmt_ptr, fmt_size),
        1 => printk(fmt_ptr, fmt_size, args[0]),
        2 => printk(fmt_ptr, fmt_size, args[0], args[1]),
        3 => printk(fmt_ptr, fmt_size, args[0], args[1], args[2]),
        _ => {
            generated::bpf_trace_vprintk(fmt_ptr, fmt_size, args.as_ptr() as _, (NUM_ARGS * 8) as _)
        }
    }
}

/// Compares the given byte `s1` with a [`&CStr`](core::ffi::CStr) `s2`.
///
/// # Examples
///
/// ```no_run
/// # use aya_ebpf::helpers::bpf_strncmp;
/// # let data = b"something";
/// assert_ne!(bpf_strncmp(data, c"foo"), core::cmp::Ordering::Equal);
/// ```
#[inline]
pub fn bpf_strncmp<const N: usize>(s1: &[u8; N], s2: &CStr) -> Ordering {
    // NB: s1 does not need to be null-terminated.
    //
    // See https://github.com/torvalds/linux/blob/adc218676/include/uapi/linux/bpf.h#L5391-L5393.
    //
    // NB: s1's size must be known at compile time to appease the verifier. This is also the typical
    // usage of strncmp in C programs.
    unsafe { generated::bpf_strncmp(s1.as_ptr() as *const _, N as u32, s2.as_ptr() as *const _) }
        .cmp(&0)
}
