1<?php
2
3/**
4 * This file contains the BinaryReader class.
5 * For more information see the class description below.
6 *
7 * @author Peter Bathory <peter.bathory@cartographia.hu>
8 * @since 2016-02-18
9 *
10 * This code is open-source and licenced under the Modified BSD License.
11 * For the full copyright and license information, please view the LICENSE
12 * file that was distributed with this source code.
13 */
14
15namespace geoPHP\Adapter;
16
17/**
18 * Helper class BinaryReader
19 *
20 * A simple binary reader supporting both byte orders
21 */
22class BinaryReader
23{
24
25    const BIG_ENDIAN = 0;
26    const LITTLE_ENDIAN = 1;
27
28    private $buffer;
29
30    private $endianness = 0;
31
32    /**
33     * BinaryReader constructor.
34     * Opens a memory buffer with the given input
35     *
36     * @param string $input
37     */
38    public function __construct($input)
39    {
40//      if (@is_readable($input)) {
41//          $this->buffer = fopen($input, 'r+');
42//      } else {
43            $this->buffer = fopen('php://memory', 'x+');
44            fwrite($this->buffer, (string) $input);
45            fseek($this->buffer, 0);
46//      }
47    }
48
49    /**
50     * Closes the memory buffer
51     */
52    public function close()
53    {
54        fclose($this->buffer);
55    }
56
57    /**
58     * @param int $endian self::BIG_ENDIAN or self::LITTLE_ENDIAN
59     */
60    public function setEndianness($endian)
61    {
62        $this->endianness = $endian === self::BIG_ENDIAN ? self::BIG_ENDIAN : self::LITTLE_ENDIAN;
63    }
64
65    /**
66     * @return int Returns 0 if reader is in BigEndian mode or 1 if in LittleEndian mode
67     */
68    public function getEndianness()
69    {
70        return $this->endianness;
71    }
72
73    /**
74     * Reads a signed 8-bit integer from the buffer
75     * @return int|null
76     */
77    public function readSInt8()
78    {
79        $char = fread($this->buffer, 1);
80        return $char !== '' ? current(unpack("c", $char)) : null;
81    }
82
83    /**
84     * Reads an unsigned 8-bit integer from the buffer
85     * @return int|null
86     */
87    public function readUInt8()
88    {
89        $char = fread($this->buffer, 1);
90        return $char !== '' ? current(unpack("C", $char)) : null;
91    }
92
93    /**
94     * Reads an unsigned 32-bit integer from the buffer
95     * @return int|null
96     */
97    public function readUInt32()
98    {
99        $int32 = fread($this->buffer, 4);
100        return $int32 !== '' ? current(unpack($this->endianness == self::LITTLE_ENDIAN ? 'V' : 'N', $int32)) : null;
101    }
102
103    /**
104     * Reads one or more double values from the buffer
105     * @param int $length How many double values to read. Default is 1
106     * @return float[]
107     */
108    public function readDoubles($length = 1)
109    {
110        $bin = fread($this->buffer, $length);
111        return $this->endianness == self::LITTLE_ENDIAN
112                ? array_values(unpack("d*", $bin))
113                : array_reverse(unpack("d*", strrev($bin)));
114    }
115
116    /**
117     * Reads an unsigned base-128 varint from the buffer
118     *
119     * Ported from https://github.com/cschwarz/wkx/blob/master/lib/binaryreader.js
120     *
121     * @return int
122     */
123    public function readUVarInt()
124    {
125        $result = 0;
126        $bytesRead = 0;
127
128        do {
129            $nextByte = $this->readUInt8();
130            $result += ($nextByte & 0x7F) << (7 * $bytesRead);
131            $bytesRead++;
132        } while ($nextByte >= 0x80);
133        return $result;
134    }
135
136    /**
137     * Reads a signed base-128 varint from the buffer
138     *
139     * @return int
140     */
141    public function readSVarInt()
142    {
143        return self::zigZagDecode($this->readUVarInt());
144    }
145
146    /**
147     * ZigZag decoding maps unsigned integers to signed integers
148     *
149     * @param int $value Encrypted positive integer value
150     * @return int Decoded signed integer
151     */
152    public static function zigZagDecode($value)
153    {
154        return ($value & 1) === 0 ? $value >> 1 : -($value >> 1) - 1;
155    }
156}
157