Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2020-01-09 10:37:44 +01:00
commit f7cc8c37fc
27 changed files with 3110 additions and 0 deletions

5
.cargo/config Normal file
View File

@ -0,0 +1,5 @@
[source]
[source.debian-packages]
directory = "/usr/share/cargo/registry"
[source.crates-io]
replace-with = "debian-packages"

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
Cargo.lock
/RSPM

6
Cargo.toml Normal file
View File

@ -0,0 +1,6 @@
[workspace]
members = [
"perlmod",
"perlmod-macro",
"perlmod-test",
]

14
perlmod-macro/Cargo.toml Normal file
View File

@ -0,0 +1,14 @@
[package]
name = "perlmod-macro"
version = "0.1.0"
authors = ["Wolfgang Bumiller <w.bumiller@proxmox.com>"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
failure = "0.1"
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0", features = [ "full" ] }

View File

@ -0,0 +1 @@
{"package":"proxmox-api-macro","files":{}}

View File

@ -0,0 +1,5 @@
rust-perlmod-macro (0.1.0) proxmox-rust; urgency=medium
* Initial packaging.
-- Proxmox Support Team <support@proxmox.com> Thu, 09 Jan 2020 13:24:33 +0100

View File

@ -0,0 +1 @@
12

View File

@ -0,0 +1,33 @@
Source: rust-perlmod-macro
Section: rust
Priority: optional
Build-Depends: debhelper (>= 12),
dh-cargo (>= 21~),
cargo:native <!nocheck>,
rustc:native <!nocheck>,
libstd-rust-dev <!nocheck>,
librust-failure-0.1+default-dev <!nocheck>,
librust-proc-macro2-1.0+default-dev <!nocheck>,
librust-quote-1.0+default-dev <!nocheck>,
librust-syn-1.0+full-dev <!nocheck>,
Maintainer: Proxmox Support Team <support@proxmox.com>
Standards-Version: 4.4.1
Package: librust-proxmox-api-macro-dev
Architecture: any
Depends:
${misc:Depends},
librust-failure-0.1+default-dev,
librust-proc-macro2-1.0+default-dev,
librust-quote-1.0+default-dev,
librust-syn-1.0+full-dev,
Provides:
librust-perlmod-macro+default-dev (= ${binary:Version}),
librust-perlmod-macro-0-dev (= ${binary:Version}),
librust-perlmod-macro-0+default-dev (= ${binary:Version}),
librust-perlmod-macro-0.1-dev (= ${binary:Version}),
librust-perlmod-macro-0.1+default-dev (= ${binary:Version}),
librust-perlmod-macro-0.1.0-dev (= ${binary:Version}),
librust-perlmod-macro-0.1.0+default-dev (= ${binary:Version}),
Description: Macro to create perl modules to exported rust functions. - Rust source code
This package contains the source for the Rust perlmod crate.

View File

@ -0,0 +1,16 @@
Copyright (C) 2020 Proxmox Server Solutions GmbH
This software is written by Proxmox Server Solutions GmbH <support@proxmox.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.

3
perlmod-macro/debian/rules Executable file
View File

@ -0,0 +1,3 @@
#!/usr/bin/make -f
%:
dh $@ --buildsystem cargo

330
perlmod-macro/src/lib.rs Normal file
View File

@ -0,0 +1,330 @@
extern crate proc_macro;
extern crate proc_macro2;
use std::convert::TryFrom;
use failure::Error;
use proc_macro::TokenStream as TokenStream_1;
use proc_macro2::{Ident, Span, TokenStream};
use quote::quote;
use syn::parse_macro_input;
use syn::AttributeArgs;
macro_rules! format_err {
($span:expr => $($msg:tt)*) => { syn::Error::new_spanned($span, format!($($msg)*)) };
($span:expr, $($msg:tt)*) => { syn::Error::new($span, format!($($msg)*)) };
}
macro_rules! bail {
($span:expr => $($msg:tt)*) => { return Err(format_err!($span => $($msg)*).into()) };
($span:expr, $($msg:tt)*) => { return Err(format_err!($span, $($msg)*).into()) };
}
fn handle_error(mut item: TokenStream, data: Result<TokenStream, Error>) -> TokenStream {
match data {
Ok(output) => output,
Err(err) => match err.downcast::<syn::Error>() {
Ok(err) => {
item.extend(err.to_compile_error());
item
}
Err(err) => panic!("error in api/router macro: {}", err),
},
}
}
struct XSub {
rust_name: Ident,
xs_name: Ident,
tokens: TokenStream,
}
/// Macro for exporting rust functions as perl xsubs.
#[proc_macro_attribute]
pub fn package(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 {
let attr = parse_macro_input!(attr as AttributeArgs);
let item: TokenStream = item.into();
handle_error(item.clone(), perlmod_impl(attr, item)).into()
}
fn perlmod_impl(attr: AttributeArgs, item: TokenStream) -> Result<TokenStream, Error> {
let item: syn::Item = syn::parse2(item)?;
match item {
syn::Item::Fn(func) => {
let func = handle_function(func)?;
Ok(func.tokens)
}
syn::Item::Mod(module) => handle_module(attr, module),
_ => bail!(item => "expected module or function"),
}
}
fn handle_function(func: syn::ItemFn) -> Result<XSub, Error> {
//let vis = core::mem::replace(&mut func.vis, syn::Visibility::Inherited);
//if let syn::Visibility::Public(_) = vis {
// // ok
//} else {
// bail!(func.sig.fn_token => "only public functions can be exported as xsubs");
//}
let sig = &func.sig;
if !sig.generics.params.is_empty() {
bail!(&sig.generics => "generic functions cannot be exported as xsubs");
}
if sig.asyncness.is_some() {
bail!(&sig.asyncness => "async fns cannot be exported as xsubs");
}
let name = &sig.ident;
let xs_name = Ident::new(&format!("xs_{}", name), name.span());
let impl_xs_name = Ident::new(&format!("impl_xs_{}", name), name.span());
let mut extract_arguments = TokenStream::new();
let mut deserialized_arguments = TokenStream::new();
let mut passed_arguments = TokenStream::new();
for arg in &sig.inputs {
let pat_ty = match arg {
syn::FnArg::Receiver(_) => bail!(arg => "cannot export self-taking methods as xsubs"),
syn::FnArg::Typed(pt) => pt,
};
let arg_name = match &*pat_ty.pat {
syn::Pat::Ident(ident) => {
if ident.by_ref.is_some() {
bail!(ident => "xsub does not support by-ref parameters");
}
if ident.subpat.is_some() {
bail!(ident => "xsub does not support sub-patterns on parameters");
}
&ident.ident
}
_ => bail!(&pat_ty.pat => "xsub does not support this kind of parameter"),
};
let arg_type = &*pat_ty.ty;
let extracted_name = Ident::new(&format!("extracted_arg_{}", arg_name), arg_name.span());
let deserialized_name =
Ident::new(&format!("deserialized_arg_{}", arg_name), arg_name.span());
let missing_message = syn::LitStr::new("missing required parameter: '{}'", arg_name.span());
extract_arguments.extend(quote! {
let #extracted_name: ::perlmod::Value = match args.next() {
Some(arg) => ::perlmod::Value::from(arg),
None => {
return Err(::perlmod::Value::new_string(#missing_message)
.into_mortal()
.into_raw());
}
};
});
deserialized_arguments.extend(quote! {
let #deserialized_name: #arg_type = match ::perlmod::from_value(#extracted_name) {
Ok(data) => data,
Err(err) => {
return Err(::perlmod::Value::new_string(&err.to_string())
.into_mortal()
.into_raw());
}
};
});
if passed_arguments.is_empty() {
passed_arguments.extend(quote! { #deserialized_name });
} else {
passed_arguments.extend(quote! {, #deserialized_name });
}
}
let tokens = quote! {
#func
#[no_mangle]
pub extern "C" fn #xs_name(cv: &::perlmod::ffi::CV) {
unsafe {
match #impl_xs_name(cv) {
Ok(sv) => ::perlmod::ffi::stack_push_raw(sv),
Err(sv) => ::perlmod::ffi::croak(sv),
}
}
}
fn #impl_xs_name(
_cv: &::perlmod::ffi::CV,
) -> Result<*mut ::perlmod::ffi::SV, *mut ::perlmod::ffi::SV> {
let argmark = unsafe { ::perlmod::ffi::pop_arg_mark() };
let mut args = argmark.iter();
#extract_arguments
drop(args);
#deserialized_arguments
unsafe {
argmark.set_stack();
}
let result = match #name(#passed_arguments) {
Ok(output) => output,
Err(err) => {
return Err(::perlmod::Value::new_string(&err.to_string())
.into_mortal()
.into_raw());
}
};
match ::perlmod::to_value(&result) {
Ok(value) => Ok(value.into_mortal().into_raw()),
Err(err) => Err(::perlmod::Value::new_string(&err.to_string())
.into_mortal()
.into_raw()),
}
}
};
Ok(XSub {
rust_name: name.to_owned(),
xs_name,
tokens,
})
}
const LIB_NAME_DEFAULT: &str = r#"($pkg =~ /(?:^|::)([^:]+)$/)"#;
const MODULE_HEAD: &str = r#"
use strict;
use warnings;
use DynaLoader ();
my $LIB;
sub __load_shared_lib {
return if $LIB;
my ($pkg) = @_;
my $auto_path = ($pkg =~ s!::!/!gr);
my ($mod_name) = {{LIB_NAME}};
my @dirs = (map "-L$_/auto/$auto_path", @INC);
my (@mod_files) = DynaLoader::dl_findfile(@dirs, '-L./target/debug', $mod_name);
die "failed to locate shared library for '$pkg' (lib${mod_name}.so)\n" if !@mod_files;
$LIB = DynaLoader::dl_load_file($mod_files[0])
or die "failed to load library '$mod_files[0]'\n";
}
sub newXS {
my ($perl_func_name, $full_symbol_name, $filename) = @_;
my $sym = DynaLoader::dl_find_symbol($LIB, $full_symbol_name);
die "failed to locate '$full_symbol_name'\n" if !defined $sym;
DynaLoader::dl_install_xsub($perl_func_name, $sym, $filename);
}
BEGIN {
__load_shared_lib(__PACKAGE__);
"#;
const MODULE_TAIL: &str = "}\n";
struct ModuleArgs {
package_name: String,
file_name: String,
lib_name: Option<String>,
}
impl TryFrom<AttributeArgs> for ModuleArgs {
type Error = syn::Error;
fn try_from(args: AttributeArgs) -> Result<Self, Self::Error> {
let mut package_name = None;
let mut file_name = None;
let mut lib_name = None;
for arg in args {
match arg {
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
path,
eq_token: _,
lit: syn::Lit::Str(litstr),
})) => {
if path.is_ident("name") {
package_name = Some(litstr.value());
} else if path.is_ident("file") {
file_name = Some(litstr.value());
} else if path.is_ident("lib") {
lib_name = Some(litstr.value());
} else {
bail!(path => "unknown argument");
}
}
_ => bail!(Span::call_site(), "unexpected attribute argument"),
}
}
let package_name = package_name
.ok_or_else(|| format_err!(Span::call_site(), "missing 'package' argument"))?;
let file_name =
file_name.unwrap_or_else(|| format!("{}.pm", package_name.replace("::", "/")));
Ok(Self {
package_name,
file_name,
lib_name,
})
}
}
fn handle_module(attr: AttributeArgs, mut module: syn::ItemMod) -> Result<TokenStream, Error> {
let args = ModuleArgs::try_from(attr)?;
let mut module_source = format!("package {};\n{}", args.package_name, MODULE_HEAD);
if let Some((_brace, ref mut items)) = module.content {
for item in items.iter_mut() {
match core::mem::replace(item, syn::Item::Verbatim(TokenStream::new())) {
syn::Item::Fn(mut func) => {
let count = func.attrs.len();
func.attrs.retain(|attr| !attr.path.is_ident("export"));
// if we removed an #[export] macro this is an exported function:
if count != func.attrs.len() {
let func = handle_function(func)?;
*item = syn::Item::Verbatim(func.tokens);
module_source = format!(
"{} newXS('{}', '{}', 'src/FIXME.rs');\n",
module_source, func.rust_name, func.xs_name,
);
} else {
*item = syn::Item::Fn(func);
}
}
other => *item = other,
}
}
}
module_source.push_str(MODULE_TAIL);
if let Some(lib) = args.lib_name {
module_source = module_source.replace("{{LIB_NAME}}", &format!("('{}')", lib));
} else {
module_source = module_source.replace("{{LIB_NAME}}", LIB_NAME_DEFAULT);
}
let path = std::path::Path::new(&args.file_name);
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(path, module_source.as_bytes())?;
Ok(quote! { #module })
}

12
perlmod-test/Cargo.toml Normal file
View File

@ -0,0 +1,12 @@
[package]
name = "perlmod-test"
version = "0.1.0"
authors = ["Wolfgang Bumiller <w.bumiller@proxmox.com>"]
edition = "2018"
[lib]
crate-type = [ "cdylib" ]
[dependencies]
failure = "0.1"
perlmod = { path = "../perlmod", features = [ "exporter" ] }

13
perlmod-test/src/lib.rs Normal file
View File

@ -0,0 +1,13 @@
#[perlmod::package(name = "RSPM::Foo", lib = "perlmod_test")]
mod export {
use failure::{bail, Error};
#[export]
fn foo(a: u32, b: u32) -> Result<u32, Error> {
if a == 42 {
bail!("dying on magic number");
}
Ok(a + b)
}
}

20
perlmod/Cargo.toml Normal file
View File

@ -0,0 +1,20 @@
[package]
name = "perlmod"
version = "0.1.0"
authors = ["Wolfgang Bumiller <w.bumiller@proxmox.com>"]
edition = "2018"
build = "build.rs"
[dependencies]
bitflags = "1.2.1"
failure = "0.1"
libc = "0.2"
serde = { version = "1.0", features = ["derive"] }
perlmod-macro = { path = "../perlmod-macro", optional = true }
[features]
default = ["exporter"]
exporter = ["perlmod-macro"]
[build-dependencies]
cc = "1.0.46"

71
perlmod/build.rs Normal file
View File

@ -0,0 +1,71 @@
extern crate cc;
use std::path::Path;
use std::process::Command;
use std::{env, fs, io};
fn main() {
let out_dir = env::var("OUT_DIR").expect("expected OUT_DIR to be set by cargo");
let include_dir = format!("{}/include", out_dir);
let ppport_h_file = format!("{}/ppport.h", include_dir);
// quoted, without exterial double qoutes
let ppport_h_file_string_inner = ppport_h_file.replace('"', "\\\"");
if let Err(err) = fs::create_dir(Path::new(&include_dir)) {
if err.kind() != io::ErrorKind::AlreadyExists {
panic!("failed to make include dir in OUT_DIR");
}
}
// perl -MDevel::PPPort -e 'Devel::PPPort::WriteFile("include/ppport.h");'
Command::new("perl")
.arg("-MDevel::PPPort")
.arg("-e")
.arg(&format!(
r#"Devel::PPPort::WriteFile("{}");"#,
ppport_h_file_string_inner
))
.output()
.expect("failed to create ppport.h file using perl's Devel::PPPort");
// get include path:
// perl -MConfig -e 'print $Config{archlib}'
let perl_archlib = Command::new("perl")
.arg("-MConfig")
.arg("-e")
.arg("print $Config{archlib}")
.output()
.expect("failed to get perl arch include directory");
// technically not a true Path, but we expect a system path which should be utf8-compatible
let archlib_include_path = format!(
"{}/CORE",
std::str::from_utf8(&perl_archlib.stdout).expect("expected perl include path to be utf8")
);
// get perl cflags:
// perl -MConfig -e 'print $Config{ccflags}'
let perl_ccflags = Command::new("perl")
.arg("-MConfig")
.arg("-e")
.arg("print $Config{ccflags}")
.output()
.expect("failed to get perl cflags");
// technically garbage as it may contain paths, but since this should only contain system
// paths and otherwise also might contain quotes and what not let's just not really care:
let ccflags = std::str::from_utf8(&perl_ccflags.stdout).expect("expected cflags to be utf8");
let mut cc = cc::Build::new();
cc.pic(true)
.shared_flag(false)
.include(include_dir)
.include(archlib_include_path);
for flag in ccflags.split_ascii_whitespace() {
cc.flag(flag);
}
// now build the static library:
cc.file("src/glue.c").compile("libglue.a");
}

223
perlmod/src/array.rs Normal file
View File

@ -0,0 +1,223 @@
use std::convert::TryFrom;
use std::marker::PhantomData;
use crate::error::CastError;
use crate::ffi::{self, AV, SV};
use crate::scalar::{Scalar, ScalarRef};
use crate::Value;
/// An owned reference to a perl array value (AV).
///
/// This keeps a reference to a value which lives in the perl interpreter.
#[repr(transparent)]
pub struct Array(Scalar);
#[allow(clippy::new_without_default)]
impl Array {
/// Create a new array value.
pub fn new() -> Self {
unsafe { Self::from_raw_move(ffi::RSPL_newAV()) }
}
/// Turn this into a `Scalar`. The underlying perl value does not change, this is a pure type
/// cast down to a less specific "pointer" type.
pub fn into_scalar(self) -> Scalar {
self.0
}
/// Get the internal perl value as a low-level `AV` pointer.
pub fn av(&self) -> *mut AV {
self.0.sv() as *mut AV
}
/// "Downcast" a `Scalar` into an `Array`.
///
/// # Safety
///
/// The caller must verify that this is legal.
pub unsafe fn from_scalar(scalar: Scalar) -> Self {
Self(scalar)
}
/// Take over a raw `AV` value, assuming that we then own a reference to it.
///
/// # Safety
///
/// This does not change the value's reference count, it is assumed that we're taking ownership
/// of one reference.
///
/// The caller must ensure that it is safe to decrease the reference count later on, or use
/// `into_raw()` instead of letting the `Array` get dropped.
pub unsafe fn from_raw_move(ptr: *mut AV) -> Self {
Self(Scalar::from_raw_move(ptr as *mut SV))
}
/// Create a new reference to an existing `AV` value. This will increase the value's reference
/// count.
///
/// # Safety
///
/// The caller may still need to decrease the reference count for the `ptr` source value.
pub unsafe fn from_raw_ref(ptr: *mut AV) -> Self {
Self(Scalar::from_raw_ref(ptr as *mut SV))
}
/// Create a new reference to this value.
pub fn clone_ref(&self) -> Self {
Self(self.0.clone_ref())
}
/// Get the length of the array.
pub fn len(&self) -> usize {
// perl returns the highest index, not the length!
unsafe { ffi::RSPL_av_len(self.av()).wrapping_add(1) }
}
/// Check if this is an empty array.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Get a value from the array.
pub fn get(&self, index: usize) -> Option<Value> {
let index = index as libc::ssize_t;
let sv: *mut *mut SV = unsafe { ffi::RSPL_av_fetch(self.av(), index, 0) };
if sv.is_null() {
None
} else {
Some(unsafe { Value::from_raw_ref(*sv) })
}
}
/// Create an iterator over this array's values.
pub fn iter(&self) -> Iter {
Iter {
array: self.clone_ref(),
at: 0,
_phantom: PhantomData,
}
}
/// Pre-extend the array to up to the specified length..
pub fn reserve(&self, more: usize) {
let idx = self.len() + more - 1;
unsafe {
ffi::RSPL_av_extend(self.av(), idx as libc::ssize_t);
}
}
/// Push a value onto the array.
pub fn push(&self, value: Value) {
unsafe {
ffi::RSPL_av_push(self.av(), value.into_raw());
}
}
/// Pop a value off of the array's end.
pub fn pop(&self) -> Option<Value> {
if self.is_empty() {
None
} else {
Some(unsafe { Value::from_raw_move(ffi::RSPL_av_pop(self.av())) })
}
}
}
impl core::ops::Deref for Array {
type Target = ScalarRef;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl core::ops::DerefMut for Array {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.0
}
}
impl TryFrom<Scalar> for Array {
type Error = CastError;
fn try_from(scalar: Scalar) -> Result<Self, CastError> {
if unsafe { ffi::RSPL_is_array(scalar.sv()) } {
Ok(Self(scalar))
} else {
Err(CastError)
}
}
}
impl std::fmt::Debug for Array {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "[")?;
let mut comma = false;
for i in self {
if comma {
write!(f, ", {:?}", i)?;
} else {
comma = true;
write!(f, "{:?}", i)?;
}
}
write!(f, "]")?;
Ok(())
}
}
pub struct Iter<'a> {
array: Array,
at: usize,
_phantom: PhantomData<&'a Array>,
}
impl<'a> Iterator for Iter<'a> {
type Item = Value;
fn next(&mut self) -> Option<Self::Item> {
let at = self.at;
if at < self.array.len() {
self.at += 1;
self.array.get(at)
} else {
None
}
}
}
impl IntoIterator for Array {
type Item = Value;
type IntoIter = Iter<'static>;
fn into_iter(self) -> Self::IntoIter {
Iter {
array: self,
at: 0,
_phantom: PhantomData,
}
}
}
impl<'a> IntoIterator for &'a Array {
type Item = Value;
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl serde::Serialize for Array {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeSeq;
let mut seq = serializer.serialize_seq(Some(self.len()))?;
for i in self {
seq.serialize_element(&i)?;
}
seq.end()
}
}

