feat(php): use new FFI for Arboricx
This commit is contained in:
138
ext/php/src/ffi.php
Normal file
138
ext/php/src/ffi.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Arboricx;
|
||||
|
||||
/**
|
||||
* FFI wrapper around libarboricx.so.
|
||||
*
|
||||
* Loads the shared library and exposes typed wrappers for the C ABI.
|
||||
*/
|
||||
final class ArboricxFFI
|
||||
{
|
||||
private static ?\FFI $ffi = null;
|
||||
|
||||
public static function init(string $libPath): void
|
||||
{
|
||||
if (self::$ffi !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Nix output layout first, then repo layout.
|
||||
$candidates = [
|
||||
__DIR__ . '/../arboricx.h',
|
||||
__DIR__ . '/../../zig/include/arboricx.h',
|
||||
];
|
||||
$headerRaw = false;
|
||||
foreach ($candidates as $path) {
|
||||
$headerRaw = file_get_contents($path);
|
||||
if ($headerRaw !== false) break;
|
||||
}
|
||||
if ($headerRaw === false) {
|
||||
throw new \RuntimeException('Cannot read arboricx.h');
|
||||
}
|
||||
|
||||
// PHP FFI only parses plain C declarations.
|
||||
$header = $headerRaw;
|
||||
$header = preg_replace('/#.*\n/', "\n", $header);
|
||||
$header = preg_replace('/extern\s+"C"\s*\{/', '', $header);
|
||||
$header = str_replace('}', '', $header);
|
||||
$header = preg_replace('/\n\s*\n+/', "\n", $header);
|
||||
|
||||
self::$ffi = \FFI::cdef($header, $libPath);
|
||||
}
|
||||
|
||||
public static function ffi(): \FFI
|
||||
{
|
||||
if (self::$ffi === null) {
|
||||
throw new \RuntimeException('ArboricxFFI not initialized. Call ArboricxFFI::init($libPath) first.');
|
||||
}
|
||||
return self::$ffi;
|
||||
}
|
||||
}
|
||||
|
||||
function ctx_init(string $libPath): \FFI\CData
|
||||
{
|
||||
ArboricxFFI::init($libPath);
|
||||
$ctx = ArboricxFFI::ffi()->arboricx_init();
|
||||
if ($ctx === null) {
|
||||
throw new \RuntimeException('arboricx_init failed');
|
||||
}
|
||||
return $ctx;
|
||||
}
|
||||
|
||||
function ctx_free(\FFI\CData $ctx): void
|
||||
{
|
||||
ArboricxFFI::ffi()->arboricx_free($ctx);
|
||||
}
|
||||
|
||||
function app(\FFI\CData $ctx, int $func, int $arg): int
|
||||
{
|
||||
return ArboricxFFI::ffi()->arb_app($ctx, $func, $arg);
|
||||
}
|
||||
|
||||
function reduce(\FFI\CData $ctx, int $root, int $fuel = 1_000_000_000): int
|
||||
{
|
||||
return ArboricxFFI::ffi()->arb_reduce($ctx, $root, $fuel);
|
||||
}
|
||||
|
||||
function ofNumber(\FFI\CData $ctx, int $n): int
|
||||
{
|
||||
return ArboricxFFI::ffi()->arb_of_number($ctx, $n);
|
||||
}
|
||||
|
||||
function ofString(\FFI\CData $ctx, string $s): int
|
||||
{
|
||||
return ArboricxFFI::ffi()->arb_of_string($ctx, $s);
|
||||
}
|
||||
|
||||
function toNumber(\FFI\CData $ctx, int $root): int
|
||||
{
|
||||
$out = ArboricxFFI::ffi()->new('uint64_t');
|
||||
$ok = ArboricxFFI::ffi()->arb_to_number($ctx, $root, \FFI::addr($out));
|
||||
if (!$ok) {
|
||||
throw new \RuntimeException('arb_to_number failed');
|
||||
}
|
||||
return (int) $out->cdata;
|
||||
}
|
||||
|
||||
function toString(\FFI\CData $ctx, int $root): string
|
||||
{
|
||||
$ptr = ArboricxFFI::ffi()->new('uint8_t*');
|
||||
$len = ArboricxFFI::ffi()->new('size_t');
|
||||
$ok = ArboricxFFI::ffi()->arb_to_string($ctx, $root, \FFI::addr($ptr), \FFI::addr($len));
|
||||
if (!$ok) {
|
||||
throw new \RuntimeException('arb_to_string failed');
|
||||
}
|
||||
$length = (int) $len->cdata;
|
||||
$result = '';
|
||||
for ($i = 0; $i < $length; $i++) {
|
||||
$result .= chr($ptr[$i]);
|
||||
}
|
||||
ArboricxFFI::ffi()->arboricx_free_buf($ctx, $ptr, $length);
|
||||
return $result;
|
||||
}
|
||||
|
||||
function toBool(\FFI\CData $ctx, int $root): bool
|
||||
{
|
||||
$out = ArboricxFFI::ffi()->new('int');
|
||||
$ok = ArboricxFFI::ffi()->arb_to_bool($ctx, $root, \FFI::addr($out));
|
||||
if (!$ok) {
|
||||
throw new \RuntimeException('arb_to_bool failed');
|
||||
}
|
||||
return (bool) $out->cdata;
|
||||
}
|
||||
|
||||
function loadBundleDefault(\FFI\CData $ctx, string $bytes): int
|
||||
{
|
||||
$cdata = ArboricxFFI::ffi()->new('uint8_t[' . strlen($bytes) . ']');
|
||||
for ($i = 0; $i < strlen($bytes); $i++) {
|
||||
$cdata[$i] = ord($bytes[$i]);
|
||||
}
|
||||
$result = ArboricxFFI::ffi()->arb_load_bundle_default($ctx, $cdata, strlen($bytes));
|
||||
if ($result === 0) {
|
||||
throw new \RuntimeException('arb_load_bundle_default failed');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
Reference in New Issue
Block a user