Skip to content

Commit 9abf18d

Browse files
authored
Merge pull request #14 from HandsomeMatt/master
Add Int64 type, methods ReadInt64() and ReadUInt64() and PHP unit tests.
2 parents 23e08d5 + 7e6c078 commit 9abf18d

5 files changed

Lines changed: 290 additions & 0 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ Methods
5757

5858
**readInt32()** returns a 32-bit signed integer
5959

60+
**readUInt64()** returns a 64-bit unsigned integer
61+
62+
**readInt64()** returns a 64-bit signed integer
63+
6064
**readSingle()** returns a 4-bytes floating-point
6165

6266
**readUBits($length)** returns a variable length of bits (unsigned)

src/BinaryReader.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PhpBinaryReader\Type\Int8;
99
use PhpBinaryReader\Type\Int16;
1010
use PhpBinaryReader\Type\Int32;
11+
use PhpBinaryReader\Type\Int64;
1112
use PhpBinaryReader\Type\Single;
1213
use PhpBinaryReader\Type\Str;
1314

@@ -78,6 +79,11 @@ class BinaryReader
7879
*/
7980
private $int32Reader;
8081

82+
/**
83+
* @var \PhpBinaryReader\Type\Int64
84+
*/
85+
private $int64Reader;
86+
8187
/**
8288
* @var \PhpBinaryReader\Type\Single
8389
*/
@@ -109,6 +115,7 @@ public function __construct($input, $endian = Endian::ENDIAN_LITTLE)
109115
$this->int8Reader = new Int8();
110116
$this->int16Reader = new Int16();
111117
$this->int32Reader = new Int32();
118+
$this->int64Reader = new Int64();
112119
$this->singleReader = new Single();
113120
}
114121

@@ -213,6 +220,22 @@ public function readUInt32()
213220
return $this->int32Reader->read($this);
214221
}
215222

223+
/**
224+
* @return int
225+
*/
226+
public function readInt64()
227+
{
228+
return $this->int64Reader->readSigned($this);
229+
}
230+
231+
/**
232+
* @return int
233+
*/
234+
public function readUInt64()
235+
{
236+
return $this->int64Reader->read($this);
237+
}
238+
216239
/**
217240
* @return float
218241
*/
@@ -435,6 +458,14 @@ public function getInt32Reader()
435458
return $this->int32Reader;
436459
}
437460

461+
/**
462+
* @return \PhpBinaryReader\Type\Int64
463+
*/
464+
public function getInt64Reader()
465+
{
466+
return $this->int64Reader;
467+
}
468+
438469
/**
439470
* @return \PhpBinaryReader\Type\Single
440471
*/

