Skip to content

feat: add support for empty immutable shared arrays #355

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

mszabo-wikia
Copy link

@mszabo-wikia mszabo-wikia commented Jan 28, 2025

Why:

  • Since PHP 7.3, it's possible for extensions to create zvals backed by an immutable shared hashtable via the ZVAL_EMPTY_ARRAY macro.
  • This helps avoid redundant hashtable allocations when returning empty arrays back to userland PHP code, and could likewise be beneficial for Rust extensions too.

What:

  • Add ZendHashTable::new_empty_immutable() to obtain a ZendHashTable that is actually an empty immutable shared hashtable.
  • Add ZendHashTable::is_immutable(). Use it to avoid attempting to free the immutable shared hashtable on drop, and to set appropriate type flags when initializing a zval with a ZendHashTable.
  • Make ZendHashTable's TryFrom implementations from Vec and HashMap return an empty immutable shared hashtable if the input collection was empty. Although this would allow every user to automatically benefit from this optimization, I'm not convinced about this part because this is technically a breaking change for consumers that construct ZendHashTables via these converters in their Rust code. Maybe it'd be better to not change the converters and let projects explicitly use ::new_empty_immutable if they deem the optimization would be useful.

@Xenira
Copy link
Collaborator

Xenira commented Jan 30, 2025

@mszabo-wikia Thank you for the contribution! I have not that much time, but will try to have a look soon.

From a quick look docsrs_bindings.rs needs to be updated.

PHP 8.2 also seems to fail. Edit: might be because the docs bindings.

@mszabo-wikia
Copy link
Author

Thanks, I've regenerated the bindings using a PHP 8.2 debug build. It seems as though the bindings might not have been updated in a while, as there were some other unrelated changes. Or do they need to be generated using 8.0?

@Xenira
Copy link
Collaborator

Xenira commented Feb 1, 2025

@mszabo-wikia pretty sure its php 8.3, but no one seems to know #334 (comment)

@mszabo-wikia
Copy link
Author

@Xenira ah thanks, this is valuable context. I've regenerated the bindings using PHP 8.3.

@Xenira
Copy link
Collaborator

Xenira commented Feb 1, 2025

Make ZendHashTable's TryFrom implementations from Vec and HashMap return an empty immutable shared hashtable if the input collection was empty

I think we should not do this, as returning a immutable hashtable is not the expected behaviour here

@Xenira Xenira added the enhancement New feature or request label Feb 1, 2025
@mszabo-wikia
Copy link
Author

Make ZendHashTable's TryFrom implementations from Vec and HashMap return an empty immutable shared hashtable if the input collection was empty

I think we should not do this, as returning a immutable hashtable is not the expected behaviour here

Yeah, I had doubts about that myself. I've removed this logic.

@Xenira
Copy link
Collaborator

Xenira commented Feb 6, 2025

@mszabo-wikia fixed the issue in nightly tests (#357). Could you rebase onto master.

You also need to update your commit message, as the new pipeline enforces conventional commits.

Why:

- Since PHP 7.3, it's possible for extensions to create zvals backed by
  an immutable shared hashtable via the ZVAL_EMPTY_ARRAY macro.
- This helps avoid redundant hashtable allocations when returning empty arrays
  back to userland PHP code, and could likewise be beneficial for Rust extensions too.

What:

- Add ZendHashTable::new_empty_immutable() to obtain a ZendHashTable
  that is actually an empty immutable shared hashtable.
- Add ZendHashTable::is_immutable(). Use it to avoid attempting to
  free the immutable shared hashtable on drop, and to set appropriate
  type flags when initializing a zval with a ZendHashTable.
@mszabo-wikia
Copy link
Author

Perfect, thanks—I was wondering about that since I didn't experience the same error locally, but I have stable Rust installed which explains it.

@mszabo-wikia mszabo-wikia changed the title Add support for empty immutable shared arrays feat: add support for empty immutable shared arrays Feb 6, 2025
Comment on lines +85 to +93
pub fn new_empty_immutable() -> ZBox<Self> {
unsafe {
// SAFETY: zend_empty_array is a static global.
let ptr = (&zend_empty_array as *const ZendHashTable) as *mut ZendHashTable;

// SAFETY: `as_mut()` panics if the pointer is null.
ZBox::from_raw(ptr.as_mut().expect("zend_empty_array inconsistent"))
}
}
Copy link
Collaborator

@Xenira Xenira Feb 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably not return a mutable vallue, as that allows mutable access to the immutable hashtable.

    let mut ht = ZendHashTable::new_empty_immutable();
    ht.insert("key", 1).unwrap();

would be valid but results in signal: 11 (SIGSEGV) (core dumped)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delayed update—yeah, this definitely isn't ideal.

std::ptr only supports mutable pointers, so making the backing ptr immutable wouldn't play well with ZBox.

Maybe the right way to go here is to have a separate helper similar to the RETURN_EMPTY_ARRAY C macro in the Zend API, since really the only major use case for this in extensions is to return an empty array to userland without overhead. Then particularly performance-sensitive extensions can opt into using this as needed.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe just have a struct ZendEmptyArray; that implements IntoZval?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems like a good solution, thanks.

@Xenira
Copy link
Collaborator

Xenira commented Feb 17, 2025

Regarding the docsrs_bindings.rs merge conflict probably best just to regenerate after rebasing.

Really need to find a way to just automate that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants