Implement `copyOfRange()` and `count()`

This commit is contained in:
CismonX 2018-03-30 19:06:39 +08:00
parent a7a39c889a
commit f7b924801b
8 changed files with 96 additions and 30 deletions

View File

@ -10,6 +10,7 @@
#include <php.h>
#include <php_ini.h>
#include <zend_interfaces.h>
#include <ext/standard/info.h>
#include "php_collections.h"
@ -18,6 +19,8 @@ zend_string* collection_property_name;
zend_string* pair_first_name;
zend_string* pair_second_name;
zend_object_handlers* collection_handlers;
zend_class_entry* collections_collection_ce;
zend_class_entry* collections_pair_ce;
@ -26,7 +29,17 @@ PHP_MINIT_FUNCTION(collections)
zend_class_entry collection_ce;
INIT_CLASS_ENTRY_EX(collection_ce, "Collection", sizeof "Collection" - 1, collection_methods);
collections_collection_ce = zend_register_internal_class(&collection_ce);
zend_class_implements(collections_collection_ce,
#if PHP_VERSION_ID < 70200
1,
#else
2, zend_ce_countable,
#endif
zend_ce_arrayaccess);
zend_declare_property_null(collections_collection_ce, "_", sizeof "_" - 1, ZEND_ACC_PRIVATE);
collection_handlers = (zend_object_handlers*)emalloc(sizeof(zend_object_handlers));
memcpy(collection_handlers, &std_object_handlers, sizeof(zend_object_handlers));
collection_handlers->count_elements = count_collection;
zend_class_entry pair_ce;
INIT_CLASS_ENTRY_EX(pair_ce, "Pair", sizeof "Pair" - 1, pair_methods);
collections_pair_ce = zend_register_internal_class(&pair_ce);

View File

