Skip to content

Commit 0a00dae

Browse files
tested and finalized vector structure implementation
1 parent 7a94c6a commit 0a00dae

File tree

7 files changed

+115
-51
lines changed

7 files changed

+115
-51
lines changed

src/packstream/v1/Packer.php

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -226,13 +226,7 @@ private function packStructure(IStructure $structure): iterable
226226
throw new PackException('Undefined parameter type in structure ' . $structure);
227227
}
228228

229-
$packerMethod = $type->getName();
230-
if ($packerMethod === 'int') {
231-
$packerMethod = 'integer';
232-
}
233-
$packerMethod = 'pack' . ucfirst($packerMethod);
234-
235-
yield from call_user_func([$this, $packerMethod], $structure->{$parameter->getName()});
229+
yield from $this->p($structure->{$parameter->getName()});
236230
}
237231
}
238232

@@ -241,7 +235,7 @@ private function packStructure(IStructure $structure): iterable
241235
*/
242236
private function packByteArray(Bytes $bytes): iterable
243237
{
244-
$size = count($bytes);
238+
$size = mb_strlen($bytes, '8bit');
245239
if ($size < self::MEDIUM) {
246240
yield chr(0xCC) . pack('C', $size) . $bytes;
247241
} elseif ($size < self::LARGE) {

src/protocol/V6.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
class V6 extends AProtocol
1414
{
1515
use \Bolt\protocol\v6\AvailableStructures;
16-
use \Bolt\protocol\v4\ServerStateTransition;
16+
use \Bolt\protocol\v5_1\ServerStateTransition;
1717

1818
use \Bolt\protocol\v1\ResetMessage;
1919

@@ -26,7 +26,12 @@ class V6 extends AProtocol
2626
use \Bolt\protocol\v4\PullMessage;
2727
use \Bolt\protocol\v4\DiscardMessage;
2828

29-
use \Bolt\protocol\v4_1\HelloMessage;
30-
3129
use \Bolt\protocol\v4_4\RouteMessage;
30+
31+
use \Bolt\protocol\v5_1\LogonMessage;
32+
use \Bolt\protocol\v5_1\LogoffMessage;
33+
34+
use \Bolt\protocol\v5_3\HelloMessage;
35+
36+
use \Bolt\protocol\v5_4\TelemetryMessage;
3237
}

src/protocol/v3/HelloMessage.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ trait HelloMessage
1212
* The HELLO message request the connection to be authorized for use with the remote database.
1313
*
1414
* @link https://www.neo4j.com/docs/bolt/current/bolt/message/#messages-hello
15-
* @param array $extra Use \Bolt\helpers\Auth to generate appropriate array
15+
* @param array $extra
1616
* @throws BoltException
1717
*/
1818
public function hello(array $extra): static

src/protocol/v4_1/HelloMessage.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ trait HelloMessage
1515
* The HELLO message request the connection to be authorized for use with the remote database.
1616
*
1717
* @link https://www.neo4j.com/docs/bolt/current/bolt/message/#messages-hello
18-
* @param array $extra Use \Bolt\helpers\Auth to generate appropiate array
18+
* @param array $extra
1919
* @throws BoltException
2020
*/
2121
public function hello(array $extra): static

src/protocol/v6/structures/Vector.php

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,21 @@ public static function encode(array $data): self
4444
throw new \InvalidArgumentException('Vector cannot have more than 4096 elements');
4545
}
4646

47-
$allIntegers = array_reduce($data, fn($carry, $item) => $carry && is_int($item), true);
48-
$allFloats = array_reduce($data, fn($carry, $item) => $carry && is_float($item), true);
49-
50-
// Check if all values are integer or float
51-
if (!$allIntegers && !$allFloats) {
52-
throw new \InvalidArgumentException('All values in the vector must be integer xor float');
53-
}
54-
47+
$anyFloat = in_array(true, array_map('is_float', $data));
5548
$minValue = min($data);
5649
$maxValue = max($data);
5750
$marker = 0;
58-
$packed = [];
5951
$packFormat = '';
6052

61-
if ($allIntegers) {
53+
if ($anyFloat) {
54+
if ($minValue >= 1.4e-45 && $maxValue <= 3.4028235e+38) { // Single precision float (FLOAT_32)
55+
$marker = 0xC6;
56+
$packFormat = 'G';
57+
} else { // Double precision float (FLOAT_64)
58+
$marker = 0xC1;
59+
$packFormat = 'E';
60+
}
61+
} else {
6262
if ($minValue >= -128 && $maxValue <= 127) { // INT_8
6363
$marker = 0xC8;
6464
$packFormat = 'c';
@@ -72,21 +72,14 @@ public static function encode(array $data): self
7272
$marker = 0xCB;
7373
$packFormat = 'q';
7474
}
75-
} elseif ($allFloats) {
76-
if ($minValue >= 1.4e-45 && $maxValue <= 3.4028235e+38) { // Single precision float (FLOAT_32)
77-
$marker = 0xC6;
78-
$packFormat = 'G';
79-
} else { // Double precision float (FLOAT_64)
80-
$marker = 0xC1;
81-
$packFormat = 'E';
82-
}
8375
}
8476

8577
if ($marker === 0) {
8678
throw new \InvalidArgumentException('Unsupported data type for vector');
8779
}
8880

8981
// Pack the data
82+
$packed = [];
9083
$littleEndian = unpack('S', "\x01\x00")[1] === 1;
9184
foreach ($data as $entry) {
9285
$value = pack($packFormat, $entry);

tests/structures/V6/StructuresTest.php

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -35,33 +35,79 @@ public function testInit(): AProtocol
3535
return $protocol;
3636
}
3737

38-
// todo ..also test encode and decode in Vector class
3938
/**
4039
* @depends testInit
4140
*/
4241
public function testVector(AProtocol $protocol)
4342
{
44-
$this->markTestIncomplete('This test has not been implemented yet.');
4543
//unpack
46-
// $res = iterator_to_array(
47-
// $protocol
48-
// ->run('RETURN ', [], ['mode' => 'r'])
49-
// ->pull()
50-
// ->getResponses(),
51-
// false
52-
// );
53-
// $this->assertInstanceOf(Vector::class, $res[1]->content[0]);
44+
$res = iterator_to_array(
45+
$protocol
46+
->run('CYPHER 25 RETURN vector([1.05, 0.123, 5], 3, FLOAT),
47+
vector([1.05, 0.123, 5], 3, FLOAT32),
48+
vector([5, 543, 342765], 3, INTEGER),
49+
vector([5, -60, 120], 3, INTEGER8),
50+
vector([5, -20000, 30000], 3, INTEGER16),
51+
vector([5, -2000000000, 2000000000], 3, INTEGER32)',
52+
[], ['mode' => 'r'])
53+
->pull()
54+
->getResponses(),
55+
false
56+
);
57+
58+
foreach ($res[1]->content as $vector) {
59+
$this->assertInstanceOf(Vector::class, $vector);
60+
}
61+
62+
// float64
63+
$values = $res[1]->content[0]->decode();
64+
$this->assertEqualsWithDelta([1.05, 0.123, 5], $values, 1e-6);
65+
// float32
66+
$values = $res[1]->content[1]->decode();
67+
$this->assertEqualsWithDelta([1.05, 0.123, 5], $values, 1e-6);
68+
// int64
69+
$values = $res[1]->content[2]->decode();
70+
$this->assertEquals([5, 543, 342765], $values);
71+
// int8
72+
$values = $res[1]->content[3]->decode();
73+
$this->assertEquals([5, -60, 120], $values);
74+
// int16
75+
$values = $res[1]->content[4]->decode();
76+
$this->assertEquals([5, -20000, 30000], $values);
77+
// int32
78+
$values = $res[1]->content[5]->decode();
79+
$this->assertEquals([5, -2000000000, 2000000000], $values);
5480

5581
//pack
56-
// $res = iterator_to_array(
57-
// $protocol
58-
// ->run('RETURN toString($p)', [
59-
// 'p' => $res[1]->content[0]
60-
// ], ['mode' => 'r'])
61-
// ->pull()
62-
// ->getResponses(),
63-
// false
64-
// );
65-
// $this->assertStringStartsWith('point(', $res[1]->content[0]);
82+
$res = iterator_to_array(
83+
$protocol
84+
->run('CYPHER 25 RETURN toFloatList($float), toIntegerList($int64), toIntegerList($int8), toIntegerList($int16), toIntegerList($int32)', [
85+
'float' => Vector::encode([1.05, 0.123, 5.0]),
86+
'int64' => Vector::encode([5, -21474836480, 21474836470]),
87+
'int8' => Vector::encode([5, -60, 120]),
88+
'int16' => Vector::encode([5, -20000, 30000]),
89+
'int32' => Vector::encode([5, -2000000000, 2000000000]),
90+
], ['mode' => 'r'])
91+
->pull()
92+
->getResponses(),
93+
false
94+
);
95+
96+
$this->assertEqualsWithDelta([1.05, 0.123, 5], $res[1]->content[0], 1e-6);
97+
$this->assertEquals([5, -21474836480, 21474836470], $res[1]->content[1]);
98+
$this->assertEquals([5, -60, 120], $res[1]->content[2]);
99+
$this->assertEquals([5, -20000, 30000], $res[1]->content[3]);
100+
$this->assertEquals([5, -2000000000, 2000000000], $res[1]->content[4]);
101+
}
102+
103+
/**
104+
* @depends testInit
105+
*/
106+
public function testVectorExceptions()
107+
{
108+
$this->expectException(\InvalidArgumentException::class);
109+
Vector::encode([]);
110+
$this->expectException(\InvalidArgumentException::class);
111+
Vector::encode(range(1, 5000));
66112
}
67113
}

tests/structures/v1/StructuresTest.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
UnboundRelationship
2727
};
2828
use Bolt\enum\Signature;
29+
use Bolt\packstream\Bytes;
2930

