Files
littlefs/scripts/parity.py
Christopher Haster 7cfcc1af1d scripts: Renamed summary.py -> csv.py
This seems like a more fitting name now that this script has evolved
into more of a general purpose high-level CSV tool.

Unfortunately this does conflict with the standard csv module in Python,
breaking every script that imports csv (which is most of them).
Fortunately, Python is flexible enough to let us remove the current
directory before imports with a bit of an ugly hack:

  # prevent local imports
  __import__('sys').path.pop(0)

These scripts are intended to be standalone anyways, so this is probably
a good pattern to adopt.
2024-11-09 12:31:16 -06:00

91 lines
2.3 KiB
Python
Executable File

#!/usr/bin/env python3
# prevent local imports
__import__('sys').path.pop(0)
import io
import os
import struct
import sys
import functools as ft
import operator as op
def openio(path, mode='r', buffering=-1):
# allow '-' for stdin/stdout
if path == '-':
if 'r' in mode:
return os.fdopen(os.dup(sys.stdin.fileno()), mode, buffering)
else:
return os.fdopen(os.dup(sys.stdout.fileno()), mode, buffering)
else:
return open(path, mode, buffering)
def popc(x):
return bin(x).count('1')
def parity(x):
return popc(x) & 1
def main(paths, **args):
# interpret as sequence of hex bytes
if args.get('hex'):
print('%01x' % parity(ft.reduce(
op.xor,
bytes(int(path, 16) for path in paths),
0)))
# interpret as strings
elif args.get('string'):
for path in paths:
print('%01x' % parity(ft.reduce(
op.xor,
path.encode('utf8'),
0)))
# default to interpreting as paths
else:
if not paths:
paths = [None]
for path in paths:
with openio(path or '-', 'rb') as f:
# calculate parity
xor = 0
while True:
block = f.read(io.DEFAULT_BUFFER_SIZE)
if not block:
break
xor = ft.reduce(op.xor, block, xor)
# print what we found
if path is not None:
print('%01x %s' % (parity(xor), path))
else:
print('%01x' % parity(xor))
if __name__ == "__main__":
import argparse
import sys
parser = argparse.ArgumentParser(
description="Calculates parity.",
allow_abbrev=False)
parser.add_argument(
'paths',
nargs='*',
help="Paths to read. Reads stdin by default.")
parser.add_argument(
'-x', '--hex',
action='store_true',
help="Interpret as a sequence of hex bytes.")
parser.add_argument(
'-s', '--string',
action='store_true',
help="Interpret as strings.")
sys.exit(main(**{k: v
for k, v in vars(parser.parse_intermixed_args()).items()
if v is not None}))