From c9f785935a451913c733294f4acaa52a11320203 Mon Sep 17 00:00:00 2001 From: CismonX Date: Thu, 20 Sep 2018 15:23:17 +0800 Subject: [PATCH 1/8] Add stub for `zip()`. --- stubs/Collection.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/stubs/Collection.php b/stubs/Collection.php index 0fd3af9..3c9daf5 100644 --- a/stubs/Collection.php +++ b/stubs/Collection.php @@ -923,4 +923,16 @@ class Collection implements ArrayAccess, Countable * @return Collection */ function values() {} + + /** + * Returns a collection of pairs built from the elements of this array and other array + * with the same index. The returned collection has length of the shortest array. + * + * Both arrays must be packed. + * + * @param array|Collection $other + * @param callable $transform[optional] ($value, $key) -> $new_value + * @return Collection + */ + function zip($other, $transform) {} } From 9b8df0bf01471eb47300a7cf28de7e49eb45efc0 Mon Sep 17 00:00:00 2001 From: CismonX Date: Thu, 11 Oct 2018 20:23:27 +0800 Subject: [PATCH 2/8] Update stubs for `zip()`. Add stubs for `windowed()`, `zipWithNext()`. --- stubs/Collection.php | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/stubs/Collection.php b/stubs/Collection.php index 3c9daf5..724cb29 100644 --- a/stubs/Collection.php +++ b/stubs/Collection.php @@ -924,15 +924,45 @@ class Collection implements ArrayAccess, Countable */ function values() {} + /** + * Returns a collection of snapshots of the window of the given size sliding along this + * collection with the given step, where each snapshot is an array. + * + * If provided, the transform function will be applied to each snapshot. + * + * @param int $size + * @param int $step[optional] = 1 + * @param bool $partial_windows[optional] = false + * @param callable $transform ($value, $key) -> $new_value + * @return Collection + */ + function windowed($size, $step, $partial_windows, $transform) {} + /** * Returns a collection of pairs built from the elements of this array and other array * with the same index. The returned collection has length of the shortest array. * + * If the transform function is provided, return a collection of the return values of + * the transform function applied to each pair of elements. + * * Both arrays must be packed. * * @param array|Collection $other - * @param callable $transform[optional] ($value, $key) -> $new_value + * @param callable $transform[optional] ($v1, $v2) -> $new_value * @return Collection */ function zip($other, $transform) {} + + /** + * Returns a list of pairs of each two adjacent elements in this collection. + * + * If the transform function is provided, return a collection of the return values of + * the transform function applied to each pair of elements. + * + * The wrapped array must be packed. + * + * @param callable $transform[optional] ($v1, $v2) -> $new_value + * @return Collection + */ + function zipWithNext($transform) {} } From db7b11648106e81938fc971f9f6f8c6554900fa8 Mon Sep 17 00:00:00 2001 From: CismonX Date: Fri, 12 Oct 2018 14:48:02 +0800 Subject: [PATCH 3/8] Add signature for `windowed()`, `zip()`, `zipWithNext()`. --- src/collections_me.c | 15 +++++++++++++++ src/collections_methods.c | 15 +++++++++++++++ src/php_collections_me.h | 3 +++ 3 files changed, 33 insertions(+) diff --git a/src/collections_me.c b/src/collections_me.c index 9bb2430..0e06003 100644 --- a/src/collections_me.c +++ b/src/collections_me.c @@ -136,6 +136,18 @@ ZEND_BEGIN_ARG_INFO(to_collection_arginfo, 0) ZEND_ARG_OBJ_INFO(0, destination, Collection, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO(windowed_arginfo, 0) + ZEND_ARG_TYPE_INFO(0, size, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, step, IS_LONG, 0) + ZEND_ARG_TYPE_INFO(0, partial_windows, _IS_BOOL, 0) + ZEND_ARG_CALLABLE_INFO(0, transform, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(zip_arginfo, 0) + ZEND_ARG_INFO(0, other) + ZEND_ARG_CALLABLE_INFO(0, transform, 0) +ZEND_END_ARG_INFO() + const zend_function_entry collection_methods[] = { PHP_ME(Collection, __construct, NULL, ZEND_ACC_PRIVATE | ZEND_ACC_CTOR) PHP_ME(Collection, addAll, elements_arginfo, ZEND_ACC_PUBLIC) @@ -240,6 +252,9 @@ const zend_function_entry collection_methods[] = { PHP_ME(Collection, toPairs, NULL, ZEND_ACC_PUBLIC) PHP_ME(Collection, union, other_arginfo, ZEND_ACC_PUBLIC) PHP_ME(Collection, values, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Collection, windowed, windowed_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(Collection, zip, zip_arginfo, ZEND_ACC_PUBLIC) + PHP_ME(Collection, zipWithNext, transform_arginfo, ZEND_ACC_PUBLIC) PHP_FE_END }; diff --git a/src/collections_methods.c b/src/collections_methods.c index de9d8a5..7bb6a9e 100644 --- a/src/collections_methods.c +++ b/src/collections_methods.c @@ -3263,6 +3263,21 @@ PHP_METHOD(Collection, values) RETVAL_NEW_COLLECTION(new_collection); } +PHP_METHOD(Collection, windowed) +{ + +} + +PHP_METHOD(Collection, zip) +{ + +} + +PHP_METHOD(Collection, zipWithNext) +{ + +} + PHP_METHOD(Pair, __construct) { zval* first; diff --git a/src/php_collections_me.h b/src/php_collections_me.h index c2344b5..1b32883 100644 --- a/src/php_collections_me.h +++ b/src/php_collections_me.h @@ -112,6 +112,9 @@ PHP_METHOD(Collection, toCollection); PHP_METHOD(Collection, toPairs); PHP_METHOD(Collection, union); PHP_METHOD(Collection, values); +PHP_METHOD(Collection, windowed); +PHP_METHOD(Collection, zip); +PHP_METHOD(Collection, zipWithNext); PHP_METHOD(Pair, __construct); From f419ce8c2a66da9b83c9fd7b88c7161f2d0c6631 Mon Sep 17 00:00:00 2001 From: CismonX Date: Mon, 22 Oct 2018 20:14:33 +0800 Subject: [PATCH 4/8] Implement `windowed()`. --- src/collections_methods.c | 56 ++++++++++++++++++++++++++++++++++++++- stubs/Collection.php | 2 ++ tests/059-windowed.phpt | 30 +++++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 tests/059-windowed.phpt diff --git a/src/collections_methods.c b/src/collections_methods.c index 7bb6a9e..55f30f0 100644 --- a/src/collections_methods.c +++ b/src/collections_methods.c @@ -3265,7 +3265,61 @@ PHP_METHOD(Collection, values) PHP_METHOD(Collection, windowed) { - + zend_long size; + zend_long step = 1; + zend_bool partial_windows = 0; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + ZEND_PARSE_PARAMETERS_START(1, 4) + Z_PARAM_LONG(size) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(step) + Z_PARAM_BOOL(partial_windows) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + zend_array* current = THIS_COLLECTION; + if (size < 1 || step < 1) { + ERR_BAD_SIZE(); + RETURN_NULL(); + } + if (!HT_IS_PACKED(current)) { + ERR_NOT_PACKED(); + RETURN_NULL(); + } + zend_bool has_transform = EX_NUM_ARGS() == 4; + INIT_FCI(&fci, 2); + uint32_t num_elements = zend_hash_num_elements(current); + uint32_t num_ret; + if (partial_windows) { + num_ret = num_elements / step + (num_elements % step ? 1 : 0); + } else { + num_ret = num_elements >= size ? (num_elements - size) / step + 1 : 0; + } + ARRAY_NEW(windowed, num_ret); + Bucket* start = current->arData; + uint32_t idx; + uint32_t pos = 0; + for (idx = 0; idx < num_ret; ++idx, pos += step) { + ARRAY_NEW(snapshot, size); + uint32_t snapshot_idx; + for (snapshot_idx = 0; snapshot_idx < size; ++snapshot_idx) { + if (partial_windows && UNEXPECTED(pos + snapshot_idx >= num_elements)) { + break; + } + Bucket* bucket = start + pos + snapshot_idx; + zend_hash_next_index_insert(snapshot, &bucket->val); + } + if (has_transform) { + ZVAL_ARR(¶ms[0], snapshot); + ZVAL_LONG(¶ms[1], idx); + zend_call_function(&fci, &fcc); + array_release(snapshot); + } else { + ZVAL_ARR(&retval, snapshot); + } + zend_hash_next_index_insert(windowed, &retval); + } + RETVAL_NEW_COLLECTION(windowed); } PHP_METHOD(Collection, zip) diff --git a/stubs/Collection.php b/stubs/Collection.php index 724cb29..4a5c106 100644 --- a/stubs/Collection.php +++ b/stubs/Collection.php @@ -930,6 +930,8 @@ class Collection implements ArrayAccess, Countable * * If provided, the transform function will be applied to each snapshot. * + * The wrapped array must be packed. + * * @param int $size * @param int $step[optional] = 1 * @param bool $partial_windows[optional] = false diff --git a/tests/059-windowed.phpt b/tests/059-windowed.phpt new file mode 100644 index 0000000..717a0c9 --- /dev/null +++ b/tests/059-windowed.phpt @@ -0,0 +1,30 @@ +--TEST-- +Test Collection::windowed(). +--FILE-- +windowed(3, 2, false)->toArray() != $array1) { + echo 'Collection::windowed() failed.', PHP_EOL; +} + +$array1 = [ + ['a', 'b', 'c', 'd', 4], + ['d', 'e', 'f', 'g', 4], + ['g', 'h', 2] +]; +$transform = function ($snapshot) { + $snapshot[] = count($snapshot); + return $snapshot; +}; +if ($collection->windowed(4, 3, true, $transform)->toArray() != $array1) { + echo 'Collection::windowed() failed.', PHP_EOL; +} +?> +--EXPECT-- From dfde69722227f1442aac880d4aaa35acc281339c Mon Sep 17 00:00:00 2001 From: CismonX Date: Mon, 22 Oct 2018 20:25:54 +0800 Subject: [PATCH 5/8] fix bug --- src/collections_methods.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/collections_methods.c b/src/collections_methods.c index 55f30f0..4780299 100644 --- a/src/collections_methods.c +++ b/src/collections_methods.c @@ -3307,6 +3307,7 @@ PHP_METHOD(Collection, windowed) break; } Bucket* bucket = start + pos + snapshot_idx; + Z_TRY_ADDREF(bucket->val); zend_hash_next_index_insert(snapshot, &bucket->val); } if (has_transform) { From e4a0cc2df8b38ed900f38a5bda73ee2920501883 Mon Sep 17 00:00:00 2001 From: CismonX Date: Wed, 24 Oct 2018 13:37:37 +0800 Subject: [PATCH 6/8] update stub --- stubs/Collection.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stubs/Collection.php b/stubs/Collection.php index 4a5c106..1aad14e 100644 --- a/stubs/Collection.php +++ b/stubs/Collection.php @@ -935,7 +935,7 @@ class Collection implements ArrayAccess, Countable * @param int $size * @param int $step[optional] = 1 * @param bool $partial_windows[optional] = false - * @param callable $transform ($value, $key) -> $new_value + * @param callable $transform[optional] ($value, $key) -> $new_value * @return Collection */ function windowed($size, $step, $partial_windows, $transform) {} From cf09a314b2be9c0b2dceea5633d40da62599bddf Mon Sep 17 00:00:00 2001 From: CismonX Date: Thu, 8 Nov 2018 22:49:58 +0800 Subject: [PATCH 7/8] update --- src/collections_methods.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/collections_methods.c b/src/collections_methods.c index 4780299..8091130 100644 --- a/src/collections_methods.c +++ b/src/collections_methods.c @@ -2594,7 +2594,7 @@ PHP_METHOD(Collection, shuffle) Z_TRY_ADDREF_P(val); zend_hash_next_index_insert(shuffled, val); ZEND_HASH_FOREACH_END(); - size_t offset = 0; + uint32_t offset = 0; Bucket* bucket = shuffled->arData; for (; offset < num_elements - 1; ++offset) { zend_long rand_idx = php_mt_rand_range(offset, num_elements - 1); @@ -2613,7 +2613,7 @@ PHP_METHOD(Collection, shuffled) Z_TRY_ADDREF_P(val); zend_hash_next_index_insert(shuffled, val); ZEND_HASH_FOREACH_END(); - size_t offset = 0; + uint32_t offset = 0; Bucket* bucket = shuffled->arData; for (; offset < num_elements - 1; ++offset) { zend_long rand_idx = php_mt_rand_range(offset, num_elements - 1); From 98b01ef3e2368f30229ece80c7bd33cb0436c2d1 Mon Sep 17 00:00:00 2001 From: CismonX Date: Fri, 9 Nov 2018 11:57:37 +0800 Subject: [PATCH 8/8] Implement `zipWithNext()`. --- src/collections_methods.c | 39 +++++++++++++++++++++++++++++++++++- tests/060-zip-with-next.phpt | 29 +++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 tests/060-zip-with-next.phpt diff --git a/src/collections_methods.c b/src/collections_methods.c index 8091130..8a16098 100644 --- a/src/collections_methods.c +++ b/src/collections_methods.c @@ -3330,7 +3330,44 @@ PHP_METHOD(Collection, zip) PHP_METHOD(Collection, zipWithNext) { - + zend_fcall_info fci; + zend_fcall_info_cache fcc; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + zend_array* current = THIS_COLLECTION; + if (!HT_IS_PACKED(current)) { + ERR_NOT_PACKED(); + RETURN_NULL(); + } + zend_bool has_transform = EX_NUM_ARGS() == 1; + INIT_FCI(&fci, 2); + uint32_t num_elements = zend_hash_num_elements(current); + ARRAY_NEW(zipped, num_elements); + if (num_elements > 1) { + Bucket* start = current->arData; + uint32_t idx; + for (idx = 0; idx < num_elements - 1; ++idx) { + Bucket* bucket = start + idx; + if (has_transform) { + ZVAL_COPY_VALUE(¶ms[0], &bucket->val); + ZVAL_COPY_VALUE(¶ms[1], &(bucket + 1)->val); + zend_call_function(&fci, &fcc); + } else { + zend_object* obj = create_pair_obj(); + Z_TRY_ADDREF(bucket->val); + Z_TRY_ADDREF((bucket + 1)->val); + pair_update_first(obj, &bucket->val); + pair_update_second(obj, &(bucket + 1)->val); + zval pair; + ZVAL_OBJ(&pair, obj); + ZVAL_COPY_VALUE(&retval, &pair); + } + zend_hash_next_index_insert(zipped, &retval); + } + } + RETVAL_NEW_COLLECTION(zipped); } PHP_METHOD(Pair, __construct) diff --git a/tests/060-zip-with-next.phpt b/tests/060-zip-with-next.phpt new file mode 100644 index 0000000..665ca35 --- /dev/null +++ b/tests/060-zip-with-next.phpt @@ -0,0 +1,29 @@ +--TEST-- +Test Collection::zipWithNext(). +--FILE-- +zipWithNext()->toArray() != []) { + echo 'Collection::zipWithNext() failed.', PHP_EOL; +} + +$array = ['a', 'b', 'c', 'd']; +$collection = Collection::init($array); +$array1 = [ + new Pair('a', 'b'), + new Pair('b', 'c'), + new Pair('c', 'd') +]; +if ($collection->zipWithNext()->toArray() != $array1) { + echo 'Collection::zipWithNext() failed.', PHP_EOL; +} + +$transform = function ($v1, $v2) { + return $v1.$v2; +}; +$array1 = ['ab', 'bc', 'cd']; +if ($collection->zipWithNext($transform)->toArray() != $array1) { + echo 'Collection::zipWithNext() failed.', PHP_EOL; +} +?> +--EXPECT--