use std::ptr::NonNull;
use libperl_sys::{AV, CV, HV, PerlInterpreter, SV, svtype};
use crate::{Av, Cv, Gv, Hv, Perl};
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Sv(NonNull<SV>);
impl Sv {
#[inline]
pub unsafe fn from_raw_unchecked(p: *mut SV) -> Self {
debug_assert!(!p.is_null(), "Sv::from_raw_unchecked received a null pointer");
Sv(unsafe { NonNull::new_unchecked(p) })
}
#[inline]
pub fn from_raw(p: *mut SV) -> Option<Self> {
NonNull::new(p).map(Sv)
}
#[inline]
pub fn as_ptr(&self) -> *mut SV {
self.0.as_ptr()
}
#[inline]
pub fn new_iv(perl: &Perl, v: crate::IV) -> Sv {
unsafe {
let raw = crate::thx_call!(perl, Perl_newSViv, v);
let raw = crate::thx_call!(perl, Perl_sv_2mortal, raw);
Sv::from_raw_unchecked(raw)
}
}
#[inline]
pub fn new_uv(perl: &Perl, v: crate::UV) -> Sv {
unsafe {
let raw = crate::thx_call!(perl, Perl_newSVuv, v);
let raw = crate::thx_call!(perl, Perl_sv_2mortal, raw);
Sv::from_raw_unchecked(raw)
}
}
#[inline]
pub fn new_nv(perl: &Perl, v: crate::NV) -> Sv {
unsafe {
let raw = crate::thx_call!(perl, Perl_newSVnv, v);
let raw = crate::thx_call!(perl, Perl_sv_2mortal, raw);
Sv::from_raw_unchecked(raw)
}
}
#[inline]
pub fn iv(&self, perl: &Perl) -> crate::IV {
unsafe { crate::thx_call!(perl, SvIV, self.0.as_ptr()) }
}
#[inline]
pub fn uv(&self, perl: &Perl) -> crate::UV {
unsafe { crate::thx_call!(perl, SvUV, self.0.as_ptr()) }
}
#[inline]
pub fn nv(&self, perl: &Perl) -> crate::NV {
unsafe { crate::thx_call!(perl, SvNV, self.0.as_ptr()) }
}
#[inline]
pub fn pv<'a>(&'a self, perl: &Perl) -> &'a [u8] {
let mut len: libperl_sys::STRLEN = 0;
let ptr = unsafe {
crate::thx_call!(
perl,
Perl_sv_2pv_flags,
self.0.as_ptr(),
&mut len,
libperl_sys::SV_GMAGIC as _,
)
};
if ptr.is_null() {
return &[];
}
unsafe { ::core::slice::from_raw_parts(ptr as *const u8, len as usize) }
}
pub fn kind(&self) -> SvKind {
let sv = self.as_ptr();
unsafe {
if libperl_sys::isGV_with_GP(sv) {
return SvKind::Glob(Gv::from_raw_unchecked(sv as *mut libperl_sys::GV));
}
match libperl_sys::SvTYPE(sv) {
svtype::SVt_PVAV => SvKind::Array(Av::from_raw_unchecked(sv as *mut AV)),
svtype::SVt_PVHV => SvKind::Hash(Hv::from_raw_unchecked(sv as *mut HV)),
svtype::SVt_PVCV => SvKind::Code(Cv::from_raw_unchecked(sv as *mut CV)),
svtype::SVt_REGEXP => SvKind::Regexp(*self),
t if (t as u32) < (svtype::SVt_PVAV as u32) => {
if libperl_sys::SvROK(sv) != 0 {
SvKind::Ref(Sv::from_raw_unchecked(libperl_sys::SvRV(sv)))
} else {
SvKind::Scalar(*self)
}
}
t => SvKind::Other(t, *self),
}
}
}
pub fn new_pv(perl: &Perl, s: &str) -> Sv {
let bytes = s.as_bytes();
unsafe {
let raw = crate::thx_call!(
perl,
Perl_newSVpvn,
bytes.as_ptr() as *const ::core::ffi::c_char,
bytes.len() as _,
);
let cur: i64 = (*raw).sv_flags as i64;
(*raw).sv_flags = (cur | (libperl_sys::SVf_UTF8 as i64)) as _;
let raw = crate::thx_call!(perl, Perl_sv_2mortal, raw);
Sv::from_raw_unchecked(raw)
}
}
}
#[derive(Clone, Copy)]
pub enum SvKind {
Scalar(Sv),
Ref(Sv),
Array(Av),
Hash(Hv),
Code(Cv),
Glob(Gv),
Regexp(Sv),
Other(svtype, Sv),
}
impl std::fmt::Debug for SvKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SvKind::Scalar(_) => f.write_str("SvKind::Scalar"),
SvKind::Ref(_) => f.write_str("SvKind::Ref"),
SvKind::Array(_) => f.write_str("SvKind::Array"),
SvKind::Hash(_) => f.write_str("SvKind::Hash"),
SvKind::Code(_) => f.write_str("SvKind::Code"),
SvKind::Glob(_) => f.write_str("SvKind::Glob"),
SvKind::Regexp(_) => f.write_str("SvKind::Regexp"),
SvKind::Other(t, _) => write!(f, "SvKind::Other({t:?})"),
}
}
}
#[inline]
pub unsafe fn sv_refcnt_inc(sv: *mut SV) -> *mut SV {
if !sv.is_null() {
unsafe { (*sv).sv_refcnt = (*sv).sv_refcnt.wrapping_add(1); }
}
sv
}
#[cfg(perl_useithreads)]
#[inline]
pub fn sv_undef_ptr(my_perl: *mut PerlInterpreter) -> *mut SV {
unsafe { &raw mut (*my_perl).Isv_undef as *mut SV }
}
#[cfg(all(not(perl_useithreads), perlapi_ver28))]
#[inline]
pub fn sv_undef_ptr(_my_perl: *mut PerlInterpreter) -> *mut SV {
unsafe { &raw mut libperl_sys::PL_sv_immortals[1] as *mut SV }
}
#[cfg(all(not(perl_useithreads), not(perlapi_ver28)))]
#[inline]
pub fn sv_undef_ptr(_my_perl: *mut PerlInterpreter) -> *mut SV {
&raw mut libperl_sys::PL_sv_undef as *mut SV
}