GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( 95c197...564e5e )
by Joni
04:32
created

BitString::withoutTrailingZeroes()   B

Complexity

Conditions 7
Paths 19

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 17
nc 19
nop 0
dl 0
loc 30
ccs 18
cts 18
cp 1
crap 7
rs 8.8333
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\ASN1\Type\Primitive;
6
7
use Sop\ASN1\Component\Identifier;
8
use Sop\ASN1\Component\Length;
9
use Sop\ASN1\Exception\DecodeException;
10
use Sop\ASN1\Feature\ElementBase;
11
use Sop\ASN1\Type\PrimitiveType;
12
use Sop\ASN1\Type\StringType;
13
use Sop\ASN1\Type\UniversalClass;
14
15
/**
16
 * Implements *BIT STRING* type.
17
 */
18
class BitString extends StringType
19
{
20
    use UniversalClass;
21
    use PrimitiveType;
22
23
    /**
24
     * Number of unused bits in the last octet.
25
     *
26
     * @var int
27
     */
28
    protected $_unusedBits;
29
30
    /**
31
     * Constructor.
32
     *
33
     * @param string $string      Content octets
34
     * @param int    $unused_bits Number of unused bits in the last octet
35
     */
36 50
    public function __construct(string $string, int $unused_bits = 0)
37
    {
38 50
        $this->_typeTag = self::TYPE_BIT_STRING;
39 50
        parent::__construct($string);
40 50
        $this->_unusedBits = $unused_bits;
41 50
    }
42
43
    /**
44
     * Get the number of bits in the string.
45
     *
46
     * @return int
47
     */
48 17
    public function numBits(): int
49
    {
50 17
        return strlen($this->_string) * 8 - $this->_unusedBits;
51
    }
52
53
    /**
54
     * Get the number of unused bits in the last octet of the string.
55
     *
56
     * @return int
57
     */
58 18
    public function unusedBits(): int
59
    {
60 18
        return $this->_unusedBits;
61
    }
62
63
    /**
64
     * Test whether bit is set.
65
     *
66
     * @param int $idx Bit index. Most significant bit of the first octet is index 0.
67
     *
68
     * @return bool
69
     */
70 11
    public function testBit(int $idx): bool
71
    {
72
        // octet index
73 11
        $oi = (int) floor($idx / 8);
74
        // if octet is outside range
75 11
        if ($oi < 0 || $oi >= strlen($this->_string)) {
76 1
            throw new \OutOfBoundsException('Index is out of bounds.');
77
        }
78
        // bit index
79 10
        $bi = $idx % 8;
80
        // if tested bit is last octet's unused bit
81 10
        if ($oi === strlen($this->_string) - 1) {
82 7
            if ($bi >= 8 - $this->_unusedBits) {
83 1
                throw new \OutOfBoundsException(
84 1
                    'Index refers to an unused bit.');
85
            }
86
        }
87 9
        $byte = $this->_string[$oi];
88
        // index 0 is the most significant bit in byte
89 9
        $mask = 0x01 << (7 - $bi);
90 9
        return (ord($byte) & $mask) > 0;
91
    }
92
93
    /**
94
     * Get range of bits.
95
     *
96
     * @param int $start  Index of first bit
97
     * @param int $length Number of bits in range
98
     *
99
     * @throws \OutOfBoundsException
100
     *
101
     * @return string Integer of $length bits
102
     */
103 9
    public function range(int $start, int $length): string
104
    {
105 9
        if (!$length) {
106 1
            return '0';
107
        }
108 8
        if ($start + $length > $this->numBits()) {
109 1
            throw new \OutOfBoundsException('Not enough bits.');
110
        }
111 7
        $bits = gmp_init(0);
112 7
        $idx = $start;
113 7
        $end = $start + $length;
114 7
        while (true) {
115 7
            $bit = $this->testBit($idx) ? 1 : 0;
116 7
            $bits |= $bit;
117 7
            if (++$idx >= $end) {
118 7
                break;
119
            }
120 7
            $bits <<= 1;
121
        }
122 7
        return gmp_strval($bits, 10);
123
    }
124
125
    /**
126
     * Get a copy of the bit string with trailing zeroes removed.
127
     *
128
     * @return self
129
     */
130 14
    public function withoutTrailingZeroes(): self
131
    {
132
        // if bit string was empty
133 14
        if (!strlen($this->_string)) {
134 1
            return new self('');
135
        }
136 13
        $bits = $this->_string;
137
        // count number of empty trailing octets
138 13
        $unused_octets = 0;
139 13
        for ($idx = strlen($bits) - 1; $idx >= 0; --$idx, ++$unused_octets) {
140 13
            if ("\x0" !== $bits[$idx]) {
141 11
                break;
142
            }
143
        }
144
        // strip trailing octets
145 13
        if ($unused_octets) {
146 7
            $bits = substr($bits, 0, -$unused_octets);
147
        }
148
        // if bit string was full of zeroes
149 13
        if (!strlen($bits)) {
150 2
            return new self('');
151
        }
152
        // count number of trailing zeroes in the last octet
153 11
        $unused_bits = 0;
154 11
        $byte = ord($bits[strlen($bits) - 1]);
155 11
        while (!($byte & 0x01)) {
156 9
            ++$unused_bits;
157 9
            $byte >>= 1;
158
        }
159 11
        return new self($bits, $unused_bits);
160
    }
161
162
    /**
163
     * {@inheritdoc}
164
     */
165 15
    protected function _encodedContentDER(): string
166
    {
167 15
        $der = chr($this->_unusedBits);
168 15
        $der .= $this->_string;
169 15
        if ($this->_unusedBits) {
170 9
            $octet = $der[strlen($der) - 1];
171
            // set unused bits to zero
172 9
            $octet &= chr(0xff & ~((1 << $this->_unusedBits) - 1));
173 9
            $der[strlen($der) - 1] = $octet;
174
        }
175 15
        return $der;
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     */
181 11
    protected static function _decodeFromDER(Identifier $identifier,
182
        string $data, int &$offset): ElementBase
183
    {
184 11
        $idx = $offset;
185 11
        $length = Length::expectFromDER($data, $idx);
186 11
        if ($length->intLength() < 1) {
187 1
            throw new DecodeException('Bit string length must be at least 1.');
188
        }
189 10
        $unused_bits = ord($data[$idx++]);
190 10
        if ($unused_bits > 7) {
191 1
            throw new DecodeException(
192 1
                'Unused bits in a bit string must be less than 8.');
193
        }
194 9
        $str_len = $length->intLength() - 1;
195 9
        if ($str_len) {
196 8
            $str = substr($data, $idx, $str_len);
197 8
            if ($unused_bits) {
198 7
                $mask = (1 << $unused_bits) - 1;
199 7
                if (ord($str[strlen($str) - 1]) & $mask) {
200 1
                    throw new DecodeException(
201 8
                        'DER encoded bit string must have zero padding.');
202
                }
203
            }
204
        } else {
205 1
            $str = '';
206
        }
207 8
        $offset = $idx + $str_len;
208 8
        return new self($str, $unused_bits);
209
    }
210
}
211