tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

ffi.php (3837B)


      1 <?php
      2 
      3 declare(strict_types=1);
      4 
      5 namespace Arboricx;
      6 
      7 /**
      8  * FFI wrapper around libarboricx.so.
      9  *
     10  * Loads the shared library and exposes typed wrappers for the C ABI.
     11  */
     12 final class ArboricxFFI
     13 {
     14     private static ?\FFI $ffi = null;
     15 
     16     public static function init(string $libPath): void
     17     {
     18         if (self::$ffi !== null) {
     19             return;
     20         }
     21 
     22         // Nix output layout first, then repo layout.
     23         $candidates = [
     24             __DIR__ . '/../arboricx.h',
     25             __DIR__ . '/../../zig/include/arboricx.h',
     26         ];
     27         $headerRaw = false;
     28         foreach ($candidates as $path) {
     29             $headerRaw = file_get_contents($path);
     30             if ($headerRaw !== false) break;
     31         }
     32         if ($headerRaw === false) {
     33             throw new \RuntimeException('Cannot read arboricx.h');
     34         }
     35 
     36         // PHP FFI only parses plain C declarations.
     37         $header = $headerRaw;
     38         $header = preg_replace('/#.*\n/', "\n", $header);
     39         $header = preg_replace('/extern\s+"C"\s*\{/', '', $header);
     40         $header = str_replace('}', '', $header);
     41         $header = preg_replace('/\n\s*\n+/', "\n", $header);
     42 
     43         self::$ffi = \FFI::cdef($header, $libPath);
     44     }
     45 
     46     public static function ffi(): \FFI
     47     {
     48         if (self::$ffi === null) {
     49             throw new \RuntimeException('ArboricxFFI not initialized. Call ArboricxFFI::init($libPath) first.');
     50         }
     51         return self::$ffi;
     52     }
     53 }
     54 
     55 function ctx_init(string $libPath): \FFI\CData
     56 {
     57     ArboricxFFI::init($libPath);
     58     $ctx = ArboricxFFI::ffi()->arboricx_init();
     59     if ($ctx === null) {
     60         throw new \RuntimeException('arboricx_init failed');
     61     }
     62     return $ctx;
     63 }
     64 
     65 function ctx_free(\FFI\CData $ctx): void
     66 {
     67     ArboricxFFI::ffi()->arboricx_free($ctx);
     68 }
     69 
     70 function app(\FFI\CData $ctx, int $func, int $arg): int
     71 {
     72     return ArboricxFFI::ffi()->arb_app($ctx, $func, $arg);
     73 }
     74 
     75 function reduce(\FFI\CData $ctx, int $root, int $fuel = 1_000_000_000): int
     76 {
     77     return ArboricxFFI::ffi()->arb_reduce($ctx, $root, $fuel);
     78 }
     79 
     80 function ofNumber(\FFI\CData $ctx, int $n): int
     81 {
     82     return ArboricxFFI::ffi()->arb_of_number($ctx, $n);
     83 }
     84 
     85 function ofString(\FFI\CData $ctx, string $s): int
     86 {
     87     return ArboricxFFI::ffi()->arb_of_string($ctx, $s);
     88 }
     89 
     90 function toNumber(\FFI\CData $ctx, int $root): int
     91 {
     92     $out = ArboricxFFI::ffi()->new('uint64_t');
     93     $ok = ArboricxFFI::ffi()->arb_to_number($ctx, $root, \FFI::addr($out));
     94     if (!$ok) {
     95         throw new \RuntimeException('arb_to_number failed');
     96     }
     97     return (int) $out->cdata;
     98 }
     99 
    100 function toString(\FFI\CData $ctx, int $root): string
    101 {
    102     $ptr = ArboricxFFI::ffi()->new('uint8_t*');
    103     $len = ArboricxFFI::ffi()->new('size_t');
    104     $ok = ArboricxFFI::ffi()->arb_to_string($ctx, $root, \FFI::addr($ptr), \FFI::addr($len));
    105     if (!$ok) {
    106         throw new \RuntimeException('arb_to_string failed');
    107     }
    108     $length = (int) $len->cdata;
    109     $result = '';
    110     for ($i = 0; $i < $length; $i++) {
    111         $result .= chr($ptr[$i]);
    112     }
    113     ArboricxFFI::ffi()->arboricx_free_buf($ctx, $ptr, $length);
    114     return $result;
    115 }
    116 
    117 function toBool(\FFI\CData $ctx, int $root): bool
    118 {
    119     $out = ArboricxFFI::ffi()->new('int');
    120     $ok = ArboricxFFI::ffi()->arb_to_bool($ctx, $root, \FFI::addr($out));
    121     if (!$ok) {
    122         throw new \RuntimeException('arb_to_bool failed');
    123     }
    124     return (bool) $out->cdata;
    125 }
    126 
    127 function loadBundleDefault(\FFI\CData $ctx, string $bytes): int
    128 {
    129     $cdata = ArboricxFFI::ffi()->new('uint8_t[' . strlen($bytes) . ']');
    130     for ($i = 0; $i < strlen($bytes); $i++) {
    131         $cdata[$i] = ord($bytes[$i]);
    132     }
    133     $result = ArboricxFFI::ffi()->arb_load_bundle_default($ctx, $cdata, strlen($bytes));
    134     if ($result === 0) {
    135         throw new \RuntimeException('arb_load_bundle_default failed');
    136     }
    137     return $result;
    138 }