diff --git a/Cargo.lock b/Cargo.lock index 749880ba..285fdfc2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,6 +73,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + [[package]] name = "ascii-canvas" version = "3.0.0" @@ -247,6 +256,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "cslice" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f8cb7306107e4b10e64994de6d3274bd08996a7c1322a27b86482392f96be0a" + [[package]] name = "dirs-next" version = "2.0.0" @@ -521,6 +536,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "libredox" version = "0.1.3" @@ -531,6 +552,14 @@ dependencies = [ "libc", ] +[[package]] +name = "linalg_externfns" +version = "0.1.0" +dependencies = [ + "cslice", + "nalgebra", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -659,17 +688,70 @@ version = "0.1.0" dependencies = [ "clap", "inkwell", + "linalg_externfns", "nac3core", "nac3parser", "parking_lot", ] +[[package]] +name = "nalgebra" +version = "0.32.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5c17de023a86f59ed79891b2e5d5a94c705dbe904a5b5c9c952ea6221b03e4" +dependencies = [ + "approx", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + [[package]] name = "new_debug_unreachable" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -699,6 +781,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "petgraph" version = "0.6.5" @@ -1070,6 +1158,18 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "simba" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", +] + [[package]] name = "similar" version = "2.6.0" @@ -1230,6 +1330,12 @@ dependencies = [ "crunchy", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unic-char-property" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index 765ab391..96fc975c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "nac3ast", "nac3parser", "nac3core", + "nac3standalone/linalg_externfns", "nac3standalone", "nac3artiq", "runkernel", diff --git a/nac3standalone/Cargo.toml b/nac3standalone/Cargo.toml index a55a26b9..ccf69ba5 100644 --- a/nac3standalone/Cargo.toml +++ b/nac3standalone/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" parking_lot = "0.12" nac3parser = { path = "../nac3parser" } nac3core = { path = "../nac3core" } +linalg_externfns = { path = "./linalg_externfns" } [dependencies.clap] version = "4.5" diff --git a/nac3standalone/linalg_externfns/Cargo.toml b/nac3standalone/linalg_externfns/Cargo.toml new file mode 100644 index 00000000..03329dc7 --- /dev/null +++ b/nac3standalone/linalg_externfns/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "linalg_externfns" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["staticlib"] + +[dependencies] +nalgebra = {version = "0.32.6", default-features = false, features = ["libm", "alloc"]} +cslice = "0.3.0" diff --git a/nac3standalone/linalg_externfns/src/lib.rs b/nac3standalone/linalg_externfns/src/lib.rs new file mode 100644 index 00000000..5d6b4c9e --- /dev/null +++ b/nac3standalone/linalg_externfns/src/lib.rs @@ -0,0 +1,408 @@ +/// Uses `nalgebra` crate to invoke `np_linalg` and `sp_linalg` functions +/// When converting between `nalgebra::Matrix` and `NDArray` following considerations are necessary +/// +/// * Both `nalgebra::Matrix` and `NDArray` require their content to be stored in row-major order +/// * `NDArray` data pointer can be directly read and converted to `nalgebra::Matrix` (row and column number must be known) +/// * `nalgebra::Matrix::as_slice` returns the content of matrix in column-major order and initial data needs to be transposed before storing it in `NDArray` data pointer +mod runtime_exception; +use core::slice; +use nalgebra::DMatrix; + +macro_rules! raise_exn { + ($name:expr, $fn_name:expr, $message:expr, $param0:expr, $param1:expr, $param2:expr) => {{ + use cslice::AsCSlice; + let name_id = $crate::runtime_exception::get_exception_id($name); + let exn = $crate::runtime_exception::Exception { + id: name_id, + file: file!().as_c_slice(), + line: line!(), + column: column!(), + // https://github.com/rust-lang/rfcs/pull/1719 + function: $fn_name.as_c_slice(), + message: $message.as_c_slice(), + param: [$param0, $param1, $param2], + }; + #[allow(unused_unsafe)] + unsafe { + $crate::runtime_exception::raise(&exn) + } + }}; + ($name:expr, $fn_name:expr, $message:expr) => {{ + raise_exn!($name, $fn_name, $message, 0, 0, 0) + }}; +} +pub struct InputMatrix { + pub ndims: usize, + pub dims: *const usize, + pub data: *mut f64, +} +impl InputMatrix { + fn get_dims(&mut self) -> Vec { + let dims = unsafe { slice::from_raw_parts(self.dims, self.ndims) }; + dims.to_vec() + } +} + +/// # Safety +/// +/// `mat1` and `mat2` should point to a valid 1DArray of `f64` floats in row-major order +#[no_mangle] +pub unsafe extern "C" fn np_dot(mat1: *mut InputMatrix, mat2: *mut InputMatrix) -> f64 { + let mat1 = mat1.as_mut().unwrap(); + let mat2 = mat2.as_mut().unwrap(); + + if !(mat1.ndims == 1 && mat2.ndims == 1) { + let err_msg = format!( + "expected 1D Vector Input, but received {}D and {}D input", + mat1.ndims, mat2.ndims + ); + raise_exn!("ValueError", "np_dot", err_msg); + } + + let dim1 = (*mat1).get_dims(); + let dim2 = (*mat2).get_dims(); + + if dim1[0] != dim2[0] { + let err_msg = format!("shapes ({},) and ({},) not aligned", dim1[0], dim2[0]); + raise_exn!("ValueError", "np_dot", err_msg); + } + let data_slice1 = unsafe { slice::from_raw_parts_mut(mat1.data, dim1[0]) }; + let data_slice2 = unsafe { slice::from_raw_parts_mut(mat2.data, dim2[0]) }; + + let matrix1 = DMatrix::from_row_slice(dim1[0], 1, data_slice1); + let matrix2 = DMatrix::from_row_slice(dim2[0], 1, data_slice2); + + matrix1.dot(&matrix2) +} + +/// # Safety +/// +/// `mat1` and `mat2` should point to a valid 2DArray of `f64` floats in row-major order +#[no_mangle] +pub unsafe extern "C" fn np_linalg_matmul( + mat1: *mut InputMatrix, + mat2: *mut InputMatrix, + out: *mut InputMatrix, +) { + let mat1 = mat1.as_mut().unwrap(); + let mat2 = mat2.as_mut().unwrap(); + let out = out.as_mut().unwrap(); + + if !(mat1.ndims == 2 && mat2.ndims == 2) { + let err_msg = format!( + "expected 2D Vector Input, but received {}D and {}D input", + mat1.ndims, mat2.ndims + ); + raise_exn!("ValueError", "np_matmul", err_msg); + } + + let dim1 = (*mat1).get_dims(); + let dim2 = (*mat2).get_dims(); + + if dim1[1] != dim2[0] { + let err_msg = format!( + "shapes ({},{}) and ({},{}) not aligned: {} (dim 1) != {} (dim 0)", + dim1[0], dim1[1], dim2[0], dim2[1], dim1[1], dim2[0] + ); + raise_exn!("ValueError", "np_matmul", err_msg); + } + + let outdim = out.get_dims(); + let out_slice = unsafe { slice::from_raw_parts_mut(out.data, outdim[0] * outdim[1]) }; + let data_slice1 = unsafe { slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]) }; + let data_slice2 = unsafe { slice::from_raw_parts_mut(mat2.data, dim2[0] * dim2[1]) }; + + let matrix1 = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1); + let matrix2 = DMatrix::from_row_slice(dim2[0], dim2[1], data_slice2); + let mut result = DMatrix::::zeros(outdim[0], outdim[1]); + + matrix1.mul_to(&matrix2, &mut result); + out_slice.copy_from_slice(result.transpose().as_slice()); +} + +/// # Safety +/// +/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order +#[no_mangle] +pub unsafe extern "C" fn np_linalg_cholesky(mat1: *mut InputMatrix, out: *mut InputMatrix) { + let mat1 = mat1.as_mut().unwrap(); + let out = out.as_mut().unwrap(); + + if mat1.ndims != 2 { + let err_msg = format!("expected 2D Vector Input, but received {}D input", mat1.ndims); + raise_exn!("ValueError", "np_linalg_cholesky", err_msg); + } + + let dim1 = (*mat1).get_dims(); + if dim1[0] != dim1[1] { + let err_msg = + format!("last 2 dimensions of the array must be square: {0} != {1}", dim1[0], dim1[1]); + raise_exn!("LinAlgError", "np_linalg_cholesky", err_msg); + } + + let outdim = out.get_dims(); + let out_slice = unsafe { slice::from_raw_parts_mut(out.data, outdim[0] * outdim[1]) }; + let data_slice1 = unsafe { slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]) }; + + let matrix1 = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1); + let result = matrix1.cholesky(); + match result { + Some(res) => { + out_slice.copy_from_slice(res.unpack().transpose().as_slice()); + } + None => { + raise_exn!("LinAlgError", "np_linalg_cholesky", "Matrix is not positive definite"); + } + }; +} + +/// # Safety +/// +/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order +#[no_mangle] +pub unsafe extern "C" fn np_linalg_qr( + mat1: *mut InputMatrix, + out_q: *mut InputMatrix, + out_r: *mut InputMatrix, +) { + let mat1 = mat1.as_mut().unwrap(); + let out_q = out_q.as_mut().unwrap(); + let out_r = out_r.as_mut().unwrap(); + + if mat1.ndims != 2 { + let err_msg = format!("expected 2D Vector Input, but received {}D input", mat1.ndims); + raise_exn!("ValueError", "np_linalg_cholesky", err_msg); + } + + let dim1 = (*mat1).get_dims(); + let outq_dim = (*out_q).get_dims(); + let outr_dim = (*out_r).get_dims(); + + let data_slice1 = unsafe { slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]) }; + let out_q_slice = unsafe { slice::from_raw_parts_mut(out_q.data, outq_dim[0] * outq_dim[1]) }; + let out_r_slice = unsafe { slice::from_raw_parts_mut(out_r.data, outr_dim[0] * outr_dim[1]) }; + + // Refer to https://github.com/dimforge/nalgebra/issues/735 + let matrix1 = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1); + + let res = matrix1.qr(); + let (q, r) = res.unpack(); + + // Uses different algo need to match numpy + out_q_slice.copy_from_slice(q.transpose().as_slice()); + out_r_slice.copy_from_slice(r.transpose().as_slice()); +} + +/// # Safety +/// +/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order +#[no_mangle] +pub unsafe extern "C" fn np_linalg_svd( + mat1: *mut InputMatrix, + outu: *mut InputMatrix, + outs: *mut InputMatrix, + outvh: *mut InputMatrix, +) { + let mat1 = mat1.as_mut().unwrap(); + let outu = outu.as_mut().unwrap(); + let outs = outs.as_mut().unwrap(); + let outvh = outvh.as_mut().unwrap(); + + if mat1.ndims != 2 { + let err_msg = format!("expected 2D Vector Input, but received {}D input", mat1.ndims); + raise_exn!("ValueError", "np_linalg_svd", err_msg); + } + + let dim1 = (*mat1).get_dims(); + let outu_dim = (*outu).get_dims(); + let outs_dim = (*outs).get_dims(); + let outvh_dim = (*outvh).get_dims(); + + let data_slice1 = unsafe { slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]) }; + let out_u_slice = unsafe { slice::from_raw_parts_mut(outu.data, outu_dim[0] * outu_dim[1]) }; + let out_s_slice = unsafe { slice::from_raw_parts_mut(outs.data, outs_dim[0]) }; + let out_vh_slice = + unsafe { slice::from_raw_parts_mut(outvh.data, outvh_dim[0] * outvh_dim[1]) }; + + let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1); + let result = matrix.svd(true, true); + out_u_slice.copy_from_slice(result.u.unwrap().transpose().as_slice()); + out_s_slice.copy_from_slice(result.singular_values.as_slice()); + out_vh_slice.copy_from_slice(result.v_t.unwrap().transpose().as_slice()); +} + +/// # Safety +/// +/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order +#[no_mangle] +pub unsafe extern "C" fn np_linalg_inv(mat1: *mut InputMatrix, out: *mut InputMatrix) { + let mat1 = mat1.as_mut().unwrap(); + let out = out.as_mut().unwrap(); + + if mat1.ndims != 2 { + let err_msg = format!("expected 2D Vector Input, but received {}D input", mat1.ndims); + raise_exn!("ValueError", "np_linalg_inv", err_msg); + } + let dim1 = (*mat1).get_dims(); + + if dim1[0] != dim1[1] { + let err_msg = + format!("last 2 dimensions of the array must be square: {0} != {1}", dim1[0], dim1[1]); + raise_exn!("LinAlgError", "np_linalg_inv", err_msg); + } + + let outdim = out.get_dims(); + let out_slice = unsafe { slice::from_raw_parts_mut(out.data, outdim[0] * outdim[1]) }; + let data_slice1 = unsafe { slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]) }; + + let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1); + if !matrix.is_invertible() { + raise_exn!("LinAlgError", "np_linalg_inv", "no inverse for Singular Matrix"); + } + let inv = matrix.try_inverse().unwrap(); + out_slice.copy_from_slice(inv.transpose().as_slice()); +} + +/// # Safety +/// +/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order +#[no_mangle] +pub unsafe extern "C" fn np_linalg_pinv(mat1: *mut InputMatrix, out: *mut InputMatrix) { + let mat1 = mat1.as_mut().unwrap(); + let out = out.as_mut().unwrap(); + + if mat1.ndims != 2 { + let err_msg = format!("expected 2D Vector Input, but received {}D input", mat1.ndims); + raise_exn!("ValueError", "np_linalg_pinv", err_msg); + } + let dim1 = (*mat1).get_dims(); + let outdim = out.get_dims(); + let out_slice = unsafe { slice::from_raw_parts_mut(out.data, outdim[0] * outdim[1]) }; + let data_slice1 = unsafe { slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]) }; + + let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1); + let svd = matrix.svd(true, true); + let inv = svd.pseudo_inverse(1e-15); + + match inv { + Ok(m) => { + out_slice.copy_from_slice(m.transpose().as_slice()); + } + Err(e) => { + raise_exn!("LinAlgError", "np_linalg_pinv", e); + } + } +} + +/// # Safety +/// +/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order +#[no_mangle] +pub unsafe extern "C" fn sp_linalg_lu( + mat1: *mut InputMatrix, + out_l: *mut InputMatrix, + out_u: *mut InputMatrix, +) { + let mat1 = mat1.as_mut().unwrap(); + let out_l = out_l.as_mut().unwrap(); + let out_u = out_u.as_mut().unwrap(); + + if mat1.ndims != 2 { + let err_msg = format!("expected 2D Vector Input, but received {}D input", mat1.ndims); + raise_exn!("ValueError", "sp_linalg_lu", err_msg); + } + + let dim1 = (*mat1).get_dims(); + let outl_dim = (*out_l).get_dims(); + let outu_dim = (*out_u).get_dims(); + + let data_slice1 = unsafe { slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]) }; + let out_l_slice = unsafe { slice::from_raw_parts_mut(out_l.data, outl_dim[0] * outl_dim[1]) }; + let out_u_slice = unsafe { slice::from_raw_parts_mut(out_u.data, outu_dim[0] * outu_dim[1]) }; + + let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1); + let (_, l, u) = matrix.lu().unpack(); + + out_l_slice.copy_from_slice(l.transpose().as_slice()); + out_u_slice.copy_from_slice(u.transpose().as_slice()); +} + +/// # Safety +/// +/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order +#[no_mangle] +pub unsafe extern "C" fn sp_linalg_schur( + mat1: *mut InputMatrix, + out_t: *mut InputMatrix, + out_z: *mut InputMatrix, +) { + let mat1 = mat1.as_mut().unwrap(); + let out_t = out_t.as_mut().unwrap(); + let out_z = out_z.as_mut().unwrap(); + + if mat1.ndims != 2 { + let err_msg = format!("expected 2D Vector Input, but received {}D input", mat1.ndims); + raise_exn!("ValueError", "sp_linalg_schur", err_msg); + } + + let dim1 = (*mat1).get_dims(); + + if dim1[0] != dim1[1] { + let err_msg = + format!("last 2 dimensions of the array must be square: {0} != {1}", dim1[0], dim1[1]); + raise_exn!("LinAlgError", "np_linalg_schur", err_msg); + } + + let out_t_dim = (*out_t).get_dims(); + let out_z_dim = (*out_z).get_dims(); + + let data_slice1 = unsafe { slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]) }; + let out_t_slice = unsafe { slice::from_raw_parts_mut(out_t.data, out_t_dim[0] * out_t_dim[1]) }; + let out_z_slice = unsafe { slice::from_raw_parts_mut(out_z.data, out_z_dim[0] * out_z_dim[1]) }; + + let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1); + let (z, t) = matrix.schur().unpack(); + + out_t_slice.copy_from_slice(t.transpose().as_slice()); + out_z_slice.copy_from_slice(z.transpose().as_slice()); +} + +/// # Safety +/// +/// `mat1` should point to a valid 2DArray of `f64` floats in row-major order +#[no_mangle] +pub unsafe extern "C" fn sp_linalg_hessenberg( + mat1: *mut InputMatrix, + out_h: *mut InputMatrix, + out_q: *mut InputMatrix, +) { + let mat1 = mat1.as_mut().unwrap(); + let out_h = out_h.as_mut().unwrap(); + let out_q = out_q.as_mut().unwrap(); + + if mat1.ndims != 2 { + let err_msg = format!("expected 2D Vector Input, but received {}D input", mat1.ndims); + raise_exn!("ValueError", "sp_linalg_hessenberg", err_msg); + } + + let dim1 = (*mat1).get_dims(); + + if dim1[0] != dim1[1] { + let err_msg = + format!("last 2 dimensions of the array must be square: {} != {}", dim1[0], dim1[1]); + raise_exn!("LinAlgError", "sp_linalg_hessenberg", err_msg); + } + + let out_h_dim = (*out_h).get_dims(); + let out_q_dim = (*out_q).get_dims(); + + let data_slice1 = unsafe { slice::from_raw_parts_mut(mat1.data, dim1[0] * dim1[1]) }; + let out_h_slice = unsafe { slice::from_raw_parts_mut(out_h.data, out_h_dim[0] * out_h_dim[1]) }; + let out_q_slice = unsafe { slice::from_raw_parts_mut(out_q.data, out_q_dim[0] * out_q_dim[1]) }; + + let matrix = DMatrix::from_row_slice(dim1[0], dim1[1], data_slice1); + let (q, h) = matrix.hessenberg().unpack(); + + out_h_slice.copy_from_slice(h.transpose().as_slice()); + out_q_slice.copy_from_slice(q.transpose().as_slice()); +} diff --git a/nac3standalone/linalg_externfns/src/runtime_exception.rs b/nac3standalone/linalg_externfns/src/runtime_exception.rs new file mode 100644 index 00000000..7179f521 --- /dev/null +++ b/nac3standalone/linalg_externfns/src/runtime_exception.rs @@ -0,0 +1,66 @@ +#![allow(non_camel_case_types)] +#![allow(unused)] + +// ARTIQ Exception struct declaration +use cslice::CSlice; + +// Note: CSlice within an exception may not be actual cslice, they may be strings that exist only +// in the host. If the length == usize:MAX, the pointer is actually a string key in the host. +#[repr(C)] +#[derive(Clone)] +pub struct Exception<'a> { + pub id: u32, + pub file: CSlice<'a, u8>, + pub line: u32, + pub column: u32, + pub function: CSlice<'a, u8>, + pub message: CSlice<'a, u8>, + pub param: [i64; 3], +} + +fn str_err(_: core::str::Utf8Error) -> core::fmt::Error { + core::fmt::Error +} + +fn exception_str<'a>(s: &'a CSlice<'a, u8>) -> Result<&'a str, core::str::Utf8Error> { + if s.len() == usize::MAX { + Ok("") + } else { + core::str::from_utf8(s.as_ref()) + } +} + +pub unsafe fn raise(exception: *const Exception) -> ! { + let e = &*exception; + let f1 = exception_str(&e.function).map_err(str_err).unwrap(); + let f2 = exception_str(&e.file).map_err(str_err).unwrap(); + let f3 = exception_str(&e.message).map_err(str_err).unwrap(); + + panic!("Exception {} from {} in {}:{}:{}, message: {}", e.id, f1, f2, e.line, e.column, f3); +} + +static EXCEPTION_ID_LOOKUP: [(&str, u32); 14] = [ + ("RuntimeError", 0), + ("RTIOUnderflow", 1), + ("RTIOOverflow", 2), + ("RTIODestinationUnreachable", 3), + ("DMAError", 4), + ("I2CError", 5), + ("CacheError", 6), + ("SPIError", 7), + ("ZeroDivisionError", 8), + ("IndexError", 9), + ("UnwrapNoneError", 10), + ("Value", 11), + ("ValueError", 12), + ("LinAlgError", 13), +]; + +pub fn get_exception_id(name: &str) -> u32 { + for (n, id) in EXCEPTION_ID_LOOKUP.iter() { + if *n == name { + return *id; + } + } + unimplemented!("unallocated internal exception id") +}