diff --git a/ext/standard/Module.php b/ext/standard/Module.php index 33bfbec7d31..bfcb897dc5b 100755 --- a/ext/standard/Module.php +++ b/ext/standard/Module.php @@ -20,6 +20,7 @@ public function getFunctions(): array { return [ $this->parseAndCompileFunction('str_repeat', __DIR__.'/str_repeat.php'), + $this->parseAndCompileFunction('decbin', __DIR__.'/decbin.php'), new abs(), new ceil(), new floor(), @@ -40,6 +41,9 @@ public function getFunctions(): array new strcmp(), new dechex(), new hexdec(), + new decoct(), + new octdec(), + new bindec(), ]; } diff --git a/ext/standard/bindec.php b/ext/standard/bindec.php new file mode 100644 index 00000000000..4b8fd90f587 --- /dev/null +++ b/ext/standard/bindec.php @@ -0,0 +1,69 @@ +calledArgs)) { + throw new \LogicException('bindec() requires exactly one argument'); + } + $v = $frame->calledArgs[0]->resolveIndirect(); + if (null === $frame->returnVar) { + return; + } + if (Variable::TYPE_STRING !== $v->type) { + throw new \LogicException('bindec() only supports strings in this compiler build'); + } + $frame->returnVar->int((int) \bindec($v->toString())); + } + + public Context $context; + + public function call(Context $context, JITVariable ...$args): Value + { + $this->context = $context; + if (1 !== count($args)) { + throw new \LogicException('bindec() requires exactly one argument'); + } + if (JITVariable::TYPE_STRING !== $args[0]->type) { + throw new \LogicException('bindec() only supports strings in this compiler build'); + } + $ptr = $this->stringDataPtr($context, $context->helper->loadValue($args[0])); + $endPtr = $context->getTypeFromString('int8**')->constPointerNull(); + $base = $context->getTypeFromString('int32')->constInt(2, false); + $fn = $context->lookupFunction('strtol'); + $raw = $context->builder->call($fn, $ptr, $endPtr, $base); + $i64 = $context->getTypeFromString('int64'); + + return $context->builder->trunc($raw, $i64); + } + + private function stringDataPtr(Context $context, Value $strPtr): Value + { + $structName = $strPtr->typeOf()->getElementType()->getName(); + $off = $context->structFieldMap[$structName]['value']; + + return $context->builder->structGep($strPtr, $off); + } +} diff --git a/ext/standard/decbin.php b/ext/standard/decbin.php new file mode 100644 index 00000000000..48352c05518 --- /dev/null +++ b/ext/standard/decbin.php @@ -0,0 +1,28 @@ + 0; $n = intval($n / 2)) { + $result = strval($n % 2) . $result; + } + + return $result; +} diff --git a/ext/standard/decoct.php b/ext/standard/decoct.php new file mode 100644 index 00000000000..73dcc57c745 --- /dev/null +++ b/ext/standard/decoct.php @@ -0,0 +1,85 @@ +calledArgs)) { + throw new \LogicException('decoct() requires exactly one argument'); + } + $v = $frame->calledArgs[0]->resolveIndirect(); + if (null === $frame->returnVar) { + return; + } + if (Variable::TYPE_INTEGER !== $v->type) { + throw new \LogicException('decoct() only supports integers in this compiler build'); + } + $frame->returnVar->string(\decoct($v->toInt())); + } + + public Context $context; + + public function call(Context $context, JITVariable ...$args): Value + { + $this->context = $context; + if (1 !== count($args)) { + throw new \LogicException('decoct() requires exactly one argument'); + } + if (JITVariable::TYPE_NATIVE_LONG !== $args[0]->type) { + throw new \LogicException('decoct() only supports integers in this compiler build'); + } + + return $this->formatToString($context, $context->helper->loadValue($args[0]), '%o'); + } + + private function formatToString(Context $context, Value $value, string $format): Value + { + $sizeT = $context->getTypeFromString('size_t'); + $charPtr = $context->getTypeFromString('char*'); + $i64 = $context->getTypeFromString('int64'); + $bufSize = $sizeT->constInt(64, false); + $buf = $context->builder->call($context->lookupFunction('__mm__malloc'), $bufSize); + $bufChar = $context->builder->pointerCast($buf, $charPtr); + $fmt = $context->builder->pointerCast( + $context->constantFromString($format), + $charPtr + ); + $written = $context->builder->call( + $context->lookupFunction('snprintf'), + $bufChar, + $bufSize, + $fmt, + $value + ); + $len = $context->builder->zExt($written, $i64); + $str = $context->builder->call( + $context->lookupFunction('__string__init'), + $len, + $bufChar + ); + $context->builder->call($context->lookupFunction('__mm__free'), $buf); + + return $str; + } +} diff --git a/ext/standard/octdec.php b/ext/standard/octdec.php new file mode 100644 index 00000000000..7919488009f --- /dev/null +++ b/ext/standard/octdec.php @@ -0,0 +1,69 @@ +calledArgs)) { + throw new \LogicException('octdec() requires exactly one argument'); + } + $v = $frame->calledArgs[0]->resolveIndirect(); + if (null === $frame->returnVar) { + return; + } + if (Variable::TYPE_STRING !== $v->type) { + throw new \LogicException('octdec() only supports strings in this compiler build'); + } + $frame->returnVar->int((int) \octdec($v->toString())); + } + + public Context $context; + + public function call(Context $context, JITVariable ...$args): Value + { + $this->context = $context; + if (1 !== count($args)) { + throw new \LogicException('octdec() requires exactly one argument'); + } + if (JITVariable::TYPE_STRING !== $args[0]->type) { + throw new \LogicException('octdec() only supports strings in this compiler build'); + } + $ptr = $this->stringDataPtr($context, $context->helper->loadValue($args[0])); + $endPtr = $context->getTypeFromString('int8**')->constPointerNull(); + $base = $context->getTypeFromString('int32')->constInt(8, false); + $fn = $context->lookupFunction('strtol'); + $raw = $context->builder->call($fn, $ptr, $endPtr, $base); + $i64 = $context->getTypeFromString('int64'); + + return $context->builder->trunc($raw, $i64); + } + + private function stringDataPtr(Context $context, Value $strPtr): Value + { + $structName = $strPtr->typeOf()->getElementType()->getName(); + $off = $context->structFieldMap[$structName]['value']; + + return $context->builder->structGep($strPtr, $off); + } +} diff --git a/test/compliance/cases/stdlib/bindec.phpt b/test/compliance/cases/stdlib/bindec.phpt new file mode 100644 index 00000000000..2c9b909ee73 --- /dev/null +++ b/test/compliance/cases/stdlib/bindec.phpt @@ -0,0 +1,13 @@ +--TEST-- +stdlib bindec() for binary strings +--FILE-- +