improve decimal support
authorMichael Wallner <mike@php.net>
Tue, 7 Dec 2021 11:41:49 +0000 (12:41 +0100)
committerMichael Wallner <mike@php.net>
Tue, 7 Dec 2021 11:41:49 +0000 (12:41 +0100)
13 files changed:
ion.c
ion.stub.php
ion_arginfo.h
ion_private.h
tests/Decimal.phpt [new file with mode: 0644]
tests/Decimal/Context.phpt [new file with mode: 0644]
tests/Decimal/Context/__construct.phpt [new file with mode: 0644]
tests/Decimal/__construct.phpt [new file with mode: 0644]
tests/Decimal/__toString.phpt [new file with mode: 0644]
tests/Decimal/equals.phpt [new file with mode: 0644]
tests/Decimal/isInt.phpt [new file with mode: 0644]
tests/Decimal/toInt.phpt [new file with mode: 0644]
tests/Decimal/toString.phpt [new file with mode: 0644]

diff --git a/ion.c b/ion.c
index 29c9ecafdf886c314978899bb63110e7d0bd1e4a..73eee24fbc3d9576c8c25f653e66cb03703aab2f 100644 (file)
--- 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);
index eac98f2e55d4d4f20518d1543caca0db9cf0f9b8..22504c79231deb772ef237b4909d083920bfb651 100644 (file)
@@ -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(
index 5e35620889feb53d5f55e04006eb7cf67de4ffbc..d11e88a68eb1acbe638e6de09e93c698af22d059 100644 (file)
@@ -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;
 }
 
index b349ead9156c148d1f72b4d6d46d7df5f68615c7..1ac49d9a0d03225b4da35bf832797b6a158ebdb1 100644 (file)
@@ -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 (file)
index 0000000..e861cae
--- /dev/null
@@ -0,0 +1,32 @@
+--TEST--
+ion\Decimal
+--EXTENSIONS--
+ion
+--FILE--
+TEST
+<?php
+var_dump(new ion\Decimal(1));
+var_dump(new ion\Decimal("1.23"));
+?>
+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 (file)
index 0000000..f2b2150
--- /dev/null
@@ -0,0 +1,22 @@
+--TEST--
+ion\Decimal\Context
+--EXTENSIONS--
+ion
+--FILE--
+TEST
+<?php
+var_dump(new ion\Decimal\Context);
+var_dump(new ion\Decimal\Context(32));
+?>
+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 (file)
index 0000000..2a96406
--- /dev/null
@@ -0,0 +1,34 @@
+--TEST--
+ion\Decimal\Context::__construct
+--EXTENSIONS--
+ion
+--FILE--
+TEST
+<?php
+use ion\Decimal\Context;
+
+new Context;
+new Context(32);
+try {
+       new Context(123);
+} catch (Throwable $e) {
+       echo $e->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 (file)
index 0000000..ee56e9f
--- /dev/null
@@ -0,0 +1,28 @@
+--TEST--
+ion\Decimal::__construct
+--EXTENSIONS--
+ion
+--FILE--
+TEST
+<?php
+use ion\Decimal;
+
+try {
+       new Decimal();
+} catch (Error $e) {
+       echo $e->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 (file)
index 0000000..8847995
--- /dev/null
@@ -0,0 +1,25 @@
+--TEST--
+ion\Decimal::__toString
+--EXTENSIONS--
+ion
+--FILE--
+TEST
+<?php
+use ion\Decimal;
+echo new Decimal(1), "\n";
+echo new Decimal("2"), "\n";
+echo new Decimal("1.2"), "\n";
+echo new Decimal(-1), "\n";
+echo new Decimal("-2"), "\n";
+echo new Decimal("-1.2"), "\n";
+?>
+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 (file)
index 0000000..9b2f92d
--- /dev/null
@@ -0,0 +1,23 @@
+--TEST--
+ion\Decimal::equals
+--EXTENSIONS--
+ion
+--FILE--
+TEST
+<?php
+use ion\Decimal;
+
+var_dump(new Decimal(1) == new Decimal("1"));
+var_dump((new Decimal(1))->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 (file)
index 0000000..da3e7fd
--- /dev/null
@@ -0,0 +1,26 @@
+--TEST--
+ion\Decimal::isInt
+--EXTENSIONS--
+ion
+--FILE--
+TEST
+<?php
+use ion\Decimal;
+
+var_dump((new Decimal(1))->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 (file)
index 0000000..b1305c3
--- /dev/null
@@ -0,0 +1,34 @@
+--TEST--
+ion\Decimal::toInt
+--EXTENSIONS--
+ion
+--FILE--
+TEST
+<?php
+use ion\Decimal;
+
+var_dump((new Decimal(1))->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 (file)
index 0000000..94b1968
--- /dev/null
@@ -0,0 +1,34 @@
+--TEST--
+ion\Decimal::toString
+--EXTENSIONS--
+ion
+--FILE--
+TEST
+<?php
+use ion\Decimal;
+
+var_dump((new Decimal(1))->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