From 5160cd44798ab39c50a19df6f849355c8425d20b Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 7 Dec 2021 12:41:49 +0100 Subject: [PATCH] improve decimal support --- ion.c | 53 +++++++++------ ion.stub.php | 18 ++--- ion_arginfo.h | 94 +++++++++++++++----------- ion_private.h | 77 ++++++++++++++------- tests/Decimal.phpt | 32 +++++++++ tests/Decimal/Context.phpt | 22 ++++++ tests/Decimal/Context/__construct.phpt | 34 ++++++++++ tests/Decimal/__construct.phpt | 28 ++++++++ tests/Decimal/__toString.phpt | 25 +++++++ tests/Decimal/equals.phpt | 23 +++++++ tests/Decimal/isInt.phpt | 26 +++++++ tests/Decimal/toInt.phpt | 34 ++++++++++ tests/Decimal/toString.phpt | 34 ++++++++++ 13 files changed, 409 insertions(+), 91 deletions(-) create mode 100644 tests/Decimal.phpt create mode 100644 tests/Decimal/Context.phpt create mode 100644 tests/Decimal/Context/__construct.phpt create mode 100644 tests/Decimal/__construct.phpt create mode 100644 tests/Decimal/__toString.phpt create mode 100644 tests/Decimal/equals.phpt create mode 100644 tests/Decimal/isInt.phpt create mode 100644 tests/Decimal/toInt.phpt create mode 100644 tests/Decimal/toString.phpt diff --git a/ion.c b/ion.c index 29c9eca..73eee24 100644 --- a/ion.c +++ b/ion.c @@ -116,6 +116,20 @@ ZEND_METHOD(ion_Timestamp, __toString) zend_call_method_with_1_params(&obj->std, obj->std.ce, NULL, "format", return_value, zend_read_property(obj->std.ce, &obj->std, ZEND_STRL("format"), 0, &fmt)); } +ZEND_METHOD(ion_Decimal_Context, __construct) +{ + php_ion_decimal_ctx *obj = php_ion_obj(decimal_ctx, Z_OBJ_P(ZEND_THIS)); + PTR_CHECK(obj); + + zend_long bits = 128; + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(bits) + ZEND_PARSE_PARAMETERS_END(); + + zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("bits"), bits); + php_ion_decimal_ctx_ctor(obj); +} ZEND_METHOD(ion_Decimal, __construct) { php_ion_decimal *obj = php_ion_obj(decimal, Z_OBJ_P(ZEND_THIS)); @@ -129,6 +143,15 @@ ZEND_METHOD(ion_Decimal, __construct) Z_PARAM_OBJ_OF_CLASS_OR_NULL(obj->ctx, ce_Decimal_Context) ZEND_PARSE_PARAMETERS_END(); + if (obj->ctx) { + GC_ADDREF(obj->ctx); + } else { + zval zdc; + object_init_ex(&zdc, ce_Decimal_Context); + obj->ctx = Z_OBJ(zdc); + php_ion_decimal_ctx_ctor(php_ion_obj(decimal_ctx, obj->ctx)); + } + if (zstr) { ION_CHECK(ion_decimal_from_string(&obj->dec, zstr->val, obj->ctx ? &php_ion_obj(decimal_ctx, obj->ctx)->ctx : NULL)); @@ -143,6 +166,7 @@ ZEND_METHOD(ion_Decimal, __construct) } php_ion_decimal_ctor(obj); + OBJ_RELEASE(obj->ctx); } ZEND_METHOD(ion_Decimal, equals) { @@ -159,46 +183,34 @@ ZEND_METHOD(ion_Decimal, equals) obj->ctx ? &php_ion_obj(decimal_ctx, obj->ctx)->ctx : NULL, &is)); RETURN_BOOL(is); } -ZEND_METHOD(ion_Decimal, zero) +ZEND_METHOD(ion_Decimal, __toString) { php_ion_decimal *obj = php_ion_obj(decimal, Z_OBJ_P(ZEND_THIS)); PTR_CHECK(obj); ZEND_PARSE_PARAMETERS_NONE(); - ION_CHECK(ion_decimal_zero(&obj->dec)); + RETURN_STR(php_ion_decimal_to_string(&obj->dec)); } -ZEND_METHOD(ion_Decimal, __toString) +ZEND_METHOD(ion_Decimal, toInt) { php_ion_decimal *obj = php_ion_obj(decimal, Z_OBJ_P(ZEND_THIS)); PTR_CHECK(obj); ZEND_PARSE_PARAMETERS_NONE(); - RETURN_STR(php_ion_decimal_to_string(&obj->dec)); + zend_long l; + php_ion_decimal_to_int(&obj->dec, &php_ion_obj(decimal_ctx, obj->ctx)->ctx, &l); + RETURN_LONG(l); } -ZEND_METHOD(ion_Decimal, toInt) +ZEND_METHOD(ion_Decimal, isInt) { php_ion_decimal *obj = php_ion_obj(decimal, Z_OBJ_P(ZEND_THIS)); PTR_CHECK(obj); ZEND_PARSE_PARAMETERS_NONE(); - int32_t i32 = 0; - iERR err = ion_decimal_to_int32(&obj->dec, - obj->ctx ? &php_ion_obj(decimal_ctx, obj->ctx)->ctx : NULL, &i32); - if (IERR_OK == err) { - RETURN_LONG(i32); - } - if (!ion_decimal_is_negative(&obj->dec)) { - uint32_t u32 = 0; - err = ion_decimal_to_uint32(&obj->dec, - obj->ctx ? &php_ion_obj(decimal_ctx, obj->ctx)->ctx : NULL, &u32); - if (IERR_OK == err) { - RETURN_LONG(u32); - } - } - ION_CHECK(err); + RETURN_BOOL(ion_decimal_is_integer(&obj->dec)); } ZEND_METHOD(ion_Reader_Options, __construct) { @@ -1458,7 +1470,6 @@ PHP_MINIT_FUNCTION(ion) ce_Collection = register_class_ion_Collection(); php_ion_register(decimal, Decimal); - oh_decimal.get_properties_for = php_ion_decimal_get_props_for; php_ion_register(decimal_ctx, Decimal_Context); php_ion_register(timestamp, Timestamp, php_date_get_date_ce()); php_ion_register(catalog, Catalog); diff --git a/ion.stub.php b/ion.stub.php index eac98f2..22504c7 100644 --- a/ion.stub.php +++ b/ion.stub.php @@ -98,15 +98,23 @@ class Collection { } + +namespace ion\Decimal; +class Context { + public function __construct( + public readonly int $bits = 128 + ) {} +} + namespace ion; class Decimal { public function __construct( - string|int $number, + public readonly string|int $number, public readonly ?Decimal\Context $context = null, ) {} public function equals(Decimal $decimal) : bool {} - public function zero() : void {} + public function isInt() : bool {} public function __toString() : string {} /** @alias ion\Decimal::__toString */ @@ -114,12 +122,6 @@ class Decimal { public function toInt() : int {} } -namespace ion\Decimal; -class Context { - -} - - namespace ion; class Timestamp extends \DateTime { public function __construct( diff --git a/ion_arginfo.h b/ion_arginfo.h index 5e35620..d11e88a 100644 --- a/ion_arginfo.h +++ b/ion_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 8858d757806dc0cbd811e4873c9770c8676c606b */ + * Stub hash: 9972b27bc2f7f20e59aae26777e1a674f1606c11 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ion_serialize, 0, 1, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, data, IS_MIXED, 0) @@ -26,6 +26,10 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ion_Symbol_equals, 0, 1, _ ZEND_ARG_OBJ_INFO(0, symbol, ion\\Symbol, 0) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ion_Decimal_Context___construct, 0, 0, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, bits, IS_LONG, 0, "128") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ion_Decimal___construct, 0, 0, 1) ZEND_ARG_TYPE_MASK(0, number, MAY_BE_STRING|MAY_BE_LONG, NULL) ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, context, ion\\Decimal\\Context, 1, "null") @@ -35,7 +39,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ion_Decimal_equals, 0, 1, ZEND_ARG_OBJ_INFO(0, decimal, ion\\Decimal, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ion_Decimal_zero, 0, 0, IS_VOID, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ion_Decimal_isInt, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ion_Decimal___toString, 0, 0, IS_STRING, 0) @@ -58,16 +62,15 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ion_Reader_getType, 0, 0, ion\\Type, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ion_Reader_hasAnnotations, 0, 0, _IS_BOOL, 0) -ZEND_END_ARG_INFO() +#define arginfo_class_ion_Reader_hasAnnotations arginfo_class_ion_Decimal_isInt ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ion_Reader_hasAnnotation, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, annotation, IS_STRING, 0) ZEND_END_ARG_INFO() -#define arginfo_class_ion_Reader_isNull arginfo_class_ion_Reader_hasAnnotations +#define arginfo_class_ion_Reader_isNull arginfo_class_ion_Decimal_isInt -#define arginfo_class_ion_Reader_isInStruct arginfo_class_ion_Reader_hasAnnotations +#define arginfo_class_ion_Reader_isInStruct arginfo_class_ion_Decimal_isInt #define arginfo_class_ion_Reader_getFieldName arginfo_class_ion_Decimal___toString @@ -91,7 +94,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ion_Reader_readNull arginfo_class_ion_Reader_getType -#define arginfo_class_ion_Reader_readBool arginfo_class_ion_Reader_hasAnnotations +#define arginfo_class_ion_Reader_readBool arginfo_class_ion_Decimal_isInt ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_ion_Reader_readInt, 0, 0, MAY_BE_LONG|MAY_BE_STRING) ZEND_END_ARG_INFO() @@ -147,16 +150,17 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ion_Reader_Options___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, skipCharacterValidation, _IS_BOOL, 0, "false") ZEND_END_ARG_INFO() -#define arginfo_class_ion_Reader_Reader_hasChildren arginfo_class_ion_Reader_hasAnnotations +#define arginfo_class_ion_Reader_Reader_hasChildren arginfo_class_ion_Decimal_isInt ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_class_ion_Reader_Reader_getChildren, 0, 0, ion\\Reader, 0) ZEND_END_ARG_INFO() -#define arginfo_class_ion_Reader_Reader_rewind arginfo_class_ion_Decimal_zero +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ion_Reader_Reader_rewind, 0, 0, IS_VOID, 0) +ZEND_END_ARG_INFO() -#define arginfo_class_ion_Reader_Reader_next arginfo_class_ion_Decimal_zero +#define arginfo_class_ion_Reader_Reader_next arginfo_class_ion_Reader_Reader_rewind -#define arginfo_class_ion_Reader_Reader_valid arginfo_class_ion_Reader_hasAnnotations +#define arginfo_class_ion_Reader_Reader_valid arginfo_class_ion_Decimal_isInt ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ion_Reader_Reader_key, 0, 0, IS_MIXED, 0) ZEND_END_ARG_INFO() @@ -165,13 +169,13 @@ ZEND_END_ARG_INFO() #define arginfo_class_ion_Reader_Reader_getType arginfo_class_ion_Reader_getType -#define arginfo_class_ion_Reader_Reader_hasAnnotations arginfo_class_ion_Reader_hasAnnotations +#define arginfo_class_ion_Reader_Reader_hasAnnotations arginfo_class_ion_Decimal_isInt #define arginfo_class_ion_Reader_Reader_hasAnnotation arginfo_class_ion_Reader_hasAnnotation -#define arginfo_class_ion_Reader_Reader_isNull arginfo_class_ion_Reader_hasAnnotations +#define arginfo_class_ion_Reader_Reader_isNull arginfo_class_ion_Decimal_isInt -#define arginfo_class_ion_Reader_Reader_isInStruct arginfo_class_ion_Reader_hasAnnotations +#define arginfo_class_ion_Reader_Reader_isInStruct arginfo_class_ion_Decimal_isInt #define arginfo_class_ion_Reader_Reader_getFieldName arginfo_class_ion_Decimal___toString @@ -189,7 +193,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ion_Reader_Reader_readNull arginfo_class_ion_Reader_getType -#define arginfo_class_ion_Reader_Reader_readBool arginfo_class_ion_Reader_hasAnnotations +#define arginfo_class_ion_Reader_Reader_readBool arginfo_class_ion_Decimal_isInt #define arginfo_class_ion_Reader_Reader_readInt arginfo_class_ion_Reader_readInt @@ -271,7 +275,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ion_Writer_Options___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, allocationPageSize, IS_LONG, 0, "0x1000") ZEND_END_ARG_INFO() -#define arginfo_class_ion_Writer_writeNull arginfo_class_ion_Decimal_zero +#define arginfo_class_ion_Writer_writeNull arginfo_class_ion_Reader_Reader_rewind ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ion_Writer_writeTypedNull, 0, 1, IS_VOID, 0) ZEND_ARG_OBJ_INFO(0, type, ion\\Type, 0) @@ -315,11 +319,11 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ion_Writer_appendLob, 0, 1 ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0) ZEND_END_ARG_INFO() -#define arginfo_class_ion_Writer_finishLob arginfo_class_ion_Decimal_zero +#define arginfo_class_ion_Writer_finishLob arginfo_class_ion_Reader_Reader_rewind #define arginfo_class_ion_Writer_startContainer arginfo_class_ion_Writer_writeTypedNull -#define arginfo_class_ion_Writer_finishContainer arginfo_class_ion_Decimal_zero +#define arginfo_class_ion_Writer_finishContainer arginfo_class_ion_Reader_Reader_rewind ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ion_Writer_writeFieldName, 0, 1, IS_VOID, 0) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) @@ -341,7 +345,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ion_Writer_writeAll arginfo_class_ion_Writer_writeOne -#define arginfo_class_ion_Writer_Writer_writeNull arginfo_class_ion_Decimal_zero +#define arginfo_class_ion_Writer_Writer_writeNull arginfo_class_ion_Reader_Reader_rewind #define arginfo_class_ion_Writer_Writer_writeTypedNull arginfo_class_ion_Writer_writeTypedNull @@ -367,11 +371,11 @@ ZEND_END_ARG_INFO() #define arginfo_class_ion_Writer_Writer_appendLob arginfo_class_ion_Writer_appendLob -#define arginfo_class_ion_Writer_Writer_finishLob arginfo_class_ion_Decimal_zero +#define arginfo_class_ion_Writer_Writer_finishLob arginfo_class_ion_Reader_Reader_rewind #define arginfo_class_ion_Writer_Writer_startContainer arginfo_class_ion_Writer_writeTypedNull -#define arginfo_class_ion_Writer_Writer_finishContainer arginfo_class_ion_Decimal_zero +#define arginfo_class_ion_Writer_Writer_finishContainer arginfo_class_ion_Reader_Reader_rewind #define arginfo_class_ion_Writer_Writer_writeFieldName arginfo_class_ion_Writer_writeFieldName @@ -433,9 +437,10 @@ ZEND_FUNCTION(ion_unserialize); ZEND_METHOD(ion_Symbol_ImportLocation, __construct); ZEND_METHOD(ion_Symbol, __construct); ZEND_METHOD(ion_Symbol, equals); +ZEND_METHOD(ion_Decimal_Context, __construct); ZEND_METHOD(ion_Decimal, __construct); ZEND_METHOD(ion_Decimal, equals); -ZEND_METHOD(ion_Decimal, zero); +ZEND_METHOD(ion_Decimal, isInt); ZEND_METHOD(ion_Decimal, __toString); ZEND_METHOD(ion_Decimal, toInt); ZEND_METHOD(ion_Timestamp, __construct); @@ -573,10 +578,16 @@ static const zend_function_entry class_ion_Collection_methods[] = { }; +static const zend_function_entry class_ion_Decimal_Context_methods[] = { + ZEND_ME(ion_Decimal_Context, __construct, arginfo_class_ion_Decimal_Context___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + + static const zend_function_entry class_ion_Decimal_methods[] = { ZEND_ME(ion_Decimal, __construct, arginfo_class_ion_Decimal___construct, ZEND_ACC_PUBLIC) ZEND_ME(ion_Decimal, equals, arginfo_class_ion_Decimal_equals, ZEND_ACC_PUBLIC) - ZEND_ME(ion_Decimal, zero, arginfo_class_ion_Decimal_zero, ZEND_ACC_PUBLIC) + ZEND_ME(ion_Decimal, isInt, arginfo_class_ion_Decimal_isInt, ZEND_ACC_PUBLIC) ZEND_ME(ion_Decimal, __toString, arginfo_class_ion_Decimal___toString, ZEND_ACC_PUBLIC) ZEND_MALIAS(ion_Decimal, toString, __toString, arginfo_class_ion_Decimal_toString, ZEND_ACC_PUBLIC) ZEND_ME(ion_Decimal, toInt, arginfo_class_ion_Decimal_toInt, ZEND_ACC_PUBLIC) @@ -584,11 +595,6 @@ static const zend_function_entry class_ion_Decimal_methods[] = { }; -static const zend_function_entry class_ion_Decimal_Context_methods[] = { - ZEND_FE_END -}; - - static const zend_function_entry class_ion_Timestamp_methods[] = { ZEND_ME(ion_Timestamp, __construct, arginfo_class_ion_Timestamp___construct, ZEND_ACC_PUBLIC) ZEND_ME(ion_Timestamp, __toString, arginfo_class_ion_Timestamp___toString, ZEND_ACC_PUBLIC) @@ -1112,30 +1118,42 @@ static zend_class_entry *register_class_ion_Collection(void) return class_entry; } -static zend_class_entry *register_class_ion_Decimal(void) +static zend_class_entry *register_class_ion_Decimal_Context(void) { zend_class_entry ce, *class_entry; - INIT_NS_CLASS_ENTRY(ce, "ion", "Decimal", class_ion_Decimal_methods); + INIT_NS_CLASS_ENTRY(ce, "ion\\Decimal", "Context", class_ion_Decimal_Context_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); - zend_string *property_context_class_ion_Decimal_Context = zend_string_init("ion\\Decimal\\Context", sizeof("ion\\Decimal\\Context")-1, 1); - zval property_context_default_value; - ZVAL_UNDEF(&property_context_default_value); - zend_string *property_context_name = zend_string_init("context", sizeof("context") - 1, 1); - zend_declare_typed_property(class_entry, property_context_name, &property_context_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_context_class_ion_Decimal_Context, 0, MAY_BE_NULL)); - zend_string_release(property_context_name); + zval property_bits_default_value; + ZVAL_UNDEF(&property_bits_default_value); + zend_string *property_bits_name = zend_string_init("bits", sizeof("bits") - 1, 1); + zend_declare_typed_property(class_entry, property_bits_name, &property_bits_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(property_bits_name); return class_entry; } -static zend_class_entry *register_class_ion_Decimal_Context(void) +static zend_class_entry *register_class_ion_Decimal(void) { zend_class_entry ce, *class_entry; - INIT_NS_CLASS_ENTRY(ce, "ion\\Decimal", "Context", class_ion_Decimal_Context_methods); + INIT_NS_CLASS_ENTRY(ce, "ion", "Decimal", class_ion_Decimal_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); + zval property_number_default_value; + ZVAL_UNDEF(&property_number_default_value); + zend_string *property_number_name = zend_string_init("number", sizeof("number") - 1, 1); + zend_declare_typed_property(class_entry, property_number_name, &property_number_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING|MAY_BE_LONG)); + zend_string_release(property_number_name); + + zend_string *property_context_class_ion_Decimal_Context = zend_string_init("ion\\Decimal\\Context", sizeof("ion\\Decimal\\Context")-1, 1); + zval property_context_default_value; + ZVAL_UNDEF(&property_context_default_value); + zend_string *property_context_name = zend_string_init("context", sizeof("context") - 1, 1); + zend_declare_typed_property(class_entry, property_context_name, &property_context_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(property_context_class_ion_Decimal_Context, 0, MAY_BE_NULL)); + zend_string_release(property_context_name); + return class_entry; } diff --git a/ion_private.h b/ion_private.h index b349ead..1ac49d9 100644 --- a/ion_private.h +++ b/ion_private.h @@ -357,25 +357,33 @@ typedef struct php_ion_decimal_ctx { zend_object std; } php_ion_decimal_ctx; -typedef struct php_ion_decimal { - ION_DECIMAL dec; - zend_object *ctx, std; -} php_ion_decimal; +static inline void php_ion_decimal_ctx_ctor(php_ion_decimal_ctx *obj) { + zval tmp, *zbits = zend_read_property(obj->std.ce, &obj->std, ZEND_STRL("bits"), 1, &tmp); -static inline void php_ion_decimal_ctor(php_ion_decimal *obj) -{ - if (obj->ctx) { - update_property_obj(&obj->std, ZEND_STRL("context"), obj->ctx); + int bits = 128; + if (zbits != &EG(uninitialized_zval)) { + bits = Z_LVAL_P(zbits); } else { - zend_update_property_null(obj->std.ce, &obj->std, ZEND_STRL("context")); + zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("bits"), bits); + } + switch (bits) { + case 32: + case 64: + case 128: + decContextDefault(&obj->ctx, bits); + break; + default: + zend_throw_exception_ex(spl_ce_InvalidArgumentException, IERR_INVALID_ARG, + "Decimal context only allows 32, 64 or 128 bits"); } -} -static inline void php_ion_decimal_dtor(php_ion_decimal *obj) -{ - ion_decimal_free(&obj->dec); } +typedef struct php_ion_decimal { + ION_DECIMAL dec; + zend_object *ctx, std; +} php_ion_decimal; + static inline zend_string *php_ion_decimal_to_string(ION_DECIMAL *dec) { zend_string *zstr = zend_string_alloc(ION_DECIMAL_STRLEN(dec), 0); @@ -383,22 +391,43 @@ static inline zend_string *php_ion_decimal_to_string(ION_DECIMAL *dec) return zend_string_truncate(zstr, strlen(zstr->val), 0); } -static zend_array *php_ion_decimal_get_props_for(zend_object *object, zend_prop_purpose purpose) +static inline void php_ion_decimal_to_int(ION_DECIMAL *dec, decContext *ctx, zend_long *l) { - php_ion_decimal *obj = php_ion_obj(decimal, object); + ION_INT *ii = NULL; + ION_CHECK(ion_int_alloc(NULL, &ii)); + ION_CHECK(ion_decimal_to_ion_int(dec, ctx, ii), ion_int_free(ii)); + int64_t i64; + ION_CHECK(ion_int_to_int64(ii, &i64), ion_int_free(ii)); + *l = i64; + ion_int_free(ii); +} - switch (purpose) { - case ZEND_PROP_PURPOSE_DEBUG: - zend_array *arr = zend_new_array(1); - zval *zv = zend_hash_str_add_empty_element(arr, ZEND_STRL("value")); - ZVAL_STR(zv, php_ion_decimal_to_string(&obj->dec)); - return arr; +static inline void php_ion_decimal_ctor(php_ion_decimal *obj) +{ + if (!obj->ctx) { + zval zdc; + object_init_ex(&zdc, ce_Decimal_Context); + obj->ctx = Z_OBJ(zdc); + php_ion_decimal_ctx_ctor(php_ion_obj(decimal_ctx, obj->ctx)); + } + update_property_obj(&obj->std, ZEND_STRL("context"), obj->ctx); - default: - return zend_std_get_properties_for(object, purpose); + if (ion_decimal_is_integer(&obj->dec)) { + zend_long l; + php_ion_decimal_to_int(&obj->dec, &php_ion_obj(decimal_ctx, obj->ctx)->ctx, &l); + zend_update_property_long(obj->std.ce, &obj->std, ZEND_STRL("number"), l); + } else { + zend_string *zstr = php_ion_decimal_to_string(&obj->dec); + zend_update_property_str(obj->std.ce, &obj->std, ZEND_STRL("number"), zstr); + zend_string_release(zstr); } } +static inline void php_ion_decimal_dtor(php_ion_decimal *obj) +{ + ion_decimal_free(&obj->dec); +} + typedef php_date_obj php_ion_timestamp; static inline void php_ion_timestamp_ctor(php_ion_timestamp *obj, zend_long precision, zend_string *fmt, zend_string *dt, zval *tz) @@ -1171,7 +1200,7 @@ static inline void php_ion_unserialize_hash(php_ion_reader *obj, zval *return_va break; } - add_assoc_zval_ex(return_value, key->val, key->len, &zvalue); + zend_symtable_update(HASH_OF(return_value), key, &zvalue); zend_string_release(key); } diff --git a/tests/Decimal.phpt b/tests/Decimal.phpt new file mode 100644 index 0000000..e861cae --- /dev/null +++ b/tests/Decimal.phpt @@ -0,0 +1,32 @@ +--TEST-- +ion\Decimal +--EXTENSIONS-- +ion +--FILE-- +TEST + +DONE +--EXPECTF-- +TEST +object(ion\Decimal)#%d (2) { + ["number"]=> + int(1) + ["context"]=> + object(ion\Decimal\Context)#%d (1) { + ["bits"]=> + int(128) + } +} +object(ion\Decimal)#%d (2) { + ["number"]=> + string(4) "1.23" + ["context"]=> + object(ion\Decimal\Context)#%d (1) { + ["bits"]=> + int(128) + } +} +DONE diff --git a/tests/Decimal/Context.phpt b/tests/Decimal/Context.phpt new file mode 100644 index 0000000..f2b2150 --- /dev/null +++ b/tests/Decimal/Context.phpt @@ -0,0 +1,22 @@ +--TEST-- +ion\Decimal\Context +--EXTENSIONS-- +ion +--FILE-- +TEST + +DONE +--EXPECTF-- +TEST +object(ion\Decimal\Context)#1 (1) { + ["bits"]=> + int(128) +} +object(ion\Decimal\Context)#1 (1) { + ["bits"]=> + int(32) +} +DONE diff --git a/tests/Decimal/Context/__construct.phpt b/tests/Decimal/Context/__construct.phpt new file mode 100644 index 0000000..2a96406 --- /dev/null +++ b/tests/Decimal/Context/__construct.phpt @@ -0,0 +1,34 @@ +--TEST-- +ion\Decimal\Context::__construct +--EXTENSIONS-- +ion +--FILE-- +TEST +getMessage(), "\n"; +} +try { + new Context("123"); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} +try { + new Context("foo"); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} +?> +DONE +--EXPECTF-- +TEST +Decimal context only allows 32, 64 or 128 bits +Decimal context only allows 32, 64 or 128 bits +ion\Decimal\Context::__construct(): Argument #1 ($bits) must be of type int, string given +DONE diff --git a/tests/Decimal/__construct.phpt b/tests/Decimal/__construct.phpt new file mode 100644 index 0000000..ee56e9f --- /dev/null +++ b/tests/Decimal/__construct.phpt @@ -0,0 +1,28 @@ +--TEST-- +ion\Decimal::__construct +--EXTENSIONS-- +ion +--FILE-- +TEST +getMessage(), "\n"; +} +new Decimal(1); +new Decimal("123"); +new Decimal(123.123); +new Decimal("123.123"); +new Decimal(1, null); +new Decimal(1, new Decimal\Context); +?> +DONE +--EXPECTF-- +TEST +ion\Decimal::__construct() expects at least 1 argument, 0 given + +Deprecated: Implicit conversion from float 123.123 to int loses precision in %s/Decimal/__construct.php on line %d +DONE diff --git a/tests/Decimal/__toString.phpt b/tests/Decimal/__toString.phpt new file mode 100644 index 0000000..8847995 --- /dev/null +++ b/tests/Decimal/__toString.phpt @@ -0,0 +1,25 @@ +--TEST-- +ion\Decimal::__toString +--EXTENSIONS-- +ion +--FILE-- +TEST + +DONE +--EXPECT-- +TEST +1d0 +2d0 +1.2 +-1d0 +-2d0 +-1.2 +DONE diff --git a/tests/Decimal/equals.phpt b/tests/Decimal/equals.phpt new file mode 100644 index 0000000..9b2f92d --- /dev/null +++ b/tests/Decimal/equals.phpt @@ -0,0 +1,23 @@ +--TEST-- +ion\Decimal::equals +--EXTENSIONS-- +ion +--FILE-- +TEST +equals(new Decimal("1"))); + +var_dump(new Decimal(1) == new Decimal(2)); +var_dump((new Decimal(1))->equals(new Decimal(2))); +?> +DONE +--EXPECT-- +TEST +bool(true) +bool(true) +bool(false) +bool(false) +DONE diff --git a/tests/Decimal/isInt.phpt b/tests/Decimal/isInt.phpt new file mode 100644 index 0000000..da3e7fd --- /dev/null +++ b/tests/Decimal/isInt.phpt @@ -0,0 +1,26 @@ +--TEST-- +ion\Decimal::isInt +--EXTENSIONS-- +ion +--FILE-- +TEST +isInt()); +var_dump((new Decimal(-1))->isInt()); +var_dump((new Decimal(123456789))->isInt()); +var_dump((new Decimal(-123456789))->isInt()); +var_dump((new Decimal("1.23"))->isInt()); +var_dump((new Decimal("-1.23"))->isInt()); +?> +DONE +--EXPECT-- +TEST +bool(true) +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) +DONE diff --git a/tests/Decimal/toInt.phpt b/tests/Decimal/toInt.phpt new file mode 100644 index 0000000..b1305c3 --- /dev/null +++ b/tests/Decimal/toInt.phpt @@ -0,0 +1,34 @@ +--TEST-- +ion\Decimal::toInt +--EXTENSIONS-- +ion +--FILE-- +TEST +toInt()); +var_dump((new Decimal(-1))->toInt()); +var_dump((new Decimal(123456789))->toInt()); +var_dump((new Decimal(-123456789))->toInt()); +try { + var_dump((new Decimal("1.23"))->toInt()); +} catch (Throwable) { + echo "caught 1.23\n"; +} +try { + var_dump((new Decimal("-1.23"))->toInt()); +} catch (Throwable) { + echo "caught -1.23\n"; +} +?> +DONE +--EXPECT-- +TEST +int(1) +int(-1) +int(123456789) +int(-123456789) +caught 1.23 +caught -1.23 +DONE diff --git a/tests/Decimal/toString.phpt b/tests/Decimal/toString.phpt new file mode 100644 index 0000000..94b1968 --- /dev/null +++ b/tests/Decimal/toString.phpt @@ -0,0 +1,34 @@ +--TEST-- +ion\Decimal::toString +--EXTENSIONS-- +ion +--FILE-- +TEST +toString()); +var_dump((new Decimal(-1))->toString()); +var_dump((new Decimal(123456789))->toString()); +var_dump((new Decimal(-123456789))->toString()); +try { + var_dump((new Decimal("1.23"))->toString()); +} catch (Throwable) { + echo "caught 1.23\n"; +} +try { + var_dump((new Decimal("-1.23"))->toString()); +} catch (Throwable) { + echo "caught -1.23\n"; +} +?> +DONE +--EXPECT-- +TEST +string(3) "1d0" +string(4) "-1d0" +string(11) "123456789d0" +string(12) "-123456789d0" +string(4) "1.23" +string(5) "-1.23" +DONE -- 2.30.2