]>
Commit | Line | Data |
---|---|---|
7a433bb6 WB |
1 | //! Module containing the [`Scalar`] and [`Mortal`] types. |
2 | ||
f3f92edc | 3 | use std::marker::PhantomData; |
e62be4a7 WB |
4 | use std::mem; |
5 | ||
f7cc8c37 WB |
6 | use bitflags::bitflags; |
7 | ||
1e46bfbe | 8 | use crate::error::MagicError; |
f7cc8c37 | 9 | use crate::ffi::{self, SV}; |
d17087df | 10 | use crate::magic::{Leakable, MagicSpec, MagicValue}; |
245de6dd WB |
11 | use crate::raw_value; |
12 | use crate::{Error, Value}; | |
f7cc8c37 WB |
13 | |
14 | /// An owned reference to a perl value. | |
15 | /// | |
16 | /// This keeps a reference to a value which lives in the perl interpreter. | |
1182e7f5 WB |
17 | /// This derefs to a [`ScalarRef`] which implements most of the basic functionality common to all |
18 | /// [`SV`] related types. | |
f7cc8c37 WB |
19 | #[repr(transparent)] |
20 | pub struct Scalar(*mut SV); | |
21 | ||
f7cc8c37 WB |
22 | impl Scalar { |
23 | /// Turn this into a "mortal" value. This will move this value's owned reference onto the | |
24 | /// mortal stack to be cleaned up after the next perl statement if no more references exist. | |
25 | /// | |
26 | /// (To be garbage collected after this perl-statement.) | |
27 | pub fn into_mortal(self) -> Mortal { | |
28 | Mortal(unsafe { ffi::RSPL_sv_2mortal(self.into_raw()) }) | |
29 | } | |
30 | ||
1182e7f5 | 31 | /// Turn this into a raw [`SV`] transferring control of one reference count. |
f7cc8c37 WB |
32 | pub fn into_raw(self) -> *mut SV { |
33 | let ptr = self.0; | |
34 | core::mem::forget(self); | |
35 | ptr | |
36 | } | |
37 | ||
1182e7f5 WB |
38 | /// Create a wrapping [`Scalar`] from an [`SV`] pointer. The [`Scalar`] takes over the owned |
39 | /// reference from the passed [`SV`], which means it must not be a mortal reference. | |
f7cc8c37 WB |
40 | /// |
41 | /// # Safety | |
42 | /// | |
43 | /// This does not change the value's reference count, it is assumed that we're taking ownership | |
44 | /// of one reference. | |
45 | /// | |
46 | /// The caller must ensure that it is safe to decrease the reference count later on, or use | |
1182e7f5 | 47 | /// [`into_raw()`](Scalar::into_raw) instead of letting the [`Scalar`] get dropped. |
f7cc8c37 WB |
48 | pub unsafe fn from_raw_move(ptr: *mut SV) -> Self { |
49 | Self(ptr) | |
50 | } | |
51 | ||
1182e7f5 | 52 | /// Increase the reference count on an [`SV`] pointer. |
f7cc8c37 WB |
53 | /// |
54 | /// # Safety | |
55 | /// | |
56 | /// The caller may still need to decrease the reference count for the `ptr` source value. | |
57 | pub unsafe fn from_raw_ref(ptr: *mut SV) -> Self { | |
c0afdc3a | 58 | unsafe { Self::from_raw_move(ffi::RSPL_SvREFCNT_inc(ptr)) } |
f7cc8c37 WB |
59 | } |
60 | ||
61 | /// Create a reference to `PL_sv_undef`. | |
62 | pub fn new_undef() -> Self { | |
63 | unsafe { Self::from_raw_ref(ffi::RSPL_get_undef()) } | |
64 | } | |
65 | ||
66 | /// Create a reference to `PL_sv_yes`. | |
67 | pub fn new_yes() -> Self { | |
68 | unsafe { Self::from_raw_ref(ffi::RSPL_get_yes()) } | |
69 | } | |
70 | ||
71 | /// Create a reference to `PL_sv_no`. | |
72 | pub fn new_no() -> Self { | |
73 | unsafe { Self::from_raw_ref(ffi::RSPL_get_no()) } | |
74 | } | |
75 | ||
76 | /// Create a new integer value: | |
77 | pub fn new_int(v: isize) -> Self { | |
78 | unsafe { Self::from_raw_move(ffi::RSPL_newSViv(v)) } | |
79 | } | |
80 | ||
81 | /// Create a new unsigned integer value: | |
82 | pub fn new_uint(v: usize) -> Self { | |
83 | unsafe { Self::from_raw_move(ffi::RSPL_newSVuv(v)) } | |
84 | } | |
85 | ||
86 | /// Create a new floating point value. | |
87 | pub fn new_float(v: f64) -> Self { | |
88 | unsafe { Self::from_raw_move(ffi::RSPL_newSVnv(v)) } | |
89 | } | |
90 | ||
91 | /// Create a new string value. | |
92 | pub fn new_string(s: &str) -> Self { | |
3f791730 WB |
93 | if s.as_bytes().iter().any(|&b| b >= 0x80) { |
94 | unsafe { | |
95 | Self::from_raw_move(ffi::RSPL_newSVpvn_utf8( | |
96 | s.as_bytes().as_ptr() as *const libc::c_char, | |
97 | s.as_bytes().len() as libc::size_t, | |
98 | )) | |
99 | } | |
100 | } else { | |
101 | Self::new_bytes(s.as_bytes()) | |
102 | } | |
f7cc8c37 WB |
103 | } |
104 | ||
105 | /// Create a new byte string. | |
106 | pub fn new_bytes(s: &[u8]) -> Self { | |
107 | unsafe { | |
108 | Self::from_raw_move(ffi::RSPL_newSVpvn( | |
109 | s.as_ptr() as *const libc::c_char, | |
110 | s.len() as libc::size_t, | |
111 | )) | |
112 | } | |
113 | } | |
e62be4a7 WB |
114 | |
115 | /// Convenience method to create a new raw pointer value. Note that pointers are stored as | |
116 | /// arbitrary "byte strings" and any such byte string value can be interpreted as a raw pointer. | |
117 | pub fn new_pointer<T>(s: *mut T) -> Self { | |
118 | Self::new_bytes(&(s as usize).to_ne_bytes()) | |
119 | } | |
24ca4777 WB |
120 | |
121 | /// Try to produce a substring from an existing "base" value and a `&str`. | |
122 | /// | |
ff419b19 | 123 | /// Returns `None` if `substr` is not part of `value` or if `substr` is the empty string. |
c9ec592f | 124 | pub fn substr_from_str_slice(value: &ScalarRef, substr: &str) -> Option<Scalar> { |
ff419b19 | 125 | if substr.is_empty() { |
c9ec592f | 126 | return None; |
ff419b19 WB |
127 | } |
128 | ||
24ca4777 WB |
129 | let value_bytes = value.pv_bytes(); |
130 | let value_beg = value_bytes.as_ptr() as usize; | |
131 | let value_end = value_beg + value_bytes.len(); | |
132 | let value_range = value_beg..value_end; | |
133 | ||
134 | let str_bytes = substr.as_bytes(); | |
135 | let str_beg = str_bytes.as_ptr() as usize; | |
ff419b19 | 136 | let str_end = str_beg + str_bytes.len() - 1; |
24ca4777 | 137 | if !value_range.contains(&str_beg) || !value_range.contains(&str_end) { |
c9ec592f | 138 | return None; |
24ca4777 WB |
139 | } |
140 | ||
141 | // we just checked the ranges: | |
cd69debd WB |
142 | let mut start = unsafe { str_bytes.as_ptr().offset_from(value_bytes.as_ptr()) as usize }; |
143 | let mut len = substr.len(); | |
144 | ||
145 | if unsafe { ffi::RSPL_SvUTF8(value.sv()) } { | |
146 | // go from byte offset to code point offset | |
147 | len = str_bytes | |
148 | .iter() | |
149 | .copied() | |
150 | .filter(|&b| (b as i8) >= -0x40) | |
151 | .count(); | |
152 | start = value_bytes[..start] | |
153 | .iter() | |
154 | .copied() | |
155 | .filter(|&b| (b as i8) >= -0x40) | |
156 | .count(); | |
157 | } | |
158 | ||
c9ec592f | 159 | Some(unsafe { |
24ca4777 WB |
160 | Scalar::from_raw_move(ffi::RSPL_substr( |
161 | ffi::RSPL_SvREFCNT_inc(value.sv()), | |
162 | start, | |
cd69debd | 163 | len, |
24ca4777 | 164 | )) |
c9ec592f | 165 | }) |
24ca4777 | 166 | } |
f7cc8c37 WB |
167 | } |
168 | ||
4fe09940 WB |
169 | impl Clone for Scalar { |
170 | #[inline] | |
171 | fn clone(&self) -> Self { | |
172 | unsafe { Self::from_raw_ref(self.sv()) } | |
173 | } | |
174 | } | |
175 | ||
f7cc8c37 WB |
176 | impl Drop for Scalar { |
177 | fn drop(&mut self) { | |
178 | unsafe { | |
179 | ffi::RSPL_SvREFCNT_dec(self.sv()); | |
180 | } | |
181 | } | |
182 | } | |
183 | ||
184 | impl core::ops::Deref for Scalar { | |
185 | type Target = ScalarRef; | |
186 | ||
187 | fn deref(&self) -> &Self::Target { | |
188 | unsafe { &*(self.0 as *mut ScalarRef) } | |
189 | } | |
190 | } | |
191 | ||
192 | impl core::ops::DerefMut for Scalar { | |
193 | fn deref_mut(&mut self) -> &mut Self::Target { | |
194 | unsafe { &mut *(self.0 as *mut ScalarRef) } | |
195 | } | |
196 | } | |
197 | ||
7a433bb6 | 198 | /// A value which has been pushed to perl's "mortal stack". |
f7cc8c37 WB |
199 | #[repr(transparent)] |
200 | pub struct Mortal(*mut SV); | |
201 | ||
202 | impl Mortal { | |
203 | /// Get the inner value. | |
204 | pub fn into_raw(self) -> *mut SV { | |
205 | self.0 | |
206 | } | |
207 | } | |
208 | ||
209 | impl core::ops::Deref for Mortal { | |
210 | type Target = ScalarRef; | |
211 | ||
212 | fn deref(&self) -> &Self::Target { | |
213 | unsafe { &*(self.0 as *mut ScalarRef) } | |
214 | } | |
215 | } | |
216 | ||
217 | impl core::ops::DerefMut for Mortal { | |
218 | fn deref_mut(&mut self) -> &mut Self::Target { | |
219 | unsafe { &mut *(self.0 as *mut ScalarRef) } | |
220 | } | |
221 | } | |
222 | ||
f3f92edc WB |
223 | /// A reference to a perl value. This is a pre reference type and cannot be constructed manually. |
224 | /// It is meant to provide methods common to `Value`, `Scalar`, `Array`, `Hash`, as these are all | |
225 | /// scalar values under the hood. | |
226 | pub struct ScalarRef(PhantomData<()>); | |
f7cc8c37 WB |
227 | |
228 | bitflags! { | |
229 | /// Represents the types a `Value` can contain. Values can usually contain multiple scalar types | |
230 | /// at once and it is unclear which is the "true" type, so we can only check whether a value | |
231 | /// contains something, not what it is originally meant to be! | |
232 | /// | |
233 | /// NOTE: The values must be the same as in our c glue code! | |
234 | pub struct Flags: u8 { | |
235 | const INTEGER = 1; | |
236 | const DOUBLE = 2; | |
237 | const STRING = 4; | |
238 | } | |
239 | } | |
240 | ||
241 | /// While scalar types aren't clearly different from another, complex types are, so we do | |
242 | /// distinguish between these: | |
243 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] | |
244 | pub enum Type { | |
245 | Scalar(Flags), | |
246 | Reference, | |
247 | Array, | |
248 | Hash, | |
249 | Other(u8), | |
250 | } | |
251 | ||
252 | impl ScalarRef { | |
253 | pub(crate) fn sv(&self) -> *mut SV { | |
254 | self as *const ScalarRef as *const SV as *mut SV | |
255 | } | |
256 | ||
245de6dd WB |
257 | /// Get the raw `*mut SV` value for this. |
258 | /// | |
259 | /// This does not affect the reference count of this value. This is up to the user. | |
260 | pub fn as_raw(&self) -> *mut SV { | |
261 | self.sv() | |
262 | } | |
263 | ||
b5c53f3d | 264 | fn get_type(sv: *mut SV) -> Type { |
f7cc8c37 | 265 | unsafe { |
9acdb782 WB |
266 | if !ffi::RSPL_is_defined(sv) { |
267 | return Type::Scalar(Flags::empty()); | |
268 | } | |
269 | ||
b5c53f3d WB |
270 | // These are simple: |
271 | if ffi::RSPL_is_reference(sv) { | |
272 | return Type::Reference; | |
273 | } else if ffi::RSPL_is_array(sv) { | |
274 | return Type::Array; | |
275 | } else if ffi::RSPL_is_hash(sv) { | |
276 | return Type::Hash; | |
277 | } | |
278 | ||
279 | // Scalars have flags: | |
280 | let flags = ffi::RSPL_type_flags(sv); | |
281 | if flags != 0 { | |
282 | return Type::Scalar(Flags::from_bits_truncate(flags as u8)); | |
283 | } | |
284 | ||
b5c53f3d WB |
285 | let ty = ffi::RSPL_svtype(sv); |
286 | if ty == 0 { | |
287 | // Looks like undef | |
3d5b075b | 288 | Type::Scalar(Flags::empty()) |
b5c53f3d WB |
289 | } else if ty == ffi::RSPL_PVLV() { |
290 | // We don't support all kinds of magic, but some lvalues are simple: | |
291 | // Try to GET the value and then check for definedness. | |
292 | ffi::RSPL_SvGETMAGIC(sv); | |
293 | if !ffi::RSPL_SvOK(sv) { | |
294 | // This happens when the value points to a non-existing hash element we could | |
295 | // auto-vivify, but we won't: | |
296 | return Type::Scalar(Flags::empty()); | |
f7cc8c37 | 297 | } |
b5c53f3d WB |
298 | |
299 | // Otherwise we just try to "recurse", which will work for substrings. | |
3d5b075b | 300 | Self::get_type(ffi::RSPL_LvTARG(sv)) |
b5c53f3d | 301 | } else { |
3d5b075b | 302 | Type::Other(ty as u8) |
f7cc8c37 | 303 | } |
3d5b075b | 304 | } |
f7cc8c37 WB |
305 | } |
306 | ||
b5c53f3d WB |
307 | /// Get some information about the value's type. |
308 | pub fn ty(&self) -> Type { | |
309 | Self::get_type(self.sv()) | |
61143f5d WB |
310 | } |
311 | ||
f7cc8c37 WB |
312 | /// Dereference this reference. |
313 | pub fn dereference(&self) -> Option<Scalar> { | |
314 | let ptr = unsafe { ffi::RSPL_dereference(self.sv()) }; | |
315 | if ptr.is_null() { | |
316 | None | |
317 | } else { | |
318 | Some(unsafe { Scalar::from_raw_ref(ptr) }) | |
319 | } | |
320 | } | |
321 | ||
1182e7f5 | 322 | /// Coerce to a double value. (perlxs `SvNV`). |
f7cc8c37 WB |
323 | pub fn nv(&self) -> f64 { |
324 | unsafe { ffi::RSPL_SvNV(self.sv()) } | |
325 | } | |
326 | ||
1182e7f5 | 327 | /// Coerce to an integer value. (perlxs `SvIV`). |
f7cc8c37 WB |
328 | pub fn iv(&self) -> isize { |
329 | unsafe { ffi::RSPL_SvIV(self.sv()) } | |
330 | } | |
331 | ||
1182e7f5 | 332 | /// Coerce to an utf8 string value. (perlxs `SvPVutf8`) |
83147b11 | 333 | pub fn pv_string_utf8(&self) -> &str { |
f7cc8c37 WB |
334 | unsafe { |
335 | let mut len: libc::size_t = 0; | |
336 | let ptr = ffi::RSPL_SvPVutf8(self.sv(), &mut len) as *const u8; | |
337 | std::str::from_utf8_unchecked(std::slice::from_raw_parts(ptr, len)) | |
338 | } | |
339 | } | |
340 | ||
1182e7f5 | 341 | /// Coerce to a string without utf8 encoding. (perlxs `SvPV`) |
83147b11 | 342 | pub fn pv_bytes(&self) -> &[u8] { |
f7cc8c37 WB |
343 | unsafe { |
344 | let mut len: libc::size_t = 0; | |
345 | let ptr = ffi::RSPL_SvPV(self.sv(), &mut len) as *const u8; | |
346 | std::slice::from_raw_parts(ptr, len) | |
347 | } | |
348 | } | |
349 | ||
83147b11 WB |
350 | /// Coerce to a byte-string, downgrading from utf-8. (perlxs `SvPVbyte`) |
351 | /// | |
352 | /// May fail if there are values which don't fit into bytes in the contained utf-8 string, in | |
353 | /// which case `None` is returned. | |
354 | pub fn pv_utf8_to_bytes(&self) -> Option<&[u8]> { | |
f7cc8c37 WB |
355 | unsafe { |
356 | let mut len: libc::size_t = 0; | |
357 | let ptr = ffi::RSPL_SvPVbyte(self.sv(), &mut len) as *const u8; | |
83147b11 WB |
358 | if ptr.is_null() { |
359 | return None; | |
360 | } | |
361 | Some(std::slice::from_raw_parts(ptr, len)) | |
f7cc8c37 WB |
362 | } |
363 | } | |
364 | ||
e62be4a7 | 365 | /// Interpret the byte string as a raw pointer. |
c837c7cc | 366 | pub fn pv_raw<T>(&self) -> Result<*mut T, Error> { |
e62be4a7 WB |
367 | let bytes = self.pv_bytes(); |
368 | ||
369 | let bytes: [u8; mem::size_of::<usize>()] = bytes | |
370 | .try_into() | |
76f6a079 | 371 | .map_err(|err| Error(format!("invalid value for pointer: {err}")))?; |
e62be4a7 WB |
372 | |
373 | Ok(usize::from_ne_bytes(bytes) as *mut T) | |
374 | } | |
375 | ||
c837c7cc WB |
376 | /// Interpret the byte string as a pointer and return it as a reference for convenience. |
377 | /// | |
378 | /// # Safety | |
379 | /// | |
380 | /// The user is responsible for making sure the underlying pointer is correct. | |
381 | pub unsafe fn pv_ref<T>(&self) -> Result<&T, Error> { | |
c0afdc3a | 382 | self.pv_raw().map(|p| unsafe { &*p }) |
c837c7cc WB |
383 | } |
384 | ||
385 | /// Interpret the byte string as a pointer and return it as a mutable reference for | |
386 | /// convenience. | |
387 | /// | |
388 | /// # Safety | |
389 | /// | |
390 | /// The user is responsible for making sure the underlying pointer is correct. | |
391 | pub unsafe fn pv_mut_ref<T>(&self) -> Result<&mut T, Error> { | |
c0afdc3a | 392 | self.pv_raw().map(|p| unsafe { &mut *p }) |
c837c7cc WB |
393 | } |
394 | ||
f7cc8c37 WB |
395 | /// Create another owned reference to this value. |
396 | pub fn clone_ref(&self) -> Scalar { | |
397 | unsafe { Scalar::from_raw_ref(self.sv()) } | |
398 | } | |
399 | ||
1182e7f5 | 400 | /// Convenience check for `SVt_NULL` |
f7cc8c37 WB |
401 | pub fn is_undef(&self) -> bool { |
402 | 0 == unsafe { ffi::RSPL_type_flags(self.sv()) } | |
403 | } | |
404 | ||
62f3c1eb | 405 | // FIXME: self consuming on a phantom type... this can probably not be useful |
1182e7f5 | 406 | /// Turn this into a [`Value`]. |
f7cc8c37 WB |
407 | pub fn into_value(self) -> Value { |
408 | Value::from_scalar(self.clone_ref()) | |
409 | } | |
89989b0f WB |
410 | |
411 | /// Get the reference type for this value. (Similar to `ref` in perl). | |
412 | /// | |
413 | /// If `blessed` is true and the value is a blessed reference, the package name will be | |
414 | /// returned, otherwise the scalar type (`"SCALAR"`, `"ARRAY"`, ...) will be returned. | |
415 | pub fn reftype(&self, blessed: bool) -> &'static str { | |
f8b49fc1 | 416 | let ptr = unsafe { ffi::RSPL_sv_reftype(self.sv(), i32::from(blessed)) }; |
89989b0f WB |
417 | |
418 | if ptr.is_null() { | |
419 | "<UNKNOWN>" | |
420 | } else { | |
421 | unsafe { | |
422 | std::ffi::CStr::from_ptr(ptr) | |
423 | .to_str() | |
424 | .unwrap_or("<NON-UTF8-CLASSNAME>") | |
425 | } | |
426 | } | |
427 | } | |
083e8236 | 428 | |
a43bf932 WB |
429 | /// Check whether this value is a substring. |
430 | pub fn is_substr(&self) -> bool { | |
431 | unsafe { | |
432 | self.find_raw_magic( | |
433 | Some(ffi::RSPL_PERL_MAGIC_substr()), | |
434 | Some(&*ffi::RSPL_vtbl_substr()), | |
435 | ) | |
436 | .is_some() | |
437 | } | |
438 | } | |
439 | ||
440 | /// Create a substring from a string. | |
441 | pub fn substr<I>(&self, index: I) -> Result<Scalar, Error> | |
442 | where | |
443 | I: std::slice::SliceIndex<[u8], Output = [u8]>, | |
444 | { | |
445 | let bytes = self.pv_bytes(); | |
446 | let slice: &[u8] = bytes | |
447 | .get(index) | |
448 | .ok_or_else(|| Error::new("substr with out of bounds range"))?; | |
449 | let start = unsafe { slice.as_ptr().offset_from(bytes.as_ptr()) }; | |
450 | let start = usize::try_from(start).map_err(|_| Error::new("bad substr index"))?; | |
451 | ||
452 | Ok(unsafe { | |
453 | Scalar::from_raw_move(ffi::RSPL_substr( | |
454 | ffi::RSPL_SvREFCNT_inc(self.sv()), | |
455 | start, | |
456 | slice.len(), | |
457 | )) | |
458 | }) | |
459 | } | |
460 | ||
083e8236 WB |
461 | /// Attach magic to this value. |
462 | /// | |
463 | /// # Safety | |
464 | /// | |
465 | /// The passed `vtbl` must stay valid for as long as the perl value exists. | |
466 | /// It is up to the user to make sure `how` has a valid value. Passing `None` will create a | |
467 | /// magic value of type `PERL_MAGIC_ext` for convenience (recommended). | |
468 | pub unsafe fn add_raw_magic( | |
469 | &self, | |
470 | obj: Option<&ScalarRef>, | |
471 | how: Option<libc::c_int>, | |
472 | vtbl: Option<&ffi::MGVTBL>, | |
473 | name: *const libc::c_char, | |
474 | namelen: i32, | |
475 | ) { | |
c0afdc3a WB |
476 | let _magic_ptr = unsafe { |
477 | ffi::RSPL_sv_magicext( | |
478 | self.sv(), | |
479 | obj.map(Self::sv).unwrap_or(std::ptr::null_mut()), | |
480 | how.unwrap_or_else(|| ffi::RSPL_PERL_MAGIC_ext()), | |
481 | vtbl, | |
482 | name, | |
483 | namelen, | |
484 | ) | |
485 | }; | |
083e8236 WB |
486 | } |
487 | ||
488 | /// Remove attached magic. | |
489 | /// | |
490 | /// If `ty` is `None`, a `PERL_MAGIC_ext` magic will be removed. | |
491 | /// | |
492 | /// # Safety | |
493 | /// | |
494 | /// It is up to the user that doing this will not crash the perl interpreter. | |
495 | pub unsafe fn remove_raw_magic(&self, ty: Option<libc::c_int>, vtbl: Option<&ffi::MGVTBL>) { | |
c0afdc3a WB |
496 | unsafe { |
497 | ffi::RSPL_sv_unmagicext( | |
498 | self.sv(), | |
499 | ty.unwrap_or_else(|| ffi::RSPL_PERL_MAGIC_ext()), | |
500 | vtbl, | |
501 | ) | |
502 | } | |
083e8236 WB |
503 | } |
504 | ||
505 | /// Find a magic value, if present. | |
506 | /// | |
8c587b3b | 507 | /// If `ty` is `None`, a `PERL_MAGIC_ext` magic will be searched for. |
083e8236 WB |
508 | pub fn find_raw_magic( |
509 | &self, | |
510 | ty: Option<libc::c_int>, | |
511 | vtbl: Option<&ffi::MGVTBL>, | |
512 | ) -> Option<&ffi::MAGIC> { | |
513 | unsafe { | |
514 | ffi::RSPL_mg_findext( | |
515 | self.sv(), | |
516 | ty.unwrap_or_else(|| ffi::RSPL_PERL_MAGIC_ext()), | |
517 | vtbl, | |
518 | ) | |
519 | .as_ref() | |
520 | } | |
521 | } | |
522 | ||
523 | /// Attach a magic tag to this value. This is a more convenient alternative to using | |
524 | /// [`add_raw_magic`](ScalarRef::add_raw_magic()) manually. | |
e5a46a38 | 525 | pub fn add_magic<T: Leakable>(&self, spec: MagicValue<'_, '_, 'static, T>) { |
083e8236 WB |
526 | unsafe { |
527 | self.add_raw_magic( | |
d17087df WB |
528 | spec.spec.obj, |
529 | spec.spec.how, | |
530 | Some(spec.spec.vtbl), | |
083e8236 WB |
531 | spec.ptr.map(Leakable::leak).unwrap_or(std::ptr::null()), |
532 | 0, | |
533 | ) | |
534 | } | |
535 | } | |
536 | ||
537 | /// Find a magic value attached to this perl value. | |
538 | /// | |
539 | /// # Safety | |
540 | /// | |
541 | /// It is up to the user to ensure the correct types are used in the provided `MagicSpec`. | |
76f6a079 WB |
542 | pub fn find_magic<'a, T: Leakable>( |
543 | &'_ self, | |
544 | spec: &'_ MagicSpec<'static, 'static, T>, | |
083e8236 WB |
545 | ) -> Option<&'a T::Pointee> { |
546 | match self.find_raw_magic(spec.how, Some(spec.vtbl)) { | |
547 | None => None, | |
548 | Some(mg) => { | |
549 | assert_eq!( | |
550 | mg.vtbl().map(|v| v as *const _), | |
551 | Some(spec.vtbl as *const _), | |
552 | "Perl_mg_findext misbehaved horribly", | |
553 | ); | |
554 | ||
555 | T::get_ref(mg.ptr()) | |
556 | } | |
557 | } | |
558 | } | |
559 | ||
560 | /// Remove a magic tag from this value previously added via | |
1e46bfbe WB |
561 | /// [`add_magic`](ScalarRef::add_magic()) and potentially reclaim the contained value of type |
562 | /// `T`. | |
083e8236 | 563 | /// |
1e46bfbe WB |
564 | /// When using a "default" magic tag via [`MagicTag::DEFAULT`](crate::magic::MagicTag::DEFAULT) |
565 | /// such as when using the [`declare_magic!`](crate::declare_magic!) macro, removing the magic | |
566 | /// implicitly causes perl call the `free` method, therefore in this case this method returns | |
567 | /// `None`. | |
568 | /// | |
569 | /// In case the magic was not found, [`MagicError::NotFound("")`] is returned. | |
083e8236 | 570 | /// |
1e46bfbe WB |
571 | /// This does not need to include the object and type information. |
572 | pub fn remove_magic<T: Leakable>( | |
573 | &self, | |
574 | spec: &MagicSpec<'static, 'static, T>, | |
575 | ) -> Result<Option<T>, MagicError> { | |
576 | let this = match self.find_raw_magic(spec.how, Some(spec.vtbl)) { | |
577 | None => Err(MagicError::NotFound("")), | |
578 | Some(mg) => { | |
579 | assert_eq!( | |
580 | mg.vtbl().map(|v| v as *const _), | |
581 | Some(spec.vtbl as *const _), | |
582 | "Perl_mg_findext misbehaved horribly", | |
583 | ); | |
584 | ||
585 | Ok(match mg.vtbl() { | |
586 | // We assume that a 'free' callback takes care of reclaiming the value! | |
587 | Some(v) if v.free.is_some() => None, | |
588 | _ => T::get_ref(mg.ptr()).map(|m| unsafe { T::reclaim(m) }), | |
589 | }) | |
590 | } | |
591 | }; | |
592 | ||
083e8236 WB |
593 | unsafe { |
594 | self.remove_raw_magic(spec.how, Some(spec.vtbl)); | |
595 | } | |
596 | this | |
597 | } | |
c9ec592f WB |
598 | |
599 | /// Merges a `Cow<str>` with this value. | |
600 | /// | |
601 | /// Note that the `Cow` part is not required here. | |
602 | /// | |
603 | /// If `self` is a UTF-8 scalar and its memory representation covers the borrowed substring, | |
604 | /// this is equivalent to calling [`Scalar::substr`] with the `index` matching the string. | |
605 | /// | |
606 | /// Otherwise (if the provided string is unrelated to `self`), this is also equivalent to | |
607 | /// calling `[Scalar::new_string]`. | |
608 | pub fn merge_str_slice(&self, text: &str) -> Scalar { | |
cd69debd | 609 | Scalar::substr_from_str_slice(self, text).unwrap_or_else(|| Scalar::new_string(text)) |
c9ec592f | 610 | } |
f7cc8c37 WB |
611 | } |
612 | ||
613 | impl std::fmt::Debug for Scalar { | |
614 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | |
3d5b075b | 615 | let this: &ScalarRef = self; |
f7cc8c37 WB |
616 | std::fmt::Debug::fmt(this, f) |
617 | } | |
618 | } | |
619 | ||
620 | impl std::fmt::Debug for ScalarRef { | |
621 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { | |
622 | use std::fmt::Debug; | |
623 | match self.ty() { | |
624 | Type::Scalar(flags) => { | |
625 | if flags.intersects(Flags::STRING) { | |
83147b11 | 626 | Debug::fmt(self.pv_string_utf8(), f) |
f7cc8c37 WB |
627 | } else if flags.intersects(Flags::INTEGER) { |
628 | write!(f, "{}", self.iv()) | |
629 | } else if flags.intersects(Flags::DOUBLE) { | |
630 | write!(f, "{}", self.nv()) | |
631 | } else { | |
632 | write!(f, "<unhandled scalar>") | |
633 | } | |
634 | } | |
635 | Type::Reference => write!(f, "<*REFERENCE>"), | |
636 | Type::Array => write!(f, "<*ARRAY>"), | |
637 | Type::Hash => write!(f, "<*HASH>"), | |
638 | Type::Other(_) => write!(f, "<*PERLTYPE>"), | |
639 | } | |
640 | } | |
641 | } | |
642 | ||
643 | impl serde::Serialize for Scalar { | |
644 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | |
645 | where | |
646 | S: serde::Serializer, | |
647 | { | |
648 | use serde::ser::Error; | |
649 | ||
245de6dd | 650 | if raw_value::is_enabled() { |
e5a46a38 | 651 | return raw_value::serialize_raw(self, serializer); |
245de6dd WB |
652 | } |
653 | ||
f7cc8c37 WB |
654 | match self.ty() { |
655 | Type::Scalar(flags) => { | |
656 | if flags.contains(Flags::STRING) { | |
83147b11 | 657 | serializer.serialize_str(self.pv_string_utf8()) |
f7cc8c37 WB |
658 | } else if flags.contains(Flags::DOUBLE) { |
659 | serializer.serialize_f64(self.nv()) | |
660 | } else if flags.contains(Flags::INTEGER) { | |
661 | serializer.serialize_i64(self.iv() as i64) | |
e077d87d WB |
662 | } else if flags.is_empty() { |
663 | serializer.serialize_none() | |
f7cc8c37 WB |
664 | } else { |
665 | serializer.serialize_unit() | |
666 | } | |
667 | } | |
e077d87d | 668 | Type::Other(other) => Err(S::Error::custom(format!( |
76f6a079 | 669 | "cannot serialize weird magic perl values ({other})", |
e077d87d | 670 | ))), |
f7cc8c37 WB |
671 | |
672 | // These are impossible as they are all handled by different Value enum types: | |
673 | Type::Reference => Value::from( | |
674 | self.dereference() | |
675 | .ok_or_else(|| S::Error::custom("failed to dereference perl value"))?, | |
676 | ) | |
677 | .serialize(serializer), | |
678 | Type::Array => { | |
679 | let this = unsafe { crate::Array::from_raw_ref(self.sv() as *mut ffi::AV) }; | |
680 | this.serialize(serializer) | |
681 | } | |
682 | Type::Hash => { | |
683 | let this = unsafe { crate::Hash::from_raw_ref(self.sv() as *mut ffi::HV) }; | |
684 | this.serialize(serializer) | |
685 | } | |
686 | } | |
687 | } | |
688 | } |