Skip to content

Commit a6a8184

Browse files
committed
wip
1 parent 2475efe commit a6a8184

File tree

4 files changed

+314
-1
lines changed

4 files changed

+314
-1
lines changed

ext/bcmath/bcmath.c

Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,13 @@ static PHP_GSHUTDOWN_FUNCTION(bcmath)
102102
}
103103
/* }}} */
104104

105+
static void bc_num_register_class(void);
106+
105107
/* {{{ PHP_MINIT_FUNCTION */
106108
PHP_MINIT_FUNCTION(bcmath)
107109
{
108110
REGISTER_INI_ENTRIES();
111+
bc_num_register_class();
109112

110113
return SUCCESS;
111114
}
@@ -763,5 +766,271 @@ PHP_FUNCTION(bcscale)
763766
}
764767
/* }}} */
765768

769+
static zend_class_entry *bc_num_ce;
770+
static zend_object_handlers bc_num_obj_handlers;
771+
#define IS_BC_NUM(zval) (Z_TYPE_P(zval) == IS_OBJECT && instanceof_function(Z_OBJCE_P(zval), bc_num_ce))
772+
773+
static inline bc_num_obj *bc_num_obj_from_obj(zend_object *obj) {
774+
return (bc_num_obj*)((char*)(obj) - XtOffsetOf(bc_num_obj, std));
775+
}
776+
777+
static inline bc_num_obj *bc_num_obj_from_zval(zval *zv) {
778+
return bc_num_obj_from_obj(Z_OBJ_P(zv));
779+
}
780+
781+
/* {{{ bc_num_ce->create_object */
782+
static zend_object *bc_num_create_obj(zend_class_entry *ce)
783+
{
784+
bc_num_obj *intern;
785+
786+
intern = zend_object_alloc(sizeof(bc_num_obj), ce);
787+
zend_object_std_init(&intern->std, ce);
788+
object_properties_init(&intern->std, ce);
789+
rebuild_object_properties(&intern->std);
790+
791+
return &intern->std;
792+
}
793+
/* }}} */
794+
795+
/* {{{ bc_num_obj_handlers.free_obj */
796+
static void bc_num_free_obj(zend_object *object)
797+
{
798+
bc_num_obj *intern = bc_num_obj_from_obj(object);
799+
if (intern->bc_num) {
800+
bc_free_num(&intern->bc_num);
801+
}
802+
efree(intern->bc_num);
803+
zend_object_std_dtor(&intern->std);
804+
}
805+
/* }}} */
806+
807+
/* {{{ bc_num_obj_handlers.clone_obj */
808+
static zend_object *bc_num_clone_obj(zend_object *object)
809+
{
810+
bc_num_obj *old_obj = bc_num_obj_from_obj(object);
811+
bc_num_obj *new_obj = bc_num_obj_from_obj(bc_num_create_obj(old_obj->std.ce));
812+
813+
zend_objects_clone_members(&new_obj->std, &old_obj->std);
814+
new_obj->bc_num = bc_copy_num(old_obj->bc_num);
815+
816+
return &new_obj->std;
817+
}
818+
/* }}} */
819+
820+
static zend_result convert_zval_to_bc_num(zval *zv, bc_num *num)
821+
{
822+
switch (Z_TYPE_P(zv)) {
823+
case IS_LONG:
824+
case IS_STRING:
825+
convert_to_string(zv);
826+
bc_init_num(&num1);
827+
if (!bc_str2num(num, Z_STRVAL_P(zv), 0)) {
828+
return FAILURE;
829+
}
830+
break;
831+
case IS_OBJECT:
832+
if (instanceof_function(Z_OBJCE_P(zv), bc_num_ce)) {
833+
bc_free_num(num);
834+
*num = bc_num_obj_from_zval(zv)->bc_num;
835+
} else {
836+
zend_argument_type_error(0, "must be of type int, string, or BcNum, %s given", zend_zval_value_name(zv));
837+
return FAILURE;
838+
}
839+
break;
840+
}
841+
842+
return SUCCESS;
843+
}
844+
845+
static zend_result bc_num_calculation(zval *result, zval *op1, zval *op2, bc_num_calculation_type type, bool is_operator)
846+
{
847+
bc_num num1, num2;
848+
849+
if (convert_zval_to_bc_num(op1, &num1) == FAILURE || convert_zval_to_bc_num(op2, &num2) == FAILURE) {
850+
return FAILURE;
851+
}
852+
853+
bc_num_obj *result_obj = bc_num_obj_from_obj(bc_num_create_obj(bc_num_ce));
854+
bc_init_num(&result_obj->bc_num);
855+
856+
size_t scale = MAX(num1->n_scale, num2->n_scale);
857+
858+
switch (type) {
859+
case BC_NUM_ADD:
860+
bc_add(num1, num2, &result_obj->bc_num, scale);
861+
break;
862+
case BC_NUM_SUB:
863+
bc_sub(num1, num2, &result_obj->bc_num, scale);
864+
break;
865+
case BC_NUM_MUL:
866+
bc_multiply(num1, num2, &result_obj->bc_num, scale);
867+
break;
868+
case BC_NUM_DIV:
869+
bc_divide(num1, num2, &result_obj->bc_num, scale);
870+
break;
871+
case BC_NUM_MOD:
872+
bc_modulo(num1, num2, &result_obj->bc_num, scale);
873+
break;
874+
case BC_NUM_POW:
875+
{
876+
long exponent = bc_num2long(num2);
877+
if (exponent == 0 && (num2->n_len > 1 || num2->n_value[0] != 0)) {
878+
zend_argument_value_error(is_operator ? 0 : 1, "exponent is too large");
879+
return FAILURE;
880+
}
881+
bc_raise(num1, bc_num2long(num2), &result_obj->bc_num, scale);
882+
}
883+
break;
884+
EMPTY_SWITCH_DEFAULT_CASE()
885+
}
886+
887+
result_obj->bc_num->n_scale = scale;
888+
ZVAL_OBJ(result, &result_obj->std);
889+
return SUCCESS;
890+
}
891+
892+
/* {{{ bc_num_obj_handlers.do_operation */
893+
static int bc_num_do_operation(uint8_t opcode, zval *result, zval *op1, zval *op2)
894+
{
895+
zend_result ret;
896+
897+
switch (opcode) {
898+
case ZEND_ADD:
899+
ret = bc_num_calculation(result, op1, op2, BC_NUM_ADD, true);
900+
break;
901+
case ZEND_SUB:
902+
ret = bc_num_calculation(result, op1, op2, BC_NUM_SUB, true);
903+
break;
904+
case ZEND_MUL:
905+
ret = bc_num_calculation(result, op1, op2, BC_NUM_MUL, true);
906+
break;
907+
case ZEND_POW:
908+
ret = bc_num_calculation(result, op1, op2, BC_NUM_POW, true);
909+
break;
910+
case ZEND_DIV:
911+
ret = bc_num_calculation(result, op1, op2, BC_NUM_DIV, true);
912+
break;
913+
case ZEND_MOD:
914+
ret = bc_num_calculation(result, op1, op2, BC_NUM_MOD, true);
915+
break;
916+
default:
917+
return FAILURE;
918+
}
919+
920+
return ret;
921+
}
922+
/* }}} */
923+
924+
/* {{{ bc_num_obj_handlers.compare */
925+
static int bc_num_compare(zval *z1, zval *z2)
926+
{
927+
bc_num_obj *obj1;
928+
bc_num_obj *obj2;
929+
930+
ZEND_COMPARE_OBJECTS_FALLBACK(z1, z2);
931+
932+
obj1 = bc_num_obj_from_zval(z1);
933+
obj2 = bc_num_obj_from_zval(z2);
934+
935+
return bc_compare(obj1->bc_num, obj2->bc_num);
936+
}
937+
/* }}} */
938+
939+
/* {{{ bc_num_obj_handlers.get_properties_for */
940+
static HashTable *bc_num_get_properties_for(zend_object *object, zend_prop_purpose purpose)
941+
{
942+
HashTable *props;
943+
bc_num_obj *bc_num_obj;
944+
945+
switch (purpose) {
946+
case ZEND_PROP_PURPOSE_DEBUG:
947+
case ZEND_PROP_PURPOSE_SERIALIZE:
948+
case ZEND_PROP_PURPOSE_VAR_EXPORT:
949+
case ZEND_PROP_PURPOSE_JSON:
950+
case ZEND_PROP_PURPOSE_ARRAY_CAST:
951+
break;
952+
default:
953+
return zend_std_get_properties_for(object, purpose);
954+
}
955+
956+
zval zv;
957+
bc_num_obj = bc_num_obj_from_obj(object);
958+
props = zend_array_dup(zend_std_get_properties(object));
959+
960+
ZVAL_STR(&zv, bc_num2str(bc_num_obj->bc_num));
961+
zend_hash_str_update(props, "num", sizeof("num")-1, &zv);
962+
ZVAL_LONG(&zv, bc_num_obj->bc_num->n_scale);
963+
zend_hash_str_update(props, "scale", sizeof("scale")-1, &zv);
964+
965+
return props;
966+
}
967+
/* }}} */
968+
969+
/* {{{ bc_num_obj_handlers.get_gc */
970+
static HashTable *bc_num_get_gc(zend_object *object, zval **table, int *n)
971+
{
972+
*table = NULL;
973+
*n = 0;
974+
return zend_std_get_properties(object);
975+
}
976+
/* }}} */
977+
978+
static void bc_num_register_class(void)
979+
{
980+
bc_num_ce = register_class_BcNum();
981+
bc_num_ce->create_object = bc_num_create_obj;
982+
bc_num_ce->default_object_handlers = &bc_num_obj_handlers;
983+
984+
memcpy(&bc_num_obj_handlers, &std_object_handlers, sizeof(zend_object_handlers));
985+
bc_num_obj_handlers.offset = XtOffsetOf(bc_num_obj, std);
986+
bc_num_obj_handlers.free_obj = bc_num_free_obj;
987+
bc_num_obj_handlers.clone_obj = bc_num_clone_obj;
988+
bc_num_obj_handlers.do_operation = bc_num_do_operation;
989+
bc_num_obj_handlers.compare = bc_num_compare;
990+
bc_num_obj_handlers.get_properties_for = bc_num_get_properties_for;
991+
bc_num_obj_handlers.get_gc = bc_num_get_gc;
992+
}
993+
994+
/* {{{ Creates new BcNum */
995+
PHP_METHOD(BcNum, __construct)
996+
{
997+
zend_string *num_str;
998+
zend_long scale_param;
999+
bool scale_param_is_null = 1;
1000+
int scale;
1001+
1002+
ZEND_PARSE_PARAMETERS_START(1, 2)
1003+
Z_PARAM_STR(num_str)
1004+
Z_PARAM_OPTIONAL
1005+
Z_PARAM_LONG_OR_NULL(scale_param, scale_param_is_null)
1006+
ZEND_PARSE_PARAMETERS_END();
1007+
1008+
if (scale_param_is_null) {
1009+
scale = BCG(bc_precision);
1010+
} else if (scale_param < 0 || scale_param > INT_MAX) {
1011+
zend_argument_value_error(2, "must be between 0 and %d", INT_MAX);
1012+
RETURN_THROWS();
1013+
} else {
1014+
scale = (int) scale_param;
1015+
}
1016+
1017+
bc_num_obj *obj = bc_num_obj_from_zval(ZEND_THIS);
1018+
bc_init_num(&obj->bc_num);
1019+
1020+
if (php_str2num(&obj->bc_num, ZSTR_VAL(num_str)) == FAILURE) {
1021+
zend_argument_value_error(1, "is not well-formed");
1022+
bc_free_num(&obj->bc_num);
1023+
RETURN_THROWS();
1024+
}
1025+
1026+
if (obj->bc_num->n_scale < scale) {
1027+
char *nptr = (char *) (obj->bc_num->n_value + obj->bc_num->n_len + obj->bc_num->n_scale);
1028+
for (int count = scale - obj->bc_num->n_scale; count > 0; count--) {
1029+
*nptr++ = 0;
1030+
}
1031+
}
1032+
obj->bc_num->n_scale = scale;
1033+
}
1034+
/* }}} */
7661035

