diff options
author | Ilya Etingof <etingof@gmail.com> | 2017-02-24 18:13:04 +0100 |
---|---|---|
committer | Ilya Etingof <etingof@gmail.com> | 2017-02-24 18:13:04 +0100 |
commit | 679408903afee6d76b682376aedf0eeee496b627 (patch) | |
tree | 7e49e98eae8272e5d3482bf2b24161c8f5535857 /pyasn1/compat | |
parent | 61ceeae369e0e41b6b8e57437430200418b51bef (diff) | |
download | pyasn1-679408903afee6d76b682376aedf0eeee496b627.tar.gz |
BitString type and codecs reimplemented
Targeting high-performance and convenience to use.
Sampling just BitString en/decoding performance -- new implementation is 100x faster. ;-\
Diffstat (limited to 'pyasn1/compat')
-rw-r--r-- | pyasn1/compat/integer.py | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/pyasn1/compat/integer.py b/pyasn1/compat/integer.py new file mode 100644 index 0000000..ae9c7e1 --- /dev/null +++ b/pyasn1/compat/integer.py @@ -0,0 +1,96 @@ +# +# This file is part of pyasn1 software. +# +# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com> +# License: http://pyasn1.sf.net/license.html +# +import sys +if sys.version_info[0:2] < (3, 2): + from binascii import a2b_hex, b2a_hex +from pyasn1.compat.octets import oct2int, null + +if sys.version_info[0:2] < (3, 2): + def from_bytes(octets, signed=False): + value = long(b2a_hex(str(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() + |