551
perlmod/src/de.rs Normal file
View File

@ -0,0 +1,551 @@
use serde::de::{self, DeserializeOwned, DeserializeSeed, MapAccess, SeqAccess, Visitor};
use crate::error::Error;
use crate::scalar::Type;
use crate::Value;
use crate::{array, ffi, hash};
pub struct Deserializer {
input: Value,
option_allowed: bool,
}
pub fn from_value<T>(input: Value) -> Result<T, Error>
where
T: DeserializeOwned,
{
let mut deserializer = Deserializer::from_value(input);
let out = T::deserialize(&mut deserializer)?;
Ok(out)
}
impl Deserializer {
pub fn from_value(input: Value) -> Self {
Deserializer {
input,
option_allowed: true,
}
}
pub fn from_some_value(input: Value) -> Self {
Deserializer {
input,
option_allowed: false,
}
}
fn deref_current(&mut self) -> Result<(), Error> {
while let Value::Reference(_) = &self.input {
self.input = self.input.dereference().ok_or_else(|| {
Error::new("failed to dereference a reference while deserializing")
})?;
}
Ok(())
}
fn sanity_check(&mut self) -> Result<(), Error> {
if let Value::Scalar(value) = &self.input {
match value.ty() {
Type::Scalar(_) => Ok(()),
Type::Other(_) => Error::fail("cannot deserialize weird magic perl values"),
// These are impossible as they are all handled by different Value enum types:
Type::Reference => Error::fail("Value::Scalar: containing a reference"),
Type::Array => Error::fail("Value::Scalar: containing an array"),
Type::Hash => Error::fail("Value::Scalar: containing a hash"),
}
} else {
Ok(())
}
}
fn get(&mut self) -> Result<&Value, Error> {
self.deref_current()?;
self.sanity_check()?;
Ok(&self.input)
}
}
impl Deserializer {
/// deserialize_any, preferring a string value
fn deserialize_any_string<'de, V>(&mut self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
match self.get()? {
Value::Scalar(value) => match value.ty() {
Type::Scalar(flags) => {
use crate::scalar::Flags;
if flags.contains(Flags::STRING) {
visitor.visit_str(value.pv_utf8())
} else if flags.contains(Flags::DOUBLE) {
visitor.visit_f64(value.nv())
} else if flags.contains(Flags::INTEGER) {
visitor.visit_i64(value.iv() as i64)
} else {
visitor.visit_unit()
}
}
_ => unreachable!(),
},
Value::Hash(value) => visitor.visit_map(HashAccess::new(value)),
Value::Array(value) => visitor.visit_seq(ArrayAccess::new(value)),
Value::Reference(_) => unreachable!(),
}
}
/// deserialize_any, preferring an integer value
fn deserialize_any_iv<'de, V>(&mut self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
match self.get()? {
Value::Scalar(value) => match value.ty() {
Type::Scalar(flags) => {
use crate::scalar::Flags;
if flags.contains(Flags::INTEGER) {
visitor.visit_i64(value.iv() as i64)
} else if flags.contains(Flags::DOUBLE) {
visitor.visit_f64(value.nv())
} else if flags.contains(Flags::STRING) {
visitor.visit_str(value.pv_utf8())
} else {
visitor.visit_unit()
}
}
_ => unreachable!(),
},
Value::Hash(value) => visitor.visit_map(HashAccess::new(value)),
Value::Array(value) => visitor.visit_seq(ArrayAccess::new(value)),
Value::Reference(_) => unreachable!(),
}
}
/// deserialize_any, preferring a float value
fn deserialize_any_nv<'de, V>(&mut self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
match self.get()? {
Value::Scalar(value) => match value.ty() {
Type::Scalar(flags) => {
use crate::scalar::Flags;
if flags.contains(Flags::DOUBLE) {
visitor.visit_f64(value.nv())
} else if flags.contains(Flags::INTEGER) {
visitor.visit_i64(value.iv() as i64)
} else if flags.contains(Flags::STRING) {
visitor.visit_str(value.pv_utf8())
} else {
visitor.visit_unit()
}
}
_ => unreachable!(),
},
Value::Hash(value) => visitor.visit_map(HashAccess::new(value)),
Value::Array(value) => visitor.visit_seq(ArrayAccess::new(value)),
Value::Reference(_) => unreachable!(),
}
}
}
impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any_string(visitor)
}
fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
match self.get()? {
Value::Scalar(value) => match value.ty() {
Type::Scalar(flags) => {
use crate::scalar::Flags;
if flags.is_empty() || flags.intersects(Flags::INTEGER | Flags::DOUBLE) {
visitor.visit_bool(unsafe { ffi::RSPL_SvTRUE(value.sv()) })
} else {
Error::fail("expected bool value")
}
}
_ => unreachable!(),
},
Value::Hash(value) => visitor.visit_map(HashAccess::new(value)),
Value::Array(value) => visitor.visit_seq(ArrayAccess::new(value)),
Value::Reference(_) => unreachable!(),
}
}
fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any_iv(visitor)
}
fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any_iv(visitor)
}
fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any_iv(visitor)
}
fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any_iv(visitor)
}
fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any_iv(visitor)
}
fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any_iv(visitor)
}
fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any_iv(visitor)
}
fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any_iv(visitor)
}
fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any_nv(visitor)
}
fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any_nv(visitor)
}
fn deserialize_char<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
match self.get()? {
Value::Scalar(value) => match value.ty() {
Type::Scalar(flags) => {
use crate::scalar::Flags;
if flags.contains(Flags::INTEGER) {
let c = value.iv();
if c < 0x100 {
visitor.visit_char(c as u8 as char)
} else {
visitor.visit_i64(c as i64)
}
} else if flags.contains(Flags::DOUBLE) {
visitor.visit_f64(value.nv())
} else if flags.contains(Flags::STRING) {
let s = value.pv_utf8();
let mut chars = s.chars();
match chars.next() {
Some(ch) if chars.next().is_none() => visitor.visit_char(ch),
_ => visitor.visit_str(value.pv_utf8()),
}
} else {
visitor.visit_unit()
}
}
_ => unreachable!(),
},
Value::Hash(value) => visitor.visit_map(HashAccess::new(value)),
Value::Array(value) => visitor.visit_seq(ArrayAccess::new(value)),
Value::Reference(_) => unreachable!(),
}
}
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_string<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
match self.get()? {
Value::Scalar(value) => match value.ty() {
Type::Scalar(flags) => {
use crate::scalar::Flags;
if flags.contains(Flags::STRING) {
visitor.visit_bytes(value.pv_bytes())
} else if flags.contains(Flags::DOUBLE) {
visitor.visit_f64(value.nv())
} else if flags.contains(Flags::INTEGER) {
visitor.visit_i64(value.iv() as i64)
} else {
visitor.visit_unit()
}
}
_ => unreachable!(),
},
Value::Hash(value) => visitor.visit_map(HashAccess::new(value)),
Value::Array(value) => visitor.visit_seq(ArrayAccess::new(value)),
Value::Reference(_) => unreachable!(),
}
}
fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_bytes(visitor)
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
if self.option_allowed {
if let Value::Scalar(value) = self.get()? {
if let Type::Scalar(flags) = value.ty() {
if flags.is_empty() {
return visitor.visit_none();
}
}
}
self.option_allowed = false;
let res = visitor.visit_some(&mut *self);
self.option_allowed = true;
res
} else {
self.deserialize_any(visitor)
}
}
fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_newtype_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_tuple_struct<V>(
self,
_name: &'static str,
_len: usize,
visitor: V,
) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_struct<V>(
self,
_name: &'static str,
_fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_map(visitor)
}
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: Visitor<'de>,
{
self.deserialize_any(visitor)
}
}
pub struct HashAccess<'a> {
hash: &'a hash::Hash,
entry: *mut ffi::HE,
finished: bool,
at_value: bool,
}
impl<'a> HashAccess<'a> {
pub fn new(value: &'a hash::Hash) -> Self {
drop(value.shared_iter()); // reset iterator
Self {
hash: &value,
entry: std::ptr::null_mut(),
finished: false,
at_value: false,
}
}
}
impl<'de, 'a> MapAccess<'de> for HashAccess<'a> {
type Error = Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
where
K: DeserializeSeed<'de>,
{
if self.finished {
return Ok(None);
}
if self.entry.is_null() {
self.entry = unsafe { ffi::RSPL_hv_iternext(self.hash.hv()) };
if self.entry.is_null() {
self.finished = true;
return Ok(None);
}
} else if self.at_value {
return Error::fail("map access value skipped");
}
self.at_value = true;
let key = unsafe { Value::from_raw_ref(ffi::RSPL_hv_iterkeysv(self.entry)) };
seed.deserialize(&mut Deserializer::from_value(key))
.map(Some)
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
where
V: DeserializeSeed<'de>,
{
if self.finished {
return Error::fail("map access value requested after end");
}
if self.entry.is_null() || !self.at_value {
return Error::fail("map access key skipped");
}
self.at_value = false;
let value =
unsafe { Value::from_raw_ref(ffi::RSPL_hv_iterval(self.hash.hv(), self.entry)) };
self.entry = std::ptr::null_mut();
seed.deserialize(&mut Deserializer::from_value(value))
}
}
pub struct ArrayAccess<'a> {
iter: array::Iter<'a>,
}
impl<'a> ArrayAccess<'a> {
pub fn new(value: &'a array::Array) -> Self {
Self { iter: value.iter() }
}
}
impl<'de, 'a> SeqAccess<'de> for ArrayAccess<'a> {
type Error = Error;
fn next_element_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
where
K: DeserializeSeed<'de>,
{
self.iter
.next()
.map(move |value| seed.deserialize(&mut Deserializer::from_value(value)))
.transpose()
}
}

