//! Module containing the [`Scalar`] and [`Mortal`] types.
-use std::convert::TryInto;
use std::marker::PhantomData;
use std::mem;
use bitflags::bitflags;
+use crate::error::MagicError;
use crate::ffi::{self, SV};
-use crate::magic::{Leakable, MagicSpec};
+use crate::magic::{Leakable, MagicSpec, MagicValue};
use crate::raw_value;
use crate::{Error, Value};
///
/// The caller may still need to decrease the reference count for the `ptr` source value.
pub unsafe fn from_raw_ref(ptr: *mut SV) -> Self {
- Self::from_raw_move(ffi::RSPL_SvREFCNT_inc(ptr))
+ unsafe { Self::from_raw_move(ffi::RSPL_SvREFCNT_inc(ptr)) }
}
/// Create a reference to `PL_sv_undef`.
///
/// The user is responsible for making sure the underlying pointer is correct.
pub unsafe fn pv_ref<T>(&self) -> Result<&T, Error> {
- self.pv_raw().map(|p| &*p)
+ self.pv_raw().map(|p| unsafe { &*p })
}
/// Interpret the byte string as a pointer and return it as a mutable reference for
///
/// The user is responsible for making sure the underlying pointer is correct.
pub unsafe fn pv_mut_ref<T>(&self) -> Result<&mut T, Error> {
- self.pv_raw().map(|p| &mut *p)
+ self.pv_raw().map(|p| unsafe { &mut *p })
}
/// Create another owned reference to this value.
}
}
+ /// Check whether this value is a substring.
+ pub fn is_substr(&self) -> bool {
+ unsafe {
+ self.find_raw_magic(
+ Some(ffi::RSPL_PERL_MAGIC_substr()),
+ Some(&*ffi::RSPL_vtbl_substr()),
+ )
+ .is_some()
+ }
+ }
+
+ /// Create a substring from a string.
+ pub fn substr<I>(&self, index: I) -> Result<Scalar, Error>
+ where
+ I: std::slice::SliceIndex<[u8], Output = [u8]>,
+ {
+ let bytes = self.pv_bytes();
+ let slice: &[u8] = bytes
+ .get(index)
+ .ok_or_else(|| Error::new("substr with out of bounds range"))?;
+ let start = unsafe { slice.as_ptr().offset_from(bytes.as_ptr()) };
+ let start = usize::try_from(start).map_err(|_| Error::new("bad substr index"))?;
+
+ Ok(unsafe {
+ Scalar::from_raw_move(ffi::RSPL_substr(
+ ffi::RSPL_SvREFCNT_inc(self.sv()),
+ start,
+ slice.len(),
+ ))
+ })
+ }
+
+ /// Try to produce a substring from an existing "base" value and a `&str`.
+ ///
+ /// Returns `None` if `substr` is not part of `value`.
+ pub fn substr_from_str_slice(value: &ScalarRef, substr: &str) -> Result<Option<Scalar>, Error> {
+ let value_bytes = value.pv_bytes();
+ let value_beg = value_bytes.as_ptr() as usize;
+ let value_end = value_beg + value_bytes.len();
+ let value_range = value_beg..value_end;
+
+ let str_bytes = substr.as_bytes();
+ let str_beg = str_bytes.as_ptr() as usize;
+ let str_end = str_beg + str_bytes.len();
+ if !value_range.contains(&str_beg) || !value_range.contains(&str_end) {
+ return Ok(None);
+ }
+
+ // we just checked the ranges:
+ let start = unsafe { str_bytes.as_ptr().offset_from(value_bytes.as_ptr()) as usize };
+ Ok(Some(unsafe {
+ Scalar::from_raw_move(ffi::RSPL_substr(
+ ffi::RSPL_SvREFCNT_inc(value.sv()),
+ start,
+ substr.len(),
+ ))
+ }))
+ }
+
/// Attach magic to this value.
///
/// # Safety
name: *const libc::c_char,
namelen: i32,
) {
- let _magic_ptr = ffi::RSPL_sv_magicext(
- self.sv(),
- obj.map(Self::sv).unwrap_or(std::ptr::null_mut()),
- how.unwrap_or_else(|| ffi::RSPL_PERL_MAGIC_ext()),
- vtbl,
- name,
- namelen,
- );
+ let _magic_ptr = unsafe {
+ ffi::RSPL_sv_magicext(
+ self.sv(),
+ obj.map(Self::sv).unwrap_or(std::ptr::null_mut()),
+ how.unwrap_or_else(|| ffi::RSPL_PERL_MAGIC_ext()),
+ vtbl,
+ name,
+ namelen,
+ )
+ };
}
/// Remove attached magic.
///
/// It is up to the user that doing this will not crash the perl interpreter.
pub unsafe fn remove_raw_magic(&self, ty: Option<libc::c_int>, vtbl: Option<&ffi::MGVTBL>) {
- ffi::RSPL_sv_unmagicext(
- self.sv(),
- ty.unwrap_or_else(|| ffi::RSPL_PERL_MAGIC_ext()),
- vtbl,
- )
+ unsafe {
+ ffi::RSPL_sv_unmagicext(
+ self.sv(),
+ ty.unwrap_or_else(|| ffi::RSPL_PERL_MAGIC_ext()),
+ vtbl,
+ )
+ }
}
/// Find a magic value, if present.
///
- /// If `ty` is `None`, a `PERL_MAGIC_ext` magic will be removed.
+ /// If `ty` is `None`, a `PERL_MAGIC_ext` magic will be searched for.
pub fn find_raw_magic(
&self,
ty: Option<libc::c_int>,
/// Attach a magic tag to this value. This is a more convenient alternative to using
/// [`add_raw_magic`](ScalarRef::add_raw_magic()) manually.
- pub fn add_magic<'o, T: Leakable>(&self, spec: MagicSpec<'o, 'static, T>) {
+ pub fn add_magic<T: Leakable>(&self, spec: MagicValue<'_, '_, 'static, T>) {
unsafe {
self.add_raw_magic(
- spec.obj,
- spec.how,
- Some(spec.vtbl),
+ spec.spec.obj,
+ spec.spec.how,
+ Some(spec.spec.vtbl),
spec.ptr.map(Leakable::leak).unwrap_or(std::ptr::null()),
0,
)
}
/// Remove a magic tag from this value previously added via
- /// [`add_magic`](ScalarRef::add_magic()) and reclaim the contained value of type `T`.
+ /// [`add_magic`](ScalarRef::add_magic()) and potentially reclaim the contained value of type
+ /// `T`.
///
- /// This does not need to include the object and type information.
+ /// When using a "default" magic tag via [`MagicTag::DEFAULT`](crate::magic::MagicTag::DEFAULT)
+ /// such as when using the [`declare_magic!`](crate::declare_magic!) macro, removing the magic
+ /// implicitly causes perl call the `free` method, therefore in this case this method returns
+ /// `None`.
///
- /// Use the [`spec`](MagicSpec::spec())` method in case you have additional information in your
- /// magic tag.
- pub fn remove_magic<T: Leakable>(&self, spec: &MagicSpec<'static, 'static, T>) -> Option<T> {
- let this = self.find_magic(spec).map(|m| unsafe { T::reclaim(m) });
+ /// In case the magic was not found, [`MagicError::NotFound("")`] is returned.
+ ///
+ /// This does not need to include the object and type information.
+ pub fn remove_magic<T: Leakable>(
+ &self,
+ spec: &MagicSpec<'static, 'static, T>,
+ ) -> Result<Option<T>, MagicError> {
+ let this = match self.find_raw_magic(spec.how, Some(spec.vtbl)) {
+ None => Err(MagicError::NotFound("")),
+ Some(mg) => {
+ assert_eq!(
+ mg.vtbl().map(|v| v as *const _),
+ Some(spec.vtbl as *const _),
+ "Perl_mg_findext misbehaved horribly",
+ );
+
+ Ok(match mg.vtbl() {
+ // We assume that a 'free' callback takes care of reclaiming the value!
+ Some(v) if v.free.is_some() => None,
+ _ => T::get_ref(mg.ptr()).map(|m| unsafe { T::reclaim(m) }),
+ })
+ }
+ };
+
unsafe {
self.remove_raw_magic(spec.how, Some(spec.vtbl));
}
use serde::ser::Error;
if raw_value::is_enabled() {
- return raw_value::serialize_raw(&self, serializer);
+ return raw_value::serialize_raw(self, serializer);
}
match self.ty() {