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
Branch php72 (57c34e)
by Joni
05:01
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 <i>BIT STRING</i> 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 49
    public function __construct(string $string, int $unused_bits = 0)
37
    {
38 49
        $this->_typeTag = self::TYPE_BIT_STRING;
39 49
        parent::__construct($string);
40 49
        $this->_unusedBits = $unused_bits;
41 49
    }
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.
67
     *                 Most significant bit of the first octet is index 0.
68
     *
69
     * @return bool
70
     */
71 11
    public function testBit(int $idx): bool
72
    {
73
        // octet index
74 11
        $oi = (int) floor($idx / 8);
75
        // if octet is outside range
76 11
        if ($oi < 0 || $oi >= strlen($this->_string)) {
77 1
            throw new \OutOfBoundsException('Index is out of bounds.');
78
        }
79
        // bit index
80 10
        $bi = $idx % 8;
81
        // if tested bit is last octet's unused bit
82 10
        if ($oi == strlen($this->_string) - 1) {
83 7
            if ($bi >= 8 - $this->_unusedBits) {
84 1
                throw new \OutOfBoundsException(
85 1
                    'Index refers to an unused bit.');
86
            }
87
        }
88 9
        $byte = $this->_string[$oi];
89
        // index 0 is the most significant bit in byte
90 9
        $mask = 0x01 << (7 - $bi);
91 9
        return (ord($byte) & $mask) > 0;
92
    }
93
94
    /**
95
     * Get range of bits.
96
     *
97
     * @param int $start  Index of first bit
98
     * @param int $length Number of bits in range
99
     *
100
     * @throws \OutOfBoundsException
101
     *
102
     * @return string Integer of $length bits
103
     */
104 9
    public function range(int $start, int $length): string
105
    {
106 9
        if (!$length) {
107 1
            return '0';
108
        }
109 8
        if ($start + $length > $this->numBits()) {
110 1
            throw new \OutOfBoundsException('Not enough bits.');
111
        }
112 7
        $bits = gmp_init(0);
113 7
        $idx = $start;
114 7
        $end = $start + $length;
115 7
        while (true) {
116 7
            $bit = $this->testBit($idx) ? 1 : 0;
117 7
            $bits |= $bit;
118 7
            if (++$idx >= $end) {
119 7
                break;
120
            }
121 7
            $bits <<= 1;
122
        }
123 7
        return gmp_strval($bits, 10);
124
    }
125
126
    /**
127
     * Get a copy of the bit string with trailing zeroes removed.
128
     *
129
     * @return self
130
     */
131 14
    public function withoutTrailingZeroes(): self
132
    {
133
        // if bit string was empty
134 14
        if (!strlen($this->_string)) {
135 1
            return new self('');
136
        }
137 13
        $bits = $this->_string;
138
        // count number of empty trailing octets
139 13
        $unused_octets = 0;
140 13
        for ($idx = strlen($bits) - 1; $idx >= 0; --$idx, ++$unused_octets) {
141 13
            if ("\x0" != $bits[$idx]) {
142 11
                break;
143
            }
144
        }
145
        // strip trailing octets
146 13
        if ($unused_octets) {
147 7
            $bits = substr($bits, 0, -$unused_octets);
148
        }
149
        // if bit string was full of zeroes
150 13
        if (!strlen($bits)) {
151 2
            return new self('');
152
        }
153
        // count number of trailing zeroes in the last octet
154 11
        $unused_bits = 0;
155 11
        $byte = ord($bits[strlen($bits) - 1]);
156 11
        while (!($byte & 0x01)) {
157 9
            ++$unused_bits;
158 9
            $byte >>= 1;
159
        }
160 11
        return new self($bits, $unused_bits);
161
    }
162
163
    /**
164
     * {@inheritdoc}
165
     */
166 15
    protected function _encodedContentDER(): string
167
    {
168 15
        $der = chr($this->_unusedBits);
169 15
        $der .= $this->_string;
170 15
        if ($this->_unusedBits) {
171 9
            $octet = $der[strlen($der) - 1];
172
            // set unused bits to zero
173 9
            $octet &= chr(0xff & ~((1 << $this->_unusedBits) - 1));
174 9
            $der[strlen($der) - 1] = $octet;
175
        }
176 15
        return $der;
177
    }
178
179
    /**
180
     * {@inheritdoc}
181
     */
182 11
    protected static function _decodeFromDER(Identifier $identifier,
183
        string $data, int &$offset): ElementBase
184
    {
185 11
        $idx = $offset;
186 11
        $length = Length::expectFromDER($data, $idx);
187 11
        if ($length->intLength() < 1) {
188 1
            throw new DecodeException('Bit string length must be at least 1.');
189
        }
190 10
        $unused_bits = ord($data[$idx++]);
191 10
        if ($unused_bits > 7) {
192 1
            throw new DecodeException(
193 1
                'Unused bits in a bit string must be less than 8.');
194
        }
195 9
        $str_len = $length->intLength() - 1;
196 9
        if ($str_len) {
197 8
            $str = substr($data, $idx, $str_len);
198 8
            if ($unused_bits) {
199 7
                $mask = (1 << $unused_bits) - 1;
200 7
                if (ord($str[strlen($str) - 1]) & $mask) {
201 1
                    throw new DecodeException(
202 8
                        'DER encoded bit string must have zero padding.');
203
                }
204
            }
205
        } else {
206 1
            $str = '';
207
        }
208 8
        $offset = $idx + $str_len;
209 8
        return new self($str, $unused_bits);
210
    }
211
}
212