1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
//! Error type
#![cfg_attr(
    feature = "cargo-clippy",
    allow(clippy::cast_sign_loss, clippy::cast_possible_wrap)
)]

use libc::c_int;
use {fmt, num, result};

pub trait NonZeroT {
    type T;
}
impl NonZeroT for i32 {
    type T = num::NonZeroU32;
}
impl NonZeroT for i64 {
    type T = num::NonZeroU64;
}

pub type NonZeroCInt = <c_int as NonZeroT>::T;

/// Errors of the `jemalloc_sys::mallct`-family of functions.
///
/// The `jemalloc-sys` crate: `mallctl`, `mallctlnametomib`, and `mallctlbymib``
/// functions return `0` on success; otherwise they return an error value.
#[repr(transparent)]
#[derive(Copy, Clone, PartialEq)]
pub struct Error(NonZeroCInt);

/// Result type
pub type Result<T> = result::Result<T, Error>;

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let code = self.0.get() as c_int;
        match description(code) {
            Some(m) => write!(f, "{}", m),
            None => write!(f, "Unknown error code: \"{}\".", code),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        <Self as fmt::Debug>::fmt(self, f)
    }
}

#[cfg(feature = "use_std")]
use std::error::Error as StdError;

#[cfg(feature = "use_std")]
impl StdError for Error {
    fn description(&self) -> &str {
        match description(self.0.get() as c_int) {
            Some(m) => m,
            None => "Unknown error",
        }
    }
    fn cause(&self) -> Option<&dyn StdError> {
        None
    }
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        None
    }
}

fn description(code: c_int) -> Option<&'static str> {
    match code {
        libc::EINVAL => Some(
            "`newp` is not `NULL`, and `newlen` is too large or too \
             small. Alternatively, `*oldlenp` is too large or too \
             small; in this case as much data as possible are read \
             despite the error.",
        ),
        libc::ENOENT => {
            Some("`name` or `mib` specifies an unknown/invalid value.")
        }
        libc::EPERM => Some(
            "Attempt to read or write `void` value, or attempt to \
             write read-only value.",
        ),
        libc::EAGAIN => Some("A memory allocation failure occurred."),
        libc::EFAULT => Some(
            "An interface with side effects failed in some way not \
             directly related to `mallctl*()` read/write processing.",
        ),
        _ => None,
    }
}

pub(crate) fn cvt(ret: c_int) -> Result<()> {
    match ret {
        0 => Ok(()),
        v => Err(Error(unsafe { NonZeroCInt::new_unchecked(v as _) })),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn size_of_result_error() {
        use mem::size_of;
        assert_eq!(size_of::<Result<()>>(), size_of::<Error>());
        assert_eq!(size_of::<Error>(), size_of::<libc::c_int>());
    }
}