45
perlmod/src/error.rs Normal file
View File

@ -0,0 +1,45 @@
#[derive(Debug)]
pub struct CastError;
impl std::fmt::Display for CastError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "wrong type")
}
}
impl std::error::Error for CastError {}
#[derive(Clone, Debug)]
pub struct Error(String);
impl Error {
#[inline]
pub fn new(s: &str) -> Self {
Self(s.to_string())
}
#[inline]
pub fn fail<T>(s: &str) -> Result<T, Self> {
Err(Self(s.to_string()))
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "error: {}", self.0)
}
}
impl std::error::Error for Error {}
impl serde::de::Error for Error {
fn custom<T: std::fmt::Display>(msg: T) -> Self {
Self(msg.to_string())
}
}
impl serde::ser::Error for Error {
fn custom<T: std::fmt::Display>(msg: T) -> Self {
Self(msg.to_string())
}
}

158
perlmod/src/ffi.rs Normal file
View File

@ -0,0 +1,158 @@
//! Unsafe ffi code we use.
//!
//! You should not use this code directly. This is used by the binding generator to implement xsubs
//! for exported functions.
#[repr(C)]
pub struct CV {
_ffi: usize,
}
#[repr(C)]
pub struct SV {
_ffi: usize,
}
#[repr(C)]
pub struct AV {
_ffi: usize,
}
#[repr(C)]
pub struct HV {
_ffi: usize,
}
#[repr(C)]
pub struct HE {
_ffi: usize,
}
// in our glue:
#[link(name = "glue", kind = "static")]
extern "C" {
pub fn RSPL_StackMark_count(this: usize) -> usize;
pub fn RSPL_stack_get(offset: usize) -> *mut SV;
pub fn RSPL_croak_sv(sv: *mut SV) -> !;
pub fn RSPL_SvNV(sv: *mut SV) -> f64;
pub fn RSPL_SvIV(sv: *mut SV) -> isize;
pub fn RSPL_SvPVutf8(sv: *mut SV, len: *mut libc::size_t) -> *const libc::c_char;
pub fn RSPL_SvPV(sv: *mut SV, len: *mut libc::size_t) -> *const libc::c_char;
pub fn RSPL_SvPVbyte(sv: *mut SV, len: *mut libc::size_t) -> *const libc::c_char;
pub fn RSPL_sv_2mortal(sv: *mut SV) -> *mut SV;
pub fn RSPL_get_undef() -> *mut SV;
pub fn RSPL_get_yes() -> *mut SV;
pub fn RSPL_get_no() -> *mut SV;
pub fn RSPL_pop_markstack_ptr() -> usize;
pub fn RSPL_stack_resize_by(count: isize);
pub fn RSPL_stack_shrink_to(count: usize);
pub fn RSPL_stack_sp() -> *mut *mut SV;
pub fn RSPL_newRV_inc(sv: *mut SV) -> *mut SV;
pub fn RSPL_newSViv(v: isize) -> *mut SV;
pub fn RSPL_newSVuv(v: usize) -> *mut SV;
pub fn RSPL_newSVnv(v: f64) -> *mut SV;
pub fn RSPL_newSVpvn(v: *const libc::c_char, len: libc::size_t) -> *mut SV;
pub fn RSPL_SvREFCNT_inc(sv: *mut SV) -> *mut SV;
pub fn RSPL_SvREFCNT_dec(sv: *mut SV);
pub fn RSPL_is_reference(sv: *mut SV) -> bool;
pub fn RSPL_dereference(sv: *mut SV) -> *mut SV;
pub fn RSPL_is_array(sv: *mut SV) -> bool;
pub fn RSPL_is_hash(sv: *mut SV) -> bool;
pub fn RSPL_type_flags(sv: *mut SV) -> u32;
pub fn RSPL_svtype(sv: *mut SV) -> u32;
pub fn RSPL_SvTRUE(sv: *mut SV) -> bool;
pub fn RSPL_newAV() -> *mut AV;
pub fn RSPL_av_extend(av: *mut AV, len: libc::ssize_t);
pub fn RSPL_av_push(av: *mut AV, sv: *mut SV);
pub fn RSPL_av_pop(av: *mut AV) -> *mut SV;
pub fn RSPL_av_len(av: *mut AV) -> usize;
pub fn RSPL_av_fetch(av: *mut AV, index: libc::ssize_t, lval: i32) -> *mut *mut SV;
pub fn RSPL_newHV() -> *mut HV;
pub fn RSPL_HvTOTALKEYS(hv: *mut HV) -> usize;
pub fn RSPL_hv_fetch(
hv: *mut HV,
key: *const libc::c_char,
klen: i32,
lval: i32,
) -> *mut *mut SV;
/// Always consumes ownership of `value`.
pub fn RSPL_hv_store(hv: *mut HV, key: *const libc::c_char, klen: i32, value: *mut SV) -> bool;
pub fn RSPL_hv_store_ent(hv: *mut HV, key: *mut SV, value: *mut SV) -> bool;
pub fn RSPL_hv_iterinit(hv: *mut HV);
pub fn RSPL_hv_iternextsv(
hv: *mut HV,
key: *mut *mut libc::c_char,
retlen: *mut i32,
) -> *mut SV;
pub fn RSPL_hv_iternext(hv: *mut HV) -> *mut HE;
pub fn RSPL_hv_iterkeysv(he: *mut HE) -> *mut SV;
pub fn RSPL_hv_iterval(hv: *mut HV, he: *mut HE) -> *mut SV;
}
/// Argument marker for the stack.
pub struct StackMark(usize);
impl StackMark {
pub fn count(&self) -> usize {
unsafe { RSPL_StackMark_count(self.0) }
}
pub fn iter(&self) -> StackIter {
StackIter {
at: self.0 + 1,
end: self.0 + 1 + self.count(),
}
}
pub unsafe fn set_stack(self) {
RSPL_stack_shrink_to(self.0);
}
}
pub struct StackIter {
at: usize,
end: usize,
}
impl Iterator for StackIter {
type Item = crate::Scalar;
fn next(&mut self) -> Option<Self::Item> {
let at = self.at;
if at == self.end {
return None;
}
unsafe {
let ptr = RSPL_stack_get(self.at);
self.at += 1;
if ptr.is_null() {
None
} else {
Some(crate::Scalar::from_raw_ref(ptr))
}
}
}
}
pub unsafe fn pop_arg_mark() -> StackMark {
StackMark(RSPL_pop_markstack_ptr())
}
pub unsafe fn stack_push_raw(value: *mut SV) {
RSPL_stack_resize_by(1);
*RSPL_stack_sp() = value;
}
pub fn stack_push(value: crate::Mortal) {
unsafe {
stack_push_raw(value.into_raw());
}
}
pub unsafe fn croak(sv: *mut SV) {
RSPL_croak_sv(sv);
}

