mirror of
https://github.com/php/pecl-numbers-bitset.git
synced 2026-03-23 23:42:23 +01:00
970 lines
24 KiB
C
970 lines
24 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| 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: |
|
|
| http://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 |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Authors: Will Fitch <willfitch@php.net> |
|
|
| Alexander Veremyev <cawa@csa.ru> |
|
|
| Remi Collet <remi@php.net> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "php.h"
|
|
#include "php_ini.h"
|
|
#include "zend_exceptions.h"
|
|
#include "ext/spl/spl_exceptions.h"
|
|
#include "ext/standard/info.h"
|
|
#include "php_bitset.h"
|
|
#include <limits.h>
|
|
|
|
#if PHP_VERSION_ID >= 80000
|
|
#include "bitset_arginfo.h"
|
|
#else
|
|
#include "bitset_legacy_arginfo.h"
|
|
#define RETURN_THROWS() return
|
|
#endif
|
|
|
|
#define BITSET_DEPRECATED_MESSAGE "The bitset_* functions are deprecated and will be removed in 3.0. Please update to the BitSet class API"
|
|
|
|
zend_class_entry *bitset_class_entry = NULL;
|
|
static zend_object_handlers bitset_object_handlers;
|
|
|
|
static php_bitset_object *bitset_get_intern_object(zval *object);
|
|
static php_bitset_object *php_bitset_object_new(zend_class_entry *ce);
|
|
static long bitset_get_highest_value_from_array(zval *arr);
|
|
static void bitset_initialize_object(php_bitset_object *intern, long bits);
|
|
|
|
|
|
/* {{{ zend_module_dep zend_module_entry ZEND_GET_MODULE
|
|
*/
|
|
|
|
zend_module_entry bitset_module_entry = {
|
|
STANDARD_MODULE_HEADER_EX, NULL,
|
|
NULL,
|
|
"bitset",
|
|
NULL,
|
|
PHP_MINIT(bitset),
|
|
PHP_MSHUTDOWN(bitset),
|
|
NULL,
|
|
NULL,
|
|
PHP_MINFO(bitset),
|
|
PHP_BITSET_VERSION,
|
|
STANDARD_MODULE_PROPERTIES
|
|
};
|
|
|
|
#ifdef COMPILE_DL_BITSET
|
|
ZEND_GET_MODULE(bitset)
|
|
#endif
|
|
/* }}} */
|
|
|
|
/* {{{ proto void BitSet::__construct(int value)
|
|
Class constructor */
|
|
PHP_METHOD(BitSet, __construct)
|
|
{
|
|
php_bitset_object *intern = NULL;
|
|
zend_long bits = 0;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &bits) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
|
|
/* Default the bit count to 64 bits */
|
|
if (bits == 0) {
|
|
bits = BITSET_DEFAULT_BITS;
|
|
} else if (bits < 0) {
|
|
/* Bits can't be negative */
|
|
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
|
|
"The total bits to allocate must be 0 or greater");
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
bitset_initialize_object(intern, bits);
|
|
|
|
if (bits % CHAR_BIT) {
|
|
intern->bitset_val[intern->bitset_len - 1] >>= (CHAR_BIT - (bits % CHAR_BIT));
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void BitSet::andOp(BitSet set)
|
|
Performs a logical AND of target bit set with provided object */
|
|
PHP_METHOD(BitSet, andOp)
|
|
{
|
|
php_bitset_object *intern, *param;
|
|
zval *param_id;
|
|
long bitset_len1, bitset_len2, i, to_bits;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", ¶m_id, bitset_class_entry) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
param = bitset_get_intern_object(param_id);
|
|
bitset_len1 = intern->bitset_len;
|
|
bitset_len2 = param->bitset_len;
|
|
to_bits = bitset_len1 > bitset_len2 ? bitset_len2 : bitset_len1;
|
|
|
|
for (i = 0; i < to_bits; i++) {
|
|
intern->bitset_val[i] &= param->bitset_val[i];
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void BitSet::andNotOp(BitSet set)
|
|
Clears all bits in this object whose bit is set in the provided object */
|
|
PHP_METHOD(BitSet, andNotOp)
|
|
{
|
|
php_bitset_object *intern, *param;
|
|
zval *param_id;
|
|
long bitset_len1, bitset_len2, i, to_bits;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", ¶m_id, bitset_class_entry) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
param = bitset_get_intern_object(param_id);
|
|
bitset_len1 = intern->bitset_len * CHAR_BIT;
|
|
bitset_len2 = param->bitset_len * CHAR_BIT;
|
|
to_bits = bitset_len1 > bitset_len2 ? bitset_len2 : bitset_len1;
|
|
|
|
for (i = 0; i < to_bits; i++) {
|
|
/* If the incoming bit is set, clear on this object */
|
|
if (param->bitset_val[i / CHAR_BIT] & (1 << (i % CHAR_BIT))) {
|
|
intern->bitset_val[i / CHAR_BIT] &= ~(1 << (i % CHAR_BIT));
|
|
}
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int BitSet::cardinality(void)
|
|
Returns the number of true bits */
|
|
PHP_METHOD(BitSet, cardinality)
|
|
{
|
|
php_bitset_object *intern;
|
|
long i, total_bits, true_bits = 0;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
|
|
total_bits = intern->bitset_len * CHAR_BIT;
|
|
|
|
for (i = 0; i < total_bits; i++) {
|
|
if (intern->bitset_val[i / CHAR_BIT] & (1 << (i % CHAR_BIT))) {
|
|
true_bits++;
|
|
}
|
|
}
|
|
|
|
RETURN_LONG(true_bits);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void BitSet::clear([int indexOrFromIndex[, int toIndex]])
|
|
Sets all bits to false */
|
|
PHP_METHOD(BitSet, clear)
|
|
{
|
|
php_bitset_object *intern;
|
|
long index_from = -1, index_to = 0, usable_index = 0;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &index_from, &index_to) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
|
|
/* Clear all bits and reset */
|
|
if (index_from == -1 && index_to == 0) {
|
|
memset(intern->bitset_val, 0, intern->bitset_len);
|
|
intern->bitset_val[intern->bitset_len] = '\0';
|
|
} else {
|
|
/* Verify the start index is not greater than total bits */
|
|
if (index_from >= intern->bitset_len * CHAR_BIT) {
|
|
zend_throw_exception_ex(spl_ce_OutOfRangeException, 0,
|
|
"The requested start index is greater than the total number of bits");
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
if (index_to == 0) {
|
|
usable_index = index_from;
|
|
} else {
|
|
usable_index = index_to > intern->bitset_len * CHAR_BIT ? intern->bitset_len * CHAR_BIT : index_to;
|
|
}
|
|
|
|
for (; index_from <= usable_index; index_from++) {
|
|
intern->bitset_val[index_from / CHAR_BIT] &= ~(1 << (index_from % CHAR_BIT));
|
|
}
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool BitSet::get(int index)
|
|
Returns the bool value of the bit at the specified index */
|
|
PHP_METHOD(BitSet, get)
|
|
{
|
|
php_bitset_object *intern;
|
|
zend_long bit;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &bit) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
|
|
/* The bit requested is larger than all bits in this set */
|
|
if (bit >= intern->bitset_len * CHAR_BIT) {
|
|
zend_throw_exception_ex(spl_ce_OutOfRangeException, 0,
|
|
"The specified index parameter exceeds the total number of bits available");
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
if (intern->bitset_val[bit / CHAR_BIT] & (1 << (bit % CHAR_BIT))) {
|
|
RETURN_TRUE;
|
|
} else {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string BitSet::getRawValue(void)
|
|
*/
|
|
PHP_METHOD(BitSet, getRawValue)
|
|
{
|
|
php_bitset_object *intern;
|
|
intern = bitset_get_intern_object(getThis());
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
if (intern->bitset_val) {
|
|
RETURN_STRINGL((char *) intern->bitset_val, intern->bitset_len);
|
|
} else {
|
|
RETURN_EMPTY_STRING();
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string BitSet::fromRawValue(void)
|
|
*/
|
|
PHP_METHOD(BitSet, fromRawValue)
|
|
{
|
|
php_bitset_object *newobj;
|
|
zend_class_entry *ce = bitset_class_entry;
|
|
zend_string *str;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
newobj = php_bitset_object_new(ce);
|
|
|
|
if (str->len == 0) {
|
|
bitset_initialize_object(newobj, BITSET_DEFAULT_BITS);
|
|
return;
|
|
}
|
|
|
|
bitset_initialize_object(newobj, (str->len * CHAR_BIT));
|
|
memcpy(newobj->bitset_val, str->val, str->len);
|
|
|
|
ZVAL_OBJ(return_value, &newobj->zo);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool BitSet::intersects(BitSet set)
|
|
Determines if the provided value has any bits set to true that are also true in this object */
|
|
PHP_METHOD(BitSet, intersects)
|
|
{
|
|
php_bitset_object *a, *b;
|
|
zval *param_id;
|
|
long bitset_len1, bitset_len2, i, to_bits;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", ¶m_id, bitset_class_entry) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
a = bitset_get_intern_object(getThis());
|
|
b = bitset_get_intern_object(param_id);
|
|
bitset_len1 = a->bitset_len;
|
|
bitset_len2 = b->bitset_len;
|
|
|
|
to_bits = bitset_len1 > bitset_len2 ? bitset_len2 : bitset_len1;
|
|
|
|
for (i = 0; i < to_bits; i++) {
|
|
/* If the bits is set in both */
|
|
if (a->bitset_val[i] & b->bitset_val[i]) {
|
|
RETURN_TRUE;
|
|
}
|
|
}
|
|
RETURN_FALSE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto bool BitSet::isEmpty(void)
|
|
Determines if this value contains no bits */
|
|
PHP_METHOD(BitSet, isEmpty)
|
|
{
|
|
php_bitset_object *intern;
|
|
long total_bits, i;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
total_bits = intern->bitset_len;
|
|
|
|
/* Loop through all bits and determine if there is a true bit. */
|
|
for (i = 0; i < total_bits; i++) {
|
|
if (intern->bitset_val[i]) {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
|
|
RETURN_TRUE;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int BitSet::length(void)
|
|
Returns the highest set bit plus one
|
|
*/
|
|
PHP_METHOD(BitSet, length)
|
|
{
|
|
php_bitset_object *intern;
|
|
long highest_bit = -1, i;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
i = intern->bitset_len * CHAR_BIT;
|
|
|
|
while (i > 0) {
|
|
i--;
|
|
|
|
if (intern->bitset_val[i / CHAR_BIT] & (1 << (i % CHAR_BIT))) {
|
|
highest_bit = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
RETURN_LONG(highest_bit + 1);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int BitSet::nextClearBit(int index)
|
|
Returns the index of the next bit after the provided index that is set to false */
|
|
PHP_METHOD(BitSet, nextClearBit)
|
|
{
|
|
php_bitset_object *intern;
|
|
zend_long start_bit = 0;
|
|
long bit_diff = 0, next_bit = 0;
|
|
short found = 0;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &start_bit) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
bit_diff = intern->bitset_len * CHAR_BIT;
|
|
|
|
if (start_bit >= bit_diff - 1) {
|
|
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
|
|
"There are no bits larger than the index provided");
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
start_bit++;
|
|
|
|
while (start_bit <= bit_diff) {
|
|
if (!(intern->bitset_val[start_bit / CHAR_BIT] & (1 << (start_bit % CHAR_BIT)))) {
|
|
next_bit = start_bit;
|
|
found = 1;
|
|
break;
|
|
}
|
|
|
|
start_bit++;
|
|
}
|
|
|
|
if (found) {
|
|
RETURN_LONG(next_bit);
|
|
} else {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int BitSet::nextSetBit(int index)
|
|
Returns the index of the next bit after the provided index that is set to true */
|
|
PHP_METHOD(BitSet, nextSetBit)
|
|
{
|
|
php_bitset_object *intern;
|
|
zend_long start_bit = 0;
|
|
long bit_diff = 0, next_bit = 0;
|
|
short found = 0;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &start_bit) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
bit_diff = intern->bitset_len * CHAR_BIT;
|
|
|
|
if (start_bit >= bit_diff - 1) {
|
|
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
|
|
"There are no bits larger than the index provided");
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
start_bit++;
|
|
|
|
while (start_bit <= bit_diff) {
|
|
if (intern->bitset_val[start_bit / CHAR_BIT] & (1 << (start_bit % CHAR_BIT))) {
|
|
next_bit = start_bit;
|
|
found = 1;
|
|
break;
|
|
}
|
|
|
|
start_bit++;
|
|
}
|
|
|
|
if (found) {
|
|
RETURN_LONG(next_bit);
|
|
} else {
|
|
RETURN_FALSE;
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void BitSet::orOp(BitSet set)
|
|
Performs a logical OR of this object with the provided argument object */
|
|
PHP_METHOD(BitSet, orOp)
|
|
{
|
|
php_bitset_object *intern, *param;
|
|
zval *param_id;
|
|
long bitset_len1, bitset_len2, i, to_bits;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", ¶m_id, bitset_class_entry) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
param = bitset_get_intern_object(param_id);
|
|
bitset_len1 = intern->bitset_len;
|
|
bitset_len2 = param->bitset_len;
|
|
to_bits = bitset_len1 > bitset_len2 ? bitset_len2 : bitset_len1;
|
|
|
|
for (i = 0; i < to_bits; i++) {
|
|
intern->bitset_val[i] |= param->bitset_val[i];
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int BitSet::previousClearBit(int index)
|
|
Returns the index of the previous bit before the provided index that is set to false */
|
|
PHP_METHOD(BitSet, previousClearBit)
|
|
{
|
|
php_bitset_object *intern;
|
|
long start_bit = 0, bit_diff = 0;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &start_bit) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
if (start_bit < 1) {
|
|
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
|
|
"There are no bits smaller than the index provided");
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
bit_diff = intern->bitset_len * CHAR_BIT;
|
|
|
|
if (start_bit > bit_diff) {
|
|
zend_throw_exception_ex(spl_ce_OutOfRangeException, 0,
|
|
"The specified index parameter exceeds the total number of bits available");
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
start_bit--;
|
|
|
|
while (start_bit >= 0) {
|
|
if (!(intern->bitset_val[start_bit / CHAR_BIT] & (1 << (start_bit % CHAR_BIT)))) {
|
|
break;
|
|
}
|
|
|
|
start_bit--;
|
|
}
|
|
|
|
if (start_bit < 0) {
|
|
RETURN_FALSE;
|
|
} else {
|
|
RETURN_LONG(start_bit);
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int BitSet::previousSetBit(int index)
|
|
Returns the index of the previous bit before the provided index that is set to true */
|
|
PHP_METHOD(BitSet, previousSetBit)
|
|
{
|
|
php_bitset_object *intern;
|
|
zend_long start_bit = 0;
|
|
long bit_diff = 0;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &start_bit) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
if (start_bit < 1) {
|
|
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
|
|
"There are no bits smaller than the index provided");
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
bit_diff = intern->bitset_len * CHAR_BIT;
|
|
|
|
if (start_bit > bit_diff) {
|
|
zend_throw_exception_ex(spl_ce_OutOfRangeException, 0,
|
|
"The specified index parameter exceeds the total number of bits available");
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
start_bit--;
|
|
|
|
while (start_bit >= 0) {
|
|
if (intern->bitset_val[start_bit / CHAR_BIT] & (1 << (start_bit % CHAR_BIT))) {
|
|
break;
|
|
}
|
|
|
|
start_bit--;
|
|
}
|
|
|
|
if (start_bit < 0) {
|
|
RETURN_FALSE;
|
|
} else {
|
|
RETURN_LONG(start_bit);
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void BitSet::set([int indexOrFromIndex[, toIndex]])
|
|
Sets the bits from the specified index or range to true
|
|
*/
|
|
PHP_METHOD(BitSet, set)
|
|
{
|
|
php_bitset_object *intern;
|
|
zend_long index_from = -1, index_to = 0;
|
|
long usable_index = 0;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &index_from, &index_to) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
|
|
/* Set all bits */
|
|
if (index_from == -1 && index_to == 0) {
|
|
for (; usable_index < intern->bitset_len * CHAR_BIT; usable_index++)
|
|
{
|
|
intern->bitset_val[usable_index / CHAR_BIT] |= (1 << (usable_index % CHAR_BIT));
|
|
}
|
|
|
|
intern->bitset_val[intern->bitset_len] = '\0';
|
|
} else {
|
|
/* Verify the start index is not greater than total bits */
|
|
if (index_from > (intern->bitset_len * CHAR_BIT - 1)) {
|
|
zend_throw_exception_ex(spl_ce_OutOfRangeException, 0,
|
|
"The requested start index is greater than the total number of bits");
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
if (index_to == 0) {
|
|
usable_index = index_from;
|
|
} else {
|
|
usable_index = index_to > intern->bitset_len * CHAR_BIT ? intern->bitset_len * CHAR_BIT : index_to;
|
|
}
|
|
|
|
for (; index_from <= usable_index; index_from++) {
|
|
intern->bitset_val[index_from / CHAR_BIT] |= (1 << (index_from % CHAR_BIT));
|
|
}
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto int BitSet::size(void)
|
|
Returns the number of bits of space in use */
|
|
PHP_METHOD(BitSet, size)
|
|
{
|
|
php_bitset_object *intern;
|
|
intern = bitset_get_intern_object(getThis());
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
RETURN_LONG(intern->bitset_len * CHAR_BIT);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto BitSet BitSet::fromString(string)
|
|
* Returns a new instance of BitSet based on the provided string
|
|
*/
|
|
PHP_METHOD(BitSet, fromString)
|
|
{
|
|
php_bitset_object *newobj;
|
|
zend_class_entry *ce = bitset_class_entry;
|
|
zend_string *str = NULL;
|
|
int i;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &str) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
newobj = php_bitset_object_new(ce);
|
|
|
|
if (str->len > 0) {
|
|
bitset_initialize_object(newobj, str->len);
|
|
} else {
|
|
bitset_initialize_object(newobj, BITSET_DEFAULT_BITS);
|
|
}
|
|
|
|
for (i = 0; i < str->len; i++) {
|
|
/* If the char is explicitly '1', set it as 1. Otherwise, it's 0 */
|
|
if (str->val[i] == '1') {
|
|
newobj->bitset_val[i / CHAR_BIT] |= (1 << (i % CHAR_BIT));
|
|
}
|
|
}
|
|
|
|
ZVAL_OBJ(return_value, &newobj->zo);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto BitSet BitSet::fromArray(array inputArray)
|
|
* Returns a new Bitset instance based on the input array. All positive integers
|
|
* within the array values are considered positions of set bits
|
|
*/
|
|
PHP_METHOD(BitSet, fromArray)
|
|
{
|
|
php_bitset_object *newobj;
|
|
zval *bit_array;
|
|
zval *entry;
|
|
zend_class_entry *ce = bitset_class_entry;
|
|
long array_len, highest_value, entry_actual;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &bit_array) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
array_len = zend_hash_num_elements(Z_ARRVAL_P(bit_array));
|
|
newobj = php_bitset_object_new(ce);
|
|
|
|
if (array_len == 0) {
|
|
bitset_initialize_object(newobj, BITSET_DEFAULT_BITS);
|
|
return;
|
|
}
|
|
|
|
highest_value = bitset_get_highest_value_from_array(bit_array);
|
|
bitset_initialize_object(newobj, highest_value+1);
|
|
|
|
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(bit_array), entry) {
|
|
zend_long entry_long = zval_get_long(entry);
|
|
|
|
if (entry_long >= 0) {
|
|
entry_actual = entry_long / CHAR_BIT;
|
|
newobj->bitset_val[entry_actual] |= (1 << (entry_long % CHAR_BIT));
|
|
}
|
|
} ZEND_HASH_FOREACH_END();
|
|
|
|
ZVAL_OBJ(return_value, &newobj->zo);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto array BitSet::toArray(void)
|
|
Returns the on bits as an array */
|
|
PHP_METHOD(BitSet, toArray)
|
|
{
|
|
php_bitset_object *intern;
|
|
long i, total_bits;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
array_init(return_value);
|
|
total_bits = intern->bitset_len * CHAR_BIT;
|
|
|
|
for (i = 0; i < total_bits; i++) {
|
|
if (intern->bitset_val[i / CHAR_BIT] & (1 << (i % CHAR_BIT))) {
|
|
add_next_index_long(return_value, i);
|
|
}
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto BitSet BitSet::fromInteger(integer value)
|
|
* Returns a new Bitset instance based on the input value
|
|
*/
|
|
PHP_METHOD(BitSet, fromInteger)
|
|
{
|
|
php_bitset_object *newobj;
|
|
zend_class_entry *ce = bitset_class_entry;
|
|
zend_long value;
|
|
int i;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &value) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
newobj = php_bitset_object_new(ce);
|
|
|
|
bitset_initialize_object(newobj, sizeof(value) * CHAR_BIT);
|
|
|
|
for (i = 0; i < sizeof(value) ; i++) {
|
|
newobj->bitset_val[i] = value & ((1 << CHAR_BIT) -1);
|
|
value >>= CHAR_BIT;
|
|
}
|
|
|
|
ZVAL_OBJ(return_value, &newobj->zo);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto integer BitSet::toInteger(void)
|
|
Returns the on bits as an integer */
|
|
PHP_METHOD(BitSet, toInteger)
|
|
{
|
|
php_bitset_object *intern;
|
|
int i;
|
|
zend_long value = 0;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
if (intern->bitset_len > sizeof(value)) {
|
|
zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0,
|
|
"The total bits doesn't fit in an integer");
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
for (i = 0; i < intern->bitset_len; i++) {
|
|
value |= intern->bitset_val[i] << (i * CHAR_BIT);
|
|
}
|
|
RETURN_LONG(value);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto void BitSet::xorOp(BitSet set)
|
|
Performs an XOR operation against the current object bit set with the specified argument */
|
|
PHP_METHOD(BitSet, xorOp)
|
|
{
|
|
php_bitset_object *intern, *param;
|
|
zval *param_id;
|
|
long bitset_len1, bitset_len2, i, to_bits;
|
|
|
|
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", ¶m_id, bitset_class_entry) == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
param = bitset_get_intern_object(param_id);
|
|
bitset_len1 = intern->bitset_len;
|
|
bitset_len2 = param->bitset_len;
|
|
to_bits = bitset_len1 > bitset_len2 ? bitset_len2 : bitset_len1;
|
|
|
|
for (i = 0; i < to_bits; i++) {
|
|
intern->bitset_val[i] ^= param->bitset_val[i];
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ proto string BitSet::__toString(void)
|
|
Returns a human-readable string representation of the bit set */
|
|
PHP_METHOD(BitSet, __toString)
|
|
{
|
|
php_bitset_object *intern = NULL;
|
|
zend_string *retval;
|
|
char *internval = NULL;
|
|
long len, i;
|
|
|
|
if (zend_parse_parameters_none() == FAILURE) {
|
|
RETURN_THROWS();
|
|
}
|
|
|
|
intern = bitset_get_intern_object(getThis());
|
|
|
|
if (intern->bitset_len == 0) {
|
|
RETURN_EMPTY_STRING();
|
|
} else {
|
|
len = intern->bitset_len * CHAR_BIT;
|
|
retval = zend_string_alloc(len, 0);
|
|
internval = ZSTR_VAL(retval);
|
|
internval[len] = '\0';
|
|
|
|
for (i = 0; i < len; i++) {
|
|
internval[i] = ((intern->bitset_val[i / CHAR_BIT] >> (i % CHAR_BIT)) & 1) ? '1' : '0';
|
|
}
|
|
|
|
RETURN_STR(retval);
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ php_bitset_object *bitset_get_intern_object
|
|
*/
|
|
static php_bitset_object *bitset_get_intern_object(zval *object)
|
|
{
|
|
zend_object *obj = Z_OBJ_P(object);
|
|
return php_bitset_fetch_object(obj);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ long bitset_get_highest_value_from_array
|
|
*/
|
|
static long bitset_get_highest_value_from_array(zval *arr)
|
|
{
|
|
zval *entry;
|
|
long highest_value = 0;
|
|
|
|
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(arr), entry) {
|
|
zend_long entry_long = zval_get_long(entry);
|
|
|
|
if (entry_long > highest_value) {
|
|
highest_value = entry_long;
|
|
}
|
|
} ZEND_HASH_FOREACH_END();
|
|
|
|
return highest_value;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ php_bitset_object php_bitset_object_new
|
|
*/
|
|
static php_bitset_object *php_bitset_object_new(zend_class_entry *ce)
|
|
{
|
|
php_bitset_object *intern;
|
|
intern = ecalloc(1, sizeof(php_bitset_object) + zend_object_properties_size(ce));
|
|
intern->bitset_val = 0;
|
|
|
|
zend_object_std_init(&intern->zo, ce);
|
|
object_properties_init(&intern->zo, ce);
|
|
|
|
intern->zo.handlers = &bitset_object_handlers;
|
|
return intern;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ void bitset_initialize_object
|
|
*/
|
|
static void bitset_initialize_object(php_bitset_object *intern, long bits)
|
|
{
|
|
intern->bitset_len = (bits + CHAR_BIT - 1) / CHAR_BIT;
|
|
intern->bitset_val = (unsigned char *) emalloc(intern->bitset_len + 1);
|
|
memset(intern->bitset_val, 0, intern->bitset_len);
|
|
intern->bitset_val[intern->bitset_len] = '\0';
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ bitset_destroy_object
|
|
*/
|
|
static void bitset_destroy_object(zend_object *obj)
|
|
{
|
|
zend_objects_destroy_object(obj);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ void bitset_free_object
|
|
*/
|
|
static void bitset_free_object(zend_object *obj)
|
|
{
|
|
php_bitset_object *bitset_obj;
|
|
bitset_obj = php_bitset_fetch_object(obj);
|
|
|
|
if (bitset_obj->bitset_val) {
|
|
efree(bitset_obj->bitset_val);
|
|
}
|
|
|
|
zend_object_std_dtor(obj);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ zend_object bitset_create_object
|
|
*/
|
|
static zend_object *bitset_create_object(zend_class_entry *ce)
|
|
{
|
|
php_bitset_object *intern;
|
|
intern = ecalloc(1, sizeof(php_bitset_object) + zend_object_properties_size(ce));
|
|
intern->bitset_val = 0;
|
|
|
|
zend_object_std_init(&intern->zo, ce);
|
|
object_properties_init(&intern->zo, ce);
|
|
|
|
intern->zo.handlers = &bitset_object_handlers;
|
|
return &intern->zo;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ PHP_MINFO_FUNCTION */
|
|
PHP_MINFO_FUNCTION(bitset)
|
|
{
|
|
php_info_print_table_start();
|
|
php_info_print_table_header(2, "BitSet Support", "enabled");
|
|
php_info_print_table_row(2, "BitSet Version", PHP_BITSET_VERSION);
|
|
php_info_print_table_row(2, "64-bit Integer Support", sizeof(unsigned long long) == 8 ? "yes" : "no");
|
|
php_info_print_table_end();
|
|
DISPLAY_INI_ENTRIES();
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ PHP_MINIT_FUNCTION */
|
|
PHP_MINIT_FUNCTION(bitset)
|
|
{
|
|
bitset_class_entry = register_class_BitSet();
|
|
|
|
bitset_class_entry->create_object = bitset_create_object;
|
|
|
|
memcpy(&bitset_object_handlers, zend_get_std_object_handlers(), sizeof(bitset_object_handlers));
|
|
bitset_object_handlers.free_obj = bitset_free_object;
|
|
bitset_object_handlers.dtor_obj = bitset_destroy_object;
|
|
bitset_object_handlers.offset = XtOffsetOf(php_bitset_object, zo);
|
|
|
|
return SUCCESS;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ PHP_MSHUTDOWN_FUNCTION */
|
|
PHP_MSHUTDOWN_FUNCTION(bitset)
|
|
{
|
|
bitset_class_entry = NULL;
|
|
return SUCCESS;
|
|
}
|
|
/* }}} */
|
|
|
|
/*
|
|
* Local variables:
|
|
* tab-width: 4
|
|
* c-basic-offset: 4
|
|
* End:
|
|
* vim600: noet sw=4 ts=4 fdm=marker
|
|
* vim<600: noet sw=4 ts=4
|
|
*/
|