Skip to content

Commit

Permalink
Replace OwnedHashTable with ZBox<HashTable>
Browse files Browse the repository at this point in the history
  • Loading branch information
davidcole1340 committed Oct 4, 2021
1 parent e556a34 commit 4760f64
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 125 deletions.
1 change: 1 addition & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ fn main() {
.rustfmt_bindings(true)
.no_copy("_zend_value")
.no_copy("_zend_string")
.no_copy("_zend_array")
.layout_tests(env::var("EXT_PHP_RS_TEST").is_ok());

for binding in ALLOWED_BINDINGS.iter() {
Expand Down
170 changes: 57 additions & 113 deletions src/php/types/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ use std::{
ffi::CString,
fmt::Debug,
iter::FromIterator,
mem::ManuallyDrop,
ops::{Deref, DerefMut},
ptr::NonNull,
u64,
};
Expand All @@ -21,7 +19,10 @@ use crate::{
HT_MIN_SIZE,
},
errors::{Error, Result},
php::enums::DataType,
php::{
boxed::{ZBox, ZBoxable},
enums::DataType,
},
};

use super::zval::{FromZval, IntoZval, Zval};
Expand All @@ -30,6 +31,27 @@ use super::zval::{FromZval, IntoZval, Zval};
pub use crate::bindings::HashTable;

impl HashTable {
/// Creates a new, empty, PHP associative array.
pub fn new() -> ZBox<Self> {
Self::with_capacity(HT_MIN_SIZE)
}

/// Creates a new, empty, PHP associative array with an initial size.
///
/// # Parameters
///
/// * `size` - The size to initialize the array with.
pub fn with_capacity(size: u32) -> ZBox<Self> {
// SAFETY: PHP allocater handles the creation of the array.
unsafe {
let ptr = _zend_new_array(size);
ZBox::from_raw(
ptr.as_mut()
.expect("Failed to allocate memory for hashtable"),
)
}
}

/// Returns the current number of elements in the array.
pub fn len(&self) -> usize {
self.nNumOfElements as usize
Expand Down Expand Up @@ -184,11 +206,11 @@ impl HashTable {
pub fn values(&self) -> Values {
Values::new(self)
}
}

/// Clones the hash table, returning an [`OwnedHashTable`].
pub fn to_owned(&self) -> OwnedHashTable {
let ptr = unsafe { zend_array_dup(self as *const HashTable as *mut HashTable) };
unsafe { OwnedHashTable::from_ptr(ptr) }
unsafe impl ZBoxable for HashTable {
fn free(&mut self) {
unsafe { zend_array_destroy(self) }
}
}

Expand All @@ -203,6 +225,17 @@ impl Debug for HashTable {
}
}

impl ToOwned for HashTable {
type Owned = ZBox<HashTable>;

fn to_owned(&self) -> Self::Owned {
unsafe {
let ptr = zend_array_dup(self as *const HashTable as *mut HashTable);
ZBox::from_raw(ptr)
}
}
}

/// Immutable iterator upon a reference to a hashtable.
pub struct Iter<'a> {
ht: &'a HashTable,
Expand Down Expand Up @@ -315,106 +348,19 @@ impl<'a> DoubleEndedIterator for Values<'a> {
}
}

/// A container used to 'own' a Zend hashtable. Dereferences to a reference to [`HashTable`].
///
/// When this struct is dropped, it will also destroy the internal hashtable, unless the `into_raw`
/// function is used.
pub struct OwnedHashTable {
ptr: NonNull<HashTable>,
}

impl OwnedHashTable {
/// Creates a new, empty, PHP associative array.
pub fn new() -> Self {
Self::with_capacity(HT_MIN_SIZE)
}

/// Creates a new, empty, PHP associative array with an initial size.
///
/// # Parameters
///
/// * `size` - The size to initialize the array with.
pub fn with_capacity(size: u32) -> Self {
// SAFETY: PHP allocater handles the creation of the array.
unsafe {
let ptr = _zend_new_array(size);
Self::from_ptr(ptr)
}
}

/// Creates an owned hashtable from a hashtable pointer, which will be freed when the
/// resulting Rust object is dropped.
///
/// # Parameters
///
/// * `ptr` - Hashtable pointer.
///
/// # Panics
///
/// Panics if the given pointer is null.
///
/// # Safety
///
/// Caller must ensure that the given pointer is a valid hashtable pointer, including
/// non-null and properly aligned.
pub unsafe fn from_ptr(ptr: *mut HashTable) -> Self {
Self {
ptr: NonNull::new(ptr).expect("Invalid hashtable pointer given"),
}
}

/// Converts the owned Zend hashtable into the internal pointer, bypassing the [`Drop`]
/// implementation.
///
/// The caller is responsible for freeing the resulting pointer using the `zend_array_destroy`
/// function.
pub fn into_inner(self) -> *mut HashTable {
let this = ManuallyDrop::new(self);
this.ptr.as_ptr()
}
}

impl Deref for OwnedHashTable {
type Target = HashTable;

fn deref(&self) -> &Self::Target {
// SAFETY: all constructors ensure a valid ptr is present
unsafe { self.ptr.as_ref() }
}
}

impl DerefMut for OwnedHashTable {
fn deref_mut(&mut self) -> &mut Self::Target {
// SAFETY: all constructors ensure a valid, owned ptr is present
unsafe { self.ptr.as_mut() }
}
}

impl Debug for OwnedHashTable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.deref().fmt(f)
}
}