295
perlmod/src/glue.c Normal file
View File

@ -0,0 +1,295 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <setjmp.h>
#include <unistd.h>
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"
typedef uintptr_t usize;
typedef intptr_t isize;
extern usize RSPL_StackMark_count(usize self) {
SV **ptr = PL_stack_base + self;
if (ptr > PL_stack_sp) {
return 0;
}
return PL_stack_sp - ptr;
}
extern SV* RSPL_stack_get(usize offset) {
SV **ptr = PL_stack_base + offset;
if (ptr > PL_stack_sp) {
return NULL;
}
return *ptr;
}
extern void RSPL_croak_sv(SV *sv) {
croak_sv(sv);
}
extern double RSPL_SvNV(SV *sv) {
return SvNV(sv);
}
extern isize RSPL_SvIV(SV *sv) {
return SvIV(sv);
}
extern const char* RSPL_SvPVutf8(SV *sv, size_t *out_len) {
size_t length;
const char *out = SvPVutf8(sv, length);
*out_len = length;
return out;
}
extern const char* RSPL_SvPV(SV *sv, size_t *out_len) {
size_t length;
const char *out = SvPV(sv, length);
*out_len = length;
return out;
}
extern const char* RSPL_SvPVbyte(SV *sv, size_t *out_len) {
size_t length;
const char *out = SvPVbyte(sv, length);
*out_len = length;
return out;
}
extern SV* RSPL_sv_2mortal(SV *sv) {
return sv_2mortal(sv);
}
extern SV* RSPL_get_undef() {
return &PL_sv_undef;
}
extern SV* RSPL_get_yes() {
return &PL_sv_yes;
}
extern SV* RSPL_get_no() {
return &PL_sv_no;
}
extern usize RSPL_PL_markstack_ptr() {
return *PL_markstack_ptr;
}
extern usize RSPL_pop_markstack_ptr() {
return *PL_markstack_ptr--;
}
extern void RSPL_stack_shrink_to(usize count) {
PL_stack_sp = PL_stack_base + count;
}
extern void RSPL_stack_resize_by(isize count) {
if (count > 0) {
isize space = PL_stack_max - PL_stack_sp;
if (space < count) {
Perl_stack_grow(aTHX_ PL_stack_sp, PL_stack_sp, count - space);
}
}
PL_stack_sp += count;
}
extern SV** RSPL_stack_sp() {
return PL_stack_sp;
}
extern SV* RSPL_newRV_inc(SV *rv) {
return newRV_inc(rv);
}
extern SV* RSPL_newSViv(isize v) {
return newSViv(v);
}
extern SV* RSPL_newSVuv(usize v) {
return newSVuv(v);
}
extern SV* RSPL_newSVnv(double v) {
return newSVnv(v);
}
extern SV* RSPL_newSVpvn(const char *v, size_t len) {
return newSVpvn(v, len);
}
extern SV* RSPL_SvREFCNT_inc(SV *sv) {
return SvREFCNT_inc(sv);
}
extern void RSPL_SvREFCNT_dec(SV *sv) {
return SvREFCNT_dec(sv);
}
extern bool RSPL_is_scalar(SV *sv) {
return SvTYPE(sv) < SVt_PVAV;
}
extern bool RSPL_SvTRUE(SV *sv) {
return SvTRUE(sv);
}
// This must be the same as in rust!
#define TYPE_FLAG_INT 1
#define TYPE_FLAG_DOUBLE 2
#define TYPE_FLAG_STRING 4
static const uint32_t type_flags[16] = {
[SVt_NULL] = 0,
[SVt_IV] = TYPE_FLAG_INT,
[SVt_NV] = TYPE_FLAG_INT | TYPE_FLAG_DOUBLE,
[SVt_PV] = TYPE_FLAG_STRING,
[SVt_PVIV] = TYPE_FLAG_STRING | TYPE_FLAG_INT,
[SVt_PVNV] = TYPE_FLAG_STRING | TYPE_FLAG_INT | TYPE_FLAG_DOUBLE,
[SVt_PVMG] = ~0,
};
extern uint32_t RSPL_svtype(SV *sv) {
return SvTYPE(sv);
}
extern uint32_t RSPL_type_flags(SV *sv) {
return type_flags[SvTYPE(sv)];
}
extern bool RSPL_has_integer(SV *sv) {
return 0 != (type_flags[SvTYPE(sv)] & TYPE_FLAG_INT);
}
extern bool RSPL_has_double(SV *sv) {
return 0 != (type_flags[SvTYPE(sv)] & TYPE_FLAG_DOUBLE);
}
extern bool RSPL_has_string(SV *sv) {
return 0 != (type_flags[SvTYPE(sv)] & TYPE_FLAG_STRING);
}
extern SV* RSPL_SvRV(SV *sv) {
return SvRV(sv);
}
extern SV* RSPL_dereference(SV *sv) {
return SvROK(sv) ? SvRV(sv) : NULL;
}
extern bool RSPL_is_reference(SV *sv) {
return SvROK(sv);
}
extern bool RSPL_is_array(SV *sv) {
return SvTYPE(sv) == SVt_PVAV;
}
extern bool RSPL_is_hash(SV *sv) {
return SvTYPE(sv) == SVt_PVHV;
}
extern AV* RSPL_newAV() {
return newAV();
}
extern usize RSPL_av_len(AV *av) {
return av_len(av);
}
extern void RSPL_av_extend(AV *av, ssize_t len) {
av_extend(av, len);
}
extern void RSPL_av_push(AV *av, SV *sv) {
av_push(av, sv);
}
extern SV* RSPL_av_pop(AV *av) {
return av_pop(av);
}
extern SV** RSPL_av_fetch(AV *av, ssize_t index, int32_t lval) {
return av_fetch(av, index, lval);
}
extern HV* RSPL_newHV() {
return newHV();
}
extern usize RSPL_HvTOTALKEYS(HV *hv) {
return HvTOTALKEYS(hv);
}
extern SV** RSPL_hv_fetch(HV *hv, const char *key, int32_t klen, int32_t lval) {
return hv_fetch(hv, key, klen, lval);
}
/// ALWAYS takes ownership of 'value'.
extern bool RSPL_hv_store(HV *hv, const char *key, int32_t klen, SV *value) {
if (hv_store(hv, key, klen, value, 0) == NULL) {
SvREFCNT_dec(value);
return false;
} else {
return true;
}
}
extern bool RSPL_hv_store_ent(HV *hv, SV *key, SV *value) {
if (hv_store_ent(hv, key, value, 0) == NULL) {
SvREFCNT_dec(value);
return false;
} else {
return true;
}
}
extern void RSPL_hv_iterinit(HV *hv) {
hv_iterinit(hv);
}
extern SV* RSPL_hv_iternextsv(HV *hv, char **key, int32_t *retlen) {
return hv_iternextsv(hv, key, retlen);
}
extern HE* RSPL_hv_iternext(HV *hv) {
return hv_iternext(hv);
}
extern SV* RSPL_hv_iterkeysv(HE *he) {
return hv_iterkeysv(he);
}
extern SV* RSPL_hv_iterval(HV *hv, HE *he) {
return hv_iterval(hv, he);
}
/*
These make are convoluted brainfarts:
SVt_NULL undef
SVt_IV all the above or int
SVt_NV all the above or a double
SVt_PV undef or a string
SVt_PVIV PV or IV
SVt_PVNV PV or NV
SVt_PVMG all of the above with tentacles, 2 heads and unicorn poop on top
These make some sense
SVt_INVLIST Bleeding smelly perl guts
SVt_REGEXP Sandpaper
SVt_PVGV Typeglob
SVt_PVLV C++ style reference to another scalar (implicit deref)
These make sense
SVt_PVAV Arrays
SVt_PVHV Hashes
SVt_PVCV Subroutine
SVt_PVFM Formats
SVt_PVIO I/O objects
*/

