mirror of
https://github.com/icewind1991/ivory.git
synced 2026-06-03 10:44:09 +02:00
splitup zval handling a bit
This commit is contained in:
parent
60f741e634
commit
f0ccd7ae1c
8 changed files with 185 additions and 63 deletions
|
|
@ -1,3 +1,20 @@
|
|||
//! Writing php extensions in rust made easy
|
||||
//!
|
||||
//! # Type casting
|
||||
//!
|
||||
//! Ivory automatically converts method parameters and return value from and to php compatible zval's
|
||||
//!
|
||||
//! The following types are supported for conversion.
|
||||
//!
|
||||
//! - rust signed and unsigned types up to 64bit to/from php `long`
|
||||
//! - rust `f64` and `f32` to/from php `double`
|
||||
//! - rust `bool` to/from php `bool`
|
||||
//! - rust `String` to/from php `string`
|
||||
//! - rust `Vec<T>` to/from php `array`
|
||||
//! - rust `Vec<(u64, T)>` to/from php `array`
|
||||
//!
|
||||
//! Where `T` is a type that can be converted from/to php
|
||||
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
pub mod error;
|
||||
|
|
|
|||
|
|
@ -108,10 +108,12 @@ macro_rules! impl_from_phpval {
|
|||
};
|
||||
}
|
||||
|
||||
impl_from_phpval!(i64, Long, isize);
|
||||
impl_from_phpval!(i64, Long, i64);
|
||||
impl_from_phpval!(i64, Long, i32);
|
||||
impl_from_phpval!(i64, Long, i16);
|
||||
impl_from_phpval!(i64, Long, i8);
|
||||
impl_from_phpval!(i64, Long, usize);
|
||||
impl_from_phpval!(i64, Long, u64);
|
||||
impl_from_phpval!(i64, Long, u32);
|
||||
impl_from_phpval!(i64, Long, u16);
|
||||
|
|
|
|||
86
ivory/src/zend/array.rs
Normal file
86
ivory/src/zend/array.rs
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
use std::alloc::{alloc, Layout};
|
||||
use std::cmp::max;
|
||||
use std::mem::size_of;
|
||||
use std::ptr;
|
||||
|
||||
use ivory_sys::*;
|
||||
|
||||
use crate::zend::string::{construct_zend_string, parse_zend_string};
|
||||
use crate::zend::{ZVal, ZValType};
|
||||
use crate::{ArrayKey, PhpVal};
|
||||
|
||||
pub(super) unsafe fn parse_zend_array(arr: zend_array) -> Vec<(ArrayKey, PhpVal)> {
|
||||
let len = arr.nNumUsed;
|
||||
let mut result = Vec::new();
|
||||
for i in 0..len {
|
||||
let elem = *arr.arData.add(i as usize);
|
||||
let key = if elem.key.is_null() {
|
||||
ArrayKey::Int(elem.h)
|
||||
} else {
|
||||
ArrayKey::String(parse_zend_string(&*elem.key))
|
||||
};
|
||||
let val: PhpVal = ZVal::from(elem.val).as_php_val();
|
||||
match val {
|
||||
PhpVal::Undef => {}
|
||||
_ => result.push((key, val)),
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
// WIP pure rust implementation of array construction in order to be able to run conversion test without linking to php
|
||||
pub(super) fn create_zend_array(vec: Vec<(ArrayKey, PhpVal)>) -> zend_array {
|
||||
let bucket_size = size_of::<Bucket>();
|
||||
let size_min = max(8, vec.len());
|
||||
let layout =
|
||||
Layout::from_size_align(bucket_size * size_min, bucket_size).expect("invalid layout");
|
||||
let bucket_mem: *mut Bucket = unsafe { alloc(layout) } as *mut Bucket;
|
||||
|
||||
let array = zend_array {
|
||||
gc: _zend_refcounted_h {
|
||||
refcount: 1,
|
||||
u: _zend_refcounted_h__bindgen_ty_1 {
|
||||
type_info: u8::from(ZValType::Array) as u32,
|
||||
},
|
||||
},
|
||||
u: _zend_array__bindgen_ty_1 { flags: 1 << 3 }, // uninitialized
|
||||
nTableMask: u32::max_value() - 1,
|
||||
arData: bucket_mem,
|
||||
nNumUsed: vec.len() as u32,
|
||||
nNumOfElements: vec.len() as u32,
|
||||
nTableSize: size_min as u32,
|
||||
nInternalPointer: 0,
|
||||
nNextFreeElement: i64::min_value(),
|
||||
pDestructor: Some(zval_ptr_dtor),
|
||||
};
|
||||
|
||||
let mut curr_bucket: *mut Bucket = bucket_mem;
|
||||
|
||||
for (key, val) in vec.into_iter() {
|
||||
unsafe {
|
||||
match key {
|
||||
ArrayKey::Int(key) => {
|
||||
(*curr_bucket).h = key;
|
||||
(*curr_bucket).key = ptr::null_mut()
|
||||
}
|
||||
ArrayKey::String(key) => {
|
||||
(*curr_bucket).h = hash_djbx33a(key.as_bytes());
|
||||
(*curr_bucket).key = construct_zend_string(key)
|
||||
}
|
||||
}
|
||||
|
||||
curr_bucket = curr_bucket.add(1);
|
||||
}
|
||||
}
|
||||
|
||||
array
|
||||
}
|
||||
|
||||
fn hash_djbx33a(data: &[u8]) -> u64 {
|
||||
let mut hash = 5381u64;
|
||||
for byte in data {
|
||||
hash = (hash * 33) + *byte as u64;
|
||||
}
|
||||
|
||||
hash | 0x8000000000000000
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ pub use self::function::*;
|
|||
pub use self::module::*;
|
||||
pub use self::zval::{ExecuteData, ZVal, ZValType};
|
||||
|
||||
mod array;
|
||||
mod function;
|
||||
mod module;
|
||||
mod string;
|
||||
mod zval;
|
||||
|
|
|
|||
46
ivory/src/zend/string.rs
Normal file
46
ivory/src/zend/string.rs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
use ivory_sys::*;
|
||||
use std::alloc::{alloc, Layout};
|
||||
use std::mem::size_of;
|
||||
use std::ptr;
|
||||
use std::str;
|
||||
|
||||
pub(super) unsafe fn parse_zend_string(string: *const zend_string) -> String {
|
||||
let len = (*string).len;
|
||||
let base = string as *const u8;
|
||||
let str_start = base.add(size_of::<ZendStringHeader>());
|
||||
|
||||
let slice: &[u8] = std::slice::from_raw_parts(str_start, len);
|
||||
str::from_utf8_unchecked(slice).to_string()
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ZendStringHeader {
|
||||
gc: zend_refcounted_h,
|
||||
h: zend_ulong,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
pub(super) fn construct_zend_string(string: String) -> *mut zend_string {
|
||||
let len = string.len();
|
||||
|
||||
let header_size = size_of::<ZendStringHeader>();
|
||||
|
||||
let layout = Layout::from_size_align(len + header_size, size_of::<zend_string>())
|
||||
.expect("invalid layout");
|
||||
let raw = unsafe { alloc(layout) };
|
||||
|
||||
let header = ZendStringHeader {
|
||||
gc: zend_refcounted_h {
|
||||
refcount: 1, // ?? no clue actually,
|
||||
u: _zend_refcounted_h__bindgen_ty_1 { type_info: 0 },
|
||||
},
|
||||
h: 0,
|
||||
len,
|
||||
};
|
||||
unsafe {
|
||||
ptr::write(raw as *mut ZendStringHeader, header);
|
||||
ptr::copy(string.as_ptr(), raw.add(header_size), len);
|
||||
};
|
||||
|
||||
raw as *mut zend_string
|
||||
}
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
use std::alloc::{alloc, Layout};
|
||||
use std::fmt;
|
||||
use std::fmt::Display;
|
||||
use std::intrinsics::transmute;
|
||||
use std::mem::size_of;
|
||||
use std::{ptr, str};
|
||||
|
||||
use ivory_sys::*;
|
||||
|
||||
use crate::zend::array::parse_zend_array;
|
||||
use crate::zend::string::{construct_zend_string, parse_zend_string};
|
||||
use crate::{ArrayKey, PhpVal};
|
||||
|
||||
#[repr(transparent)]
|
||||
|
|
@ -58,51 +58,15 @@ impl Iterator for IntoArgIterator {
|
|||
}
|
||||
}
|
||||
|
||||
unsafe fn zend_str_as_string(string: *const zend_string) -> String {
|
||||
let len = (*string).len;
|
||||
let base = string as *const u8;
|
||||
let str_start = base.add(size_of::<ZendStringHeader>());
|
||||
|
||||
let slice: &[u8] = std::slice::from_raw_parts(str_start, len);
|
||||
str::from_utf8_unchecked(slice).to_string()
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ZendStringHeader {
|
||||
gc: zend_refcounted_h,
|
||||
h: zend_ulong,
|
||||
len: usize,
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)] // alignment of pointer casts is guaranteed by the layout
|
||||
fn string_into_zend_str(string: String) -> *mut zend_string {
|
||||
let len = string.len();
|
||||
|
||||
let header_size = size_of::<ZendStringHeader>();
|
||||
|
||||
let layout = Layout::from_size_align(len + header_size, size_of::<zend_string>())
|
||||
.expect("invalid layout");
|
||||
let raw = unsafe { alloc(layout) };
|
||||
|
||||
let header = ZendStringHeader {
|
||||
gc: zend_refcounted_h {
|
||||
refcount: 1, // ?? no clue actually,
|
||||
u: _zend_refcounted_h__bindgen_ty_1 { type_info: 0 },
|
||||
},
|
||||
h: 0,
|
||||
len,
|
||||
};
|
||||
unsafe {
|
||||
ptr::write(raw as *mut ZendStringHeader, header);
|
||||
ptr::copy(string.as_ptr(), raw.add(header_size), len);
|
||||
};
|
||||
|
||||
raw as *mut zend_string
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct ZVal(zval);
|
||||
|
||||
impl From<zval> for ZVal {
|
||||
fn from(val: zval) -> Self {
|
||||
ZVal(val)
|
||||
}
|
||||
}
|
||||
|
||||
impl ZVal {
|
||||
pub fn get_type(&self) -> ZValType {
|
||||
unsafe { self.0.u1.v.type_.into() }
|
||||
|
|
@ -117,27 +81,11 @@ impl ZVal {
|
|||
}
|
||||
|
||||
pub unsafe fn as_str(&self) -> String {
|
||||
zend_str_as_string(self.0.value.str)
|
||||
parse_zend_string(self.0.value.str)
|
||||
}
|
||||
|
||||
pub unsafe fn as_array(&self) -> Vec<(ArrayKey, PhpVal)> {
|
||||
let arr = *self.0.value.arr;
|
||||
let len = arr.nNumUsed;
|
||||
let mut result = Vec::new();
|
||||
for i in 0..len {
|
||||
let elem = *arr.arData.add(i as usize);
|
||||
let key = if elem.key.is_null() {
|
||||
ArrayKey::Int(elem.h)
|
||||
} else {
|
||||
ArrayKey::String(zend_str_as_string(&*elem.key))
|
||||
};
|
||||
let val: PhpVal = ZVal(elem.val).as_php_val();
|
||||
match val {
|
||||
PhpVal::Undef => {}
|
||||
_ => result.push((key, val)),
|
||||
}
|
||||
}
|
||||
result
|
||||
parse_zend_array(*self.0.value.arr)
|
||||
}
|
||||
|
||||
pub fn as_php_val(&self) -> PhpVal {
|
||||
|
|
@ -189,6 +137,12 @@ impl Display for ZValType {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<ZValType> for u8 {
|
||||
fn from(val: ZValType) -> Self {
|
||||
unsafe { transmute(val) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for ZValType {
|
||||
fn from(val: u8) -> Self {
|
||||
if val > 10 {
|
||||
|
|
@ -253,7 +207,7 @@ impl From<PhpVal> for ZVal {
|
|||
}),
|
||||
PhpVal::String(val) => ZVal(zval {
|
||||
value: zend_value {
|
||||
str: string_into_zend_str(val),
|
||||
str: construct_zend_string(val),
|
||||
},
|
||||
u1: ty.into(),
|
||||
u2: _zval_struct__bindgen_ty_2 { extra: 0 },
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ fn main() {
|
|||
.whitelist_function("_zend_new_array")
|
||||
.whitelist_function("add_index_zval")
|
||||
.whitelist_function("add_assoc_zval_ex")
|
||||
.whitelist_function("zval_ptr_dtor")
|
||||
.whitelist_type("zval")
|
||||
.whitelist_type("zend_execute_data")
|
||||
.whitelist_type("zend_module_entry")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#[link(libphp)]
|
||||
use maplit::hashmap;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
|
|
@ -40,6 +41,19 @@ fn cast_into_php_val() {
|
|||
]),
|
||||
vec![(0u8, 1), (3, 2), (6, 3)].into()
|
||||
);
|
||||
assert_eq!(
|
||||
PhpVal::Array(vec![
|
||||
(ArrayKey::String("asd".to_string()), PhpVal::Long(1)),
|
||||
(ArrayKey::String("foo".to_string()), PhpVal::Long(2)),
|
||||
(ArrayKey::String("bar".to_string()), PhpVal::Long(3))
|
||||
]),
|
||||
vec![
|
||||
("asd".to_string(), 1),
|
||||
("foo".to_string(), 2),
|
||||
("bar".to_string(), 3)
|
||||
]
|
||||
.into()
|
||||
);
|
||||
assert_eq!(
|
||||
PhpVal::Array(vec![
|
||||
(ArrayKey::Int(0), PhpVal::Long(1)),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue