integer.py 2.92 KB
#
# 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

try:
    import platform

    implementation = platform.python_implementation()

except (ImportError, AttributeError):
    implementation = 'CPython'

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

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()