208
perlmod/src/hash.rs Normal file
View File

@ -0,0 +1,208 @@
use std::convert::TryFrom;
use crate::error::CastError;
use crate::ffi::{self, HV, SV};
use crate::scalar::{Scalar, ScalarRef};
use crate::Value;
/// An owned reference to a perl hash value (HV).
///
/// This keeps a reference to a value which lives in the perl interpreter.
#[repr(transparent)]
pub struct Hash(Scalar);
#[allow(clippy::new_without_default)]
impl Hash {
/// Create a new array value.
pub fn new() -> Self {
unsafe { Self::from_raw_move(ffi::RSPL_newHV()) }
}
/// Turn this into a `Scalar`. The underlying perl value does not change, this is a pure type
/// cast down to a less specific "pointer" type.
pub fn into_scalar(self) -> Scalar {
self.0
}
/// Get the internal perl value as a low-level `HV` pointer.
pub fn hv(&self) -> *mut HV {
self.0.sv() as *mut HV
}
/// "Downcast" a `Scalar` into a `Hash`. The caller must verify that this is legal.
///
/// # Safety
///
/// The caller must verify that this is legal.
pub unsafe fn from_scalar(scalar: Scalar) -> Self {
Self(scalar)
}
/// Take over a raw `HV` value, assuming that we then own a reference to it.
///
/// # Safety
///
/// This does not change the value's reference count, it is assumed that we're taking ownership
/// of one reference.
///
/// The caller must ensure that it is safe to decrease the reference count later on, or use
/// `into_raw()` instead of letting the `Hash` get dropped.
pub unsafe fn from_raw_move(ptr: *mut HV) -> Self {
Self(Scalar::from_raw_move(ptr as *mut SV))
}
/// Create a new reference to an existing `HV` value. This will increase the value's reference
/// count.
///
/// # Safety
///
/// The caller may still need to decrease the reference count for the `ptr` source value.
pub unsafe fn from_raw_ref(ptr: *mut HV) -> Self {
Self(Scalar::from_raw_ref(ptr as *mut SV))
}
/// Create a new reference to this value.
pub fn clone_ref(&self) -> Self {
Self(self.0.clone_ref())
}
/// Get the number of keys in this hash.
pub fn len(&self) -> usize {
unsafe { ffi::RSPL_HvTOTALKEYS(self.hv()) }
}
/// Check if this is an empty hash.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Get a value from the hash. Note that this only uses utf8 strings. For a more generic method
/// see `get_by_bytes`.
pub fn get(&self, key: &str) -> Option<Value> {
self.get_by_bytes(key.as_bytes())
}
/// Get a value from the hash, but with a raw byte string as index.
pub fn get_by_bytes(&self, key: &[u8]) -> Option<Value> {
let sv: *mut *mut SV = unsafe {
ffi::RSPL_hv_fetch(
self.hv(),
key.as_ptr() as *const libc::c_char,
key.len() as i32,
0,
)
};
if sv.is_null() {
None
} else {
Some(unsafe { Value::from_raw_ref(*sv) })
}
}
/// Insert a value into the hash.
pub fn insert(&self, key: &str, value: Value) {
self.insert_by_bytes(key.as_bytes(), value);
}
/// Insert a value into the hash with a byte string as key.
pub fn insert_by_bytes(&self, key: &[u8], value: Value) {
unsafe {
ffi::RSPL_hv_store(
self.hv(),
key.as_ptr() as *const u8 as *const libc::c_char,
key.len() as i32,
value.into_raw(),
);
}
}
/// Insert a value using an existin value as a key.
pub fn insert_by_value(&self, key: &Value, value: Value) {
unsafe {
ffi::RSPL_hv_store_ent(self.hv(), key.sv(), value.sv());
}
}
/// Get the *shared* iterator over this hash's elements.
///
/// Note that this uses the hash's internal iterator, so any other iterator as well as `each`
/// statement within perl code is affected by it, and it is usually a bad idea to have multiple
/// iterators over the same hash simultaneously.
pub fn shared_iter(&self) -> Iter {
unsafe {
ffi::RSPL_hv_iterinit(self.hv());
}
Iter { hash: self }
}
}
impl core::ops::Deref for Hash {
type Target = ScalarRef;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl core::ops::DerefMut for Hash {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *self.0
}
}
impl TryFrom<Scalar> for Hash {
type Error = CastError;
fn try_from(scalar: Scalar) -> Result<Self, CastError> {
if unsafe { ffi::RSPL_is_hash(scalar.sv()) } {
Ok(Self(scalar))
} else {
Err(CastError)
}
}
}
impl std::fmt::Debug for Hash {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{{HASH}}")
}
}
pub struct Iter<'a> {
hash: &'a Hash,
}
impl<'a> Iterator for Iter<'a> {
type Item = (&'a [u8], Value);
fn next(&mut self) -> Option<Self::Item> {
let mut key: *mut libc::c_char = std::ptr::null_mut();
let mut keylen: i32 = 0;
let value = unsafe { ffi::RSPL_hv_iternextsv(self.hash.hv(), &mut key, &mut keylen) };
if value.is_null() {
return None;
}
unsafe {
Some((
std::slice::from_raw_parts(key as *mut u8, keylen as usize),
Value::from_raw_ref(value),
))
}
}
}
impl serde::Serialize for Hash {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeMap;
let mut map = serializer.serialize_map(Some(self.len()))?;
for (k, v) in self.shared_iter() {
map.serialize_key(&k)?;
map.serialize_value(&v)?;
}
map.end()
}
}