7671036
#endif

ext/bcmath/bcmath.stub.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,12 @@ function bcceil(string $num): string {}
3838

3939
/** @refcount 1 */
4040
function bcround(string $num, int $precision = 0, int $mode = PHP_ROUND_HALF_UP): string {}
41+
42+
class BcNum
43+
{
44+
public function __construct(string $num, ?int $scale = null) {}
45+
46+
//public static function getGlobalScale(): int {}
47+
48+
//public static function setGlobalScale(int $scale): void {}
49+
}

ext/bcmath/bcmath_arginfo.h

Lines changed: 22 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/bcmath/php_bcmath.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,18 @@ ZEND_TSRMLS_CACHE_EXTERN()
4242
ZEND_EXTERN_MODULE_GLOBALS(bcmath)
4343
#define BCG(v) ZEND_MODULE_GLOBALS_ACCESSOR(bcmath, v)
4444

45+
typedef struct _bc_num_obj {
46+
zend_object std;
47+
bc_num bc_num;
48+
} bc_num_obj;
49+
50+
typedef enum {
51+
BC_NUM_ADD,
52+
BC_NUM_SUB,
53+
BC_NUM_MUL,
54+
BC_NUM_DIV,
55+
BC_NUM_MOD,
56+
BC_NUM_POW
57+
} bc_num_calculation_type;
58+
4559
#endif /* PHP_BCMATH_H */

0 commit comments

Comments
 (0)