src/Type/Int64.php

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
3+
namespace PhpBinaryReader\Type;
4+
5+
use PhpBinaryReader\BinaryReader;
6+
use PhpBinaryReader\BitMask;
7+
use PhpBinaryReader\Endian;
8+
9+
class Int64 implements TypeInterface
10+
{
11+
/**
12+
* @var string
13+
*/
14+
private $endianBig = 'N';
15+
16+
/**
17+
* @var string
18+
*/
19+
private $endianLittle = 'V';
20+
21+
/**
22+
* Returns an Unsigned 64-bit Integer
23+
*
24+
* @param \PhpBinaryReader\BinaryReader $br
25+
* @param null $length
26+
* @return int
27+
* @throws \OutOfBoundsException
28+
*/
29+
public function read(BinaryReader &$br, $length = null)
30+
{
31+
if (!$br->canReadBytes(8)) {
32+
throw new \OutOfBoundsException('Cannot read 64-bit int, it exceeds the boundary of the file');
33+
}
34+
35+
$endian = $br->getEndian() == Endian::ENDIAN_BIG ? $this->endianBig : $this->endianLittle;
36+
$firstSegment = $br->readFromHandle(4);
37+
$secondSegment = $br->readFromHandle(4);
38+
39+
$firstHalf = unpack($endian, $firstSegment)[1];
40+
$secondHalf = unpack($endian, $secondSegment)[1];
41+
42+
if ($br->getEndian() == Endian::ENDIAN_BIG) {
43+
$value = bcadd($secondHalf, bcmul($firstHalf, "4294967296"));
44+
} else {
45+
$value = bcadd($firstHalf, bcmul($secondHalf, "4294967296"));
46+
}
47+
48+
if ($br->getCurrentBit() != 0) {
49+
$value = $this->bitReader($br, $value);
50+
}
51+
52+
return $value;
53+
}
54+
55+
/**
56+
* Returns a Signed 64-Bit Integer
57+
*
58+
* @param \PhpBinaryReader\BinaryReader $br
59+
* @return int
60+
*/
61+
public function readSigned(&$br)
62+
{
63+
$value = $this->read($br);
64+
if (bccomp($value, bcpow(2, 63)) >= 0) {
65+
$value = bcsub($value, bcpow(2, 64));
66+
}
67+
68+
return $value;
69+
}
70+
71+
/**
72+
* @param \PhpBinaryReader\BinaryReader $br
73+
* @param int $data
74+
* @return int
75+
*/
76+
private function bitReader(&$br, $data)
77+
{
78+
$bitmask = new BitMask();
79+
$loMask = $bitmask->getMask($br->getCurrentBit(), BitMask::MASK_LO);
80+
$hiMask = $bitmask->getMask($br->getCurrentBit(), BitMask::MASK_HI);
81+
$hiBits = ($br->getNextByte() & $hiMask) << 56;
82+
$miBits = ($data & 0xFFFFFFFFFFFFFF00) >> (8 - $br->getCurrentBit());
83+
$loBits = ($data & $loMask);
84+
$br->setNextByte($data & 0xFF);
85+
86+
return $hiBits | $miBits | $loBits;
87+
}
88+
89+
/**
90+
* @param string $endianBig
91+
*/
92+
public function setEndianBig($endianBig)
93+
{
94+
$this->endianBig = $endianBig;
95+
}
96+
97+
/**
98+
* @return string
99+
*/
100+
public function getEndianBig()
101+
{
102+
return $this->endianBig;
103+
}
104+
105+
/**
106+
* @param string $endianLittle
107+
*/
108+
public function setEndianLittle($endianLittle)
109+
{
110+
$this->endianLittle = $endianLittle;
111+
}
112+
113+
/**
114+
* @return string
115+
*/
116+
public function getEndianLittle()
117+
{
118+
return $this->endianLittle;
119+
}
120+
}

test/BinaryReaderTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,21 @@ public function testInt32($brBig, $brLittle)
114114
$this->assertEquals(3, $brLittle->readUInt32());
115115
}
116116

