Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
binary.py885 linesDownload Raw Back to utils
1"""2passlib.utils.binary - binary data encoding/decoding/manipulation3"""4#=============================================================================5# imports6#=============================================================================7# core8from __future__ import absolute_import, division, print_function9from base64 import (10    b64encode,11    b64decode,12    b32decode as _b32decode,13    b32encode as _b32encode,14)15from binascii import b2a_base64, a2b_base64, Error as _BinAsciiError16import logging17log = logging.getLogger(__name__)18# site19# pkg20from passlib import exc21from passlib.utils.compat import (22    PY3, bascii_to_str,23    irange, imap, iter_byte_chars, join_byte_values, join_byte_elems,24    nextgetter, suppress_cause,25    u, unicode, unicode_or_bytes_types,26)27from passlib.utils.decor import memoized_property28# from passlib.utils import BASE64_CHARS, HASH64_CHARS29# local30__all__ = [31    # constants32    "BASE64_CHARS", "PADDED_BASE64_CHARS",33    "AB64_CHARS",34    "HASH64_CHARS",35    "BCRYPT_CHARS",36    "HEX_CHARS", "LOWER_HEX_CHARS", "UPPER_HEX_CHARS",37 38    "ALL_BYTE_VALUES",39 40    # misc41    "compile_byte_translation",42 43    # base6444    'ab64_encode', 'ab64_decode',45    'b64s_encode', 'b64s_decode',46 47    # base3248    "b32encode", "b32decode",49 50    # custom encodings51    'Base64Engine',52    'LazyBase64Engine',53    'h64',54    'h64big',55    'bcrypt64',56]57 58#=============================================================================59# constant strings60#=============================================================================61 62#-------------------------------------------------------------63# common salt_chars & checksum_chars values64#-------------------------------------------------------------65 66#: standard base64 charmap67BASE64_CHARS = u("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")68 69#: alt base64 charmap -- "." instead of "+"70AB64_CHARS =   u("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./")71 72#: charmap used by HASH64 encoding.73HASH64_CHARS = u("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")74 75#: charmap used by BCrypt76BCRYPT_CHARS = u("./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")77 78#: std base64 chars + padding char79PADDED_BASE64_CHARS = BASE64_CHARS + u("=")80 81#: all hex chars82HEX_CHARS = u("0123456789abcdefABCDEF")83 84#: upper case hex chars85UPPER_HEX_CHARS = u("0123456789ABCDEF")86 87#: lower case hex chars88LOWER_HEX_CHARS = u("0123456789abcdef")89 90#-------------------------------------------------------------91# byte strings92#-------------------------------------------------------------93 94#: special byte string containing all possible byte values95#: NOTE: for efficiency, this is treated as singleton by some of the code96ALL_BYTE_VALUES = join_byte_values(irange(256))97 98#: some string constants we reuse99B_EMPTY = b''100B_NULL = b'\x00'101B_EQUAL = b'='102 103#=============================================================================104# byte translation105#=============================================================================106 107#: base list used to compile byte translations108_TRANSLATE_SOURCE = list(iter_byte_chars(ALL_BYTE_VALUES))109 110def compile_byte_translation(mapping, source=None):111    """112    return a 256-byte string for translating bytes using specified mapping.113    bytes not specified by mapping will be left alone.114 115    :param mapping:116        dict mapping input byte (str or int) -> output byte (str or int).117 118    :param source:119        optional existing byte translation string to use as base.120        (must be 255-length byte string).  defaults to identity mapping.121 122    :returns:123        255-length byte string for passing to bytes().translate.124    """125    if source is None:126        target = _TRANSLATE_SOURCE[:]127    else:128        assert isinstance(source, bytes) and len(source) == 255129        target = list(iter_byte_chars(source))130    for k, v in mapping.items():131        if isinstance(k, unicode_or_bytes_types):132            k = ord(k)133        assert isinstance(k, int) and 0 <= k < 256134        if isinstance(v, unicode):135            v = v.encode("ascii")136        assert isinstance(v, bytes) and len(v) == 1137        target[k] = v138    return B_EMPTY.join(target)139 140#=============================================================================141# unpadding / stripped base64 encoding142#=============================================================================143def b64s_encode(data):144    """145    encode using shortened base64 format which omits padding & whitespace.146    uses default ``+/`` altchars.147    """148    return b2a_base64(data).rstrip(_BASE64_STRIP)149 150def b64s_decode(data):151    """152    decode from shortened base64 format which omits padding & whitespace.153    uses default ``+/`` altchars.154    """155    if isinstance(data, unicode):156        # needs bytes for replace() call, but want to accept ascii-unicode ala a2b_base64()157        try:158            data = data.encode("ascii")159        except UnicodeEncodeError:160            raise suppress_cause(ValueError("string argument should contain only ASCII characters"))161    off = len(data) & 3162    if off == 0:163        pass164    elif off == 2:165        data += _BASE64_PAD2166    elif off == 3:167        data += _BASE64_PAD1168    else:  # off == 1169        raise ValueError("invalid base64 input")170    try:171        return a2b_base64(data)172    except _BinAsciiError as err:173        raise suppress_cause(TypeError(err))174 175#=============================================================================176# adapted-base64 encoding177#=============================================================================178_BASE64_STRIP = b"=\n"179_BASE64_PAD1 = b"="180_BASE64_PAD2 = b"=="181 182# XXX: Passlib 1.8/1.9 -- deprecate everything that's using ab64_encode(),183#      have it start outputing b64s_encode() instead? can use a64_decode() to retain backwards compat.184 185def ab64_encode(data):186    """187    encode using shortened base64 format which omits padding & whitespace.188    uses custom ``./`` altchars.189 190    it is primarily used by Passlib's custom pbkdf2 hashes.191    """192    return b64s_encode(data).replace(b"+", b".")193 194def ab64_decode(data):195    """196    decode from shortened base64 format which omits padding & whitespace.197    uses custom ``./`` altchars, but supports decoding normal ``+/`` altchars as well.198 199    it is primarily used by Passlib's custom pbkdf2 hashes.200    """201    if isinstance(data, unicode):202        # needs bytes for replace() call, but want to accept ascii-unicode ala a2b_base64()203        try:204            data = data.encode("ascii")205        except UnicodeEncodeError:206            raise suppress_cause(ValueError("string argument should contain only ASCII characters"))207    return b64s_decode(data.replace(b".", b"+"))208 209#=============================================================================210# base32 codec211#=============================================================================212 213def b32encode(source):214    """215    wrapper around :func:`base64.b32encode` which strips padding,216    and returns a native string.217    """218    # NOTE: using upper case by default here, since 'I & L' are less219    #       visually ambiguous than 'i & l'220    return bascii_to_str(_b32encode(source).rstrip(B_EQUAL))221 222#: byte translation map to replace common mistyped base32 chars.223#: XXX: could correct '1' -> 'I', but could be a mistyped lower-case 'l', so leaving it alone.224_b32_translate = compile_byte_translation({"8": "B", "0": "O"})225 226#: helper to add padding227_b32_decode_pad = B_EQUAL * 8228 229def b32decode(source):230    """231    wrapper around :func:`base64.b32decode`232    which handles common mistyped chars.233    padding optional, ignored if present.234    """235    # encode & correct for typos236    if isinstance(source, unicode):237        source = source.encode("ascii")238    source = source.translate(_b32_translate)239 240    # pad things so final string is multiple of 8241    remainder = len(source) & 0x7242    if remainder:243        source += _b32_decode_pad[:-remainder]244 245    # XXX: py27 stdlib's version of this has some inefficiencies,246    #      could look into using optimized version.247    return _b32decode(source, True)248 249#=============================================================================250# base64-variant encoding251#=============================================================================252 253class Base64Engine(object):254    """Provides routines for encoding/decoding base64 data using255    arbitrary character mappings, selectable endianness, etc.256 257    :arg charmap:258        A string of 64 unique characters,259        which will be used to encode successive 6-bit chunks of data.260        A character's position within the string should correspond261        to its 6-bit value.262 263    :param big:264        Whether the encoding should be big-endian (default False).265 266    .. note::267        This class does not currently handle base64's padding characters268        in any way what so ever.269 270    Raw Bytes <-> Encoded Bytes271    ===========================272    The following methods convert between raw bytes,273    and strings encoded using the engine's specific base64 variant:274 275    .. automethod:: encode_bytes276    .. automethod:: decode_bytes277    .. automethod:: encode_transposed_bytes278    .. automethod:: decode_transposed_bytes279 280    ..281        .. automethod:: check_repair_unused282        .. automethod:: repair_unused283 284    Integers <-> Encoded Bytes285    ==========================286    The following methods allow encoding and decoding287    unsigned integers to and from the engine's specific base64 variant.288    Endianess is determined by the engine's ``big`` constructor keyword.289 290    .. automethod:: encode_int6291    .. automethod:: decode_int6292 293    .. automethod:: encode_int12294    .. automethod:: decode_int12295 296    .. automethod:: encode_int24297    .. automethod:: decode_int24298 299    .. automethod:: encode_int64300    .. automethod:: decode_int64301 302    Informational Attributes303    ========================304    .. attribute:: charmap305 306        unicode string containing list of characters used in encoding;307        position in string matches 6bit value of character.308 309    .. attribute:: bytemap310 311        bytes version of :attr:`charmap`312 313    .. attribute:: big314 315        boolean flag indicating this using big-endian encoding.316    """317 318    #===================================================================319    # instance attrs320    #===================================================================321    # public config322    bytemap = None # charmap as bytes323    big = None # little or big endian324 325    # filled in by init based on charmap.326    # (byte elem: single byte under py2, 8bit int under py3)327    _encode64 = None # maps 6bit value -> byte elem328    _decode64 = None # maps byte elem -> 6bit value329 330    # helpers filled in by init based on endianness331    _encode_bytes = None # throws IndexError if bad value (shouldn't happen)332    _decode_bytes = None # throws KeyError if bad char.333 334    #===================================================================335    # init336    #===================================================================337    def __init__(self, charmap, big=False):338        # validate charmap, generate encode64/decode64 helper functions.339        if isinstance(charmap, unicode):340            charmap = charmap.encode("latin-1")341        elif not isinstance(charmap, bytes):342            raise exc.ExpectedStringError(charmap, "charmap")343        if len(charmap) != 64:344            raise ValueError("charmap must be 64 characters in length")345        if len(set(charmap)) != 64:346            raise ValueError("charmap must not contain duplicate characters")347        self.bytemap = charmap348        self._encode64 = charmap.__getitem__349        lookup = dict((value, idx) for idx, value in enumerate(charmap))350        self._decode64 = lookup.__getitem__351 352        # validate big, set appropriate helper functions.353        self.big = big354        if big:355            self._encode_bytes = self._encode_bytes_big356            self._decode_bytes = self._decode_bytes_big357        else:358            self._encode_bytes = self._encode_bytes_little359            self._decode_bytes = self._decode_bytes_little360 361        # TODO: support padding character362        ##if padding is not None:363        ##    if isinstance(padding, unicode):364        ##        padding = padding.encode("latin-1")365        ##    elif not isinstance(padding, bytes):366        ##        raise TypeError("padding char must be unicode or bytes")367        ##    if len(padding) != 1:368        ##        raise ValueError("padding must be single character")369        ##self.padding = padding370 371    @property372    def charmap(self):373        """charmap as unicode"""374        return self.bytemap.decode("latin-1")375 376    #===================================================================377    # encoding byte strings378    #===================================================================379    def encode_bytes(self, source):380        """encode bytes to base64 string.381 382        :arg source: byte string to encode.383        :returns: byte string containing encoded data.384        """385        if not isinstance(source, bytes):386            raise TypeError("source must be bytes, not %s" % (type(source),))387        chunks, tail = divmod(len(source), 3)388        if PY3:389            next_value = nextgetter(iter(source))390        else:391            next_value = nextgetter(ord(elem) for elem in source)392        gen = self._encode_bytes(next_value, chunks, tail)393        out = join_byte_elems(imap(self._encode64, gen))394        ##if tail:395        ##    padding = self.padding396        ##    if padding:397        ##        out += padding * (3-tail)398        return out399 400    def _encode_bytes_little(self, next_value, chunks, tail):401        """helper used by encode_bytes() to handle little-endian encoding"""402        #403        # output bit layout:404        #405        # first byte:   v1 543210406        #407        # second byte:  v1 ....76408        #              +v2 3210..409        #410        # third byte:   v2 ..7654411        #              +v3 10....412        #413        # fourth byte:  v3 765432414        #415        idx = 0416        while idx < chunks:417            v1 = next_value()418            v2 = next_value()419            v3 = next_value()420            yield v1 & 0x3f421            yield ((v2 & 0x0f)<<2)|(v1>>6)422            yield ((v3 & 0x03)<<4)|(v2>>4)423            yield v3>>2424            idx += 1425        if tail:426            v1 = next_value()427            if tail == 1:428                # note: 4 msb of last byte are padding429                yield v1 & 0x3f430                yield v1>>6431            else:432                assert tail == 2433                # note: 2 msb of last byte are padding434                v2 = next_value()435                yield v1 & 0x3f436                yield ((v2 & 0x0f)<<2)|(v1>>6)437                yield v2>>4438 439    def _encode_bytes_big(self, next_value, chunks, tail):440        """helper used by encode_bytes() to handle big-endian encoding"""441        #442        # output bit layout:443        #444        # first byte:   v1 765432445        #446        # second byte:  v1 10....447        #              +v2 ..7654448        #449        # third byte:   v2 3210..450        #              +v3 ....76451        #452        # fourth byte:  v3 543210453        #454        idx = 0455        while idx < chunks:456            v1 = next_value()457            v2 = next_value()458            v3 = next_value()459            yield v1>>2460            yield ((v1&0x03)<<4)|(v2>>4)461            yield ((v2&0x0f)<<2)|(v3>>6)462            yield v3 & 0x3f463            idx += 1464        if tail:465            v1 = next_value()466            if tail == 1:467                # note: 4 lsb of last byte are padding468                yield v1>>2469                yield (v1&0x03)<<4470            else:471                assert tail == 2472                # note: 2 lsb of last byte are padding473                v2 = next_value()474                yield v1>>2475                yield ((v1&0x03)<<4)|(v2>>4)476                yield ((v2&0x0f)<<2)477 478    #===================================================================479    # decoding byte strings480    #===================================================================481 482    def decode_bytes(self, source):483        """decode bytes from base64 string.484 485        :arg source: byte string to decode.486        :returns: byte string containing decoded data.487        """488        if not isinstance(source, bytes):489            raise TypeError("source must be bytes, not %s" % (type(source),))490        ##padding = self.padding491        ##if padding:492        ##    # TODO: add padding size check?493        ##    source = source.rstrip(padding)494        chunks, tail = divmod(len(source), 4)495        if tail == 1:496            # only 6 bits left, can't encode a whole byte!497            raise ValueError("input string length cannot be == 1 mod 4")498        next_value = nextgetter(imap(self._decode64, source))499        try:500            return join_byte_values(self._decode_bytes(next_value, chunks, tail))501        except KeyError as err:502            raise ValueError("invalid character: %r" % (err.args[0],))503 504    def _decode_bytes_little(self, next_value, chunks, tail):505        """helper used by decode_bytes() to handle little-endian encoding"""506        #507        # input bit layout:508        #509        # first byte:   v1 ..543210510        #              +v2 10......511        #512        # second byte:  v2 ....5432513        #              +v3 3210....514        #515        # third byte:   v3 ......54516        #              +v4 543210..517        #518        idx = 0519        while idx < chunks:520            v1 = next_value()521            v2 = next_value()522            v3 = next_value()523            v4 = next_value()524            yield v1 | ((v2 & 0x3) << 6)525            yield (v2>>2) | ((v3 & 0xF) << 4)526            yield (v3>>4) | (v4<<2)527            idx += 1528        if tail:529            # tail is 2 or 3530            v1 = next_value()531            v2 = next_value()532            yield v1 | ((v2 & 0x3) << 6)533            # NOTE: if tail == 2, 4 msb of v2 are ignored (should be 0)534            if tail == 3:535                # NOTE: 2 msb of v3 are ignored (should be 0)536                v3 = next_value()537                yield (v2>>2) | ((v3 & 0xF) << 4)538 539    def _decode_bytes_big(self, next_value, chunks, tail):540        """helper used by decode_bytes() to handle big-endian encoding"""541        #542        # input bit layout:543        #544        # first byte:   v1 543210..545        #              +v2 ......54546        #547        # second byte:  v2 3210....548        #              +v3 ....5432549        #550        # third byte:   v3 10......551        #              +v4 ..543210552        #553        idx = 0554        while idx < chunks:555            v1 = next_value()556            v2 = next_value()557            v3 = next_value()558            v4 = next_value()559            yield (v1<<2) | (v2>>4)560            yield ((v2&0xF)<<4) | (v3>>2)561            yield ((v3&0x3)<<6) | v4562            idx += 1563        if tail:564            # tail is 2 or 3565            v1 = next_value()566            v2 = next_value()567            yield (v1<<2) | (v2>>4)568            # NOTE: if tail == 2, 4 lsb of v2 are ignored (should be 0)569            if tail == 3:570                # NOTE: 2 lsb of v3 are ignored (should be 0)571                v3 = next_value()572                yield ((v2&0xF)<<4) | (v3>>2)573 574    #===================================================================575    # encode/decode helpers576    #===================================================================577 578    # padmap2/3 - dict mapping last char of string ->579    # equivalent char with no padding bits set.580 581    def __make_padset(self, bits):582        """helper to generate set of valid last chars & bytes"""583        pset = set(c for i,c in enumerate(self.bytemap) if not i & bits)584        pset.update(c for i,c in enumerate(self.charmap) if not i & bits)585        return frozenset(pset)586 587    @memoized_property588    def _padinfo2(self):589        """mask to clear padding bits, and valid last bytes (for strings 2 % 4)"""590        # 4 bits of last char unused (lsb for big, msb for little)591        bits = 15 if self.big else (15<<2)592        return ~bits, self.__make_padset(bits)593 594    @memoized_property595    def _padinfo3(self):596        """mask to clear padding bits, and valid last bytes (for strings 3 % 4)"""597        # 2 bits of last char unused (lsb for big, msb for little)598        bits = 3 if self.big else (3<<4)599        return ~bits, self.__make_padset(bits)600 601    def check_repair_unused(self, source):602        """helper to detect & clear invalid unused bits in last character.603 604        :arg source:605            encoded data (as ascii bytes or unicode).606 607        :returns:608            `(True, result)` if the string was repaired,609            `(False, source)` if the string was ok as-is.610        """611        # figure out how many padding bits there are in last char.612        tail = len(source) & 3613        if tail == 2:614            mask, padset = self._padinfo2615        elif tail == 3:616            mask, padset = self._padinfo3617        elif not tail:618            return False, source619        else:620            raise ValueError("source length must != 1 mod 4")621 622        # check if last char is ok (padset contains bytes & unicode versions)623        last = source[-1]624        if last in padset:625            return False, source626 627        # we have dirty bits - repair the string by decoding last char,628        # clearing the padding bits via <mask>, and encoding new char.629        if isinstance(source, unicode):630            cm = self.charmap631            last = cm[cm.index(last) & mask]632            assert last in padset, "failed to generate valid padding char"633        else:634            # NOTE: this assumes ascii-compat encoding, and that635            # all chars used by encoding are 7-bit ascii.636            last = self._encode64(self._decode64(last) & mask)637            assert last in padset, "failed to generate valid padding char"638            if PY3:639                last = bytes([last])640        return True, source[:-1] + last641 642    def repair_unused(self, source):643        return self.check_repair_unused(source)[1]644 645    ##def transcode(self, source, other):646    ##    return ''.join(647    ##        other.charmap[self.charmap.index(char)]648    ##        for char in source649    ##    )650 651    ##def random_encoded_bytes(self, size, random=None, unicode=False):652    ##    "return random encoded string of given size"653    ##    data = getrandstr(random or rng,654    ##                      self.charmap if unicode else self.bytemap, size)655    ##    return self.repair_unused(data)656 657    #===================================================================658    # transposed encoding/decoding659    #===================================================================660    def encode_transposed_bytes(self, source, offsets):661        """encode byte string, first transposing source using offset list"""662        if not isinstance(source, bytes):663            raise TypeError("source must be bytes, not %s" % (type(source),))664        tmp = join_byte_elems(source[off] for off in offsets)665        return self.encode_bytes(tmp)666 667    def decode_transposed_bytes(self, source, offsets):668        """decode byte string, then reverse transposition described by offset list"""669        # NOTE: if transposition does not use all bytes of source,670        # the original can't be recovered... and join_byte_elems() will throw671        # an error because 1+ values in <buf> will be None.672        tmp = self.decode_bytes(source)673        buf = [None] * len(offsets)674        for off, char in zip(offsets, tmp):675            buf[off] = char676        return join_byte_elems(buf)677 678    #===================================================================679    # integer decoding helpers - mainly used by des_crypt family680    #===================================================================681    def _decode_int(self, source, bits):682        """decode base64 string -> integer683 684        :arg source: base64 string to decode.685        :arg bits: number of bits in resulting integer.686 687        :raises ValueError:688            * if the string contains invalid base64 characters.689            * if the string is not long enough - it must be at least690              ``int(ceil(bits/6))`` in length.691 692        :returns:693            a integer in the range ``0 <= n < 2**bits``694        """695        if not isinstance(source, bytes):696            raise TypeError("source must be bytes, not %s" % (type(source),))697        big = self.big698        pad = -bits % 6699        chars = (bits+pad)/6700        if len(source) != chars:701            raise ValueError("source must be %d chars" % (chars,))702        decode = self._decode64703        out = 0704        try:705            for c in source if big else reversed(source):706                out = (out<<6) + decode(c)707        except KeyError:708            raise ValueError("invalid character in string: %r" % (c,))709        if pad:710            # strip padding bits711            if big:712                out >>= pad713            else:714                out &= (1<<bits)-1715        return out716 717    #---------------------------------------------------------------718    # optimized versions for common integer sizes719    #---------------------------------------------------------------720 721    def decode_int6(self, source):722        """decode single character -> 6 bit integer"""723        if not isinstance(source, bytes):724            raise TypeError("source must be bytes, not %s" % (type(source),))725        if len(source) != 1:726            raise ValueError("source must be exactly 1 byte")727        if PY3:728            # convert to 8bit int before doing lookup729            source = source[0]730        try:731            return self._decode64(source)732        except KeyError:733            raise ValueError("invalid character")734 735    def decode_int12(self, source):736        """decodes 2 char string -> 12-bit integer"""737        if not isinstance(source, bytes):738            raise TypeError("source must be bytes, not %s" % (type(source),))739        if len(source) != 2:740            raise ValueError("source must be exactly 2 bytes")741        decode = self._decode64742        try:743            if self.big:744                return decode(source[1]) + (decode(source[0])<<6)745            else:746                return decode(source[0]) + (decode(source[1])<<6)747        except KeyError:748            raise ValueError("invalid character")749 750    def decode_int24(self, source):751        """decodes 4 char string -> 24-bit integer"""752        if not isinstance(source, bytes):753            raise TypeError("source must be bytes, not %s" % (type(source),))754        if len(source) != 4:755            raise ValueError("source must be exactly 4 bytes")756        decode = self._decode64757        try:758            if self.big:759                return decode(source[3]) + (decode(source[2])<<6)+ \760                       (decode(source[1])<<12) + (decode(source[0])<<18)761            else:762                return decode(source[0]) + (decode(source[1])<<6)+ \763                       (decode(source[2])<<12) + (decode(source[3])<<18)764        except KeyError:765            raise ValueError("invalid character")766 767    def decode_int30(self, source):768        """decode 5 char string -> 30 bit integer"""769        return self._decode_int(source, 30)770 771    def decode_int64(self, source):772        """decode 11 char base64 string -> 64-bit integer773 774        this format is used primarily by des-crypt & variants to encode775        the DES output value used as a checksum.776        """777        return self._decode_int(source, 64)778 779    #===================================================================780    # integer encoding helpers - mainly used by des_crypt family781    #===================================================================782    def _encode_int(self, value, bits):783        """encode integer into base64 format784 785        :arg value: non-negative integer to encode786        :arg bits: number of bits to encode787 788        :returns:789            a string of length ``int(ceil(bits/6.0))``.790        """791        assert value >= 0, "caller did not sanitize input"792        pad = -bits % 6793        bits += pad794        if self.big:795            itr = irange(bits-6, -6, -6)796            # shift to add lsb padding.797            value <<= pad798        else:799            itr = irange(0, bits, 6)800            # padding is msb, so no change needed.801        return join_byte_elems(imap(self._encode64,802                                ((value>>off) & 0x3f for off in itr)))803 804    #---------------------------------------------------------------805    # optimized versions for common integer sizes806    #---------------------------------------------------------------807 808    def encode_int6(self, value):809        """encodes 6-bit integer -> single hash64 character"""810        if value < 0 or value > 63:811            raise ValueError("value out of range")812        if PY3:813            return self.bytemap[value:value+1]814        else:815            return self._encode64(value)816 817    def encode_int12(self, value):818        """encodes 12-bit integer -> 2 char string"""819        if value < 0 or value > 0xFFF:820            raise ValueError("value out of range")821        raw = [value & 0x3f, (value>>6) & 0x3f]822        if self.big:823            raw = reversed(raw)824        return join_byte_elems(imap(self._encode64, raw))825 826    def encode_int24(self, value):827        """encodes 24-bit integer -> 4 char string"""828        if value < 0 or value > 0xFFFFFF:829            raise ValueError("value out of range")830        raw = [value & 0x3f, (value>>6) & 0x3f,831               (value>>12) & 0x3f, (value>>18) & 0x3f]832        if self.big:833            raw = reversed(raw)834        return join_byte_elems(imap(self._encode64, raw))835 836    def encode_int30(self, value):837        """decode 5 char string -> 30 bit integer"""838        if value < 0 or value > 0x3fffffff:839            raise ValueError("value out of range")840        return self._encode_int(value, 30)841 842    def encode_int64(self, value):843        """encode 64-bit integer -> 11 char hash64 string844 845        this format is used primarily by des-crypt & variants to encode846        the DES output value used as a checksum.847        """848        if value < 0 or value > 0xffffffffffffffff:849            raise ValueError("value out of range")850        return self._encode_int(value, 64)851 852    #===================================================================853    # eof854    #===================================================================855 856class LazyBase64Engine(Base64Engine):857    """Base64Engine which delays initialization until it's accessed"""858    _lazy_opts = None859 860    def __init__(self, *args, **kwds):861        self._lazy_opts = (args, kwds)862 863    def _lazy_init(self):864        args, kwds = self._lazy_opts865        super(LazyBase64Engine, self).__init__(*args, **kwds)866        del self._lazy_opts867        self.__class__ = Base64Engine868 869    def __getattribute__(self, attr):870        if not attr.startswith("_"):871            self._lazy_init()872        return object.__getattribute__(self, attr)873 874#-------------------------------------------------------------875# common variants876#-------------------------------------------------------------877 878h64 = LazyBase64Engine(HASH64_CHARS)879h64big = LazyBase64Engine(HASH64_CHARS, big=True)880bcrypt64 = LazyBase64Engine(BCRYPT_CHARS, big=True)881 882#=============================================================================883# eof884#=============================================================================885 
codekingpro/portable-devtools · Team Ai