@ -35,11 +35,6 @@ ZEND_BEGIN_ARG_INFO(elements_arginfo, 0)
ZEND_ARG_INFO(0, elements)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(from_to_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, from_index, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, to_index, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(initial_operation_arginfo, 0)
ZEND_ARG_INFO(0, initial)
ZEND_ARG_CALLABLE_INFO(0, operation, 0)
@ -95,6 +90,11 @@ ZEND_BEGIN_ARG_INFO(copy_of_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, new_size, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(copy_of_range_arginfo, 0)
ZEND_ARG_TYPE_INFO(0, from_idx, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, num_elements, IS_LONG, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(fill_arginfo, 0)
ZEND_ARG_INFO(0, element)
ZEND_ARG_TYPE_INFO(0, from_index, IS_LONG, 0)
@ -135,7 +135,7 @@ const zend_function_entry collection_methods[] = {
PHP_ME(Collection, containsKey, key_arginfo, ZEND_ACC_PUBLIC)
PHP_ME(Collection, containsValue, element_arginfo, ZEND_ACC_PUBLIC)
PHP_ME(Collection, copyOf, copy_of_arginfo, ZEND_ACC_PUBLIC)
PHP_ME(Collection, copyOfRange, from_to_arginfo, ZEND_ACC_PUBLIC)
PHP_ME(Collection, copyOfRange, copy_of_range_arginfo, ZEND_ACC_PUBLIC)
PHP_ME(Collection, count, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Collection, distinct, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Collection, distinctBy, selector_arginfo, ZEND_ACC_PUBLIC)
@ -210,7 +210,6 @@ const zend_function_entry collection_methods[] = {
PHP_ME(Collection, shuffle, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Collection, single, predicate_arginfo, ZEND_ACC_PUBLIC)
PHP_ME(Collection, slice, keys_arginfo, ZEND_ACC_PUBLIC)
PHP_ME(Collection, sliceRange, from_to_arginfo, ZEND_ACC_PUBLIC)
PHP_ME(Collection, sort, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Collection, sortBy, selector_arginfo, ZEND_ACC_PUBLIC)
PHP_ME(Collection, sortByDescending, selector_arginfo, ZEND_ACC_PUBLIC)

View File

@ -9,14 +9,16 @@
#include "php_collections.h"
#include "php_collections_me.h"
#define NEW_OBJ(name, ce) \
#define NEW_OBJ(name, ce, object_handlers) \
zend_object* (name) = (zend_object*)ecalloc(1, sizeof(zend_object) + \
zend_object_properties_size(ce)); \
zend_object_std_init(name, ce); \
object_properties_init(name, ce); \
(name)->handlers = &std_object_handlers;
#define NEW_COLLECTION_OBJ(name) NEW_OBJ(name, collections_collection_ce)
#define NEW_PAIR_OBJ(name) NEW_OBJ(name, collections_pair_ce)
(name)->handlers = object_handlers;
#define NEW_COLLECTION_OBJ(name) \
NEW_OBJ(name, collections_collection_ce, collection_handlers)
#define NEW_PAIR_OBJ(name) \
NEW_OBJ(name, collections_pair_ce, &std_object_handlers)
#define IS_COLLECTION_P(zval) \
Z_TYPE_P(zval) == IS_OBJECT && Z_OBJCE_P(zval) == collections_collection_ce
@ -58,12 +60,12 @@
else \
equal_check_func = fast_equal_check_function;
#define PHP_COLLECTIONS_ERROR(type, msg) php_error_docref(NULL, type, msg)
#define ERR_BAD_ARGUMENT_TYPE() PHP_COLLECTIONS_ERROR(E_WARNING, "Bad argument type")
#define ERR_BAD_KEY_TYPE() PHP_COLLECTIONS_ERROR(E_WARNING, "Key must be integer or string")
#define ERR_BAD_CALLBACK_RETVAL() PHP_COLLECTIONS_ERROR(E_WARNING, "Bad callback return value")
#define ERR_BAD_SIZE() PHP_COLLECTIONS_ERROR(E_WARNING, "Size must be non-negative")
#define ERR_BAD_INDEX() PHP_COLLECTIONS_ERROR(E_WARNING, "Index must be non-negative")
#define ERR_NOT_ARITHMETIC() PHP_COLLECTIONS_ERROR(E_WARNING, "Elements should be int or double")
#define ELEMENTS_VALIDATE(elements) \
@ -101,6 +103,14 @@
return; \
}
int count_collection(zval* obj, zend_long* count)
{
zval rv;
zval* current = COLLECTION_FETCH(obj);
*count = zend_hash_num_elements(Z_ARRVAL_P(current));
return SUCCESS;
}
PHP_METHOD(Collection, __construct) {}
PHP_METHOD(Collection, addAll)
@ -387,12 +397,38 @@ PHP_METHOD(Collection, copyOf)
PHP_METHOD(Collection, copyOfRange)
{
zend_long from_idx, num_elements;
ZEND_PARSE_PARAMETERS_START(2, 2)
Z_PARAM_LONG(from_idx)
Z_PARAM_LONG(num_elements)
ZEND_PARSE_PARAMETERS_END();
if (from_idx < 0) {
ERR_BAD_INDEX();
RETURN_NULL();
}
if (num_elements < 0) {
ERR_BAD_SIZE();
RETURN_NULL();
}
zval rv;
zval* current = COLLECTION_FETCH_EX();
ARRAY_NEW(new_collection, num_elements);
Bucket* bucket = Z_ARRVAL_P(current)->arData;
Bucket* end = bucket + Z_ARRVAL_P(current)->nNumUsed;
for (bucket += from_idx; num_elements > 0 && bucket < end; ++bucket, --num_elements) {
if (bucket->key)
zend_hash_add(new_collection, bucket->key, &bucket->val);
else
zend_hash_next_index_insert(new_collection, &bucket->val);
}
RETVAL_NEW_COLLECTION(new_collection);
}
PHP_METHOD(Collection, count)
{
zend_long count;
count_collection(getThis(), &count);
RETVAL_LONG(count);
}
PHP_METHOD(Collection, distinct)
@ -783,11 +819,6 @@ PHP_METHOD(Collection, slice)
}
PHP_METHOD(Collection, sliceRange)
{
}
PHP_METHOD(Collection, sort)
{

View File

@ -27,6 +27,10 @@ extern zend_string* pair_second_name;
extern PHP_COLLECTIONS_API zend_class_entry* collections_collection_ce;
extern PHP_COLLECTIONS_API zend_class_entry* collections_pair_ce;
extern zend_object_handlers* collection_handlers;
int count_collection(zval* obj, zend_long* count);
extern const zend_function_entry collection_methods[];
extern const zend_function_entry pair_methods[];

View File

@ -96,7 +96,6 @@ PHP_METHOD(Collection, reversed);
PHP_METHOD(Collection, shuffle);
PHP_METHOD(Collection, single);
PHP_METHOD(Collection, slice);
PHP_METHOD(Collection, sliceRange);
PHP_METHOD(Collection, sort);
PHP_METHOD(Collection, sortBy);
PHP_METHOD(Collection, sortByDescending);

View File

@ -117,10 +117,10 @@ class Collection implements ArrayAccess, Countable
* Returns new collection which is a copy of range of original collection.
*
* @param int $from_index
* @param int $to_index
* @param int $num_elements
* @return Collection
*/
function copyOfRange($from_index, $to_index) {}
function copyOfRange($from_index, $num_elements) {}
/**
* Returns the number of elements in this collection.
@ -746,14 +746,6 @@ class Collection implements ArrayAccess, Countable
*/
function slice($keys) {}
/**
* Returns a collection containing elements at indices in the specified indices range.
*
* @param int $from_index
* @param int $to_index[optional]
*/
function sliceRange($from_index, $to_index) {}
/**
* Sorts the collection in-place according to the natural order of its elements.
*

View File

@ -0,0 +1,16 @@
--TEST--
Test Collection::copyOfRange();
--FILE--
<?php
// An ordinary array.
$array = [3, 7, 6, 9, 2];
// An associative array, however, Collection::copyOfRange() still works,
// and string keys will be preserved.
// Note that zend_array is ordered and random access has constant time complexity.
$array1 = ['a' => 'b', 'c', 'd' => 'e'];
$array2 = Collection::init($array)->copyOfRange(2, 4)->toArray();
$array3 = Collection::init($array1)->copyOfRange(1, 2)->toArray();
if ($array2 != array_slice($array, 2, 4) || $array3 != array_slice($array1, 1, 2))
echo 'Collection::copyOfRange() failed.', PHP_EOL;
?>
--EXPECT--

12
tests/012-countable.phpt Normal file
View File

@ -0,0 +1,12 @@
--TEST--
Test Collection::count() and countable interface.
--FILE--
<?php
$array = [1, 2, 3, 4, 5, 6];
$collection = Collection::init($array);
if ($collection->count() != count($array))
echo 'Collection::count() failed.', PHP_EOL;
if (count($collection) != count($array))
echo 'Test for countable interface failed.', PHP_EOL;
?>
--EXPECT--