Mercurial > hgrepos > Python > apps > py-cutils
changeset 179:53614a724bf0
Also write a (standard) CRC-32 checksum for each block of output
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Sat, 11 Jan 2025 22:26:00 +0100 |
| parents | dac26a2d9de5 |
| children | d038f0a9ba49 |
| files | cutils/treesum.py |
| diffstat | 1 files changed, 62 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/cutils/treesum.py Sat Jan 11 21:18:53 2025 +0100 +++ b/cutils/treesum.py Sat Jan 11 22:26:00 2025 +0100 @@ -22,6 +22,7 @@ import stat import sys import time +import zlib from . import (__version__, __revision__) from . import util @@ -278,6 +279,7 @@ out_cm = open(opts.output, "ab") else: out_cm = open(opts.output, "wb") + out_cm = CRC32Output(out_cm) with out_cm as outfp: for d in opts.directories: @@ -321,6 +323,7 @@ """ self._outfp = outfp + self._outfp.resetdigest() self._outfp.write(format_bsd_line("VERSION", "1", None, False)) self._outfp.flush() @@ -394,9 +397,13 @@ "./@", self._use_base64)) self._outfp.flush() + self._outfp.write(format_bsd_line( + "CRC32", self._outfp.hexcrcdigest(), None, False)) return self._generate(os.path.normpath(root), tuple()) + self._outfp.write(format_bsd_line( + "CRC32", self._outfp.hexcrcdigest(), None, False)) def _generate(self, root, top): logging.debug("Handling %s/%r", root, top) @@ -522,6 +529,60 @@ return (dir_dgst.digest(), dir_size) +class CRC32Output(object): + + """Wrapper for a minimal binary file contextmanager that calculates + the CRC32 of the written bytes on the fly. + + Also acts as context manager proxy for the given context manager. + + """ + + __slots__ = ("_fp_cm", "_fp", "_crc32") + + def __init__(self, fp_cm): + self._fp_cm = fp_cm + self._fp = None + self.resetdigest() + + def __enter__(self): + assert self._fp is None + self._fp = self._fp_cm.__enter__() + return self + + def __exit__(self, *args): + rv = self._fp_cm.__exit__(*args) + self._fp = None + return rv + + def write(self, what): + self._fp.write(what) + self._crc32 = zlib.crc32(what, self._crc32) + + def flush(self): + self._fp.flush() + + def resetdigest(self): + """Reset the current CRC digest""" + self._crc32 = zlib.crc32(b"") + + def hexcrcdigest(self): + """ + + :rtype: str + + """ + return (hex(self._crc32)[2:]).upper() + + def crcdigest(self): + """ + + :rtype: int + + """ + return self._crc32 + + def normalized_compatible_mode_str(mode): # XXX FIXME: Windows and "executable" modebits = stat.S_IMODE(mode) @@ -546,7 +607,7 @@ if what == b"TIMESTAMP": assert filename is None return util.interpolate_bytes(b"TIMESTAMP = %d%s", value, ls) - if what in (b"ISOTIMESTAMP", b"FLAGS", b"VERSION"): + if what in (b"ISOTIMESTAMP", b"FLAGS", b"VERSION", b"CRC32"): assert filename is None if not isinstance(value, bytes): value = value.encode("ascii")
