diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/arithmetic.h | 286 | ||||
-rw-r--r-- | src/includes.h | 2 | ||||
-rw-r--r-- | src/value.cpp | 304 |
3 files changed, 520 insertions, 72 deletions
diff --git a/src/arithmetic.h b/src/arithmetic.h new file mode 100644 index 0000000..ae690bc --- /dev/null +++ b/src/arithmetic.h @@ -0,0 +1,286 @@ +/** + * Arithmethic.h + * + * Helper class that takes care of arithmetic operations on PHP variables + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2013 Copernica BV + */ + +/** + * Set up namespace + */ +namespace Php { + +/** + * Class definition + */ +template < template<typename T> class F> +class Arithmetic +{ +public: + /** + * Constructor + * @param value The original object + */ + Arithmetic(Value *value) : _value(value) {} + + /** + * Destructor + */ + virtual ~Arithmetic() {} + + /** + * Apply a number, and return a new value object after running the arithmetic function + * @param value + * @return Value + */ + Value apply(const Value &value) + { + // is this a type a floating point type? + if (value.isFloat()) return apply(value.floatValue()); + + // we are going to treat it as a numeric (non floating) type + return apply(value.numericValue()); + } + + /** + * Apply a number, and return a new value object after running the arithmetic function + * @param value + * @return Value + */ + Value apply(int16_t value) + { + // check if the current object is a floating point number + if (_value->isFloat()) return Value(F<double>()(_value->floatValue(), value)); + + // apply to natural numbers + return Value(F<long>()(_value->numericValue(), value)); + } + + /** + * Apply a number, and return a new value object after running the arithmetic function + * @param value + * @return Value + */ + Value apply(int32_t value) + { + // check if the current object is a floating point number + if (_value->isFloat()) return Value(F<double>()(_value->floatValue(), value)); + + // apply to natural numbers + return Value(F<long>()(_value->numericValue(), value)); + } + + /** + * Apply a number, and return a new value object after running the arithmetic function + * @param value + * @return Value + */ + Value apply(int64_t value) + { + // check if the current object is a floating point number + if (_value->isFloat()) return Value(F<double>()(_value->floatValue(), value)); + + // apply to natural numbers + return Value(F<long>()(_value->numericValue(), value)); + } + + /** + * Apply a boolean (treat is as 0 or 1), and return a new value object after running the arithmetic function + * @param value + * @return Value + */ + Value apply(bool value) + { + // check if the current object is a floating point number + if (_value->isFloat()) return Value(F<double>()(_value->floatValue(), value?1:0)); + + // apply to natural numbers + return Value(F<long>()(_value->numericValue(), value?1:0)); + } + + /** + * Apply a character (value between '0' and '9'), and return a new value object after running the arithmetic function + * @param value + * @return Value + */ + Value apply(char value) + { + // convert to an integer + int v = value < '0' || value > '9' ? 0 : value - '0'; + + // check if the current object is a floating point number + if (_value->isFloat()) return Value(F<double>()(_value->floatValue(), v)); + + // apply to natural numbers + return Value(F<long>()(_value->numericValue(), v)); + } + + /** + * Apply a string (representing a number), and return a new value object after running the arithmetic function + * @param value + * @return Value + */ + Value apply(const std::string &value) + { + // convert string to integer + return apply(atoi(value.c_str())); + } + + /** + * Apply a string (representing a number), and return a new value object after running the arithmetic function + * @param value + * @return Value + */ + Value apply(const char *value) + { + // convert string to integer + return apply(atoi(value)); + } + + /** + * Apply a string (representing a number), and return a new value object after running the arithmetic function + * @param value + * @return Value + */ + Value apply(double value) + { + return Value(F<double>()(_value->floatValue(), value)); + } + + /** + * Assign a different value object, applying the arithmetic operation + * @param value + * @return Value + */ + Value &assign(const Value &value) + { + // is this a type a floating point type? + if (value.isFloat()) return assign(value.floatValue()); + + // we are going to treat it as a numeric (non floating) type + return assign(value.numericValue()); + } + + /** + * Assign a 16bit number, applying the arithmetic operation + * @param value + * @return Value + */ + Value &assign(int16_t value) + { + // is the current object a floating point type? + if (_value->isFloat()) return _value->operator=(F<double>()(_value->floatValue(), value)); + + // do a numeric operation + return _value->operator=(F<long>()(_value->numericValue(), value)); + } + + /** + * Assign 32bit integer, applying the arithmetic operation + * @param value + * @return Value + */ + Value &assign(int32_t value) + { + // is the current object a floating point type? + if (_value->isFloat()) return _value->operator=(F<double>()(_value->floatValue(), value)); + + // do a numeric operation + return _value->operator=(F<long>()(_value->numericValue(), value)); + } + + /** + * Assign 64bit integer, applying the arithmetic operation + * @param value + * @return Value + */ + Value &assign(int64_t value) + { + // is the current object a floating point type? + if (_value->isFloat()) return _value->operator=(F<double>()(_value->floatValue(), value)); + + // do a numeric operation + return _value->operator=(F<int64_t>()(_value->numericValue(), value)); + } + + /** + * Assign 64bit integer - which is treated as 1 or 0 - applying the arithmetic operation + * @param value + * @return Value + */ + Value &assign(bool value) + { + // is the current object a floating point type? + if (_value->isFloat()) return _value->operator=(F<double>()(_value->floatValue(), value?1:0)); + + // do a numeric operation + return _value->operator=(F<int64_t>()(_value->numericValue(), value?1:0)); + } + + /** + * Assign a single character - which is treated as an int, and applying the arithmetic function + * @param value + * @return Value + */ + Value &assign(char value) + { + // convert to an integer + int v = value < '0' || value > '9' ? 0 : value - '0'; + + // is the current object a floating point type? + if (_value->isFloat()) return _value->operator=(F<double>()(_value->floatValue(), v)); + + // do a numeric operation + return _value->operator=(F<int64_t>()(_value->numericValue(), v)); + } + + /** + * Assign a a string - treating it as an integer, and applying the arithmetic function + * @param value + * @return Value + */ + Value &assign(const std::string &value) + { + // assign integer + return assign(atoi(value.c_str())); + } + + /** + * Assign a string - treating it as an integer, and applying the arithmetic function + * @param value + * @return Value + */ + Value &assign(const char *value) + { + // assign integer + return assign(atoi(value)); + } + + /** + * Assign a double, applying the arithmetic operation + * @param value + * @return Value + */ + Value &assign(double value) + { + // do float operation + return _value->operator=(F<double>()(_value->floatValue(), value)); + } + +private: + /** + * Pointer to the original value object + * @var Value + */ + Value *_value; + + +}; + +/** + * End of namespace + */ +} + diff --git a/src/includes.h b/src/includes.h index 0ea9138..4effa90 100644 --- a/src/includes.h +++ b/src/includes.h @@ -69,3 +69,5 @@ #include "stringmember.h" #include "doublemember.h" #include "methodmember.h" +#include "arithmetic.h" + diff --git a/src/value.cpp b/src/value.cpp index 0f6951b..a299f4d 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -43,7 +43,7 @@ Value::Value() /** * Constructor for null ptr - */ +*/ Value::Value(std::nullptr_t value) { // create a null zval @@ -55,7 +55,18 @@ Value::Value(std::nullptr_t value) * Constructor based on integer value * @param value */ -Value::Value(int value) +Value::Value(int16_t value) +{ + // create an integer zval + MAKE_STD_ZVAL(_val); + ZVAL_LONG(_val, value); +} + +/** + * Constructor based on integer value + * @param value + */ +Value::Value(int32_t value) { // create an integer zval MAKE_STD_ZVAL(_val); @@ -66,7 +77,7 @@ Value::Value(int value) * Constructor based on long value * @param value */ -Value::Value(long value) +Value::Value(int64_t value) { // create an integer zval MAKE_STD_ZVAL(_val); @@ -164,14 +175,29 @@ Value::Value(struct _zval_struct *val, bool ref) */ Value::Value(const Value &that) { - // just copy the zval - _val = that._val; - - // and we have one more reference + // how many references does the other object has? + if (Z_REFCOUNT_P(that._val) > 1 && !Z_ISREF_P(that._val)) + { + // there are already multiple variables linked to this value, and it + // is not a reference. this implies that we can not turn this variable + // into a reference, otherwise strange things could happen, we're going + // to create a new zval + ALLOC_ZVAL(_val); + INIT_PZVAL_COPY(_val, that._val); + zval_copy_ctor(_val); + } + else + { + // simply use the same zval + _val = that._val; + } + + // the other object only has one variable using it, or it is already + // a variable by reference, we can safely add one more reference to it + // and make it a variable by reference if it was not already a ref Z_ADDREF_P(_val); - // two value objects are both sharing the same zval, we turn the zval into a reference - // @todo this is not necessarily correct + // make reference Z_SET_ISREF_P(_val); } @@ -207,11 +233,11 @@ Value::~Value() } /** - * Assignment operator + * Move operator * @param value * @return Value */ -Value &Value::operator=(const Value &value) +Value &Value::operator=(Value &&value) { // skip self assignment if (this == &value) return *this; @@ -219,6 +245,8 @@ Value &Value::operator=(const Value &value) // is the object a reference? if (Z_ISREF_P(_val)) { + // @todo difference if the other object is a reference or not? + // the current object is a reference, this means that we should // keep the zval object, and copy the other value into it, get // the current refcount @@ -229,11 +257,33 @@ Value &Value::operator=(const Value &value) // make the copy *_val = *value._val; - zval_copy_ctor(_val); - // restore refcount and reference setting + // restore reference and refcount setting Z_SET_ISREF_TO_P(_val, true); Z_SET_REFCOUNT_P(_val, refcount); + + // how many references did the old variable have? + if (Z_REFCOUNT_P(value._val) > 1) + { + // the other object already had multiple references, this + // implies that many other PHP variables are also referring + // to it, and we still need to store its contents, with one + // reference less + Z_DELREF_P(value._val); + + // and we need to run the copy constructor on the current + // value, because we're making a deep copy + zval_copy_ctor(_val); + } + else + { + // the last and only reference to the other object was + // removed, we no longer need it + FREE_ZVAL(value._val); + + // the other object is no longer valid + value._val = nullptr; + } } else { @@ -241,23 +291,23 @@ Value &Value::operator=(const Value &value) // and only destruct if there are no other references left) zval_ptr_dtor(&_val); - // just copy the zval, and the refcounter + // just copy the zval completely _val = value._val; - // and we have one more reference - Z_ADDREF_P(_val); + // the other object is no longer valid + value._val = nullptr; } - + // update the object return *this; } /** - * Move operator + * Assignment operator * @param value * @return Value */ -Value &Value::operator=(Value &&value) +Value &Value::operator=(const Value &value) { // skip self assignment if (this == &value) return *this; @@ -265,9 +315,6 @@ Value &Value::operator=(Value &&value) // is the object a reference? if (Z_ISREF_P(_val)) { - // @todo difference if the other object is a reference or not? - - // the current object is a reference, this means that we should // keep the zval object, and copy the other value into it, get // the current refcount @@ -278,33 +325,11 @@ Value &Value::operator=(Value &&value) // make the copy *_val = *value._val; + zval_copy_ctor(_val); - // restore reference and refcount setting + // restore refcount and reference setting Z_SET_ISREF_TO_P(_val, true); Z_SET_REFCOUNT_P(_val, refcount); - - // how many references did the old variable have? - if (Z_ISREF_P(value._val) > 1) - { - // the other object already had multiple references, this - // implies that many other PHP variables are also referring - // to it, and we still need to store its contents, with one - // reference less - Z_DELREF_P(value._val); - - // and we need to run the copy constructor on the current - // value, because we're making a deep copy - zval_copy_ctor(_val); - } - else - { - // the last and only reference to the other object was - // removed, we no longer need it - FREE_ZVAL(value._val); - - // the other object is no longer valid - value._val = nullptr; - } } else { @@ -312,12 +337,32 @@ Value &Value::operator=(Value &&value) // and only destruct if there are no other references left) zval_ptr_dtor(&_val); - // just copy the zval completely + // just copy the zval, and the refcounter _val = value._val; - // the other object is no longer valid - value._val = nullptr; + // and we have one more reference + Z_ADDREF_P(_val); } + + // update the object + return *this; +} + +/** + * Assignment operator + * @param value + * @return Value + */ +Value &Value::operator=(int16_t value) +{ + // if this is not a reference variable, we should detach it to implement copy on write + SEPARATE_ZVAL_IF_NOT_REF(&_val); + + // deallocate current zval (without cleaning the zval structure) + zval_dtor(_val); + + // set new value + ZVAL_LONG(_val, value); // update the object return *this; @@ -328,7 +373,7 @@ Value &Value::operator=(Value &&value) * @param value * @return Value */ -Value &Value::operator=(int value) +Value &Value::operator=(int32_t value) { // if this is not a reference variable, we should detach it to implement copy on write SEPARATE_ZVAL_IF_NOT_REF(&_val); @@ -348,7 +393,7 @@ Value &Value::operator=(int value) * @param value * @return Value */ -Value &Value::operator=(long value) +Value &Value::operator=(int64_t value) { // if this is not a reference variable, we should detach it to implement copy on write SEPARATE_ZVAL_IF_NOT_REF(&_val); @@ -464,6 +509,127 @@ Value &Value::operator=(double value) } /** + * Add a value to the object + * @param value + * @return Value + */ +Value &Value::operator+=(const Value &value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(int16_t value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(int32_t value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(int64_t value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(bool value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(char value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(const std::string &value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(const char *value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(double value) { return Arithmetic<std::plus>(this).assign(value); } + +/** + * Subtract a value from the object + * @param value + * @return Value + */ +Value &Value::operator-=(const Value &value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(int16_t value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(int32_t value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(int64_t value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(bool value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(char value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(const std::string &value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(const char *value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(double value) { return Arithmetic<std::minus>(this).assign(value); } + +/** + * Multiply the object with a certain value + * @param value + * @return Value + */ +Value &Value::operator*=(const Value &value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(int16_t value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(int32_t value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(int64_t value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(bool value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(char value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(const std::string &value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(const char *value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(double value) { return Arithmetic<std::multiplies>(this).assign(value); } + +/** + * Divide the object with a certain value + * @param value + * @return Value + */ +Value &Value::operator/=(const Value &value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(int16_t value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(int32_t value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(int64_t value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(bool value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(char value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(const std::string &value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(const char *value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(double value) { return Arithmetic<std::divides>(this).assign(value); } + +/** + * Assignment operator + * @param value + * @return Value + */ +Value Value::operator+(const Value &value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(int16_t value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(int32_t value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(int64_t value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(bool value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(char value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(const std::string &value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(const char *value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(double value) { return Arithmetic<std::plus>(this).apply(value); } + +/** + * Subtraction operator + * @param value + * @return Value + */ +Value Value::operator-(const Value &value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(int16_t value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(int32_t value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(int64_t value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(bool value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(char value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(const std::string &value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(const char *value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(double value) { return Arithmetic<std::minus>(this).apply(value); } + +/** + * Multiplication operator + * @param value + * @return Value + */ +Value Value::operator*(const Value &value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(int16_t value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(int32_t value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(int64_t value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(bool value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(char value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(const std::string &value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(const char *value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(double value) { return Arithmetic<std::multiplies>(this).apply(value); } + +/** + * Division operator + * @param value + * @return Value + */ +Value Value::operator/(const Value &value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(int16_t value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(int32_t value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(int64_t value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(bool value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(char value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(const std::string &value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(const char *value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(double value) { return Arithmetic<std::divides>(this).apply(value); } + + +/** * The type of object * @return Type */ @@ -488,8 +654,8 @@ Value &Value::setType(Type type) // run the conversion switch (type) { case nullType: convert_to_null(_val); break; - case longType: convert_to_long(_val); break; - case decimalType: convert_to_double(_val); break; + case numericType: convert_to_long(_val); break; + case floatType: convert_to_double(_val); break; case boolType: convert_to_boolean(_val); break; case arrayType: convert_to_array(_val); break; case objectType: convert_to_object(_val); break; @@ -540,13 +706,15 @@ Value Value::clone(Type type) const * Retrieve the value as integer * @return long */ -long Value::longValue() const +long Value::numericValue() const { // already a long? - if (isLong()) return Z_LVAL_P(_val); + if (isNumeric()) return Z_LVAL_P(_val); + + std::cout << "clone to numeric" << std::endl; // make a clone - return clone(longType).longValue(); + return clone(numericType).numericValue(); } /** @@ -592,13 +760,13 @@ const char *Value::rawValue() const * Retrieve the value as decimal * @return double */ -double Value::decimalValue() const +double Value::floatValue() const { // already a double - if (isDecimal()) return Z_DVAL_P(_val); + if (isFloat()) return Z_DVAL_P(_val); // make a clone - return clone(decimalType).decimalValue(); + return clone(floatType).floatValue(); } /** @@ -617,7 +785,7 @@ int Value::size() const Value copy(*this); // convert the copy to a string - copy.setType(decimalType); + copy.setType(stringType); // return the string size return copy.size(); @@ -807,18 +975,10 @@ HashMember<std::string> Value::operator[](const char *key) return HashMember<std::string>(this, key); } -/** - * Array access operator - * This can be used for adding a record to the array - * Be aware: if the 'this' object is not already an array, it will be converted into one! - * @param key - * @return Value - */ -//Value Value::operator[]() -//{ -// -// -//} +int Value::refcount() +{ + return Z_REFCOUNT_P(_val); +} /** * End of namespace |