-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Proposal: Add ImmutableKeyValueSequence (rewindable, immutable, memory-efficient, allows any key&repeating keys) #6655
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
Conversation
I only glanced at the implementation, which looks okay at a glance. A few suggestions:
This PR would complement the work I'm doing for PHP 8.1 with |
Still undecided about the name, CachedIterable or CopiedIterable or ClonedIterable is probably a better name than RewindIterable. The similarity to the name CachingIterator may cause confusion. Definitely makes sense for the helper methods going on the IteratorAggregate instead.
I think ArrayAccess would lead to more bugs in application code for those expecting
I was planning on something similar |
(Usage of "current_observed_frame" varible looks unsafe to me).
/** TODO: Does C guarantee that this has the same memory layout as an array of zvals? */ | ||
typedef struct _zval_pair { | ||
zval key; | ||
zval value; | ||
} zval_pair; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general there may be padding bits between the members, though in practice I haven't found a case where two adjacent members of the same type have any padding bits between them. Do you use the layout for a specific optimization?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it's just easier to write/read code with zval_pair than entries[offset*2]
, entries[offset*2+1]
- I doubt it's faster
721126a
to
2e6002a
Compare
50ac0e1
to
114de5b
Compare
e2a6d35
to
67bfa99
Compare
ext/spl/config.m4
Outdated
PHP_ADD_EXTENSION_DEP(spl, pcre, true) | ||
PHP_ADD_EXTENSION_DEP(spl, standard, true) | ||
PHP_ADD_EXTENSION_DEP(spl, json, true) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there specific reason for wanting this? I don't think existing structures are json serializable, so is there specific motivation for adding it and incurring this dependency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://wiki.php.net/rfc/always_enable_json passed in PHP 8.0.
Before that, adding a dependency on json would complicate package maintenance, and the fact that json could be compiled as a shared extension that was potentially disabled would mean that adding it was impractical (can't have an always-on extension depend on an extension that can be disabled)
Some applications may depend on the existing structures json behavior, e.g. I've used new ArrayObject($array)
in the past to get associative arrays always encoded as {}
, even empty ones
ext/spl/spl_iterators.h
Outdated
@@ -28,6 +29,7 @@ | |||
#define spl_ce_Serializable zend_ce_serializable | |||
#define spl_ce_Countable zend_ce_countable | |||
#define spl_ce_Stringable zend_ce_stringable | |||
#define spl_ce_JsonSerializable php_json_serializable_ce |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can tell, this is never used. Maybe through macro magic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it's the REGISTER_SPL_IMPLEMENTS(CachedIterable, JsonSerializable);
magic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests passed for me locally on Mac with -fsanitize=undefined,address
being set.
b7a0060
to
db8dfaf
Compare
414c1ef
to
8861fac
Compare
The CI failures would be fixed by properly declaring the dependency on json - #7117 does that for a different class (figured this out by running travis/compile.sh instead and manually running |
859307e
to
2c988ba
Compare
1. This eagerly evaluates any iterable, and stores a read only copy of the keys and values. 2. This allows using any value as a key, and allows repeating keys. For example, this can be used to store the exact result of keys and values yielded by a generator. Longer-term, this may be useful in creating internal helper functions/methods such as `someprefix_map(iterable $iterable): ImmutableIterable`, `someprefix_flip(iterable $iterable): ImmutableIterable`, ```php function my_generator() { yield '123' => 'numeric string is preserved'; yield '123' => 'allows repeating keys'; yield new stdClass() => 'false'; } $iterable = new ImmutableIterable(my_generator()); foreach ($iterable as $key1 => $value1) { foreach ($iterable as $key2 => $value2) { ... } } ``` ```php final class ImmutableIterable implements IteratorAggregate, Countable, JsonSerializable { public function __construct(iterable $iterator) {} public function getIterator(): InternalIterator {} public function count(): int {} public static function fromPairs(iterable $pairs): ImmutableIterable; public function toPairs(): array; // [[$k1, $v1], [$k2, $v2]] public function __serialize(): array {} // [$k1, $v1, $k2, $v2,...] public function __unserialize(array $data): void {} // useful for converting iterables back to values public function keys(): array {} // [$k1, $k2, ...] public function values(): array {} // [$v1, $v2, ...] // useful to efficiently get offsets at the middle/end of a long iterable public function keyAt(int $offset): mixed {} public function valueAt(int $offset): mixed {} public function jsonSerialize(): array; // [[$k1, $v1], [$k2, $v2]] // dynamic properties are forbidden } ``` Rename to ImmutableIterable Fix bugs, implement `__set_state`
2c988ba
to
541f3d4
Compare
iterable
,and stores a read only copy of the keys and values.
For example, this can be used to store the exact result of keys and values
yielded by a
Generator
.Longer-term, this may be useful in creating internal helper functions/methods such as
someprefix_map(iterable $iterable): ImmutableKeyValueSequence
,someprefix_flip(iterable $iterable): ImmutableKeyValueSequence
,NOTE: This should not be confused with the pre-existing class CachingIterator. CachingIterator is not rewindable if the underlying iterator is not rewindable (e.g. generators). CachingIterator->getCache() is an array, meaning keys are limited to integers/strings and cannot be repeated.