Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ZBox<T> to replace owned variants #94

Merged
merged 6 commits into from
Oct 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
env:
LIBCLANG_PATH: ${{ runner.temp }}/llvm-${{ matrix.llvm }}/lib
EXT_PHP_RS_TEST:
run: cargo build --release --features alloc,closure
run: cargo build --release --all-features
- name: Test guide examples
env:
CARGO_PKG_NAME: mdbook-tests
Expand Down
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ regex = "1"
cc = "1.0"

[features]
alloc = []
closure = []

[workspace]
Expand Down
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
2 changes: 1 addition & 1 deletion example/skel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
ext-php-rs = { path = "../../", features = ["alloc", "closure"] }
ext-php-rs = { path = "../../", features = ["closure"] }

[lib]
name = "skel"
Expand Down
6 changes: 4 additions & 2 deletions ext-php-rs-derive/src/zval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,13 @@ fn parse_struct(
Ok(quote! {
impl #into_impl_generics ::ext_php_rs::php::types::object::IntoZendObject for #ident #ty_generics #into_where_clause {
fn into_zend_object(self) -> ::ext_php_rs::errors::Result<
::ext_php_rs::php::types::object::OwnedZendObject
::ext_php_rs::php::boxed::ZBox<
::ext_php_rs::php::types::object::ZendObject
>
> {
use ::ext_php_rs::php::types::zval::IntoZval;

let mut obj = ::ext_php_rs::php::types::object::OwnedZendObject::new_stdclass();
let mut obj = ::ext_php_rs::php::types::object::ZendObject::new_stdclass();
#(#into_fields)*
::ext_php_rs::errors::Result::Ok(obj)
}
Expand Down
126 changes: 126 additions & 0 deletions src/php/boxed.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//! A pointer type for heap allocation using the Zend memory manager.
//!
//! Heap memory in PHP is usually request-bound and allocated inside [memory arenas], which are cleared
//! at the start and end of a PHP request. Allocating and freeing memory that is allocated on the Zend
//! heap is done through two separate functions [`efree`] and [`emalloc`].
//!
//! As such, most heap-allocated PHP types **cannot** be allocated on the stack, such as [`ZendStr`], which
//! is a dynamically-sized type, and therefore must be allocated on the heap. A regular [`Box`] would not
//! work in this case, as the memory needs to be freed from a separate function `zend_string_release`. The
//! [`ZBox`] type provides a wrapper which calls the relevant release functions based on the type and what is
//! inside the implementation of [`ZBoxable`].
//!
//! This type is not created directly, but rather through a function implemented on the downstream type. For
//! example, [`ZendStr`] has a function `new` which returns a [`ZBox<ZendStr>`].
//!
//! [memory arenas]: https://en.wikipedia.org/wiki/Region-based_memory_management
//! [`ZendStr`]: super::types::string::ZendStr
//! [`emalloc`]: super::alloc::efree

use std::{
borrow::Borrow,
fmt::Debug,
mem::ManuallyDrop,
ops::{Deref, DerefMut},
ptr::NonNull,
};

use super::alloc::efree;

/// A pointer type for heap allocation using the Zend memory manager.
///
/// See the [module level documentation](../index.html) for more.
pub struct ZBox<T: ZBoxable>(NonNull<T>);

impl<T: ZBoxable> ZBox<T> {
/// Creates a new box from a given pointer.
///
/// # Parameters
///
/// * `ptr` - A non-null, well-aligned pointer to a `T`.
///
/// # Safety
///
/// Caller must ensure that `ptr` is non-null, well-aligned and pointing to a `T`.
pub unsafe fn from_raw(ptr: *mut T) -> Self {
Self(NonNull::new_unchecked(ptr))
}

/// Returns the pointer contained by the box, dropping the box in the process. The data pointed to by
/// the returned pointer is not released.
///
/// # Safety
///
/// The caller is responsible for managing the memory pointed to by the returned pointer, including
/// freeing the memory.
pub fn into_raw(self) -> &'static mut T {
let mut this = ManuallyDrop::new(self);
// SAFETY: All constructors ensure the contained pointer is well-aligned and dereferencable.
unsafe { this.0.as_mut() }
}
}

impl<T: ZBoxable> Drop for ZBox<T> {
#[inline]
fn drop(&mut self) {
self.deref_mut().free()
}
}

impl<T: ZBoxable> Deref for ZBox<T> {
type Target = T;

#[inline]
fn deref(&self) -> &Self::Target {
// SAFETY: All constructors ensure the contained pointer is well-aligned and dereferencable.
unsafe { self.0.as_ref() }
}
}

impl<T: ZBoxable> DerefMut for ZBox<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
// SAFETY: All constructors ensure the contained pointer is well-aligned and dereferencable.
unsafe { self.0.as_mut() }
}
}

impl<T: ZBoxable + Debug> Debug for ZBox<T> {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
(&**self).fmt(f)
}
}

impl<T: ZBoxable> Borrow<T> for ZBox<T> {
#[inline]
fn borrow(&self) -> &T {
&**self
}
}