impl Default for OwnedHashTable {
impl Default for ZBox<HashTable> {
fn default() -> Self {
Self::new()
HashTable::new()
}
}

impl Clone for OwnedHashTable {
impl Clone for ZBox<HashTable> {
fn clone(&self) -> Self {
self.deref().to_owned()
}
}

impl Drop for OwnedHashTable {
fn drop(&mut self) {
unsafe { zend_array_destroy(self.ptr.as_mut()) };
(**self).to_owned()
}
}

impl IntoZval for OwnedHashTable {
impl IntoZval for ZBox<HashTable> {
const TYPE: DataType = DataType::Array;

fn set_zval(self, zv: &mut Zval, _: bool) -> Result<()> {
Expand Down Expand Up @@ -455,17 +401,16 @@ where
}
}

impl<K, V> TryFrom<HashMap<K, V>> for OwnedHashTable
impl<K, V> TryFrom<HashMap<K, V>> for ZBox<HashTable>
where
K: AsRef<str>,
V: IntoZval,
{
type Error = Error;

fn try_from(value: HashMap<K, V>) -> Result<Self> {
let mut ht = OwnedHashTable::with_capacity(
value.len().try_into().map_err(|_| Error::IntegerOverflow)?,
);
let mut ht =
HashTable::with_capacity(value.len().try_into().map_err(|_| Error::IntegerOverflow)?);

for (k, v) in value.into_iter() {
ht.insert(k.as_ref(), v)?;
Expand Down Expand Up @@ -521,16 +466,15 @@ where
}
}

impl<T> TryFrom<Vec<T>> for OwnedHashTable
impl<T> TryFrom<Vec<T>> for ZBox<HashTable>
where
T: IntoZval,
{
type Error = Error;

fn try_from(value: Vec<T>) -> Result<Self> {
let mut ht = OwnedHashTable::with_capacity(
value.len().try_into().map_err(|_| Error::IntegerOverflow)?,
);
let mut ht =
HashTable::with_capacity(value.len().try_into().map_err(|_| Error::IntegerOverflow)?);

for val in value.into_iter() {
ht.push(val)?;
Expand Down Expand Up @@ -564,9 +508,9 @@ where
}
}

impl FromIterator<Zval> for OwnedHashTable {
impl FromIterator<Zval> for ZBox<HashTable> {
fn from_iter<T: IntoIterator<Item = Zval>>(iter: T) -> Self {
let mut ht = OwnedHashTable::new();
let mut ht = HashTable::new();
for item in iter.into_iter() {
// Inserting a zval cannot fail, as `push` only returns `Err` if converting `val` to a zval
// fails.
Expand All @@ -576,9 +520,9 @@ impl FromIterator<Zval> for OwnedHashTable {
}
}

impl FromIterator<(u64, Zval)> for OwnedHashTable {
impl FromIterator<(u64, Zval)> for ZBox<HashTable> {
fn from_iter<T: IntoIterator<Item = (u64, Zval)>>(iter: T) -> Self {
let mut ht = OwnedHashTable::new();
let mut ht = HashTable::new();
for (key, val) in iter.into_iter() {
// Inserting a zval cannot fail, as `push` only returns `Err` if converting `val` to a zval
// fails.
Expand All @@ -588,9 +532,9 @@ impl FromIterator<(u64, Zval)> for OwnedHashTable {
}
}

impl<'a> FromIterator<(&'a str, Zval)> for OwnedHashTable {
impl<'a> FromIterator<(&'a str, Zval)> for ZBox<HashTable> {
fn from_iter<T: IntoIterator<Item = (&'a str, Zval)>>(iter: T) -> Self {
let mut ht = OwnedHashTable::new();
let mut ht = HashTable::new();
for (key, val) in iter.into_iter() {
// Inserting a zval cannot fail, as `push` only returns `Err` if converting `val` to a zval
// fails.
Expand Down
3 changes: 1 addition & 2 deletions src/php/types/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ use crate::{
flags::ZvalTypeFlags,
function::FunctionBuilder,
globals::ExecutorGlobals,
types::array::OwnedHashTable,
},
};

Expand Down Expand Up @@ -979,7 +978,7 @@ impl ZendObjectHandlers {

let props = zend_std_get_properties(object)
.as_mut()
.or_else(|| OwnedHashTable::new().into_inner().as_mut())
.or_else(|| Some(HashTable::new().into_raw()))
.expect("Failed to get property hashtable");

if let Err(e) = internal::<T>(object, props) {
Expand Down
14 changes: 4 additions & 10 deletions src/php/types/zval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,7 @@ use crate::{

use crate::php::{enums::DataType, flags::ZvalTypeFlags, types::long::ZendLong};

use super::{
array::{HashTable, OwnedHashTable},
callable::Callable,
object::ZendObject,
rc::PhpRc,
string::ZendStr,
};
use super::{array::HashTable, callable::Callable, object::ZendObject, rc::PhpRc, string::ZendStr};

/// Zend value. Represents most data types that are in the Zend engine.
pub type Zval = zval;
Expand Down Expand Up @@ -421,7 +415,7 @@ impl Zval {
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_array<T: TryInto<OwnedHashTable, Error = Error>>(&mut self, val: T) -> Result<()> {
pub fn set_array<T: TryInto<ZBox<HashTable>, Error = Error>>(&mut self, val: T) -> Result<()> {
self.set_hashtable(val.try_into()?);
Ok(())
}
Expand All @@ -431,9 +425,9 @@ impl Zval {
/// # Parameters
///
/// * `val` - The value to set the zval as.
pub fn set_hashtable(&mut self, val: OwnedHashTable) {
pub fn set_hashtable(&mut self, val: ZBox<HashTable>) {
self.change_type(ZvalTypeFlags::ArrayEx);
self.value.arr = val.into_inner();
self.value.arr = val.into_raw();
}

/// Sets the value of the zval as a pointer.
Expand Down

0 comments on commit 4760f64

Please sign in to comment.