use std::{
ffi::{CStr, NulError},
fmt::{self, Display, Formatter},
result,
};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use singe_core::impl_enum_conversion;
use singe_cuda::error::Error as CudaError;
use singe_cusparse_sys as sys;
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive, IntoPrimitive)]
#[repr(u32)]
pub enum Status {
Success = sys::cusparseStatus_t::CUSPARSE_STATUS_SUCCESS as _,
NotInitialized = sys::cusparseStatus_t::CUSPARSE_STATUS_NOT_INITIALIZED as _,
AllocFailed = sys::cusparseStatus_t::CUSPARSE_STATUS_ALLOC_FAILED as _,
InvalidValue = sys::cusparseStatus_t::CUSPARSE_STATUS_INVALID_VALUE as _,
ArchMismatch = sys::cusparseStatus_t::CUSPARSE_STATUS_ARCH_MISMATCH as _,
MappingError = sys::cusparseStatus_t::CUSPARSE_STATUS_MAPPING_ERROR as _,
ExecutionFailed = sys::cusparseStatus_t::CUSPARSE_STATUS_EXECUTION_FAILED as _,
InternalError = sys::cusparseStatus_t::CUSPARSE_STATUS_INTERNAL_ERROR as _,
MatrixTypeNotSupported = sys::cusparseStatus_t::CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED as _,
ZeroPivot = sys::cusparseStatus_t::CUSPARSE_STATUS_ZERO_PIVOT as _,
NotSupported = sys::cusparseStatus_t::CUSPARSE_STATUS_NOT_SUPPORTED as _,
InsufficientResources = sys::cusparseStatus_t::CUSPARSE_STATUS_INSUFFICIENT_RESOURCES as _,
}
impl_enum_conversion!(sys::cusparseStatus_t, Status);
impl Status {
pub const fn description(self) -> &'static str {
match self {
Self::Success => "success",
Self::NotInitialized => "not initialized",
Self::AllocFailed => "allocation failed",
Self::InvalidValue => "invalid value",
Self::ArchMismatch => "architecture mismatch",
Self::MappingError => "mapping error",
Self::ExecutionFailed => "execution failed",
Self::InternalError => "internal error",
Self::MatrixTypeNotSupported => "matrix type not supported",
Self::ZeroPivot => "zero pivot",
Self::NotSupported => "not supported",
Self::InsufficientResources => "insufficient resources",
}
}
}
impl Display for Status {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Success => write!(f, "CUSPARSE_STATUS_SUCCESS"),
Self::NotInitialized => write!(f, "CUSPARSE_STATUS_NOT_INITIALIZED"),
Self::AllocFailed => write!(f, "CUSPARSE_STATUS_ALLOC_FAILED"),
Self::InvalidValue => write!(f, "CUSPARSE_STATUS_INVALID_VALUE"),
Self::ArchMismatch => write!(f, "CUSPARSE_STATUS_ARCH_MISMATCH"),
Self::MappingError => write!(f, "CUSPARSE_STATUS_MAPPING_ERROR"),
Self::ExecutionFailed => write!(f, "CUSPARSE_STATUS_EXECUTION_FAILED"),
Self::InternalError => write!(f, "CUSPARSE_STATUS_INTERNAL_ERROR"),
Self::MatrixTypeNotSupported => {
write!(f, "CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED")
}
Self::ZeroPivot => write!(f, "CUSPARSE_STATUS_ZERO_PIVOT"),
Self::NotSupported => write!(f, "CUSPARSE_STATUS_NOT_SUPPORTED"),
Self::InsufficientResources => {
write!(f, "CUSPARSE_STATUS_INSUFFICIENT_RESOURCES")
}
}
}
}
#[derive(Error, Debug)]
pub enum Error {
#[error("cuda error: {0}")]
Cuda(#[from] CudaError),
#[error("cusparse error ({code}): {message}")]
Cusparse { code: Status, message: String },
#[error("string contains interior nul byte")]
InteriorNul,
#[error("unexpected null handle")]
NullHandle,
#[error("`{name}` is out of range")]
OutOfRange { name: String },
#[error("scalar pointer modes do not match")]
ScalarPointerModeMismatch,
#[error("plan belongs to a different cusparse context")]
PlanContextMismatch,
#[error("stream belongs to a different cuda context")]
StreamContextMismatch,
}
pub type Result<T> = result::Result<T, Error>;
impl From<sys::cusparseStatus_t> for Error {
fn from(status: sys::cusparseStatus_t) -> Self {
debug_assert_ne!(status, sys::cusparseStatus_t::CUSPARSE_STATUS_SUCCESS);
let message = unsafe {
let c_ptr = sys::cusparseGetErrorString(status);
if c_ptr.is_null() {
String::from("unknown cusparse error")
} else {
CStr::from_ptr(c_ptr).to_string_lossy().into_owned()
}
};
Self::Cusparse {
code: status.into(),
message,
}
}
}
impl From<NulError> for Error {
fn from(_: NulError) -> Self {
Self::InteriorNul
}
}
#[macro_export]
macro_rules! try_ffi {
($expr:expr) => {{
let status = { $expr };
if status != singe_cusparse_sys::cusparseStatus_t::CUSPARSE_STATUS_SUCCESS {
Err($crate::error::Error::from(status))
} else {
Ok(())
}
}};
}