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.

BitString::withoutTrailingZeroes()   B
last analyzed

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