29
perlmod/src/lib.rs Normal file
View File

@ -0,0 +1,29 @@
pub(crate) mod error;
pub mod de;
pub mod ffi;
pub mod ser;
#[doc(inline)]
pub use de::from_value;
#[doc(inline)]
pub use ser::to_value;
pub mod scalar;
#[doc(inline)]
pub use scalar::{Mortal, Scalar};
pub mod array;
#[doc(inline)]
pub use array::Array;
pub mod hash;
#[doc(inline)]
pub use hash::Hash;
pub mod value;
#[doc(inline)]
pub use value::Value;
#[cfg(feature = "exporter")]
pub use perlmod_macro::package;

335
perlmod/src/scalar.rs Normal file
View File

@ -0,0 +1,335 @@
use bitflags::bitflags;
use crate::ffi::{self, SV};
use crate::Value;
/// An owned reference to a perl value.
///
/// This keeps a reference to a value which lives in the perl interpreter.
/// This derefs to a `ScalarRef` which implements most of the basic functionality common to all
/// `SV` related types.
///
/// This implements `Send` but not `Sync`, because it is effectively the same as an `Arc` to a
/// value which is inherently `Send` but not `Sync`.
#[repr(transparent)]
pub struct Scalar(*mut SV);
unsafe impl Send for Scalar {}
impl Scalar {
/// Turn this into a "mortal" value. This will move this value's owned reference onto the
/// mortal stack to be cleaned up after the next perl statement if no more references exist.
///
/// (To be garbage collected after this perl-statement.)
pub fn into_mortal(self) -> Mortal {
Mortal(unsafe { ffi::RSPL_sv_2mortal(self.into_raw()) })
}
/// Turn this into a raw `SV` transferring control of one reference count.
pub fn into_raw(self) -> *mut SV {
let ptr = self.0;
core::mem::forget(self);
ptr
}
/// Create a wrapping `Scalar` from an `SV` pointer. The `Scalar` takes over the owned
/// reference from the passed `SV`, which means it must not be a mortal reference.
///
/// # Safety
///
/// This does not change the value's reference count, it is assumed that we're taking ownership
/// of one reference.
///
/// The caller must ensure that it is safe to decrease the reference count later on, or use
/// `into_raw()` instead of letting the `Scalar` get dropped.
pub unsafe fn from_raw_move(ptr: *mut SV) -> Self {
Self(ptr)
}
/// Increase the reference count on an `SV` pointer.
///
/// # Safety
///
/// 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))
}
/// Create a reference to `PL_sv_undef`.
pub fn new_undef() -> Self {
unsafe { Self::from_raw_ref(ffi::RSPL_get_undef()) }
}
/// Create a reference to `PL_sv_yes`.
pub fn new_yes() -> Self {
unsafe { Self::from_raw_ref(ffi::RSPL_get_yes()) }
}
/// Create a reference to `PL_sv_no`.
pub fn new_no() -> Self {
unsafe { Self::from_raw_ref(ffi::RSPL_get_no()) }
}
/// Create a new integer value:
pub fn new_int(v: isize) -> Self {
unsafe { Self::from_raw_move(ffi::RSPL_newSViv(v)) }
}
/// Create a new unsigned integer value:
pub fn new_uint(v: usize) -> Self {
unsafe { Self::from_raw_move(ffi::RSPL_newSVuv(v)) }
}
/// Create a new floating point value.
pub fn new_float(v: f64) -> Self {
unsafe { Self::from_raw_move(ffi::RSPL_newSVnv(v)) }
}
/// Create a new string value.
pub fn new_string(s: &str) -> Self {
Self::new_bytes(s.as_bytes())
}
/// Create a new byte string.
pub fn new_bytes(s: &[u8]) -> Self {
unsafe {
Self::from_raw_move(ffi::RSPL_newSVpvn(
s.as_ptr() as *const libc::c_char,
s.len() as libc::size_t,
))
}
}
}
impl Drop for Scalar {
fn drop(&mut self) {
unsafe {
ffi::RSPL_SvREFCNT_dec(self.sv());
}
}
}
impl core::ops::Deref for Scalar {
type Target = ScalarRef;
fn deref(&self) -> &Self::Target {
unsafe { &*(self.0 as *mut ScalarRef) }
}
}
impl core::ops::DerefMut for Scalar {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self.0 as *mut ScalarRef) }
}
}
#[repr(transparent)]
pub struct Mortal(*mut SV);
impl Mortal {
/// Get the inner value.
pub fn into_raw(self) -> *mut SV {
self.0
}
}
impl core::ops::Deref for Mortal {
type Target = ScalarRef;
fn deref(&self) -> &Self::Target {
unsafe { &*(self.0 as *mut ScalarRef) }
}
}
impl core::ops::DerefMut for Mortal {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self.0 as *mut ScalarRef) }
}
}
pub struct ScalarRef;
bitflags! {
/// Represents the types a `Value` can contain. Values can usually contain multiple scalar types
/// at once and it is unclear which is the "true" type, so we can only check whether a value
/// contains something, not what it is originally meant to be!
///
/// NOTE: The values must be the same as in our c glue code!
pub struct Flags: u8 {
const INTEGER = 1;
const DOUBLE = 2;
const STRING = 4;
}
}
/// While scalar types aren't clearly different from another, complex types are, so we do
/// distinguish between these:
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Type {
Scalar(Flags),
Reference,
Array,
Hash,
Other(u8),
}
impl ScalarRef {
pub(crate) fn sv(&self) -> *mut SV {
self as *const ScalarRef as *const SV as *mut SV
}
/// Get some information about the value's type.
pub fn ty(&self) -> Type {
unsafe {
if ffi::RSPL_is_reference(self.sv()) {
Type::Reference
} else {
let flags = ffi::RSPL_type_flags(self.sv());
if flags > 0xff {
panic!("bad C type returned");
} else if flags != 0 {
Type::Scalar(Flags::from_bits(flags as u8).unwrap())
} else if ffi::RSPL_is_array(self.sv()) {
Type::Array
} else if ffi::RSPL_is_hash(self.sv()) {
Type::Hash
} else {
Type::Other(ffi::RSPL_svtype(self.sv()) as u8)
}
}
}
}
/// Dereference this reference.
pub fn dereference(&self) -> Option<Scalar> {
let ptr = unsafe { ffi::RSPL_dereference(self.sv()) };
if ptr.is_null() {
None
} else {
Some(unsafe { Scalar::from_raw_ref(ptr) })
}
}
/// Coerce to a double value. (perlxs SvNV).
pub fn nv(&self) -> f64 {
unsafe { ffi::RSPL_SvNV(self.sv()) }
}
/// Coerce to an integer value. (perlxs SvIV).
pub fn iv(&self) -> isize {
unsafe { ffi::RSPL_SvIV(self.sv()) }
}
/// Coerce to an utf8 string value. (perlxs SvPVutf8)
pub fn pv_utf8(&self) -> &str {
unsafe {
let mut len: libc::size_t = 0;
let ptr = ffi::RSPL_SvPVutf8(self.sv(), &mut len) as *const u8;
std::str::from_utf8_unchecked(std::slice::from_raw_parts(ptr, len))
}
}
/// Coerce to a string without utf8 encoding. (perlxs SvPV)
pub fn pv_string_bytes(&self) -> &[u8] {
unsafe {
let mut len: libc::size_t = 0;
let ptr = ffi::RSPL_SvPV(self.sv(), &mut len) as *const u8;
std::slice::from_raw_parts(ptr, len)
}
}
/// Coerce to a byte-string. (perlxs SvPVbyte)
pub fn pv_bytes(&self) -> &[u8] {
unsafe {
let mut len: libc::size_t = 0;
let ptr = ffi::RSPL_SvPVbyte(self.sv(), &mut len) as *const u8;
std::slice::from_raw_parts(ptr, len)
}
}
/// Create another owned reference to this value.
pub fn clone_ref(&self) -> Scalar {
unsafe { Scalar::from_raw_ref(self.sv()) }
}
/// Convenience check for SVt_NULL
pub fn is_undef(&self) -> bool {
0 == unsafe { ffi::RSPL_type_flags(self.sv()) }
}
/// Turn this into a `Value`.
pub fn into_value(self) -> Value {
Value::from_scalar(self.clone_ref())
}
}
impl std::fmt::Debug for Scalar {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let this: &ScalarRef = &self;
std::fmt::Debug::fmt(this, f)
}
}
impl std::fmt::Debug for ScalarRef {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use std::fmt::Debug;
match self.ty() {
Type::Scalar(flags) => {
if flags.intersects(Flags::STRING) {
Debug::fmt(self.pv_utf8(), f)
} else if flags.intersects(Flags::INTEGER) {
write!(f, "{}", self.iv())
} else if flags.intersects(Flags::DOUBLE) {
write!(f, "{}", self.nv())
} else {
write!(f, "<unhandled scalar>")
}
}
Type::Reference => write!(f, "<*REFERENCE>"),
Type::Array => write!(f, "<*ARRAY>"),
Type::Hash => write!(f, "<*HASH>"),
Type::Other(_) => write!(f, "<*PERLTYPE>"),
}
}
}
impl serde::Serialize for Scalar {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::Error;
match self.ty() {
Type::Scalar(flags) => {
if flags.contains(Flags::STRING) {
serializer.serialize_str(self.pv_utf8())
} else if flags.contains(Flags::DOUBLE) {
serializer.serialize_f64(self.nv())
} else if flags.contains(Flags::INTEGER) {
serializer.serialize_i64(self.iv() as i64)
} else {
serializer.serialize_unit()
}
}
Type::Other(_) => Err(S::Error::custom(
"cannot deserialize weird magic perl values",
)),
// These are impossible as they are all handled by different Value enum types:
Type::Reference => Value::from(
self.dereference()
.ok_or_else(|| S::Error::custom("failed to dereference perl value"))?,
)
.serialize(serializer),
Type::Array => {
let this = unsafe { crate::Array::from_raw_ref(self.sv() as *mut ffi::AV) };
this.serialize(serializer)
}
Type::Hash => {
let this = unsafe { crate::Hash::from_raw_ref(self.sv() as *mut ffi::HV) };
this.serialize(serializer)
}
}
}
}

