Completed
Push — master ( a22894...37130c )
by thomas
62:03 queued 58:09
created

Number::serialize()   C

Complexity

Conditions 8
Paths 33

Size

Total Lines 31
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 8

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 8
eloc 18
c 3
b 0
f 0
nc 33
nop 0
dl 0
loc 31
ccs 21
cts 21
cp 1
crap 8
rs 5.3846
1
<?php
2
3
namespace BitWasp\Bitcoin\Script\Interpreter;
4
5
use BitWasp\Bitcoin\Bitcoin;
6
use BitWasp\Bitcoin\Math\Math;
7
use BitWasp\Bitcoin\Serializable;
8
use BitWasp\Buffertools\Buffer;
9
use BitWasp\Buffertools\BufferInterface;
10
11
class Number extends Serializable
12
{
13
    const MAX_NUM_SIZE = 4;
14
    const MAX = 2147483647; // 2^31-1
15
    const MIN = -2147483647; // -2^31+1
16
17
    /**
18
     * @var Math
19
     */
20
    private $math;
21
22
    /**
23
     * @var int
24
     */
25
    private $number;
26
27
    /**
28
     * Number constructor.
29
     * @param int $number
30
     * @param Math $math
31
     */
32 225
    public function __construct($number, Math $math)
33
    {
34 225
        $this->number = $number;
35 225
        $this->math = $math;
36 225
    }
37
38
    /**
39
     * @param int $number
40
     * @param Math|null $math
41
     * @return self
42
     */
43 183
    public static function int($number, Math $math = null)
44
    {
45 183
        return new self(
46 122
            $number,
47 183
            $math ?: Bitcoin::getMath()
48 122
        );
49
    }
50
51
    /**
52
     * @param BufferInterface $vch
53
     * @param bool $fRequireMinimal
54
     * @param int $maxNumSize
55
     * @param Math|null $math
56
     * @return self
57
     */
58 144
    public static function buffer(BufferInterface $vch, $fRequireMinimal, $maxNumSize = self::MAX_NUM_SIZE, Math $math = null)
59
    {
60 144
        $size = $vch->getSize();
61 144
        if ($size > $maxNumSize) {
62
            throw new \RuntimeException('Script number overflow');
63
        }
64
65 144
        if ($fRequireMinimal && $size > 0) {
66
            $binary = $vch->getBinary();
67
            if (ord($binary[$size - 1]) & 0x7f === 0) {
68
                if ($size <= 1 || ord($binary[$size - 2]) & 0x80 === 0) {
69
                    throw new \RuntimeException('Non-minimally encoded script number');
70
                }
71
            }
72
        }
73
74 144
        $math = $math ?: Bitcoin::getMath();
75 144
        $number = new self(0, $math);
76 144
        $number->number = $number->parseBuffer($vch);
0 ignored issues
show
Documentation Bug introduced by
The property $number was declared of type integer, but $number->parseBuffer($vch) is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
77 144
        return $number;
78
    }
79
80
    /**
81
     * @param BufferInterface $buffer
82
     * @return int
83
     */
84 144
    private function parseBuffer(BufferInterface $buffer)
85
    {
86 144
        $size = $buffer->getSize();
87 144
        if ($size === 0) {
88 33
            return '0';
89
        }
90
91 132
        $chars = array_map(function ($binary) {
92 132
            return ord($binary);
93
94 132
        }, str_split($buffer->getBinary(), 1));
95
96 132
        $result = gmp_init(0);
97 132
        for ($i = 0; $i < $size; $i++) {
98 132
            $mul = $i * 8;
99 132
            $byte = $this->math->leftShift(gmp_init($chars[$i], 10), $mul);
100 132
            $result = $this->math->bitwiseOr($result, $byte);
101 88
        }
102
103 132
        if ($chars[count($chars)-1] & 0x80) {
104 15
            $mask = gmp_com($this->math->leftShift(gmp_init(0x80), (8 * ($size - 1))));
105 15
            $result = $this->math->sub(gmp_init(0), $this->math->bitwiseAnd($result, $mask));
106 10
        }
107
108 132
        return gmp_strval($result, 10);
109
    }
110
111
    /**
112
     * @return BufferInterface
113
     */
114 183
    private function serialize()
115
    {
116 183
        if ($this->number == 0) {
117 51
            return new Buffer('', 0);
118
        }
119
120 153
        $zero = gmp_init(0);
121
        // Using array of integers instead of bytes
122 153
        $result = [];
123 153
        $negative = $this->math->cmp(gmp_init($this->number), $zero) < 0;
124 153
        $abs = $negative ? $this->math->sub($zero, gmp_init($this->number, 10)) : gmp_init($this->number, 10);
125
126 153
        while ($this->math->cmp($abs, $zero) > 0) {
0 ignored issues
show
Bug introduced by
It seems like $abs defined by $negative ? $this->math-...init($this->number, 10) on line 124 can also be of type resource; however, Mdanter\Ecc\Math\GmpMath::cmp() does only seem to accept object<GMP>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
127 153
            $result[] = (int) gmp_strval($this->math->bitwiseAnd($abs, gmp_init(0xff)), 10);
0 ignored issues
show
Bug introduced by
It seems like $abs defined by $negative ? $this->math-...init($this->number, 10) on line 124 can also be of type resource; however, Mdanter\Ecc\Math\GmpMath::bitwiseAnd() does only seem to accept object<GMP>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
128 153
            $abs = $this->math->rightShift($abs, 8);
0 ignored issues
show
Bug introduced by
It seems like $abs can also be of type resource; however, Mdanter\Ecc\Math\GmpMath::rightShift() does only seem to accept object<GMP>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
129 102
        }
130
131 153
        if ($result[count($result) - 1] & 0x80) {
132 6
            $result[] = $negative ? 0x80 : 0;
133 151
        } else if ($negative) {
134 12
            $result[count($result) - 1] |= 0x80;
135 8
        }
136
137 153
        $s = '';
138 153
        foreach ($result as $i) {
139 153
            $s .= chr($i);
140 102
        }
141
142 153
        return new Buffer($s, null, $this->math);
143
144
    }
145
146
    /**
147
     * @return BufferInterface
148
     */
149 183
    public function getBuffer()
150
    {
151 183
        return $this->serialize();
152
    }
153
154
    /**
155
     * @return int
156
     */
157 144
    public function getInt()
158
    {
159 144
        if ($this->math->cmp(gmp_init($this->number, 10), gmp_init(self::MAX)) > 0) {
160
            return self::MAX;
161 144
        } else if ($this->math->cmp(gmp_init($this->number, 10), gmp_init(self::MIN)) < 0) {
162
            return self::MIN;
163
        }
164
165 144
        return $this->number;
166
    }
167
}
168