]> git.proxmox.com Git - perlmod.git/blame - perlmod/src/scalar.rs
destructors work...
[perlmod.git] / perlmod / src / scalar.rs
CommitLineData
7a433bb6
WB
1//! Module containing the [`Scalar`] and [`Mortal`] types.
2
f7cc8c37
WB
3use bitflags::bitflags;
4
5use crate::ffi::{self, SV};
6use crate::Value;
7
8/// An owned reference to a perl value.
9///
10/// This keeps a reference to a value which lives in the perl interpreter.
11/// This derefs to a `ScalarRef` which implements most of the basic functionality common to all
12/// `SV` related types.
f7cc8c37
WB
13#[repr(transparent)]
14pub struct Scalar(*mut SV);
15
f7cc8c37
WB
16impl Scalar {
17 /// Turn this into a "mortal" value. This will move this value's owned reference onto the
18 /// mortal stack to be cleaned up after the next perl statement if no more references exist.
19 ///
20 /// (To be garbage collected after this perl-statement.)
21 pub fn into_mortal(self) -> Mortal {
22 Mortal(unsafe { ffi::RSPL_sv_2mortal(self.into_raw()) })
23 }
24
25 /// Turn this into a raw `SV` transferring control of one reference count.
26 pub fn into_raw(self) -> *mut SV {
27 let ptr = self.0;
28 core::mem::forget(self);
29 ptr
30 }
31
32 /// Create a wrapping `Scalar` from an `SV` pointer. The `Scalar` takes over the owned
33 /// reference from the passed `SV`, which means it must not be a mortal reference.
34 ///
35 /// # Safety
36 ///
37 /// This does not change the value's reference count, it is assumed that we're taking ownership
38 /// of one reference.
39 ///
40 /// The caller must ensure that it is safe to decrease the reference count later on, or use
41 /// `into_raw()` instead of letting the `Scalar` get dropped.
42 pub unsafe fn from_raw_move(ptr: *mut SV) -> Self {
43 Self(ptr)
44 }
45
46 /// Increase the reference count on an `SV` pointer.
47 ///
48 /// # Safety
49 ///
50 /// The caller may still need to decrease the reference count for the `ptr` source value.
51 pub unsafe fn from_raw_ref(ptr: *mut SV) -> Self {
52 Self::from_raw_move(ffi::RSPL_SvREFCNT_inc(ptr))
53 }
54
55 /// Create a reference to `PL_sv_undef`.
56 pub fn new_undef() -> Self {
57 unsafe { Self::from_raw_ref(ffi::RSPL_get_undef()) }
58 }
59
60 /// Create a reference to `PL_sv_yes`.
61 pub fn new_yes() -> Self {
62 unsafe { Self::from_raw_ref(ffi::RSPL_get_yes()) }
63 }
64
65 /// Create a reference to `PL_sv_no`.
66 pub fn new_no() -> Self {
67 unsafe { Self::from_raw_ref(ffi::RSPL_get_no()) }
68 }
69
70 /// Create a new integer value:
71 pub fn new_int(v: isize) -> Self {
72 unsafe { Self::from_raw_move(ffi::RSPL_newSViv(v)) }
73 }
74
75 /// Create a new unsigned integer value:
76 pub fn new_uint(v: usize) -> Self {
77 unsafe { Self::from_raw_move(ffi::RSPL_newSVuv(v)) }
78 }
79
80 /// Create a new floating point value.
81 pub fn new_float(v: f64) -> Self {
82 unsafe { Self::from_raw_move(ffi::RSPL_newSVnv(v)) }
83 }
84
85 /// Create a new string value.
86 pub fn new_string(s: &str) -> Self {
87 Self::new_bytes(s.as_bytes())
88 }
89
90 /// Create a new byte string.
91 pub fn new_bytes(s: &[u8]) -> Self {
92 unsafe {
93 Self::from_raw_move(ffi::RSPL_newSVpvn(
94 s.as_ptr() as *const libc::c_char,
95 s.len() as libc::size_t,
96 ))
97 }
98 }
99}
100
101impl Drop for Scalar {
102 fn drop(&mut self) {
103 unsafe {
104 ffi::RSPL_SvREFCNT_dec(self.sv());
105 }
106 }
107}
108
109impl core::ops::Deref for Scalar {
110 type Target = ScalarRef;
111
112 fn deref(&self) -> &Self::Target {
113 unsafe { &*(self.0 as *mut ScalarRef) }
114 }
115}
116
117impl core::ops::DerefMut for Scalar {
118 fn deref_mut(&mut self) -> &mut Self::Target {
119 unsafe { &mut *(self.0 as *mut ScalarRef) }
120 }
121}
122
7a433bb6 123/// A value which has been pushed to perl's "mortal stack".
f7cc8c37
WB
124#[repr(transparent)]
125pub struct Mortal(*mut SV);
126
127impl Mortal {
128 /// Get the inner value.
129 pub fn into_raw(self) -> *mut SV {
130 self.0
131 }
132}
133
134impl core::ops::Deref for Mortal {
135 type Target = ScalarRef;
136
137 fn deref(&self) -> &Self::Target {
138 unsafe { &*(self.0 as *mut ScalarRef) }
139 }
140}
141
142impl core::ops::DerefMut for Mortal {
143 fn deref_mut(&mut self) -> &mut Self::Target {
144 unsafe { &mut *(self.0 as *mut ScalarRef) }
145 }
146}
147
148pub struct ScalarRef;
149
150bitflags! {
151 /// Represents the types a `Value` can contain. Values can usually contain multiple scalar types
152 /// at once and it is unclear which is the "true" type, so we can only check whether a value
153 /// contains something, not what it is originally meant to be!
154 ///
155 /// NOTE: The values must be the same as in our c glue code!
156 pub struct Flags: u8 {
157 const INTEGER = 1;
158 const DOUBLE = 2;
159 const STRING = 4;
160 }
161}
162
163/// While scalar types aren't clearly different from another, complex types are, so we do
164/// distinguish between these:
165#[derive(Clone, Copy, Debug, Eq, PartialEq)]
166pub enum Type {
167 Scalar(Flags),
168 Reference,
169 Array,
170 Hash,
171 Other(u8),
172}
173
174impl ScalarRef {
175 pub(crate) fn sv(&self) -> *mut SV {
176 self as *const ScalarRef as *const SV as *mut SV
177 }
178
179 /// Get some information about the value's type.
180 pub fn ty(&self) -> Type {
181 unsafe {
182 if ffi::RSPL_is_reference(self.sv()) {
183 Type::Reference
184 } else {
185 let flags = ffi::RSPL_type_flags(self.sv());
186 if flags > 0xff {
187 panic!("bad C type returned");
188 } else if flags != 0 {
189 Type::Scalar(Flags::from_bits(flags as u8).unwrap())
190 } else if ffi::RSPL_is_array(self.sv()) {
191 Type::Array
192 } else if ffi::RSPL_is_hash(self.sv()) {
193 Type::Hash
194 } else {
195 Type::Other(ffi::RSPL_svtype(self.sv()) as u8)
196 }
197 }
198 }
199 }
200
201 /// Dereference this reference.
202 pub fn dereference(&self) -> Option<Scalar> {
203 let ptr = unsafe { ffi::RSPL_dereference(self.sv()) };
204 if ptr.is_null() {
205 None
206 } else {
207 Some(unsafe { Scalar::from_raw_ref(ptr) })
208 }
209 }
210
211 /// Coerce to a double value. (perlxs SvNV).
212 pub fn nv(&self) -> f64 {
213 unsafe { ffi::RSPL_SvNV(self.sv()) }
214 }
215
216 /// Coerce to an integer value. (perlxs SvIV).
217 pub fn iv(&self) -> isize {
218 unsafe { ffi::RSPL_SvIV(self.sv()) }
219 }
220
221 /// Coerce to an utf8 string value. (perlxs SvPVutf8)
222 pub fn pv_utf8(&self) -> &str {
223 unsafe {
224 let mut len: libc::size_t = 0;
225 let ptr = ffi::RSPL_SvPVutf8(self.sv(), &mut len) as *const u8;
226 std::str::from_utf8_unchecked(std::slice::from_raw_parts(ptr, len))
227 }
228 }
229
230 /// Coerce to a string without utf8 encoding. (perlxs SvPV)
231 pub fn pv_string_bytes(&self) -> &[u8] {
232 unsafe {
233 let mut len: libc::size_t = 0;
234 let ptr = ffi::RSPL_SvPV(self.sv(), &mut len) as *const u8;
235 std::slice::from_raw_parts(ptr, len)
236 }
237 }
238
239 /// Coerce to a byte-string. (perlxs SvPVbyte)
240 pub fn pv_bytes(&self) -> &[u8] {
241 unsafe {
242 let mut len: libc::size_t = 0;
243 let ptr = ffi::RSPL_SvPVbyte(self.sv(), &mut len) as *const u8;
244 std::slice::from_raw_parts(ptr, len)
245 }
246 }
247
248 /// Create another owned reference to this value.
249 pub fn clone_ref(&self) -> Scalar {
250 unsafe { Scalar::from_raw_ref(self.sv()) }
251 }
252
253 /// Convenience check for SVt_NULL
254 pub fn is_undef(&self) -> bool {
255 0 == unsafe { ffi::RSPL_type_flags(self.sv()) }
256 }
257
258 /// Turn this into a `Value`.
259 pub fn into_value(self) -> Value {
260 Value::from_scalar(self.clone_ref())
261 }
262}
263
264impl std::fmt::Debug for Scalar {
265 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
266 let this: &ScalarRef = &self;
267 std::fmt::Debug::fmt(this, f)
268 }
269}
270
271impl std::fmt::Debug for ScalarRef {
272 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
273 use std::fmt::Debug;
274 match self.ty() {
275 Type::Scalar(flags) => {
276 if flags.intersects(Flags::STRING) {
277 Debug::fmt(self.pv_utf8(), f)
278 } else if flags.intersects(Flags::INTEGER) {
279 write!(f, "{}", self.iv())
280 } else if flags.intersects(Flags::DOUBLE) {
281 write!(f, "{}", self.nv())
282 } else {
283 write!(f, "<unhandled scalar>")
284 }
285 }
286 Type::Reference => write!(f, "<*REFERENCE>"),
287 Type::Array => write!(f, "<*ARRAY>"),
288 Type::Hash => write!(f, "<*HASH>"),
289 Type::Other(_) => write!(f, "<*PERLTYPE>"),
290 }
291 }
292}
293
294impl serde::Serialize for Scalar {
295 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
296 where
297 S: serde::Serializer,
298 {
299 use serde::ser::Error;
300
301 match self.ty() {
302 Type::Scalar(flags) => {
303 if flags.contains(Flags::STRING) {
304 serializer.serialize_str(self.pv_utf8())
305 } else if flags.contains(Flags::DOUBLE) {
306 serializer.serialize_f64(self.nv())
307 } else if flags.contains(Flags::INTEGER) {
308 serializer.serialize_i64(self.iv() as i64)
309 } else {
310 serializer.serialize_unit()
311 }
312 }
313 Type::Other(_) => Err(S::Error::custom(
314 "cannot deserialize weird magic perl values",
315 )),
316
317 // These are impossible as they are all handled by different Value enum types:
318 Type::Reference => Value::from(
319 self.dereference()
320 .ok_or_else(|| S::Error::custom("failed to dereference perl value"))?,
321 )
322 .serialize(serializer),
323 Type::Array => {
324 let this = unsafe { crate::Array::from_raw_ref(self.sv() as *mut ffi::AV) };
325 this.serialize(serializer)
326 }
327 Type::Hash => {
328 let this = unsafe { crate::Hash::from_raw_ref(self.sv() as *mut ffi::HV) };
329 this.serialize(serializer)
330 }
331 }
332 }
333}