use std::ptr::NonNull;
use libperl_sys::{AV, SV};
use crate::{Perl, Rv, Sv, sv_refcnt_inc};
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Av(NonNull<AV>);
impl Av {
#[inline]
pub fn new(perl: &Perl) -> Av {
unsafe {
let av = crate::thx_call!(perl, Perl_newAV,);
crate::thx_call!(perl, Perl_sv_2mortal, av as *mut SV);
Av(NonNull::new(av).expect("Perl_newAV returned null"))
}
}
#[inline]
pub fn push(&self, perl: &Perl, sv: Sv) {
unsafe {
let inc = sv_refcnt_inc(sv.as_ptr());
crate::thx_call!(perl, Perl_av_push, self.0.as_ptr(), inc);
}
}
#[inline]
pub fn into_rv(self, perl: &Perl) -> Rv<Av> {
unsafe {
let rv = crate::thx_call!(perl, Perl_newRV, self.0.as_ptr() as *mut SV);
crate::thx_call!(perl, Perl_sv_2mortal, rv);
Rv::from_raw_sv(rv)
}
}
#[inline]
pub unsafe fn from_raw_unchecked(p: *mut AV) -> Av {
debug_assert!(!p.is_null(), "Av::from_raw_unchecked received a null pointer");
Av(unsafe { NonNull::new_unchecked(p) })
}
#[inline]
pub fn len(&self, perl: &Perl) -> usize {
let n = unsafe { crate::thx_call!(perl, Perl_av_len, self.0.as_ptr()) };
if n < 0 { 0 } else { (n + 1) as usize }
}
#[inline]
pub fn get(&self, perl: &Perl, idx: usize) -> Option<Sv> {
let svp = unsafe {
crate::thx_call!(perl, Perl_av_fetch, self.0.as_ptr(), idx as isize, 0)
};
if svp.is_null() {
return None;
}
Sv::from_raw(unsafe { *svp })
}
#[inline]
pub fn iter<'a>(&'a self, perl: &'a Perl) -> AvIter<'a> {
let len = self.len(perl);
AvIter { perl, av: self.0, idx: 0, len }
}
#[inline]
pub fn as_ptr(&self) -> *mut AV {
self.0.as_ptr()
}
}
pub struct AvIter<'a> {
perl: &'a Perl,
av: NonNull<AV>,
idx: usize,
len: usize,
}
impl<'a> Iterator for AvIter<'a> {
type Item = Option<Sv>;
fn next(&mut self) -> Option<Self::Item> {
if self.idx >= self.len {
return None;
}
let i = self.idx;
self.idx += 1;
let svp = unsafe {
crate::thx_call!(self.perl, Perl_av_fetch, self.av.as_ptr(), i as isize, 0)
};
Some(if svp.is_null() {
None
} else {
Sv::from_raw(unsafe { *svp })
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
let r = self.len - self.idx;
(r, Some(r))
}
}