view cutils/util/crc32.py @ 359:3e97788e85d5

FIX: add missing REUSE.toml to the sdist
author Franz Glasner <fzglas.hg@dom66.de>
date Wed, 02 Apr 2025 14:14:44 +0200
parents 48430941c18c
children
line wrap: on
line source

# -*- coding: utf-8 -*-
# :-
# SPDX-FileCopyrightText: © 2025 Franz Glasner
# SPDX-License-Identifier: BSD-3-Clause
# :-
r"""Wrap :func:`zlib.crc32` into a :mod:`hashlib` compatible interface.

"""

from __future__ import print_function, absolute_import


__all__ = ["crc32"]


import struct
import zlib

from . import PY2


try:
    long
except NameError:
    INT_TYPES = (int, )
else:
    INT_TYPES = (int, long)            # noqa: F821  long is undefined


class crc32(object):

    __slots__ = ["_crc"]

    zlib_crc32 = zlib.crc32

    def __init__(self, data=b""):
        self._crc = self.zlib_crc32(data)

    def update(self, data):
        self._crc = self.zlib_crc32(data, self._crc)

    def digest(self):
        return struct.pack(">I", self._get_crc_as_uint32())

    def hexdigest(self):
        return "{:08X}".format(self._get_crc_as_uint32())

    def copy(self):
        copied_crc = crc32()
        copied_crc._crc = self._crc
        return copied_crc

    def __eq__(self, other):
        if isinstance(other, crc32):
            return self._crc == other._crc
        elif isinstance(other, INT_TYPES):
            try:
                return self._get_crc_as_uint32() == self.as_uint32(other)
            except OverflowError:
                return False
        else:
            return NotImplemented

    def __ne__(self, other):
        equal = self.__eq__(other)
        return equal if equal is NotImplemented else not equal

    def _get_crc_as_uint32(self):
        """Get the current CRC always as positive number with the same bit
        pattern because Python2 yields negative numbers also.

        :return: The current CRC value as positive number on all Python
                 versions
        :rtype: int

        """
        if PY2:
            if self._crc < 0:
                # Return the bitpattern as unsigned 32-bit number
                return (~self._crc ^ 0xFFFFFFFF)
            else:
                return self._crc
        else:
            return self._crc

    @staticmethod
    def as_uint32(i):
        if i < 0:
            if i < -2147483648:
                raise OverflowError("invalid number for a 32-bit CRC")
            return (~i ^ 0xFFFFFFFF)
        else:
            return i