summaryrefslogtreecommitdiff
path: root/pyasn1/compat/integer.py
blob: df6bed487b49778e5e2927a8c9740546abc28928 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#
# This file is part of pyasn1 software.
#
# Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com>
# License: http://snmplabs.com/pyasn1/license.html
#
import sys
import platform

from pyasn1.compat.octets import oct2int, null, ensureString


implementation = platform.python_implementation()

if sys.version_info[0:2] < (3, 2) or implementation != 'CPython':
    from binascii import a2b_hex, b2a_hex

    if sys.version_info[0] > 2:
        long = int

    def from_bytes(octets, signed=False):
        if not octets:
            return 0

        value = long(b2a_hex(ensureString(octets)), 16)

        if signed and oct2int(octets[0]) & 0x80:
            return value - (1 << len(octets) * 8)

        return value

    def to_bytes(value, signed=False, length=0):
        if value < 0:
            if signed:
                bits = bitLength(value)

                # two's complement form
                maxValue = 1 << bits
                valueToEncode = (value + maxValue) % maxValue

            else:
                raise OverflowError('can\'t convert negative int to unsigned')
        elif value == 0 and length == 0:
            return null
        else:
            bits = 0
            valueToEncode = value

        hexValue = hex(valueToEncode)[2:]
        if hexValue.endswith('L'):
            hexValue = hexValue[:-1]

        if len(hexValue) & 1:
            hexValue = '0' + hexValue

        # padding may be needed for two's complement encoding
        if value != valueToEncode or length:
            hexLength = len(hexValue) * 4

            padLength = max(length, bits)

            if padLength > hexLength:
                hexValue = '00' * ((padLength - hexLength - 1) // 8 + 1) + hexValue
            elif length and hexLength - length > 7:
                raise OverflowError('int too big to convert')

        firstOctet = int(hexValue[:2], 16)

        if signed:
            if firstOctet & 0x80:
                if value >= 0:
                    hexValue = '00' + hexValue
            elif value < 0:
                hexValue = 'ff' + hexValue

        octets_value = a2b_hex(hexValue)

        return octets_value

    def bitLength(number):
        # bits in unsigned number
        hexValue = hex(abs(number))
        bits = len(hexValue) - 2
        if hexValue.endswith('L'):
            bits -= 1
        if bits & 1:
            bits += 1
        bits *= 4
        # TODO: strip lhs zeros
        return bits

else:

    def from_bytes(octets, signed=False):
        return int.from_bytes(bytes(octets), 'big', signed=signed)

    def to_bytes(value, signed=False, length=0):
        length = max(value.bit_length(), length)

        if signed and length % 8 == 0:
            length += 1

        return value.to_bytes(length // 8 + (length % 8 and 1 or 0), 'big', signed=signed)

    def bitLength(number):
        return int(number).bit_length()