Skip to content

Commit 3612f23

Browse files
committed
Make Deque::push(...$values) and unshift(...$values) variadic
1 parent 7c879c2 commit 3612f23

File tree

4 files changed

+87
-22
lines changed

4 files changed

+87
-22
lines changed

ext/spl/spl_deque.c

Lines changed: 62 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -970,38 +970,85 @@ static void spl_deque_shrink_capacity(spl_deque_entries *array, size_t new_capac
970970

971971
PHP_METHOD(Deque, push)
972972
{
973+
const zval *args;
974+
uint32_t argc;
973975
zval *value;
974976

975-
ZEND_PARSE_PARAMETERS_START(1, 1)
976-
Z_PARAM_ZVAL(value)
977+
ZEND_PARSE_PARAMETERS_START(0, -1)
978+
Z_PARAM_VARIADIC('+', args, argc)
977979
ZEND_PARSE_PARAMETERS_END();
978980

979-
spl_deque_push_back(Z_DEQUE_P(ZEND_THIS), value);
981+
if (UNEXPECTED(argc == 0)) {
982+
return;
983+
}
984+
985+
spl_deque *intern = Z_DEQUE_P(ZEND_THIS);
986+
size_t old_size = intern->array.size;
987+
const size_t new_size = old_size + argc;
988+
size_t mask = intern->array.mask;
989+
const size_t old_capacity = mask ? mask + 1 : 0;
990+
991+
if (new_size > old_capacity) {
992+
const size_t new_capacity = spl_deque_next_pow2_capacity(new_size);
993+
spl_deque_raise_capacity(&intern->array, new_capacity);
994+
mask = intern->array.mask;
995+
}
996+
zval *const circular_buffer = intern->array.circular_buffer;
997+
const size_t old_offset = intern->array.offset;
998+
999+
while (1) {
1000+
zval *dest = &circular_buffer[(old_offset + old_size) & mask];
1001+
ZVAL_COPY(dest, args);
1002+
if (++old_size >= new_size) {
1003+
break;
1004+
}
1005+
args++;
1006+
}
1007+
intern->array.size = new_size;
9801008
}
9811009

9821010
PHP_METHOD(Deque, unshift)
9831011
{
1012+
const zval *args;
1013+
uint32_t argc;
9841014
zval *value;
9851015

986-
ZEND_PARSE_PARAMETERS_START(1, 1)
987-
Z_PARAM_ZVAL(value)
1016+
ZEND_PARSE_PARAMETERS_START(0, -1)
1017+
Z_PARAM_VARIADIC('+', args, argc)
9881018
ZEND_PARSE_PARAMETERS_END();
9891019

1020+
if (UNEXPECTED(argc == 0)) {
1021+
return;
1022+
}
1023+
9901024
spl_deque *intern = Z_DEQUE_P(ZEND_THIS);
991-
const size_t old_size = intern->array.size;
992-
const size_t old_mask = intern->array.mask;
993-
const size_t old_capacity = old_mask ? old_mask + 1 : 0;
1025+
size_t old_size = intern->array.size;
1026+
const size_t new_size = old_size + argc;
1027+
size_t mask = intern->array.mask;
1028+
const size_t old_capacity = mask ? mask + 1 : 0;
9941029

995-
if (old_size >= old_capacity) {
996-
ZEND_ASSERT(old_size == old_capacity);
997-
const size_t new_capacity = old_capacity > 0 ? old_capacity * 2 : 4;
1030+
if (new_size > old_capacity) {
1031+
const size_t new_capacity = spl_deque_next_pow2_capacity(new_size);
9981032
spl_deque_raise_capacity(&intern->array, new_capacity);
1033+
mask = intern->array.mask;
9991034
}
1000-
intern->array.offset = (intern->array.offset - 1) & intern->array.mask;
1001-
intern->array.size++;
1035+
size_t offset = intern->array.offset;
1036+
zval *const circular_buffer = intern->array.circular_buffer;
1037+
1038+
do {
1039+
offset = (offset - 1) & mask;
1040+
zval *dest = &circular_buffer[offset];
1041+
ZVAL_COPY(dest, args);
1042+
if (--argc == 0) {
1043+
break;
1044+
}
1045+
args++;
1046+
} while (1);
1047+
1048+
intern->array.offset = offset;
1049+
intern->array.size = new_size;
1050+
10021051
DEBUG_ASSERT_CONSISTENT_DEQUE(&intern->array);
1003-
zval *dest = &intern->array.circular_buffer[intern->array.offset];
1004-
ZVAL_COPY(dest, value);
10051052
}
10061053

10071054
static zend_always_inline void spl_deque_try_shrink_capacity(spl_deque *intern, size_t old_size)

ext/spl/spl_deque.stub.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ public function __unserialize(array $data): void {}
4444
/** Construct the Deque from the values of the array, ignoring keys */
4545
public static function __set_state(array $array): Deque {}
4646

47-
/** Appends a value to the end of the Deque. */
48-
public function push(mixed $value): void {}
49-
/** Prepends a value to the start of the Deque. */
50-
public function unshift(mixed $value): void {}
47+
/** Appends value(s) to the end of the Deque. */
48+
public function push(mixed ...$values): void {}
49+
/** Prepends value(s) to the start of the Deque. */
50+
public function unshift(mixed ...$values): void {}
5151
/**
5252
* Pops a value from the end of the Deque.
5353
* @throws UnderflowException if the Deque is empty

ext/spl/spl_deque_arginfo.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* This is a generated file, edit the .stub.php file instead.
2-
* Stub hash: d8d7e5ffff1a572ea16142a05b4db08a9898e5b8 */
2+
* Stub hash: 49a35ccd63900ec4e1bcb992357d1d3a211f999e */
33

44
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Deque___construct, 0, 0, 0)
55
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, iterator, IS_ITERABLE, 0, "[]")
@@ -28,8 +28,8 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_Deque___set_state, 0, 1, De
2828
ZEND_ARG_TYPE_INFO(0, array, IS_ARRAY, 0)
2929
ZEND_END_ARG_INFO()
3030

31-
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Deque_push, 0, 1, IS_VOID, 0)
32-
ZEND_ARG_TYPE_INFO(0, value, IS_MIXED, 0)
31+
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Deque_push, 0, 0, IS_VOID, 0)
32+
ZEND_ARG_VARIADIC_TYPE_INFO(0, values, IS_MIXED, 0)
3333
ZEND_END_ARG_INFO()
3434

3535
#define arginfo_class_Deque_unshift arginfo_class_Deque_push
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
--TEST--
2+
Deque constructed from array
3+
--FILE--
4+
<?php
5+
// discards keys
6+
$it = new Deque();
7+
// no-op to support any length of variadic arguments
8+
$it->push();
9+
printf("it=%s count=%d\n", json_encode($it), $it->count());
10+
$it->push(...range(0, 19));
11+
printf("it=%s count=%d\n", json_encode($it), $it->count());
12+
$it->unshift(...range(0, 19));
13+
printf("it=%s count=%d\n", json_encode($it), $it->count());
14+
?>
15+
--EXPECT--
16+
it=[] count=0
17+
it=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19] count=20
18+
it=[19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19] count=40

0 commit comments

Comments
 (0)