3031
/**
3132
* Class StructuresTest
@@ -406,4 +407,29 @@ public function testTime(int $timestamp, string $timezone, AProtocol|V4_4|V4_3|V
406407
$time = preg_replace(["/\.?0+(.\d{2}:\d{2})$/", "/\+00:00$/"], ['$1', 'Z'], $time);
407408
$this->assertEquals($time, $res[1]->content[0], 'pack ' . $time . ' != ' . $res[1]->content[0]);
408409
}
410+
411+
/**
412+
* @depends testInit
413+
*/
414+
public function testBytes(AProtocol|V4_4|V4_3|V4_2|V3 $protocol): void
415+
{
416+
$bytes = new Bytes([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]);
417+
$res = iterator_to_array(
418+
$protocol
419+
->begin()
420+
->run('CREATE (:Test { k: 123, b: $b })', [
421+
'b' => $bytes
422+
], ['mode' => 'w'])
423+
->run('MATCH (n:Test { k: 123 }) RETURN n.b')
424+
->pull()
425+
->rollback()
426+
->getResponses(),
427+
false
428+
);
429+
$this->assertInstanceOf(Bytes::class, $res[3]->content[0]);
430+
$this->assertEquals(
431+
(string)$bytes,
432+
(string)($res[3]->content[0])
433+
);
434+
}
409435
}

0 commit comments

Comments
 (0)