use crate::c::{
SpiceBoolean, SpiceCell, SpiceChar, SpiceDLADescr, SpiceDSKDescr, SpiceDouble, SpiceEKAttDsc,
SpiceEKSegSum, SpiceEllipse, SpiceInt, SpicePlane,
};
use crate::MAX_LEN_OUT;
use std::ffi::{CStr, CString};
pub type UdFunc = unsafe extern "C" fn(x: SpiceDouble, value: *mut SpiceDouble);
pub type UdFuns = UdFunc;
pub type UdFunb =
unsafe extern "C" fn(udfuns: Option<UdFuns>, x: SpiceDouble, xbool: *mut SpiceBoolean);
pub type UdStep = unsafe extern "C" fn(et: SpiceDouble, step: *mut SpiceDouble);
pub type UdRefn = unsafe extern "C" fn(
t1: SpiceDouble,
t2: SpiceDouble,
s1: SpiceBoolean,
s2: SpiceBoolean,
t: *mut SpiceDouble,
);
pub type UdRepi =
unsafe extern "C" fn(cnfine: *mut SpiceCell, srcpre: *mut SpiceChar, srcsuf: *mut SpiceChar);
pub type UdRepu = unsafe extern "C" fn(ivbeg: SpiceDouble, ivend: SpiceDouble, et: SpiceDouble);
pub type UdRepf = unsafe extern "C" fn();
pub type UdBail = unsafe extern "C" fn() -> SpiceBoolean;
const INLINE_IN: usize = 64;
pub struct Buffer<const N: usize> {
inline: [SpiceChar; N],
heap: Option<Vec<SpiceChar>>,
}
impl<const N: usize> Buffer<N> {
pub fn from_text(value: &str) -> Self {
let bytes = value.as_bytes();
if bytes.len() < N && !bytes.contains(&0) {
let mut inline = [0; N];
for (target, byte) in inline.iter_mut().zip(bytes) {
*target = *byte as SpiceChar;
}
return Self { inline, heap: None };
}
let owned = to_cstring(value);
let heap = owned
.as_bytes_with_nul()
.iter()
.map(|&byte| byte as SpiceChar)
.collect();
Self {
inline: [0; N],
heap: Some(heap),
}
}
pub fn with_len(len: usize) -> Self {
let len = len.max(1);
if len <= N {
return Self {
inline: [0; N],
heap: None,
};
}
Self {
inline: [0; N],
heap: Some(vec![0; len]),
}
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut SpiceChar {
match &mut self.heap {
Some(heap) => heap.as_mut_ptr(),
None => self.inline.as_mut_ptr(),
}
}
pub fn into_string(self) -> String {
match &self.heap {
Some(heap) => from_cbuf(heap),
None => from_cbuf(&self.inline),
}
}
}
pub trait SpiceArg {
type Owned;
type Raw;
fn own(self) -> Self::Owned;
fn raw(owned: &mut Self::Owned) -> Self::Raw;
}
pub struct In<T: SpiceArg> {
owned: T::Owned,
}
impl<T: SpiceArg> In<T> {
#[inline]
pub fn new(value: T) -> Self {
Self { owned: value.own() }
}
#[inline]
pub fn raw(&mut self) -> T::Raw {
T::raw(&mut self.owned)
}
}
macro_rules! scalar_arg {
($($ty:ty => $raw:ty),* $(,)?) => {$(
impl SpiceArg for $ty {
type Owned = $ty;
type Raw = $raw;
#[inline]
fn own(self) -> Self::Owned {
self
}
#[inline]
fn raw(owned: &mut Self::Owned) -> Self::Raw {
*owned as $raw
}
}
)*};
}
scalar_arg! {
f32 => SpiceDouble,
f64 => SpiceDouble,
i8 => SpiceInt,
i16 => SpiceInt,
i32 => SpiceInt,
i64 => SpiceInt,
isize => SpiceInt,
u8 => SpiceInt,
u16 => SpiceInt,
u32 => SpiceInt,
u64 => SpiceInt,
usize => SpiceInt,
bool => SpiceBoolean,
}
macro_rules! array_arg {
($($ty:ty => $raw:ty),* $(,)?) => {$(
impl<const N: usize> SpiceArg for [$ty; N] {
type Owned = [$ty; N];
type Raw = *mut $raw;
#[inline]
fn own(self) -> Self::Owned {
self
}
#[inline]
fn raw(owned: &mut Self::Owned) -> Self::Raw {
owned.as_mut_ptr()
}
}
impl<const M: usize, const N: usize> SpiceArg for [[$ty; N]; M] {
type Owned = [[$ty; N]; M];
type Raw = *mut $raw;
#[inline]
fn own(self) -> Self::Owned {
self
}
#[inline]
fn raw(owned: &mut Self::Owned) -> Self::Raw {
owned.as_mut_ptr().cast()
}
}
)*};
}
array_arg! {
f64 => SpiceDouble,
i32 => SpiceInt,
}
impl<'a, T> SpiceArg for &'a [T] {
type Owned = &'a [T];
type Raw = *const T;
#[inline]
fn own(self) -> Self::Owned {
self
}
#[inline]
fn raw(owned: &mut Self::Owned) -> Self::Raw {
owned.as_ptr()
}
}
impl<'a, T> SpiceArg for &'a mut [T] {
type Owned = &'a mut [T];
type Raw = *mut T;
#[inline]
fn own(self) -> Self::Owned {
self
}
#[inline]
fn raw(owned: &mut Self::Owned) -> Self::Raw {
owned.as_mut_ptr()
}
}
impl SpiceArg for char {
type Owned = char;
type Raw = SpiceChar;
#[inline]
fn own(self) -> Self::Owned {
self
}
#[inline]
fn raw(owned: &mut Self::Owned) -> Self::Raw {
*owned as u32 as SpiceChar
}
}
impl SpiceArg for &str {
type Owned = Buffer<INLINE_IN>;
type Raw = *mut SpiceChar;
#[inline]
fn own(self) -> Self::Owned {
Buffer::from_text(self)
}
#[inline]
fn raw(owned: &mut Self::Owned) -> Self::Raw {
owned.as_mut_ptr()
}
}
impl SpiceArg for &String {
type Owned = Buffer<INLINE_IN>;
type Raw = *mut SpiceChar;
#[inline]
fn own(self) -> Self::Owned {
Buffer::from_text(self)
}
#[inline]
fn raw(owned: &mut Self::Owned) -> Self::Raw {
owned.as_mut_ptr()
}
}
impl SpiceArg for String {
type Owned = Buffer<INLINE_IN>;
type Raw = *mut SpiceChar;
#[inline]
fn own(self) -> Self::Owned {
Buffer::from_text(&self)
}
#[inline]
fn raw(owned: &mut Self::Owned) -> Self::Raw {
owned.as_mut_ptr()
}
}
macro_rules! struct_arg {
($($ty:ty),* $(,)?) => {$(
impl SpiceArg for $ty {
type Owned = $ty;
type Raw = *mut $ty;
#[inline]
fn own(self) -> Self::Owned {
self
}
#[inline]
fn raw(owned: &mut Self::Owned) -> Self::Raw {
owned as *mut $ty
}
}
)*};
}
struct_arg!(SpiceDLADescr, SpiceDSKDescr, SpicePlane, SpiceEllipse);
pub trait SpiceRet: Sized {
type Buf;
type Raw;
fn buf() -> Self::Buf;
fn buf_with_len(len: usize) -> Self::Buf {
let _ = len;
Self::buf()
}
fn raw(buf: &mut Self::Buf) -> Self::Raw;
fn get(buf: Self::Buf) -> Self;
}
pub struct Out<T: SpiceRet> {
buf: T::Buf,
}
impl<T: SpiceRet> Out<T> {
#[inline]
pub fn new() -> Self {
Self { buf: T::buf() }
}
#[inline]
pub fn with_len(len: usize) -> Self {
Self {
buf: T::buf_with_len(len),
}
}
#[inline]
pub fn raw(&mut self) -> T::Raw {
T::raw(&mut self.buf)
}
#[inline]
pub fn get(self) -> T {
T::get(self.buf)
}
}
impl<T: SpiceRet> Default for Out<T> {
fn default() -> Self {
Self::new()
}
}
macro_rules! scalar_ret {
($($ty:ty => $raw:ty, $zero:expr, $read:expr);* $(;)?) => {$(
impl SpiceRet for $ty {
type Buf = $raw;
type Raw = *mut $raw;
#[inline]
fn buf() -> Self::Buf {
$zero
}
#[inline]
fn raw(buf: &mut Self::Buf) -> Self::Raw {
buf as *mut $raw
}
#[inline]
fn get(buf: Self::Buf) -> Self {
#[allow(clippy::redundant_closure_call)]
($read)(buf)
}
}
)*};
}
scalar_ret! {
f64 => SpiceDouble, 0.0, |value| value;
i32 => SpiceInt, 0, |value| value;
bool => SpiceBoolean, 0, |value: SpiceBoolean| value != 0;
}
macro_rules! array_ret {
($($ty:ty => $raw:ty),* $(,)?) => {$(
impl<const N: usize> SpiceRet for [$ty; N] {
type Buf = [$ty; N];
type Raw = *mut $raw;
#[inline]
fn buf() -> Self::Buf {
[<$ty>::default(); N]
}
#[inline]
fn raw(buf: &mut Self::Buf) -> Self::Raw {
buf.as_mut_ptr()
}
#[inline]
fn get(buf: Self::Buf) -> Self {
buf
}
}
impl<const M: usize, const N: usize> SpiceRet for [[$ty; N]; M] {
type Buf = [[$ty; N]; M];
type Raw = *mut $raw;
#[inline]
fn buf() -> Self::Buf {
[[<$ty>::default(); N]; M]
}
#[inline]
fn raw(buf: &mut Self::Buf) -> Self::Raw {
buf.as_mut_ptr().cast()
}
#[inline]
fn get(buf: Self::Buf) -> Self {
buf
}
}
)*};
}
array_ret! {
f64 => SpiceDouble,
i32 => SpiceInt,
}
impl SpiceRet for String {
type Buf = Buffer<MAX_LEN_OUT>;
type Raw = *mut SpiceChar;
#[inline]
fn buf() -> Self::Buf {
Buffer::with_len(MAX_LEN_OUT)
}
#[inline]
fn buf_with_len(len: usize) -> Self::Buf {
Buffer::with_len(len)
}
#[inline]
fn raw(buf: &mut Self::Buf) -> Self::Raw {
buf.as_mut_ptr()
}
#[inline]
fn get(buf: Self::Buf) -> Self {
buf.into_string()
}
}
macro_rules! struct_ret {
($($ty:ty),* $(,)?) => {$(
impl SpiceRet for $ty {
type Buf = $ty;
type Raw = *mut $ty;
#[inline]
fn buf() -> Self::Buf {
unsafe { std::mem::zeroed() }
}
#[inline]
fn raw(buf: &mut Self::Buf) -> Self::Raw {
buf as *mut $ty
}
#[inline]
fn get(buf: Self::Buf) -> Self {
buf
}
}
)*};
}
struct_ret!(
SpiceDLADescr,
SpiceDSKDescr,
SpicePlane,
SpiceEllipse,
SpiceEKAttDsc,
SpiceEKSegSum,
);
pub trait SpiceReturn {
type Raw;
unsafe fn from_c(raw: Self::Raw) -> Self;
}
impl SpiceReturn for f64 {
type Raw = SpiceDouble;
#[inline]
unsafe fn from_c(raw: Self::Raw) -> Self {
raw
}
}
impl SpiceReturn for i32 {
type Raw = SpiceInt;
#[inline]
unsafe fn from_c(raw: Self::Raw) -> Self {
raw
}
}
impl SpiceReturn for bool {
type Raw = SpiceBoolean;
#[inline]
unsafe fn from_c(raw: Self::Raw) -> Self {
raw != 0
}
}
impl SpiceReturn for String {
type Raw = *mut SpiceChar;
#[inline]
unsafe fn from_c(raw: Self::Raw) -> Self {
if raw.is_null() {
return String::new();
}
unsafe { CStr::from_ptr(raw) }
.to_string_lossy()
.into_owned()
}
}
pub fn to_cstring<S: AsRef<str>>(string: S) -> CString {
let string = string.as_ref();
CString::new(string).unwrap_or_else(|_| {
panic!("a string passed to CSPICE must not contain a null byte, got {string:?}")
})
}
pub fn from_cbuf(buf: &[SpiceChar]) -> String {
let bytes = buf.iter().map(|&byte| byte as u8).collect::<Vec<u8>>();
let end = bytes
.iter()
.position(|&byte| byte == 0)
.unwrap_or(bytes.len());
String::from_utf8_lossy(&bytes[..end])
.trim_end()
.to_string()
}
pub fn to_strided<S: AsRef<str>>(values: &[S]) -> (Vec<SpiceChar>, usize) {
let stride = values
.iter()
.map(|value| value.as_ref().len() + 1)
.max()
.unwrap_or(1);
let mut buffer = vec![0 as SpiceChar; values.len().max(1) * stride];
for (index, value) in values.iter().enumerate() {
let slot = &mut buffer[index * stride..(index + 1) * stride];
for (target, byte) in slot.iter_mut().zip(value.as_ref().as_bytes()) {
*target = *byte as SpiceChar;
}
}
(buffer, stride)
}
pub fn from_strided(buffer: &[SpiceChar], stride: usize, count: usize) -> Vec<String> {
(0..count)
.map(|index| from_cbuf(&buffer[index * stride..(index + 1) * stride]))
.collect()
}
pub(crate) const CELL_CTRLSZ: usize = crate::c::SPICE_CELL_CTRLSZ as usize;
impl<'a, T: crate::core::cell::CellItem> SpiceArg for &'a mut crate::core::cell::Cell<T> {
type Owned = &'a mut crate::core::cell::Cell<T>;
type Raw = *mut SpiceCell;
#[inline]
fn own(self) -> Self::Owned {
self
}
#[inline]
fn raw(owned: &mut Self::Owned) -> Self::Raw {
owned.as_mut_ptr()
}
}