1use std::{
2 ffi::{CStr, NulError},
3 fmt, result,
4};
5
6use singe_cuda::error::Error as CudaError;
7use singe_cusparse_sys as sys;
8use thiserror::Error;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12#[non_exhaustive]
13pub enum Status {
14 Success,
16 NotInitialized,
20 AllocFailed,
23 InvalidValue,
25 ArchitectureMismatch,
27 MappingError,
32 ExecutionFailed,
35 InternalError,
38 MatrixTypeNotSupported,
42 ZeroPivot,
46 NotSupported,
48 InsufficientResources,
51 Unknown(u32),
53}
54
55impl Status {
56 pub fn description(self) -> String {
57 match sys::cusparseStatus_t::try_from(self.raw()) {
58 Ok(status) => cusparse_error_string(status),
59 Err(_) => String::from("unknown cusparse error"),
60 }
61 }
62
63 pub const fn raw(self) -> u32 {
64 match self {
65 Self::Success => sys::cusparseStatus_t::CUSPARSE_STATUS_SUCCESS as _,
66 Self::NotInitialized => sys::cusparseStatus_t::CUSPARSE_STATUS_NOT_INITIALIZED as _,
67 Self::AllocFailed => sys::cusparseStatus_t::CUSPARSE_STATUS_ALLOC_FAILED as _,
68 Self::InvalidValue => sys::cusparseStatus_t::CUSPARSE_STATUS_INVALID_VALUE as _,
69 Self::ArchitectureMismatch => sys::cusparseStatus_t::CUSPARSE_STATUS_ARCH_MISMATCH as _,
70 Self::MappingError => sys::cusparseStatus_t::CUSPARSE_STATUS_MAPPING_ERROR as _,
71 Self::ExecutionFailed => sys::cusparseStatus_t::CUSPARSE_STATUS_EXECUTION_FAILED as _,
72 Self::InternalError => sys::cusparseStatus_t::CUSPARSE_STATUS_INTERNAL_ERROR as _,
73 Self::MatrixTypeNotSupported => {
74 sys::cusparseStatus_t::CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED as _
75 }
76 Self::ZeroPivot => sys::cusparseStatus_t::CUSPARSE_STATUS_ZERO_PIVOT as _,
77 Self::NotSupported => sys::cusparseStatus_t::CUSPARSE_STATUS_NOT_SUPPORTED as _,
78 Self::InsufficientResources => {
79 sys::cusparseStatus_t::CUSPARSE_STATUS_INSUFFICIENT_RESOURCES as _
80 }
81 Self::Unknown(code) => code,
82 }
83 }
84}
85
86impl TryFrom<u32> for Status {
87 type Error = u32;
88
89 fn try_from(code: u32) -> result::Result<Self, u32> {
90 match code {
91 code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_SUCCESS as u32 => {
92 Ok(Self::Success)
93 }
94 code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_NOT_INITIALIZED as u32 => {
95 Ok(Self::NotInitialized)
96 }
97 code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_ALLOC_FAILED as u32 => {
98 Ok(Self::AllocFailed)
99 }
100 code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_INVALID_VALUE as u32 => {
101 Ok(Self::InvalidValue)
102 }
103 code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_ARCH_MISMATCH as u32 => {
104 Ok(Self::ArchitectureMismatch)
105 }
106 code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_MAPPING_ERROR as u32 => {
107 Ok(Self::MappingError)
108 }
109 code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_EXECUTION_FAILED as u32 => {
110 Ok(Self::ExecutionFailed)
111 }
112 code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_INTERNAL_ERROR as u32 => {
113 Ok(Self::InternalError)
114 }
115 code if code
116 == sys::cusparseStatus_t::CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED as u32 =>
117 {
118 Ok(Self::MatrixTypeNotSupported)
119 }
120 code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_ZERO_PIVOT as u32 => {
121 Ok(Self::ZeroPivot)
122 }
123 code if code == sys::cusparseStatus_t::CUSPARSE_STATUS_NOT_SUPPORTED as u32 => {
124 Ok(Self::NotSupported)
125 }
126 code if code
127 == sys::cusparseStatus_t::CUSPARSE_STATUS_INSUFFICIENT_RESOURCES as u32 =>
128 {
129 Ok(Self::InsufficientResources)
130 }
131 code => Err(code),
132 }
133 }
134}
135
136impl From<sys::cusparseStatus_t> for Status {
137 fn from(status: sys::cusparseStatus_t) -> Self {
138 Self::try_from(status as u32).unwrap_or_else(Self::Unknown)
139 }
140}
141
142impl TryFrom<Status> for sys::cusparseStatus_t {
143 type Error = Status;
144
145 fn try_from(status: Status) -> result::Result<Self, Status> {
146 match status {
147 Status::Success => Ok(Self::CUSPARSE_STATUS_SUCCESS),
148 Status::NotInitialized => Ok(Self::CUSPARSE_STATUS_NOT_INITIALIZED),
149 Status::AllocFailed => Ok(Self::CUSPARSE_STATUS_ALLOC_FAILED),
150 Status::InvalidValue => Ok(Self::CUSPARSE_STATUS_INVALID_VALUE),
151 Status::ArchitectureMismatch => Ok(Self::CUSPARSE_STATUS_ARCH_MISMATCH),
152 Status::MappingError => Ok(Self::CUSPARSE_STATUS_MAPPING_ERROR),
153 Status::ExecutionFailed => Ok(Self::CUSPARSE_STATUS_EXECUTION_FAILED),
154 Status::InternalError => Ok(Self::CUSPARSE_STATUS_INTERNAL_ERROR),
155 Status::MatrixTypeNotSupported => Ok(Self::CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED),
156 Status::ZeroPivot => Ok(Self::CUSPARSE_STATUS_ZERO_PIVOT),
157 Status::NotSupported => Ok(Self::CUSPARSE_STATUS_NOT_SUPPORTED),
158 Status::InsufficientResources => Ok(Self::CUSPARSE_STATUS_INSUFFICIENT_RESOURCES),
159 Status::Unknown(_) => Err(status),
160 }
161 }
162}
163
164impl fmt::Display for Status {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 match self {
167 Self::Success => f.write_str("CUSPARSE_STATUS_SUCCESS"),
168 Self::NotInitialized => f.write_str("CUSPARSE_STATUS_NOT_INITIALIZED"),
169 Self::AllocFailed => f.write_str("CUSPARSE_STATUS_ALLOC_FAILED"),
170 Self::InvalidValue => f.write_str("CUSPARSE_STATUS_INVALID_VALUE"),
171 Self::ArchitectureMismatch => f.write_str("CUSPARSE_STATUS_ARCH_MISMATCH"),
172 Self::MappingError => f.write_str("CUSPARSE_STATUS_MAPPING_ERROR"),
173 Self::ExecutionFailed => f.write_str("CUSPARSE_STATUS_EXECUTION_FAILED"),
174 Self::InternalError => f.write_str("CUSPARSE_STATUS_INTERNAL_ERROR"),
175 Self::MatrixTypeNotSupported => {
176 f.write_str("CUSPARSE_STATUS_MATRIX_TYPE_NOT_SUPPORTED")
177 }
178 Self::ZeroPivot => f.write_str("CUSPARSE_STATUS_ZERO_PIVOT"),
179 Self::NotSupported => f.write_str("CUSPARSE_STATUS_NOT_SUPPORTED"),
180 Self::InsufficientResources => f.write_str("CUSPARSE_STATUS_INSUFFICIENT_RESOURCES"),
181 Self::Unknown(code) => write!(f, "UNKNOWN_CUSPARSE_STATUS({code})"),
182 }
183 }
184}
185
186fn cusparse_error_string(status: sys::cusparseStatus_t) -> String {
187 unsafe {
188 let c_ptr = sys::cusparseGetErrorString(status);
189 if c_ptr.is_null() {
190 String::from("unknown cusparse error")
191 } else {
192 CStr::from_ptr(c_ptr).to_string_lossy().into_owned()
193 }
194 }
195}
196
197#[derive(Error, Debug)]
198#[non_exhaustive]
199pub enum Error {
200 #[error("cuda error: {0}")]
201 Cuda(#[from] CudaError),
202
203 #[error("cusparse error ({code}): {message}")]
204 Cusparse { code: Status, message: String },
205
206 #[error("string contains interior nul byte")]
207 InteriorNul,
208
209 #[error("unexpected null handle")]
210 NullHandle,
211
212 #[error("`{name}` is out of range")]
213 OutOfRange { name: String },
214
215 #[error("scalar pointer modes do not match")]
216 ScalarPointerModeMismatch,
217
218 #[error("plan belongs to a different cusparse context")]
219 PlanContextMismatch,
220
221 #[error("stream belongs to a different cuda context")]
222 StreamContextMismatch,
223}
224
225pub type Result<T> = result::Result<T, Error>;
226
227impl From<sys::cusparseStatus_t> for Error {
228 fn from(status: sys::cusparseStatus_t) -> Self {
229 let code = Status::from(status);
230 Self::from(code)
231 }
232}
233
234impl From<Status> for Error {
235 fn from(code: Status) -> Self {
236 Self::Cusparse {
237 code,
238 message: code.description(),
239 }
240 }
241}
242
243impl From<NulError> for Error {
244 fn from(_: NulError) -> Self {
245 Self::InteriorNul
246 }
247}
248
249#[macro_export]
250macro_rules! try_ffi {
251 ($expr:expr) => {{
252 let status = { $expr };
253 if status != singe_cusparse_sys::cusparseStatus_t::CUSPARSE_STATUS_SUCCESS {
254 Err($crate::error::Error::from(status))
255 } else {
256 Ok(())
257 }
258 }};
259}