@@ -71,10 +71,19 @@ zend_class_entry *spl_ce_Deque;
71
71
static const zval empty_entry_list [1 ];
72
72
73
73
typedef struct _spl_deque_entries {
74
+ /* The number of elements in the Deque. */
74
75
size_t size ;
75
76
/* One less than a power of two (the capacity) */
76
77
size_t mask ;
78
+ /* The offset of the start of the deque in the circular buffer. */
77
79
size_t offset ;
80
+ /*
81
+ * This is a counter which increases when adding elements to the front and decreases when removing elements from the front.
82
+ * This is used so that iteration works as expected when the front position is modified during foreach.
83
+ *
84
+ * This is a uint64_t to work consistently and avoid easily overflowing 4 billion on 32-bit builds.
85
+ */
86
+ uint64_t iteration_offset ;
78
87
/** This is a circular buffer with an offset, size, and capacity(mask + 1) */
79
88
zval * circular_buffer ;
80
89
} spl_deque_entries ;
@@ -116,7 +125,8 @@ typedef struct _spl_deque {
116
125
/* Used by InternalIterator returned by Deque->getIterator() */
117
126
typedef struct _spl_deque_it {
118
127
zend_object_iterator intern ;
119
- zend_long current ;
128
+ /* This starts at iteration_offset and increases by one when next() is called. */
129
+ uint64_t current ;
120
130
} spl_deque_it ;
121
131
122
132
static zend_always_inline void spl_deque_push_back (spl_deque * intern , zval * value );
@@ -153,29 +163,43 @@ static bool spl_deque_entries_uninitialized(spl_deque_entries *array)
153
163
return array -> circular_buffer == NULL ;
154
164
}
155
165
156
- /* Based n zend_hash_check_size. Finds the next largest power of 2. */
166
+ /* Based on zend_hash_check_size which supports 32-bit integers . Finds the next largest power of 2. */
157
167
static inline size_t spl_deque_next_pow2_capacity (size_t nSize ) {
158
168
if (nSize < DEQUE_MIN_CAPACITY ) {
159
169
return DEQUE_MIN_CAPACITY ;
160
170
}
171
+ /* Note that for values such as 63 or 31 of the form ((2^n) - 1),
172
+ * subtracting and xor are the same things for numbers in the range of 0 to the max. */
161
173
#ifdef ZEND_WIN32
162
174
unsigned long index ;
175
+ #if SIZEOF_SIZE_T > 4
176
+ if (BitScanReverse64 (& index , nSize - 1 )) {
177
+ return 0x2u << ((63 - index ) ^ 0x3f );
178
+ }
179
+ #else
163
180
if (BitScanReverse (& index , nSize - 1 )) {
164
181
return 0x2u << ((31 - index ) ^ 0x1f );
165
- } else {
166
- /* nSize is ensured to be in the valid range, fall back to it
167
- rather than using an undefined bis scan result. */
168
- return nSize ;
169
182
}
183
+ #endif
184
+ /* nSize is ensured to be in the valid range, fall back to it
185
+ * rather than using an undefined bit scan result. */
186
+ return nSize ;
170
187
#elif (defined(__GNUC__ ) || __has_builtin (__builtin_clz )) && defined(PHP_HAVE_BUILTIN_CLZ )
188
+ #if SIZEOF_SIZE_T > SIZEOF_INT
189
+ return 0x2u << (__builtin_clzl (nSize - 1 ) ^ (sizeof (long ) * 8 - 1 ));
190
+ #else
171
191
return 0x2u << (__builtin_clz (nSize - 1 ) ^ 0x1f );
192
+ #endif
172
193
#else
173
194
nSize -= 1 ;
174
195
nSize |= (nSize >> 1 );
175
196
nSize |= (nSize >> 2 );
176
197
nSize |= (nSize >> 4 );
177
198
nSize |= (nSize >> 8 );
178
199
nSize |= (nSize >> 16 );
200
+ #if SIZEOF_SIZE_T > 4
201
+ nSize |= (nSize >> 32 );
202
+ #endif
179
203
return nSize + 1 ;
180
204
#endif
181
205
}
@@ -184,6 +208,7 @@ static void spl_deque_entries_init_from_array(spl_deque_entries *array, zend_arr
184
208
{
185
209
zend_long size = zend_hash_num_elements (values );
186
210
array -> offset = 0 ;
211
+ array -> iteration_offset = 0 ;
187
212
array -> size = 0 ; /* reset size and capacity in case emalloc() fails */
188
213
array -> mask = 0 ;
189
214
if (size > 0 ) {
@@ -212,6 +237,7 @@ static void spl_deque_entries_init_from_traversable(spl_deque_entries *array, ze
212
237
zend_long size = 0 , capacity = 0 ;
213
238
array -> size = 0 ;
214
239
array -> offset = 0 ;
240
+ array -> iteration_offset = 0 ;
215
241
array -> circular_buffer = NULL ;
216
242
zval * circular_buffer = NULL ;
217
243
zval tmp_obj ;
@@ -274,6 +300,7 @@ static void spl_deque_entries_copy_ctor(spl_deque_entries *to, const spl_deque_e
274
300
to -> size = 0 ; /* reset size in case emalloc() fails */
275
301
to -> mask = 0 ;
276
302
to -> offset = 0 ;
303
+ to -> iteration_offset = 0 ;
277
304
if (!size ) {
278
305
to -> circular_buffer = (zval * )empty_entry_list ;
279
306
return ;
@@ -321,6 +348,7 @@ static void spl_deque_entries_dtor(spl_deque_entries *array)
321
348
array -> offset = 0 ;
322
349
array -> size = 0 ;
323
350
array -> mask = 0 ;
351
+ /* Changing iteration_offset doesn't matter */
324
352
do {
325
353
if (p == end ) {
326
354
p = circular_buffer ;
@@ -504,6 +532,7 @@ PHP_METHOD(Deque, __construct)
504
532
505
533
if (iterable == NULL ) {
506
534
intern -> array .offset = 0 ;
535
+ intern -> array .iteration_offset = 0 ;
507
536
intern -> array .size = 0 ;
508
537
intern -> array .mask = 0 ;
509
538
intern -> array .circular_buffer = (zval * )empty_entry_list ;
@@ -535,15 +564,17 @@ static void spl_deque_it_dtor(zend_object_iterator *iter)
535
564
536
565
static void spl_deque_it_rewind (zend_object_iterator * iter )
537
566
{
538
- ((spl_deque_it * )iter )-> current = 0 ;
567
+ const spl_deque * object = Z_DEQUE_P (& iter -> data );
568
+ ((spl_deque_it * )iter )-> current = object -> array .iteration_offset ;
539
569
}
540
570
541
571
static int spl_deque_it_valid (zend_object_iterator * iter )
542
572
{
543
573
const spl_deque_it * iterator = (spl_deque_it * )iter ;
544
574
const spl_deque * object = Z_DEQUE_P (& iter -> data );
575
+ const size_t offset = iterator -> current - object -> array .iteration_offset ;
545
576
546
- if (iterator -> current >= 0 && ( zend_ulong ) iterator -> current < object -> array .size ) {
577
+ if (offset < object -> array .size ) {
547
578
return SUCCESS ;
548
579
}
549
580
@@ -566,8 +597,9 @@ static zval *spl_deque_it_get_current_data(zend_object_iterator *iter)
566
597
{
567
598
const spl_deque_it * iterator = (spl_deque_it * )iter ;
568
599
spl_deque * object = Z_DEQUE_P (& iter -> data );
600
+ const uint64_t offset = iterator -> current - object -> array .iteration_offset ;
569
601
570
- zval * data = spl_deque_read_offset_helper (object , iterator -> current );
602
+ zval * data = spl_deque_read_offset_helper (object , offset );
571
603
572
604
if (UNEXPECTED (data == NULL )) {
573
605
return & EG (uninitialized_zval );
@@ -580,8 +612,8 @@ static void spl_deque_it_get_current_key(zend_object_iterator *iter, zval *key)
580
612
{
581
613
const spl_deque_it * iterator = (spl_deque_it * )iter ;
582
614
const spl_deque * object = Z_DEQUE_P (& iter -> data );
615
+ const uint64_t offset = iterator -> current - object -> array .iteration_offset ;
583
616
584
- size_t offset = iterator -> current ;
585
617
if (offset >= object -> array .size ) {
586
618
ZVAL_NULL (key );
587
619
} else {
@@ -1020,6 +1052,7 @@ PHP_METHOD(Deque, unshift)
1020
1052
}
1021
1053
1022
1054
spl_deque * intern = Z_DEQUE_P (ZEND_THIS );
1055
+ intern -> array .iteration_offset -= argc ; /* The front moved backwards by the number of elements */
1023
1056
size_t old_size = intern -> array .size ;
1024
1057
const size_t new_size = old_size + argc ;
1025
1058
size_t mask = intern -> array .mask ;
@@ -1105,6 +1138,7 @@ PHP_METHOD(Deque, shift)
1105
1138
}
1106
1139
1107
1140
intern -> array .size -- ;
1141
+ intern -> array .iteration_offset ++ ; /* The front moved forward */
1108
1142
const size_t old_offset = intern -> array .offset ;
1109
1143
const size_t old_mask = intern -> array .mask ;
1110
1144
intern -> array .offset ++ ;
0 commit comments