From aa71ba37c9443860017b495a43802d589afb4985 Mon Sep 17 00:00:00 2001 From: Yuichi Ishikawa Date: Sun, 17 Mar 2024 12:20:37 +0900 Subject: [PATCH] First Commit --- .gitattributes | 6 + .github/workflows/tests.yml | 31 ++ .gitignore | 5 + LICENSE | 29 ++ README.md | 29 +- composer.json | 19 + src/Buffer.php | 181 +++++++++ src/BufferFactory.php | 17 + src/buffer.h | 5 + tests/.gitignore | 4 + tests/Bootstrap.php | 18 + .../Math/Matrix/Buffer/FFI/BufferTest.php | 380 ++++++++++++++++++ tests/autoload.php | 3 + tests/init_autoloader.php | 11 + tests/phpunit.xml | 9 + 15 files changed, 746 insertions(+), 1 deletion(-) create mode 100644 .gitattributes create mode 100644 .github/workflows/tests.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 composer.json create mode 100644 src/Buffer.php create mode 100644 src/BufferFactory.php create mode 100644 src/buffer.h create mode 100644 tests/.gitignore create mode 100644 tests/Bootstrap.php create mode 100644 tests/RindowTest/Math/Matrix/Buffer/FFI/BufferTest.php create mode 100644 tests/autoload.php create mode 100644 tests/init_autoloader.php create mode 100644 tests/phpunit.xml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ef3adcf --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +* text=auto +/tests export-ignore +/vendor export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.travis.yml export-ignore diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..8c72c5e --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,31 @@ +name: tests + +on: [push] + +jobs: + tests: + runs-on: ubuntu-22.04 + + strategy: + fail-fast: true + matrix: + php: ['8.1','8.2','8.3'] + + steps: + - name: Checkout codes + uses: "actions/checkout@v4" + + - name: Composer + uses: php-actions/composer@v6 + with: + php_version: ${{ matrix.php }} + php_extensions: ffi + + - name: PHPUnit Tests + uses: php-actions/phpunit@v3 + with: + configuration: tests/phpunit.xml + version: 10.5 + php_version: ${{ matrix.php }} + php_extensions: ffi + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5544455 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +vendor/ +*.log +composer.lock +composer.phar +tests/.phpunit.* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5a03803 --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2018, rindow +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 9506bc9..c2aca1e 100644 --- a/README.md +++ b/README.md @@ -1 +1,28 @@ -# rindow-math-buffer-ffi \ No newline at end of file +The Buffer for math libraries +============================= +Status: +[![Build Status](https://github.com/rindow/rindow-math-buffer-ffi/workflows/tests/badge.svg)](https://github.com/rindow/rindow-math-buffer-ffi/actions) +[![Downloads](https://img.shields.io/packagist/dt/rindow/rindow-math-buffer-ffi)](https://packagist.org/packages/rindow/rindow-math-buffer-ffi) +[![Latest Stable Version](https://img.shields.io/packagist/v/rindow/rindow-math-buffer-ffi)](https://packagist.org/packages/rindow/rindow-math-buffer-ffi) +[![License](https://img.shields.io/packagist/l/rindow/rindow-math-buffer-ffi)](https://packagist.org/packages/rindow/rindow-math-buffer-ffi) + +"The rindow math buffer ffi" is a buffer for the math library. Available in libraries with FFI interface. + +- Provides Universal Buffer for 1-dimension for data exchange between C,C+ language and PHP. + + +Please see the documents about Buffer objects on [Rindow Mathematics](https://rindow.github.io/mathematics/matrix/arrayobjects.html#buffer-object) web pages. + +Requirements +============ + +- PHP 8.1 or PHP8.2 or PHP8.3 +- Linux or Windows + +How to setup +============ +Install using composer. + +$ composer require rindow/rindow-math-buffer-ffi + + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a8acf91 --- /dev/null +++ b/composer.json @@ -0,0 +1,19 @@ +{ + "name": "rindow/rindow-math-buffer-ffi", + "type": "library", + "description": "The Buffer for math libraries", + "keywords": ["rindow","math","matrix","buffer","FFI"], + "license": "BSD-3-Clause", + "require": { + "php": "^8.1", + "interop-phpobjects/polite-math": "~1.0.6" + }, + "require-dev": { + "ext-ffi": "*" + }, + "autoload": { + "psr-4": { + "Rindow\\Math\\Buffer\\FFI\\": "src/" + } + } +} diff --git a/src/Buffer.php b/src/Buffer.php new file mode 100644 index 0000000..826e18c --- /dev/null +++ b/src/Buffer.php @@ -0,0 +1,181 @@ + 'uint8_t', + NDArray::int8 => 'int8_t', + NDArray::int16 => 'int16_t', + NDArray::int32 => 'int32_t', + NDArray::int64 => 'int64_t', + NDArray::uint8 => 'uint8_t', + NDArray::uint16 => 'uint16_t', + NDArray::uint32 => 'uint32_t', + NDArray::uint64 => 'uint64_t', + //NDArray::float8 => 'N/A', + //NDArray::float16 => 'N/A', + NDArray::float32 => 'float', + NDArray::float64 => 'double', + //NDArray::complex16 => 'N/A', + //NDArray::complex32 => 'N/A', + NDArray::complex64 => 'rindow_complex_float', + NDArray::complex128 => 'rindow_complex_double', + ]; + protected static $valueSize = [ + NDArray::bool => 1, + NDArray::int8 => 1, + NDArray::int16 => 2, + NDArray::int32 => 4, + NDArray::int64 => 8, + NDArray::uint8 => 1, + NDArray::uint16 => 2, + NDArray::uint32 => 4, + NDArray::uint64 => 8, + //NDArray::float8 => 'N/A', + //NDArray::float16 => 'N/A', + NDArray::float32 => 4, + NDArray::float64 => 8, + //NDArray::complex16 => 'N/A', + //NDArray::complex32 => 'N/A', + NDArray::complex64 => 8, + NDArray::complex128 => 16, + ]; + + protected int $size; + protected int $dtype; + protected object $data; + + public function __construct(int $size, int $dtype) + { + if(self::$ffi===null) { + $code = file_get_contents(__DIR__.'/buffer.h'); + self::$ffi = FFI::cdef($code); + } + if(!isset(self::$typeString[$dtype])) { + throw new InvalidArgumentException("Invalid data type"); + } + $limitsize = intdiv(self::MAX_BYTES,self::$valueSize[$dtype]); + if($size>=$limitsize) { + throw new InvalidArgumentException("Data size is too large."); + } + $this->size = $size; + $this->dtype = $dtype; + $declaration = self::$typeString[$dtype]; + $this->data = self::$ffi->new("{$declaration}[{$size}]"); + } + + protected function assertOffset($method, mixed $offset) : void + { + if(!is_int($offset)) { + throw new TypeError($method.'(): Argument #1 ($offset) must be of type int'); + } + if($offset<0 || $offset>=$this->size) { + throw new OutOfRangeException($method.'(): Index invalid or out of range'); + } + } + + protected function assertOffsetIsInt($method, mixed $offset) : void + { + if(!is_int($offset)) { + throw new TypeError($method.'(): Argument #1 ($offset) must be of type int'); + } + } + + protected function isComplex($dtype=null) : bool + { + $dtype = $dtype ?? $this->dtype; + return $dtype==NDArray::complex64||$dtype==NDArray::complex128; + } + + public function dtype() : int + { + return $this->dtype; + } + + public function value_size() : int + { + return $this::$valueSize[$this->dtype]; + } + + public function addr(int $offset) : FFI\CData + { + return FFI::addr($this->data[$offset]); + } + + public function count() : int + { + return $this->size; + } + + public function offsetExists(mixed $offset) : bool + { + $this->assertOffsetIsInt('offsetExists',$offset); + return ($offset>=0)&&($offset<$this->size); + } + + public function offsetGet(mixed $offset): mixed + { + $this->assertOffset('offsetGet',$offset); + $value = $this->data[$offset]; + if($this->dtype===NDArray::bool) { + $value = $value ? true : false; + } + return $value; + } + + public function offsetSet(mixed $offset, mixed $value): void + { + $this->assertOffset('offsetSet',$offset); + if($this->isComplex()) { + if(is_array($value)) { + [$real,$imag] = $value; + } elseif(is_object($value)) { + $real = $value->real; + $imag = $value->imag; + } else { + $type = gettype($value); + throw new InvalidArgumentException("Cannot convert to complex number.: ".$type); + } + $value = self::$ffi->new(self::$typeString[$this->dtype]); + $value->real = $real; + $value->imag = $imag; + } + $this->data[$offset] = $value; + } + + public function offsetUnset(mixed $offset): void + { + //$this->assertOffsetIsInt('offsetUnset',$offset); + throw new LogicException("Illigal Operation"); + } + + public function dump() : string + { + $byte = self::$valueSize[$this->dtype] * $this->size; + $buf = self::$ffi->new("char[$byte]"); + FFI::memcpy($buf,$this->data,$byte); + return FFI::string($buf,$byte); + } + + public function load(string $string) : void + { + $byte = self::$valueSize[$this->dtype] * $this->size; + $strlen = strlen($string); + if($strlen!=$byte) { + throw new InvalidArgumentException("Unmatch data size. buffer size is $byte. $strlen byte given."); + } + FFI::memcpy($this->data,$string,$byte); + } +} diff --git a/src/BufferFactory.php b/src/BufferFactory.php new file mode 100644 index 0000000..891167b --- /dev/null +++ b/src/BufferFactory.php @@ -0,0 +1,17 @@ +assertEquals('0.1.7',phpversion('rindow_openblas')); + //} + + public function testNormal() + { + $buf = new Buffer(3,NDArray::float32); + $buf[0] = 0.5; + $buf[1] = 1.5; + $buf[2] = 2.5; + $this->assertEquals(3,count($buf)); + $this->assertEquals(NDArray::float32,$buf->dtype()); + $this->assertTrue(is_float($buf[0])); + $this->assertEquals(0.5,$buf[0]); + $this->assertEquals(1.5,$buf[1]); + $this->assertEquals(2.5,$buf[2]); + } + + public function testDtypesAndOffsetOfDtypes() + { + $buf = new Buffer(3,NDArray::bool); + $buf[2] = true; + $this->assertEquals(NDArray::bool,$buf->dtype()); + $this->assertTrue(is_bool($buf[0])); + $this->assertEquals(true,$buf[2]); + + $buf = new Buffer(3,NDArray::int8); + $buf[2] = -1; + $this->assertEquals(NDArray::int8,$buf->dtype()); + $this->assertTrue(is_int($buf[0])); + $this->assertEquals(-1,$buf[2]); + + $buf = new Buffer(3,NDArray::uint8); + $buf[2] = -1; + $this->assertEquals(NDArray::uint8,$buf->dtype()); + $this->assertTrue(is_int($buf[0])); + $this->assertEquals(255,$buf[2]); + + $buf = new Buffer(3,NDArray::int16); + $buf[2] = -1; + $this->assertEquals(NDArray::int16,$buf->dtype()); + $this->assertTrue(is_int($buf[0])); + $this->assertEquals(-1,$buf[2]); + + $buf = new Buffer(3,NDArray::uint16); + $buf[2] = -1; + $this->assertEquals(NDArray::uint16,$buf->dtype()); + $this->assertTrue(is_int($buf[0])); + $this->assertEquals(65535,$buf[2]); + + $buf = new Buffer(3,NDArray::int32); + $buf[2] = -1; + $this->assertEquals(NDArray::int32,$buf->dtype()); + $this->assertTrue(is_int($buf[0])); + $this->assertEquals(-1,$buf[2]); + + $buf = new Buffer(3,NDArray::uint32); + $buf[2] = -1; + $this->assertEquals(NDArray::uint32,$buf->dtype()); + $this->assertTrue(is_int($buf[0])); + $this->assertEquals(4294967295,$buf[2]); + + $buf = new Buffer(3,NDArray::int64); + $buf[2] = -1; + $this->assertEquals(NDArray::int64,$buf->dtype()); + $this->assertTrue(is_int($buf[0])); + $this->assertEquals(-1,$buf[2]); + + $buf = new Buffer(3,NDArray::uint64); + $buf[2] = -1; + $this->assertEquals(NDArray::uint64,$buf->dtype()); + $this->assertTrue(is_int($buf[0])); + $this->assertEquals(-1,$buf[2]); // *** CAUTION **** + + $buf = new Buffer(3,NDArray::float32); + $buf[2] = 0.5; + $this->assertEquals(NDArray::float32,$buf->dtype()); + $this->assertTrue(is_float($buf[0])); + $this->assertEquals(0.5,$buf[2]); + + $buf = new Buffer(3,NDArray::float64); + $buf[2] = 0.5; + $this->assertEquals(NDArray::float64,$buf->dtype()); + $this->assertTrue(is_float($buf[0])); + $this->assertEquals(0.5,$buf[2]); + + $buf = new Buffer(3,NDArray::complex64); + $this->assertEquals(NDArray::complex64,$buf->dtype()); + $this->assertEquals(3,count($buf)); + $this->assertEquals(8,$buf->value_size()); + $this->assertEquals(NDArray::complex64,$buf->dtype()); + $buf[1] = [1.5,2.5]; + $buf[2] = (object)['real'=>3.5,'imag'=>4.5]; + $vv = $buf[1]; + $this->assertEquals(1.5,$vv->real); + $this->assertEquals(2.5,$vv->imag); + $vv = $buf[2]; + $this->assertEquals(3.5,$vv->real); + $this->assertEquals(4.5,$vv->imag); + + $buf = new Buffer(3,NDArray::complex128); + $this->assertEquals(NDArray::complex128,$buf->dtype()); + $this->assertEquals(3,count($buf)); + $this->assertEquals(16,$buf->value_size()); + $this->assertEquals(NDArray::complex128,$buf->dtype()); + $buf[1] = [1.5,2.5]; + $buf[2] = (object)['real'=>3.5,'imag'=>4.5]; + $vv = $buf[1]; + $this->assertEquals(1.5,$vv->real); + $this->assertEquals(2.5,$vv->imag); + $vv = $buf[2]; + $this->assertEquals(3.5,$vv->real); + $this->assertEquals(4.5,$vv->imag); + } + + public function testOffsetExists() + { + $buf = new Buffer(3,NDArray::float32); + $this->assertTrue(isset($buf[0])); + $this->assertTrue(isset($buf[2])); + $this->assertFalse(isset($buf[-1])); + $this->assertFalse(isset($buf[3])); + } + + public function testUnset() + { + $buf = new Buffer(3,NDArray::float32); + $buf[0] = 1; + $this->assertEquals(1,$buf[0]); + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Illigal Operation'); + unset($buf[0]); // unset means set zero + //$this->assertEquals(0,$buf[0]); + } + + public function testDumpAndLoad() + { + $buf = new Buffer(3,NDArray::float32); + $buf[0] = 1; + $buf[1] = 2; + $buf[2] = 3; + + $buf2 = new Buffer(3,NDArray::float32); + $buf2[0] = 0; + $buf2[1] = 0; + $buf2[2] = 0; + + $dump = $buf->dump(); + $buf2->load($dump); + $this->assertEquals(1,$buf2[0]); + $this->assertEquals(2,$buf2[1]); + $this->assertEquals(3,$buf2[2]); + } + + public function testSetOutOfBoundsWithHighOffset() + { + //$buf = new \SplFixedArray(3); + $buf = new Buffer(3,NDArray::float32); + $this->expectException(OutOfRangeException::class); + $this->expectExceptionMessage('Index invalid or out of range'); + $buf[3] = 1; + } + + public function testSetOutOfBoundsWithLowOffset() + { + //$buf = new \SplFixedArray(3); + $buf = new Buffer(3,NDArray::float32); + $this->expectException(OutOfRangeException::class); + $this->expectExceptionMessage('Index invalid or out of range'); + $buf[-1] = 1; + } + + public function testGetOutOfBoundsWithHighOffset() + { + //$buf = new \SplFixedArray(3); + $buf = new Buffer(3,NDArray::float32); + $this->expectException(OutOfRangeException::class); + $this->expectExceptionMessage('Index invalid or out of range'); + $x = $buf[3]; + } + + public function testGetOutOfBoundsWithLowOffset() + { + //$buf = new \SplFixedArray(3); + $buf = new Buffer(3,NDArray::float32); + $this->expectException(OutOfRangeException::class); + $this->expectExceptionMessage('Index invalid or out of range'); + $x = $buf[-1]; + } + + public function testUnsetOutOfBoundsWithHighOffset() + { + //$buf = new \SplFixedArray(3); + $buf = new Buffer(3,NDArray::float32); + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Illigal Operation'); + unset($buf[3]); + //$this->assertTrue(true); + } + + public function testUnsetOutOfBoundsWithLowOffset() + { + //$buf = new \SplFixedArray(3); + $buf = new Buffer(3,NDArray::float32); + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Illigal Operation'); + unset($buf[-1]); + //$this->assertTrue(true); + } + + public function testIsExistsOutOfBoundsWithHighOffset() + { + //$buf = new \SplFixedArray(3); + $buf = new Buffer(3,NDArray::float32); + $this->assertFalse(isset($buf[3])); + } + + public function testIsExistsOutOfBoundsWithLowOffset() + { + //$buf = new \SplFixedArray(3); + $buf = new Buffer(3,NDArray::float32); + $this->assertFalse(isset($buf[-1])); + } + + public function testOffsetSetWithNoOffset() + { + $buf = new Buffer(3,NDArray::float32); + $this->expectException(ArgumentCountError::class); + //if(version_compare(PHP_VERSION, '8.0.0')<0) { + // $this->expectExceptionMessage('offsetSet() expects exactly 2 parameters, 0 given'); + //} else { + // $this->expectExceptionMessage('offsetSet() expects exactly 2 arguments, 0 given'); + //} + $this->expectExceptionMessage('Too few arguments to function'); + $a = $buf->offsetSet(); + } + + public function testOffsetSetIllegalTypeOffset() + { + $buf = new Buffer(3,NDArray::float32); + $this->expectException(TypeError::class); + if(version_compare(PHP_VERSION, '8.0.0')<0) { + $this->expectExceptionMessage('offsetSet() expects parameter 1 to be int'); + } else { + $this->expectExceptionMessage('offsetSet(): Argument #1 ($offset) must be of type int'); + } + $buf->offsetSet(new \stdClass(),1); + } + + public function testOffsetGetWithNoOffset() + { + $buf = new Buffer(3,NDArray::float32); + $this->expectException(ArgumentCountError::class); + //if(version_compare(PHP_VERSION, '8.0.0')<0) { + // $this->expectExceptionMessage('offsetGet() expects exactly 1 parameter, 0 given'); + //} else { + // $this->expectExceptionMessage('offsetGet() expects exactly 1 argument, 0 given'); + //} + $this->expectExceptionMessage('Too few arguments to function'); + $a = $buf->offsetGet(); + } + + public function testOffsetGetIllegalType() + { + $buf = new Buffer(3,NDArray::float32); + $this->expectException(TypeError::class); + if(version_compare(PHP_VERSION, '8.0.0')<0) { + $this->expectExceptionMessage('offsetGet() expects parameter 1 to be int'); + } else { + $this->expectExceptionMessage('offsetGet(): Argument #1 ($offset) must be of type int'); + } + $a = $buf->offsetGet(new \stdClass()); + } + + public function testOffsetUnsetWithNoOffset() + { + $buf = new Buffer(3,NDArray::float32); + $this->expectException(ArgumentCountError::class); + //if(version_compare(PHP_VERSION, '8.0.0')<0) { + // $this->expectExceptionMessage('offsetUnset() expects exactly 1 parameter, 0 given'); + //} else { + // $this->expectExceptionMessage('offsetUnset() expects exactly 1 argument, 0 given'); + //} + $this->expectExceptionMessage('Too few arguments to function'); + $buf->offsetUnset(); + } + + public function testOffsetUnsetIllegalType() + { + $buf = new Buffer(3,NDArray::float32); + //$this->expectException(TypeError::class); + //if(version_compare(PHP_VERSION, '8.0.0')<0) { + // $this->expectExceptionMessage('offsetUnset() expects parameter 1 to be int'); + //} else { + // $this->expectExceptionMessage('offsetUnset(): Argument #1 ($offset) must be of type int'); + //} + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Illigal Operation'); + $buf->offsetUnset(new \stdClass()); + } + + public function testLoadWithNoOffset() + { + $buf = new Buffer(3,NDArray::float32); + $this->expectException(ArgumentCountError::class); + //if(version_compare(PHP_VERSION, '8.0.0')<0) { + // $this->expectExceptionMessage('load() expects exactly 1 parameter, 0 given'); + //} else { + // $this->expectExceptionMessage('load() expects exactly 1 argument, 0 given'); + //} + $this->expectExceptionMessage('Too few arguments to function'); + $buf->load(); + } + + public function testLoadIllegalType() + { + $buf = new Buffer(3,NDArray::float32); + $this->expectException(TypeError::class); + if(version_compare(PHP_VERSION, '8.0.0')<0) { + $this->expectExceptionMessage('load() expects parameter 1 to be string'); + } else { + //$this->expectExceptionMessage('load(): Argument #1 must be of type string'); + $this->expectExceptionMessage('load(): Argument #1 ($string) must be of type'); + } + $buf->load(new \stdClass()); + } + + public function testConstractWithNoArgument() + { + $this->expectException(ArgumentCountError::class); + //if(version_compare(PHP_VERSION, '8.0.0')<0) { + // $this->expectExceptionMessage('__construct() expects exactly 2 parameters, 0 given'); + //} else { + // $this->expectExceptionMessage('__construct() expects exactly 2 arguments, 0 given'); + //} + $this->expectExceptionMessage('Too few arguments to function'); + $buf = new Buffer(); + } + + public function testConstractIllegalType() + { + $this->expectException(TypeError::class); + if(version_compare(PHP_VERSION, '8.0.0')<0) { + $this->expectExceptionMessage('__construct() expects parameter 1 to be int'); + } else { + $this->expectExceptionMessage('__construct(): Argument #1 ($size) must be of type int'); + } + $buf = new Buffer(new \stdClass(),NDArray::float32); + } + + public function testAddr() + { + $buf = new Buffer(4,NDArray::int32); + $buf[0] = 10; + $buf[1] = 11; + $buf[2] = 12; + $buf[3] = 13; + $addr = $buf->addr(1); + $this->assertInstanceOf(FFI\CData::class,$addr); + $this->assertEquals(11,$addr[0]); + $this->assertEquals(12,$addr[1]); + $this->assertEquals(13,$addr[2]); + } +} diff --git a/tests/autoload.php b/tests/autoload.php new file mode 100644 index 0000000..22c0516 --- /dev/null +++ b/tests/autoload.php @@ -0,0 +1,3 @@ +addPsr4('Rindow\\Math\\Buffer\\FFI\\',__DIR__.'/../src'); +$loader->addPsr4('Interop\\Polite\\Math\\', __DIR__.'/../../../interop-phpobjects/polite-math/src'); + +return $loader; diff --git a/tests/phpunit.xml b/tests/phpunit.xml new file mode 100644 index 0000000..42ef8f1 --- /dev/null +++ b/tests/phpunit.xml @@ -0,0 +1,9 @@ + + + + + + ./RindowTest + + +