fn testit(#[cv] cv: Value, arg: &str) {
let _ = (cv, arg);
}
+
+ #[export(raw_return)]
+ fn test_substr_return(#[raw] value: Value) -> Result<Value, Error> {
+ Ok(value.substr(3..6)?)
+ }
}
#[perlmod::package(name = "RSPM::EnvVarLibrary", lib = "x-${CARGO_PKG_NAME}-y")]
pub fn RSPL_MAGIC_ptr(mg: *const MAGIC) -> *const libc::c_char;
pub fn RSPL_MAGIC_len(mg: *const MAGIC) -> isize;
pub fn RSPL_PERL_MAGIC_ext() -> libc::c_int;
+
+ pub fn RSPL_PERL_MAGIC_substr() -> libc::c_int;
+ pub fn RSPL_vtbl_substr() -> *const MGVTBL;
+ pub fn RSPL_substr(orig: *mut SV, off: usize, len: usize) -> *mut SV;
}
/// Argument marker for the stack.
return LvTARG(sv);
}
+/// Takes ownership of `orig`, returns an owned scalar.
+/// This does NOT check `off` and `len`. That's up to the caller.
+extern SV* RSPL_substr(SV *orig, usize off, usize len) {
+ SV *ret = newSV_type(SVt_PVLV);
+ sv_magic(ret, NULL, PERL_MAGIC_substr, NULL, 0);
+ LvTYPE(ret) = 'x';
+ LvTARG(ret) = orig;
+ LvTARGOFF(ret) = off;
+ LvTARGLEN(ret) = len;
+ return ret;
+}
+
// We prefer this unsigned.
//extern unsigned char RSPL_LvTYPE(SV *sv) {
// return (unsigned char)LvTYPE(sv);
return SvGETMAGIC(sv);
}
+extern const MGVTBL* RSPL_vtbl_substr() {
+ return &PL_vtbl_substr;
+}
+
+extern int RSPL_PERL_MAGIC_substr() {
+ return PERL_MAGIC_substr;
+}
+
/// Create a new all-zeroes vtbl as perl docs suggest this is the safest way to
/// make sure what a `PERL_MAGIC_ext` magic actually means, as the ptr value
/// may be arbitrary. So this function is actually used to allocate "tags".
}
}
+ /// 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
let _perl = Box::leak(box_);
Ok(this)
}
+
+ /// Attempt to create a substring, provided the contained value is actually a string.
+ pub fn substr<I>(&self, index: I) -> Result<Value, Error>
+ where
+ I: std::slice::SliceIndex<[u8], Output = [u8]>,
+ {
+ match self {
+ Value::Scalar(s) => s.substr(index).map(Value::Scalar),
+ _ => Err(Error::new("substr called on non-scalar")),
+ }
+ }
}
impl From<Scalar> for Value {
RSPM::Foo142::test_trailing_optional(1, 99);
RSPM::Foo142::test_trailing_optional(2, undef);
RSPM::Foo142::test_trailing_optional(3);
+
+print("Substring test\n");
+my $orig = "OneTwoThree";
+my $sub = RSPM::Foo142::test_substr_return($orig);
+print("[$orig] [$sub]\n");
1, Some(99)
2, None
3, None
+Substring test
+[OneTwoThree] [Two]