376
perlmod/src/ser.rs Normal file
View File

@ -0,0 +1,376 @@
use serde::{ser, Serialize};
use crate::error::Error;
use crate::Value;
use crate::{array, hash};
pub struct Serializer;
pub fn to_value<T>(value: &T) -> Result<Value, Error>
where
T: Serialize,
{
value.serialize(&mut Serializer)
}
pub struct SerHash {
hash: hash::Hash,
key: Option<Value>,
}
pub struct SerArray {
array: array::Array,
}
pub struct SerVariant<T> {
hash: hash::Hash,
inner: T,
}
impl<'a> ser::Serializer for &'a mut Serializer {
type Ok = Value;
type Error = Error;
type SerializeSeq = SerArray;
type SerializeTuple = SerArray;
type SerializeTupleStruct = SerArray;
type SerializeTupleVariant = SerVariant<SerArray>;
type SerializeMap = SerHash;
type SerializeStruct = SerHash;
type SerializeStructVariant = SerVariant<SerHash>;
fn serialize_bool(self, v: bool) -> Result<Value, Error> {
Ok(Value::new_uint(if v { 1 } else { 0 }))
}
fn serialize_i8(self, v: i8) -> Result<Value, Error> {
self.serialize_i64(i64::from(v))
}
fn serialize_i16(self, v: i16) -> Result<Value, Error> {
self.serialize_i64(i64::from(v))
}
fn serialize_i32(self, v: i32) -> Result<Value, Error> {
self.serialize_i64(i64::from(v))
}
fn serialize_i64(self, v: i64) -> Result<Value, Error> {
Ok(Value::new_int(v as isize))
}
fn serialize_u8(self, v: u8) -> Result<Value, Error> {
self.serialize_u64(u64::from(v))
}
fn serialize_u16(self, v: u16) -> Result<Value, Error> {
self.serialize_u64(u64::from(v))
}
fn serialize_u32(self, v: u32) -> Result<Value, Error> {
self.serialize_u64(u64::from(v))
}
fn serialize_u64(self, v: u64) -> Result<Value, Error> {
Ok(Value::new_uint(v as usize))
}
fn serialize_f32(self, v: f32) -> Result<Value, Error> {
self.serialize_f64(f64::from(v))
}
fn serialize_f64(self, v: f64) -> Result<Value, Error> {
Ok(Value::new_float(v))
}
fn serialize_char(self, v: char) -> Result<Value, Error> {
self.serialize_str(&v.to_string())
}
fn serialize_str(self, v: &str) -> Result<Value, Error> {
Ok(Value::new_string(v))
}
fn serialize_bytes(self, v: &[u8]) -> Result<Value, Error> {
Ok(Value::new_bytes(v))
}
fn serialize_none(self) -> Result<Value, Error> {
self.serialize_unit()
}
fn serialize_some<T>(self, value: &T) -> Result<Value, Error>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
fn serialize_unit(self) -> Result<Value, Error> {
Ok(Value::new_undef())
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value, Error> {
self.serialize_unit()
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Value, Error> {
self.serialize_str(variant)
}
fn serialize_newtype_struct<T>(self, _name: &'static str, value: &T) -> Result<Value, Error>
where
T: ?Sized + Serialize,
{
value.serialize(self)
}
fn serialize_newtype_variant<T>(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Value, Error>
where
T: ?Sized + Serialize,
{
let value = value.serialize(&mut Serializer)?;
let hash = hash::Hash::new();
hash.insert(variant, value);
Ok(Value::from(hash))
}
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Error> {
Ok(SerArray::new(len))
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Error> {
self.serialize_seq(Some(len))
}
fn serialize_tuple_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct, Error> {
self.serialize_seq(Some(len))
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeTupleVariant, Error> {
Ok(SerVariant::<SerArray>::new(variant, Some(len)))
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Error> {
Ok(SerHash::new())
}
fn serialize_struct(
self,
_name: &'static str,
len: usize,
) -> Result<Self::SerializeStruct, Error> {
self.serialize_map(Some(len))
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Error> {
Ok(SerVariant::<SerHash>::new(variant))
}
}
impl SerArray {
fn new(len: Option<usize>) -> Self {
let array = array::Array::new();
if let Some(len) = len {
array.reserve(len);
}
Self { array }
}
}
impl ser::SerializeSeq for SerArray {
type Ok = Value;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
where
T: ?Sized + Serialize,
{
self.array.push(value.serialize(&mut Serializer)?);
Ok(())
}
fn end(self) -> Result<Value, Error> {
Ok(Value::new_ref(&self.array))
}
}
impl ser::SerializeTuple for SerArray {
type Ok = Value;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
where
T: ?Sized + Serialize,
{
self.array.push(value.serialize(&mut Serializer)?);
Ok(())
}
fn end(self) -> Result<Value, Error> {
Ok(Value::new_ref(&self.array))
}
}
impl ser::SerializeTupleStruct for SerArray {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
where
T: ?Sized + Serialize,
{
self.array.push(value.serialize(&mut Serializer)?);
Ok(())
}
fn end(self) -> Result<Value, Error> {
Ok(Value::new_ref(&self.array))
}
}
impl SerHash {
fn new() -> Self {
Self {
hash: hash::Hash::new(),
key: None,
}
}
}
impl ser::SerializeMap for SerHash {
type Ok = Value;
type Error = Error;
fn serialize_key<T>(&mut self, value: &T) -> Result<(), Error>
where
T: ?Sized + Serialize,
{
if self.key.is_some() {
Error::fail("serialize_key called twice")
} else {
self.key = Some(value.serialize(&mut Serializer)?);
Ok(())
}
}
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Error>
where
T: ?Sized + Serialize,
{
match self.key.take() {
None => Error::fail("serialize_value called without key"),
Some(key) => {
let value = value.serialize(&mut Serializer)?;
self.hash.insert_by_value(&key, value);
Ok(())
}
}
}
fn end(self) -> Result<Value, Error> {
if self.key.is_some() {
Error::fail("missing value for key")
} else {
Ok(Value::new_ref(&self.hash))
}
}
}
impl ser::SerializeStruct for SerHash {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, field: &'static str, value: &T) -> Result<(), Error>
where
T: ?Sized + Serialize,
{
self.hash.insert(field, value.serialize(&mut Serializer)?);
Ok(())
}
fn end(self) -> Result<Value, Error> {
Ok(Value::new_ref(&self.hash))
}
}
impl SerVariant<SerArray> {
fn new(variant: &str, len: Option<usize>) -> Self {
let inner = SerArray::new(len);
let hash = hash::Hash::new();
hash.insert(variant, Value::new_ref(&inner.array));
Self { hash, inner }
}
}
impl ser::SerializeTupleVariant for SerVariant<SerArray> {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
where
T: ?Sized + Serialize,
{
self.inner.array.push(value.serialize(&mut Serializer)?);
Ok(())
}
fn end(self) -> Result<Value, Error> {
Ok(Value::new_ref(&self.inner.array))
}
}
impl SerVariant<SerHash> {
fn new(variant: &str) -> Self {
let inner = SerHash::new();
let hash = hash::Hash::new();
hash.insert(variant, Value::new_ref(&inner.hash));
Self { hash, inner }
}
}
impl ser::SerializeStructVariant for SerVariant<SerHash> {
type Ok = Value;
type Error = Error;
fn serialize_field<T>(&mut self, field: &'static str, value: &T) -> Result<(), Error>
where
T: ?Sized + Serialize,
{
self.inner
.hash
.insert(field, value.serialize(&mut Serializer)?);
Ok(())
}
fn end(self) -> Result<Value, Error> {
Ok(Value::new_ref(&self.hash))
}
}

355
perlmod/src/value.rs Normal file
View File

@ -0,0 +1,355 @@
use std::fmt;
use serde::{Deserialize, Serialize};
use crate::ffi::{self, SV};
use crate::scalar::ScalarRef;
use crate::{Array, Hash, Scalar};
/// A higher level value. This is basically an `SV` already cast to `AV` or `HV` for arrays and
/// hashes.
pub enum Value {
Scalar(Scalar),
Reference(Scalar),
Array(Array),
Hash(Hash),
}
impl Value {
/// Create a new undef value:
pub fn new_undef() -> Self {
Value::Scalar(Scalar::new_undef())
}
/// Create a new integer value:
pub fn new_int(v: isize) -> Self {
Value::Scalar(Scalar::new_int(v))
}
/// Create a new unsigned integer value:
pub fn new_uint(v: usize) -> Self {
Value::Scalar(Scalar::new_uint(v))
}
/// Create a new floating point value.
pub fn new_float(v: f64) -> Self {
Value::Scalar(Scalar::new_float(v))
}
/// Create a new string value.
pub fn new_string(s: &str) -> Self {
Value::Scalar(Scalar::new_string(s))
}
/// Create a new byte string.
pub fn new_bytes(s: &[u8]) -> Self {
Value::Scalar(Scalar::new_bytes(s))
}
/// Create an actual perl reference to the value. (The equivalent of perl's backslash
/// operator).
pub fn new_ref<T>(value: &T) -> Self
where
T: std::ops::Deref<Target = ScalarRef>,
{
Value::Reference(unsafe { Scalar::from_raw_move(ffi::RSPL_newRV_inc(value.sv())) })
}
/// Take over a raw `SV` value, assuming that we then own a reference to it.
///
/// # Safety
///
/// This does not change the value's reference count, it is assumed that we're taking ownership
/// of one reference.
///
/// The caller must ensure that it is safe to decrease the reference count later on, or use
/// `into_raw()` instead of letting the `Value` get dropped.
pub unsafe fn from_raw_move(ptr: *mut SV) -> Self {
Self::from_scalar(Scalar::from_raw_move(ptr as *mut SV))
}
/// Create a new reference to an existing `SV` value. This will increase the value's reference
/// count.
///
/// # Safety
///
/// 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_scalar(Scalar::from_raw_ref(ptr as *mut SV))
}
pub fn from_scalar(scalar: Scalar) -> Self {
Self::from(scalar)
}
/// Create a new reference to this value.
pub fn clone_ref(&self) -> Self {
match self {
Value::Scalar(v) => Value::Scalar(v.clone_ref()),
Value::Reference(v) => Value::Reference(v.clone_ref()),
Value::Array(v) => Value::Array(v.clone_ref()),
Value::Hash(v) => Value::Hash(v.clone_ref()),
}
}
/// Dereference this reference value.
pub fn dereference(&self) -> Option<Value> {
match self {
Value::Reference(v) => v.dereference().map(Value::from_scalar),
_ => None,
}
}
/// Turn this into a raw `SV` transferring control of one reference count.
pub fn into_raw(self) -> *mut SV {
match self {
Value::Scalar(v) => v.into_raw(),
Value::Reference(v) => v.into_raw(),
Value::Array(v) => v.into_scalar().into_raw(),
Value::Hash(v) => v.into_scalar().into_raw(),
}
}
pub fn into_mortal(self) -> crate::scalar::Mortal {
match self {
Value::Scalar(v) => v.into_mortal(),
Value::Reference(v) => v.into_mortal(),
Value::Array(v) => v.into_scalar().into_mortal(),
Value::Hash(v) => v.into_scalar().into_mortal(),
}
}
/// If this is value is an array, get the value at the specified index.
pub fn get(&self, index: usize) -> Option<Value> {
if let Value::Array(a) = self {
a.get(index)
} else {
None
}
}
}
impl From<Scalar> for Value {
fn from(scalar: Scalar) -> Self {
unsafe {
if ffi::RSPL_is_array(scalar.sv()) {
Value::Array(Array::from_scalar(scalar))
} else if ffi::RSPL_is_hash(scalar.sv()) {
Value::Hash(Hash::from_scalar(scalar))
} else if ffi::RSPL_is_reference(scalar.sv()) {
Value::Reference(scalar)
} else {
Value::Scalar(scalar)
}
}
}
}
impl From<Hash> for Value {
fn from(hash: Hash) -> Self {
Value::Hash(hash)
}
}
impl From<Array> for Value {
fn from(array: Array) -> Self {
Value::Array(array)
}
}
impl fmt::Debug for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use fmt::Debug;
match self {
Value::Scalar(v) => Debug::fmt(v, f),
Value::Reference(v) => Debug::fmt(v, f),
Value::Array(v) => Debug::fmt(v, f),
Value::Hash(v) => Debug::fmt(v, f),
}
}
}
impl core::ops::Deref for Value {
type Target = ScalarRef;
fn deref(&self) -> &Self::Target {
match self {
Value::Scalar(v) => &*v,
Value::Reference(v) => &*v,
Value::Array(v) => &*v,
Value::Hash(v) => &*v,
}
}
}
impl core::ops::DerefMut for Value {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
Value::Scalar(v) => &mut *v,
Value::Reference(v) => &mut *v,
Value::Array(v) => &mut *v,
Value::Hash(v) => &mut *v,
}
}
}
impl Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::Error;
match self {
Value::Scalar(this) => this.serialize(serializer),
Value::Reference(this) => Value::from(
this.dereference()
.ok_or_else(|| S::Error::custom("failed to dereference perl value"))?,
)
.serialize(serializer),
Value::Array(value) => value.serialize(serializer),
Value::Hash(value) => value.serialize(serializer),
}
}
}
impl<'de> Deserialize<'de> for Value {
fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Visitor;
struct ValueVisitor;
impl<'de> Visitor<'de> for ValueVisitor {
type Value = Value;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("any valid PERL value")
}
#[inline]
fn visit_bool<E>(self, value: bool) -> Result<Value, E> {
Ok(Value::new_int(if value { 1 } else { 0 }))
}
#[inline]
fn visit_i64<E>(self, value: i64) -> Result<Value, E> {
Ok(Value::new_int(value as isize))
}
#[inline]
fn visit_u64<E>(self, value: u64) -> Result<Value, E> {
Ok(Value::new_uint(value as usize))
}
#[inline]
fn visit_f64<E>(self, value: f64) -> Result<Value, E> {
Ok(Value::new_float(value))
}
#[inline]
fn visit_str<E>(self, value: &str) -> Result<Value, E>
where
E: serde::de::Error,
{
Ok(Value::new_string(value))
}
#[inline]
fn visit_string<E>(self, value: String) -> Result<Value, E>
where
E: serde::de::Error,
{
self.visit_str(&value)
}
#[inline]
fn visit_none<E>(self) -> Result<Value, E>
where
E: serde::de::Error,
{
Ok(Value::new_undef())
}
#[inline]
fn visit_some<D>(self, deserializer: D) -> Result<Value, D::Error>
where
D: serde::Deserializer<'de>,
{
Deserialize::deserialize(deserializer)
}
#[inline]
fn visit_unit<E>(self) -> Result<Value, E> {
Ok(Value::new_undef())
}
#[inline]
fn visit_seq<V>(self, mut visitor: V) -> Result<Value, V::Error>
where
V: serde::de::SeqAccess<'de>,
{
let array = Array::new();
while let Some(elem) = visitor.next_element()? {
array.push(elem);
}
Ok(Value::Array(array))
}
fn visit_map<V>(self, mut visitor: V) -> Result<Value, V::Error>
where
V: serde::de::MapAccess<'de>,
{
// We use this to hint the deserializer that we're expecting a string-ish value.
struct KeyClassifier;
struct KeyClass(String);
impl<'de> serde::de::DeserializeSeed<'de> for KeyClassifier {
type Value = KeyClass;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_str(self)
}
}
impl<'de> Visitor<'de> for KeyClassifier {
type Value = KeyClass;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string key")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(KeyClass(s.to_owned()))
}
fn visit_string<E>(self, s: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(KeyClass(s))
}
}
let hash = Hash::new();
while let Some(key) = visitor.next_key_seed(KeyClassifier)? {
let value: Value = visitor.next_value()?;
hash.insert(&key.0, value);
}
Ok(Value::from(hash))
}
}
deserializer.deserialize_any(ValueVisitor)
}
}

1
rust-toolchain Normal file
View File

@ -0,0 +1 @@
nightly

1
rustfmt.toml Normal file
View File

@ -0,0 +1 @@
edition = "2018"