impl<T: ZBoxable> AsRef<T> for ZBox<T> {
#[inline]
fn as_ref(&self) -> &T {
self
}
}

/// Implemented on types that can be heap allocated using the Zend memory manager. These types are stored
/// inside a [`ZBox`] when heap-allocated, and the [`free`] method is called when the box is dropped.
///
/// # Safety
///
/// The default implementation of the [`free`] function uses the [`efree`] function to free the memory without
/// calling any destructors.
///
/// The implementor must ensure that any time a pointer to the implementor is passed into a [`ZBox`] that the
/// memory pointed to was allocated by the Zend memory manager.
///
/// [`free`]: #method.free
pub unsafe trait ZBoxable {
/// Frees the memory pointed to by `self`, calling any destructors required in the process.
fn free(&mut self) {
unsafe { efree(self as *mut _ as *mut u8) };
}
}
16 changes: 8 additions & 8 deletions src/php/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use crate::{
exceptions::PhpException,
execution_data::ExecutionData,
function::FunctionBuilder,
types::object::{ClassObject, ConstructorMeta, ConstructorResult, ZendObject},
types::object::{ConstructorMeta, ConstructorResult, ZendClassObject, ZendObject},
},
};
use std::{alloc::Layout, convert::TryInto, ffi::CString, fmt::Debug};
use std::{alloc::Layout, convert::TryInto, ffi::CString, fmt::Debug, ops::DerefMut};

use crate::bindings::{
zend_class_entry, zend_declare_class_constant, zend_declare_property,
Expand All @@ -22,7 +22,7 @@ use super::{
globals::ExecutorGlobals,
types::{
object::RegisteredClass,
string::ZendString,
string::ZendStr,
zval::{IntoZval, Zval},
},
};
Expand All @@ -43,10 +43,10 @@ impl ClassEntry {
/// not be found or the class table has not been initialized.
pub fn try_find(name: &str) -> Option<&'static Self> {
ExecutorGlobals::get().class_table()?;
let mut name = ZendString::new(name, false).ok()?;
let mut name = ZendStr::new(name, false).ok()?;

unsafe {
crate::bindings::zend_lookup_class_ex(name.as_mut_zend_str(), std::ptr::null_mut(), 0)
crate::bindings::zend_lookup_class_ex(name.deref_mut(), std::ptr::null_mut(), 0)
.as_ref()
}
}
Expand Down Expand Up @@ -277,8 +277,8 @@ impl ClassBuilder {
extern "C" fn create_object<T: RegisteredClass>(_: *mut ClassEntry) -> *mut ZendObject {
// SAFETY: After calling this function, PHP will always call the constructor defined below,
// which assumes that the object is uninitialized.
let obj = unsafe { ClassObject::<T>::new_uninit() };
obj.into_inner().get_mut_zend_obj()
let obj = unsafe { ZendClassObject::<T>::new_uninit() };
obj.into_raw().get_mut_zend_obj()
}

extern "C" fn constructor<T: RegisteredClass>(ex: &mut ExecutionData, _: &mut Zval) {
Expand Down Expand Up @@ -337,7 +337,7 @@ impl ClassBuilder {
///
/// Returns an [`Error`] variant if the class could not be registered.
pub fn build(mut self) -> Result<&'static mut ClassEntry> {
self.ptr.name = ZendString::new_interned(&self.name, true)?.into_inner();
self.ptr.name = ZendStr::new_interned(&self.name, true)?.into_raw();

self.methods.push(FunctionEntry::end());
let func = Box::into_raw(self.methods.into_boxed_slice()) as *const FunctionEntry;
Expand Down
4 changes: 1 addition & 3 deletions src/php/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
//! Objects relating to PHP and the Zend engine.

#[cfg(any(docs, feature = "alloc"))]
#[cfg_attr(docs, doc(cfg(feature = "alloc")))]
pub mod alloc;

pub mod args;
pub mod boxed;
pub mod class;
pub mod constants;
pub mod enums;
Expand Down
7 changes: 1 addition & 6 deletions src/php/pack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,13 @@ use crate::bindings::{ext_php_rs_zend_string_init, zend_string};
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
pub unsafe trait Pack: Clone {
/// Packs a given vector into a Zend binary string. Can be passed to PHP and then unpacked
/// using the [`unpack`] function. Note you should probably use the [`set_binary`] method on the
/// [`Zval`] struct instead of this function directly, as there is currently no way to set a
/// [`ZendString`] on a [`Zval`] directly.
/// using the [`unpack`] function.
///
/// # Parameters
///
/// * `vec` - The vector to pack into a binary string.
///
/// [`unpack`]: https://www.php.net/manual/en/function.unpack.php
/// [`Zval`]: crate::php::types::zval::Zval
/// [`ZendString`]: crate::php::types::string::ZendString
/// [`set_binary`]: crate::php::types::zval::Zval#method.set_binary
fn pack_into(vec: Vec<Self>) -> *mut zend_string;

/// Unpacks a given Zend binary string into a Rust vector. Can be used to pass data from `pack`
Expand Down
Loading