diff options
Diffstat (limited to 'src/Execution/MemInst.php')
| -rw-r--r-- | src/Execution/MemInst.php | 192 |
1 files changed, 191 insertions, 1 deletions
diff --git a/src/Execution/MemInst.php b/src/Execution/MemInst.php index 240e415..4b2ee4b 100644 --- a/src/Execution/MemInst.php +++ b/src/Execution/MemInst.php @@ -13,7 +13,197 @@ final class MemInst */ public function __construct( public readonly MemType $type, - public array $data, + private array $data, ) { } + + public function size(): int + { + return count($this->data); + } + + /** + * @return ?S32 + */ + public function loadI32(int $ptr, int $n, bool $signed): ?int + { + assert($n !== 4 || $signed); + $buf = $this->sliceNBytes($ptr, $n); + if ($buf === null) { + return null; + } + $result = unpack(match ($n) { + 1 => $signed ? 'c' : 'C', + 2 => $signed ? 's' : 'S', + 4 => 'l', + default => throw new \LogicException('Invalid byte length'), + }, $buf); + assert($result !== false); + return $result[1]; + } + + /** + * @return ?S64 + */ + public function loadI64(int $ptr, int $n, bool $signed): ?int + { + assert($n !== 8 || $signed); + $buf = $this->sliceNBytes($ptr, $n); + if ($buf === null) { + return null; + } + $result = unpack(match ($n) { + 1 => $signed ? 'c' : 'C', + 2 => $signed ? 's' : 'S', + 4 => $signed ? 'l' : 'L', + 8 => 'q', + default => throw new \LogicException('Invalid byte length'), + }, $buf); + assert($result !== false); + return $result[1]; + } + + /** + * @return ?F32 + */ + public function loadF32(int $ptr): ?float + { + $buf = $this->sliceNBytes($ptr, 4); + if ($buf === null) { + return null; + } + $result = unpack('f', $buf); + assert($result !== false); + return $result[1]; + } + + /** + * @return ?F64 + */ + public function loadF64(int $ptr): ?float + { + $buf = $this->sliceNBytes($ptr, 8); + if ($buf === null) { + return null; + } + $result = unpack('d', $buf); + assert($result !== false); + return $result[1]; + } + + /** + * @return ?int + */ + public function loadByte(int $ptr): ?int + { + if (count($this->data) < $ptr) { + return null; + } + $c = $this->data[$ptr]; + assert(0x00 <= $c && $c <= 0xFF); + return $c; + } + + /** + * @return bool + */ + public function storeByte(int $ptr, int $c): bool + { + assert(0x00 <= $c && $c <= 0xFF); + if (count($this->data) < $ptr) { + return false; + } + // @phpstan-ignore-next-line + $this->data[$ptr] = $c; + return true; + } + + /** + * @param S32 $c + * @return bool + */ + public function storeI32(int $ptr, int $c, int $n): bool + { + if (count($this->data) < $ptr + $n) { + return false; + } + $buf = pack(match ($n) { + 1 => 'c', + 2 => 's', + 4 => 'l', + default => throw new \LogicException('Invalid byte length'), + }, $c); + for ($i = 0; $i < $n; $i++) { + $this->storeByte($ptr + $i, ord($buf[$i])); + } + return true; + } + + /** + * @param S64 $c + * @return bool + */ + public function storeI64(int $ptr, int $c, int $n): bool + { + if (count($this->data) < $ptr + $n) { + return false; + } + $buf = pack(match ($n) { + 1 => 'c', + 2 => 's', + 4 => 'l', + 8 => 'q', + default => throw new \LogicException('Invalid byte length'), + }, $c); + for ($i = 0; $i < $n; $i++) { + $this->storeByte($ptr + $i, ord($buf[$i])); + } + return true; + } + + /** + * @param F32 $c + * @return bool + */ + public function storeF32(int $ptr, float $c): bool + { + if (count($this->data) < $ptr + 4) { + return false; + } + $buf = pack('f', $c); + for ($i = 0; $i < 4; $i++) { + $this->storeByte($ptr + $i, ord($buf[$i])); + } + return true; + } + + /** + * @param F64 $c + * @return bool + */ + public function storeF64(int $ptr, float $c): bool + { + if (count($this->data) < $ptr + 8) { + return false; + } + $buf = pack('d', $c); + for ($i = 0; $i < 8; $i++) { + $this->storeByte($ptr + $i, ord($buf[$i])); + } + return true; + } + + private function sliceNBytes(int $ptr, int $len): ?string + { + if ($this->size() < $ptr + $len) { + return null; + } + $buf = ''; + for ($i = 0; $i < $len; $i++) { + $b = $this->data[$ptr + $i]; + assert(0x00 <= $b && $b <= 0xFF); + $buf .= chr($b); + } + return $buf; + } } |