117+
/**
118+
* @dataProvider binaryReaders
119+
*/
120+
public function testInt64($brBig, $brLittle)
121+
{
122+
$this->assertEquals(12885059444, $brBig->readInt64());
123+
$this->assertEquals(8387672839590772739, $brLittle->readInt64());
124+
125+
$brLittle->setPosition(0);
126+
$brBig->setPosition(0);
127+
128+
$this->assertEquals(12885059444, $brBig->readUInt64());
129+
$this->assertEquals(8387672839590772739, $brLittle->readUInt64());
130+
}
131+
117132
/**
118133
* @param \PhpBinaryReader\BinaryReader $brBig
119134
* @param \PhpBinaryReader\BinaryReader $brLittle
@@ -321,6 +336,7 @@ public function testReaders()
321336
$this->assertInstanceOf('\PhpBinaryReader\Type\Byte', $brBig->getByteReader());
322337
$this->assertInstanceOf('\PhpBinaryReader\Type\Int16', $brBig->getInt16Reader());
323338
$this->assertInstanceOf('\PhpBinaryReader\Type\Int32', $brBig->getInt32Reader());
339+
$this->assertInstanceOf('\PhpBinaryReader\Type\Int64', $brBig->getInt64Reader());
324340
$this->assertInstanceOf('\PhpBinaryReader\Type\Int8', $brBig->getInt8Reader());
325341
$this->assertInstanceOf('\PhpBinaryReader\Type\Str', $brBig->getStringReader());
326342
$this->assertInstanceOf('\PhpBinaryReader\Type\Single', $brBig->getSingleReader());

test/Type/Int64Test.php

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
<?php
2+
3+
namespace PhpBinaryReader\Type;
4+
5+
use PhpBinaryReader\AbstractTestCase;
6+
use PhpBinaryReader\BinaryReader;
7+
use PhpBinaryReader\Endian;
8+
9+
/**
10+
* @coversDefaultClass \PhpBinaryReader\Type\Int64
11+
*/
12+
class Int64Test extends AbstractTestCase
13+
{
14+
/**
15+
* @var Int64
16+
*/
17+
public $int64;
18+
19+
public function setUp()
20+
{
21+
$this->int64 = new Int64();
22+
}
23+
24+
/**
25+
* @dataProvider binaryReaders
26+
*/
27+
public function testUnsignedReaderWithBigEndian($brBig, $brLittle)
28+
{
29+
$this->assertEquals(12885059444, $this->int64->read($brBig));
30+
$this->assertEquals(7310314309530157055, $this->int64->read($brBig));
31+
}
32+
33+
/**
34+
* @dataProvider binaryReaders
35+
*/
36+
public function testSignedReaderWithBigEndian($brBig, $brLittle)
37+
{
38+
$brBig->setPosition(12);
39+
$this->assertEquals(-3229614080, $this->int64->readSigned($brBig));
40+
}
41+
42+
/**
43+
* @dataProvider binaryReaders
44+
*/
45+
public function testReaderWithLittleEndian($brBig, $brLittle)
46+
{
47+
$this->assertEquals(8387672839590772739, $this->int64->read($brLittle));
48+
$this->assertEquals(18446744069975864165, $this->int64->read($brLittle));
49+
}
50+
51+
/**
52+
* @dataProvider binaryReaders
53+
*/
54+
public function testSignedReaderWithLittleEndian($brBig, $brLittle)
55+
{
56+
$brLittle->setPosition(12);
57+
$this->assertEquals(4575657225703391231, $this->int64->readSigned($brLittle));
58+
}
59+
60+
/**
61+
* @dataProvider binaryReaders
62+
*/
63+
public function testBitReaderWithBigEndian($brBig, $brLittle)
64+
{
65+
$brBig->setPosition(6);
66+
$brBig->readBits(4);
67+
$this->assertEquals(504403158265495567, $this->int64->read($brBig));
68+
}
69+
70+
/**
71+
* @dataProvider binaryReaders
72+
*/
73+
public function testBitReaderWithLittleEndian($brBig, $brLittle)
74+
{
75+
$brLittle->setPosition(6);
76+
$brLittle->readBits(4);
77+
$this->assertEquals(504403158265495567, $this->int64->read($brLittle));
78+
}
79+
80+
/**
81+
* @expectedException \OutOfBoundsException
82+
* @dataProvider binaryReaders
83+
*/
84+
public function testOutOfBoundsExceptionIsThrownWithBigEndian($brBig, $brLittle)
85+
{
86+
$brBig->readBits(360);
87+
$this->int64->read($brBig);
88+
}
89+
90+
/**
91+
* @expectedException \OutOfBoundsException
92+
* @dataProvider binaryReaders
93+
*/
94+
public function testOutOfBoundsExceptionIsThrownWithLittleEndian($brBig, $brLittle)
95+
{
96+
$brLittle->readBits(360);
97+
$this->int64->read($brLittle);
98+
}
99+
100+
/**
101+
* @dataProvider binaryReaders
102+
*/
103+
public function testAlternateMachineByteOrderSigned($brBig, $brLittle)
104+
{
105+
$brLittle->setMachineByteOrder(Endian::ENDIAN_BIG);
106+
$brLittle->setEndian(Endian::ENDIAN_LITTLE);
107+
$this->assertEquals(8387672839590772739, $this->int64->readSigned($brLittle));
108+
}
109+
110+
public function testEndian()
111+
{
112+
$this->int64->setEndianBig('X');
113+
$this->assertEquals('X', $this->int64->getEndianBig());
114+
115+
$this->int64->setEndianLittle('Y');
116+
$this->assertEquals('Y', $this->int64->getEndianLittle());
117+
}
118+
119+
}

0 commit comments

Comments
 (0)