Skip to content

RFC: Add final class Collections\Deque to PHP #7500

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 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,344 changes: 1,344 additions & 0 deletions ext/collections/collections_deque.c

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions ext/collections/collections_deque.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| [email protected] so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Tyson Andre <[email protected]> |
+----------------------------------------------------------------------+
*/

#ifndef COLLECTIONS_DEQUE_H
#define COLLECTIONS_DEQUE_H

extern zend_class_entry *collections_ce_Deque;

PHP_MINIT_FUNCTION(collections_deque);

#endif /* COLLECTIONS_DEQUE_H */
112 changes: 112 additions & 0 deletions ext/collections/collections_deque.stub.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

/** @generate-class-entries */

namespace Collections;

/**
* A double-ended queue (Typically abbreviated as Deque, pronounced "deck", like "cheque")
* represented internally as a circular buffer.
*
* This has much lower memory usage than SplDoublyLinkedList or its subclasses (SplStack, SplStack),
* and operations are significantly faster than SplDoublyLinkedList.
*
* See https://en.wikipedia.org/wiki/Double-ended_queue
*
* This supports amortized constant time pushing and popping onto the front or back of the Deque.
*
* Naming is based on https://www.php.net/spldoublylinkedlist
* and on array_push/pop/pushFront/popFront and array_key_first.
*/
final class Deque implements \IteratorAggregate, \Countable, \JsonSerializable, \ArrayAccess
{
/** Construct the Deque from the values of the Traversable/array, ignoring keys */
public function __construct(iterable $iterator = []) {}
/**
* Returns an iterator that accounts for calls to popFront/pushFront tracking the position of the start of the Deque.
* Calls to popFront/pushFront will do the following:
* - Increase/Decrease the value returned by the iterator's key()
* by the number of elements added/removed to/from the start of the Deque.
* (`$deque[$iteratorKey] === $iteratorValue` at the time the key and value are returned).
* - Repeated calls to popFront will cause valid() to return false if the iterator's
* position ends up before the start of the Deque at the time iteration resumes.
* - They will not cause the remaining values to be iterated over more than once or skipped.
*/
public function getIterator(): \InternalIterator {}
/** Returns the number of elements in the Deque. */
public function count(): int {}
/** Returns true if there are 0 elements in the Deque. */
public function isEmpty(): bool {}
/** Removes all elements from the Deque. */
public function clear(): void {}

/** @implementation-alias Collections\Deque::toArray */
public function __serialize(): array {}
public function __unserialize(array $data): void {}
/** Construct the Deque from the values of the array, ignoring keys */
public static function __set_state(array $array): Deque {}

/** Appends value(s) to the end of the Deque, like array_push. */
public function push(mixed ...$values): void {}
/** Prepends value(s) to the start of the Deque, like array_pushFront. */
public function pushFront(mixed ...$values): void {}
/**
* Pops a value from the end of the Deque.
* @throws \UnderflowException if the Deque is empty
*/
public function pop(): mixed {}
/**
* Pops a value from the start of the Deque.
* @throws \UnderflowException if the Deque is empty
*/
public function popFront(): mixed {}

/**
* Peeks at the value at the start of the Deque.
* @throws \UnderflowException if the Deque is empty
*/
public function first(): mixed {}
/**
* Peeks at the value at the end of the Deque.
* @throws \UnderflowException if the Deque is empty
*/
public function last(): mixed {}

/**
* Returns a list of the elements from the start to the end.
*/
public function toArray(): array {}

/**
* Insert 0 or more values at the given offset of the Deque.
* @throws \OutOfBoundsException if the value of $offset is not within the bounds of this Deque.
*/
public function insert(int $offset, mixed ...$values): void {}
// Must be mixed for compatibility with ArrayAccess
/**
* Returns the value at offset (int)$offset (relative to the start of the Deque)
* @throws \OutOfBoundsException if the value of (int)$offset is not within the bounds of this Deque.
*/
public function offsetGet(mixed $offset): mixed {}
/**
* Returns true if `0 <= (int)$offset && (int)$offset < $this->count()
* AND the value at that offset is non-null.
*/
public function offsetExists(mixed $offset): bool {}
/**
* Sets the value at offset $offset (relative to the start of the Deque) to $value
* @throws \OutOfBoundsException if the value of (int)$offset is not within the bounds of this Deque.
*/
public function offsetSet(mixed $offset, mixed $value): void {}
/**
* Removes the value at (int)$offset from the deque.
* @throws \OutOfBoundsException if the value of (int)$offset is not within the bounds of this Deque.
*/
public function offsetUnset(mixed $offset): void {}

/**
* This is JSON serialized as a JSON array with elements from the start to the end.
* @implementation-alias Collections\Deque::toArray
*/
public function jsonSerialize(): array {}
}
129 changes: 129 additions & 0 deletions ext/collections/collections_deque_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 58 additions & 0 deletions ext/collections/collections_internaliterator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| [email protected] so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Tyson Andre <[email protected]> |
+----------------------------------------------------------------------+
*/

#ifndef COLLECTIONS_INTERNALITERATOR_H
#define COLLECTIONS_INTERNALITERATOR_H

typedef struct _collections_intrusive_dllist_node {
struct _collections_intrusive_dllist_node *prev;
struct _collections_intrusive_dllist_node *next;
} collections_intrusive_dllist_node;

typedef struct _collections_intrusive_dllist {
struct _collections_intrusive_dllist_node *first;
} collections_intrusive_dllist;

static zend_always_inline void collections_intrusive_dllist_prepend(collections_intrusive_dllist *list, collections_intrusive_dllist_node *node) {
collections_intrusive_dllist_node *first = list->first;
ZEND_ASSERT(node != first);
node->next = first;
node->prev = NULL;
list->first = node;

if (first) {
ZEND_ASSERT(first->prev == NULL);
first->prev = node;
}
}

static zend_always_inline void collections_intrusive_dllist_remove(collections_intrusive_dllist *list, const collections_intrusive_dllist_node *node) {
collections_intrusive_dllist_node *next = node->next;
collections_intrusive_dllist_node *prev = node->prev;
ZEND_ASSERT(node != next);
ZEND_ASSERT(node != prev);
ZEND_ASSERT(next != prev || next == NULL);
if (next) {
next->prev = prev;
}
if (list->first == node) {
list->first = next;
ZEND_ASSERT(prev == NULL);
} else if (prev) {
prev->next = next;
}
}
#endif
41 changes: 41 additions & 0 deletions ext/collections/collections_util.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| [email protected] so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: Tyson Andre <[email protected]> |
+----------------------------------------------------------------------+
*/

#include "collections_util.h"

/* Override get_properties_for and use the default implementation of get_properties. See https://github.com/php/php-src/issues/9697#issuecomment-1273613175 */
HashTable* collections_noop_empty_array_get_properties_for(zend_object *obj, zend_prop_purpose purpose) {
(void)obj;
(void)purpose;
return NULL;
}

HashTable* collections_noop_get_gc(zend_object *obj, zval **table, int *n) {
/* Zend/zend_gc.c does not initialize table or n. So we need to set n to 0 at minimum. */
*n = 0;
(void) table;
(void) obj;
/* Nothing needs to be garbage collected */
return NULL;
}

HashTable *collections_internaliterator_get_gc(zend_object_iterator *iter, zval **table, int *n)
{
*table = &iter->data;
*n = 1;
return NULL;
}

Loading