]> git.proxmox.com Git - perlmod.git/blobdiff - perlmod/src/scalar.rs
experimental direct substr support
[perlmod.git] / perlmod / src / scalar.rs
index d641c5e35200449f258dc14933a0a0f6ca76caae..ecb62779afa5e00b74b2c9619b512e5c673282ad 100644 (file)
@@ -1,13 +1,13 @@
 //! 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};
 
@@ -55,7 +55,7 @@ impl Scalar {
     ///
     /// 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`.
@@ -332,7 +332,7 @@ impl ScalarRef {
     ///
     /// 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
@@ -342,7 +342,7 @@ impl ScalarRef {
     ///
     /// 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.
@@ -379,6 +379,65 @@ impl ScalarRef {
         }
     }
 
+    /// 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
@@ -394,14 +453,16 @@ impl ScalarRef {
         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.
@@ -412,16 +473,18 @@ impl ScalarRef {
     ///
     /// 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>,
@@ -439,12 +502,12 @@ impl ScalarRef {
 
     /// 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,
             )
@@ -475,14 +538,38 @@ impl ScalarRef {
     }
 
     /// 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));
         }
@@ -528,7 +615,7 @@ impl serde::Serialize for Scalar {
         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() {