Completed
Pull Request — master (#333)
by thomas
43:53 queued 39:08
created

Parser::rewind()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace BitWasp\Bitcoin\Script\Parser;
4
5
use BitWasp\Bitcoin\Script\Opcodes;
6
use BitWasp\Bitcoin\Math\Math;
7
use BitWasp\Bitcoin\Script\ScriptInterface;
8
use BitWasp\Buffertools\Buffer;
9
use BitWasp\Buffertools\BufferInterface;
10
11
class Parser implements \Iterator
12
{
13
    /**
14
     * @var Math
15
     */
16
    private $math;
17
18
    /**
19
     * @var BufferInterface
20
     */
21
    private $empty;
22
23
    /**
24
     * @var ScriptInterface
25
     */
26
    private $script;
27
28
    /**
29
     * @var int
30
     */
31
    private $position = 0;
32
33
    /**
34
     * @var int
35
     */
36
    private $end = 0;
37
38
    /**
39
     * @var int
40
     */
41
    private $execPtr = 0;
42
43
    /**
44
     * @var string
45
     */
46
    private $data = '';
47
48
    /**
49
     * @var Operation[]
50
     */
51
    private $array = array();
52
53
    /**
54
     * ScriptParser constructor.
55
     * @param Math $math
56
     * @param ScriptInterface $script
57
     */
58 3864
    public function __construct(Math $math, ScriptInterface $script)
59
    {
60 3864
        $this->math = $math;
61 3864
        $buffer = $script->getBuffer();
62 3864
        $this->data = $buffer->getBinary();
63 3864
        $this->end = $buffer->getSize();
64 3864
        $this->script = $script;
65 3864
        $this->empty = new Buffer('', 0, $math);
66 3864
    }
67
68
    /**
69
     * @return int
70
     */
71 3
    public function getPosition()
72
    {
73 3
        return $this->position;
74
    }
75
76
    /**
77
     * @param string $packFormat
78
     * @param integer $strSize
79
     * @return array|bool
80
     */
81 120
    private function unpackSize($packFormat, $strSize)
82
    {
83 120
        if ($this->end - $this->position < $strSize) {
84
            return false;
85
        }
86
87 120
        $size = unpack($packFormat, substr($this->data, $this->position, $strSize));
88 120
        $size = $size[1];
89 120
        $this->position += $strSize;
90
91 120
        return $size;
92
    }
93
94
    /**
95
     * @param int $ptr
96
     * @return Operation
97
     */
98 3825
    private function doNext($ptr)
99
    {
100 3825
        if ($this->position >= $this->end) {
101
            throw new \RuntimeException('Position exceeds end of script!');
102
        }
103
104 3825
        $opCode = ord($this->data[$this->position++]);
105 3825
        $pushData = $this->empty;
106 3825
        $dataSize = 0;
107
108 3825
        if ($opCode <= Opcodes::OP_PUSHDATA4) {
109 2808
            if ($opCode < Opcodes::OP_PUSHDATA1) {
110 2745
                $dataSize = $opCode;
111 1912
            } else if ($opCode === Opcodes::OP_PUSHDATA1) {
112 63
                $dataSize = $this->unpackSize('C', 1);
113 101
            } else if ($opCode === Opcodes::OP_PUSHDATA2) {
114 45
                $dataSize = $this->unpackSize('v', 2);
115 30
            } else {
116 18
                $dataSize = $this->unpackSize('V', 4);
117
            }
118
119 2808
            $delta = ($this->end - $this->position);
120 2808
            if ($dataSize === false || $delta < 0 || $delta < $dataSize) {
121 12
                throw new \RuntimeException('Failed to unpack data from Script');
122
            }
123
124 2799
            if ($dataSize > 0) {
125 2040
                $pushData = new Buffer(substr($this->data, $this->position, $dataSize), $dataSize, $this->math);
126 1360
            }
127
128 2799
            $this->position += $dataSize;
129 1866
        }
130
131 3816
        $this->array[$ptr] = new Operation($opCode, $pushData, $dataSize);
132
133 3816
        return $this->array[$ptr];
134
    }
135
136
    /**
137
     *
138
     */
139 3843
    public function rewind()
140
    {
141 3843
        $this->execPtr = 0;
142 3843
    }
143
144
    /**
145
     * @return Operation
146
     */
147 3825
    public function current()
148
    {
149 3825
        if (isset($this->array[$this->execPtr])) {
150
            $exec = $this->array[$this->execPtr];
151
        } else {
152 3825
            $exec = $this->doNext($this->execPtr);
153
        }
154
155 3816
        return $exec;
156
    }
157
158
    /**
159
     * @return int
160
     */
161
    public function key()
162
    {
163
        return $this->execPtr;
164
    }
165
166
    /**
167
     * @return Operation
168
     */
169 3735
    public function next()
170
    {
171 3735
        $ptr = $this->execPtr;
172 3735
        if (isset($this->array[$ptr])) {
173 3717
            $this->execPtr++;
174 3717
            return $this->array[$ptr];
175
        }
176
177 18
        return null;
178
    }
179
180
    /**
181
     * @return bool
182
     */
183 3843
    public function valid()
184
    {
185 3843
        return isset($this->array[$this->execPtr]) || $this->position < $this->end;
186
    }
187
188
    /**
189
     * @return Operation[]
190
     */
191 2001
    public function decode()
192
    {
193 2001
        $result = [];
194 2001
        foreach ($this as $operation) {
195 1983
            $result[] = $operation;
196 1334
        }
197
198 2001
        return $result;
199
    }
200
201
    /**
202
     * @return string
203
     */
204 351
    public function getHumanReadable()
205
    {
206 351
        return implode(' ', array_map(
207 351
            function (Operation $operation) {
208 351
                return $operation->isPush()
209 304
                    ? $operation->getData()->getHex()
210 351
                    : $this->script->getOpcodes()->getOp($operation->getOp());
211 351
            },
212 351
            $this->decode()
213 234
        ));
214
    }
215
}
216