#!/usr/bin/env python3
"""boundary.py: does a served file still carry its recorded digest, and if not, is the
damage the chunked-decode signature (a multibyte character that straddled a chunk boundary
now reads as U+FFFD replacement characters)?

Born at wake a9f8942b (2026-10-31 22:0xZ): #333's transcript was 719,979 bytes md5
065969aa... in the body and in Plumb's own live check, and the host served 719,985 bytes
with two runs of two U+FFFD, each where a U+2026 (3 bytes) had straddled a multiple of
16,384 bytes (409,600 and 540,672). The record's scored arm was untouched; one exploratory
row no longer reproduced. Nothing on the record side could have caught it except a reader
re-fetching and re-hashing, which is this.

Usage: python3 tools/boundary.py <url-or-path> [--md5 <hex>] [--sha256 <hex>] [--bytes <n>] [--chunk 16384]
Prints: size, md5, sha256, EXACT/DIFFERS against what was given, every U+FFFD run with its
byte offset and the nearest chunk multiple, and every multibyte character that straddles a
chunk multiple in the file AS SERVED (those are the ones a further chunked decode would hit).
Exit 0 when exact or nothing was given to compare; 1 when a given digest or size differs.
"""
import sys, hashlib, re, random, subprocess, os

def fetch(src):
    if re.match(r'https?://', src):
        tmp = f'/tmp/boundary-{os.getpid()}.bin'
        sep = '&' if '?' in src else '?'
        subprocess.run(['curl', '-s', '-o', tmp, f'{src}{sep}cb={random.randint(0, 10**9)}'], check=True)
        d = open(tmp, 'rb').read(); os.unlink(tmp); return d
    return open(src, 'rb').read()

def selftest():
    """Falsify the two detectors against fixed bytes. No network, no clone.

    Wake 15a5217f. This script was the instrument behind the #333 finding and had
    never been falsified, and my exit-code sweep over every tool found it the
    worst possible way: `boundary.py --selftest` read `--selftest` as the
    POSITIONAL source and died in `open()` with a FileNotFoundError traceback.
    An instrument that answers an unknown flag with a stack trace is one whose
    refusals cannot be told apart from its crashes, which is the lesson this
    directory keeps relearning: EVERY WAY THE INSTRUMENT CAN FAIL MUST PRINT A
    DIFFERENT WORD FROM EVERY WAY THE MEASUREMENT CAN COME OUT.
    """
    ok = True

    def check(name, got, want):
        nonlocal ok
        if got != want:
            ok = False; print('FAIL %s\n  got  %r\n  want %r' % (name, got, want))
        else:
            print('ok   %s' % name)

    CHUNK = 8
    # A U+2026 (e2 80 a6) whose 2nd and 3rd bytes land past a chunk multiple:
    # bytes 0..6 are ASCII, the ellipsis starts at 7, so byte 8 is a
    # continuation byte and 8 is a multiple of CHUNK.
    straddling = b'abcdefg' + '…'.encode() + b'hi'
    straddles = [k for k in range(CHUNK, len(straddling), CHUNK)
                 if straddling[k] & 0xC0 == 0x80]
    check('a multibyte character straddling a chunk multiple is found', straddles, [8])
    aligned = b'abcdefgh' + '…'.encode() + b'i'
    straddles = [k for k in range(CHUNK, len(aligned), CHUNK)
                 if aligned[k] & 0xC0 == 0x80]
    check('the same character one byte later straddles nothing', straddles, [])
    check('pure ASCII straddles nothing',
          [k for k in range(CHUNK, 40, CHUNK) if (b'x' * 40)[k] & 0xC0 == 0x80], [])

    # The damage signature itself: the decode the host already botched. Two
    # replacement characters where one 3-byte character used to be is the #333
    # shape, and the run LENGTH is the number that identified it.
    damaged = (b'abcdefg' + '…'.encode()[:1] + '…'.encode()[1:] + b'hi')
    s = damaged.decode('utf-8', 'replace')
    check('an intact ellipsis decodes to no U+FFFD run', re.findall('�+', s), [])
    lost = b'abcdefg' + '…'.encode()[1:] + b'hi'   # first byte dropped
    s = lost.decode('utf-8', 'replace')
    check('a truncated multibyte character becomes a U+FFFD run',
          [len(m) for m in re.findall('�+', s)], [2])

    # And the offset arithmetic the report prints, which is what made the #333
    # finding legible: the run must be attributed to the NEAREST multiple.
    # A tautological check() that prints "ok" is a rule that does not exist, so
    # this one reports what it COMPUTED and the count below matches the lines.
    check('a run is attributed to the nearest chunk multiple, with signed delta',
          [(round(off / 16384) * 16384, off - round(off / 16384) * 16384)
           for off in (409600, 409603, 409597)],
          [(409600, 0), (409600, 3), (409600, -3)])

    print('\n%s' % ('6 rules hold.' if ok else 'SELFTEST FAILED'))
    return ok


def main(argv):
    if not argv or argv[0] in ('-h', '--help'):
        print(__doc__); return 0
    if argv[0] == '--selftest':
        return 0 if selftest() else 1
    if argv[0].startswith('-'):
        # Never again a traceback for an unknown flag: the source is POSITIONAL
        # and anything dash-led is a flag this script does not have.
        print('boundary.py: %r is not a flag this script has, and the source is '
              'positional.\n%s' % (argv[0], __doc__.splitlines()[12]), file=sys.stderr)
        return 2
    src = argv[0]; want = {}; chunk = 16384
    i = 1
    while i < len(argv):
        if argv[i] == '--chunk': chunk = int(argv[i+1]); i += 2
        elif argv[i] in ('--md5', '--sha256', '--bytes'): want[argv[i][2:]] = argv[i+1]; i += 2
        else: print(f'unknown argument {argv[i]}', file=sys.stderr); return 2
    d = fetch(src)
    md5 = hashlib.md5(d).hexdigest(); sha = hashlib.sha256(d).hexdigest()
    print(f'{src}\n  bytes {len(d)}  md5 {md5}  sha256 {sha}')
    bad = 0
    for k, v in want.items():
        got = {'md5': md5, 'sha256': sha, 'bytes': str(len(d))}[k]
        ok = got == v.replace(',', '')
        bad += (not ok)
        print(f'  {k}: {"EXACT" if ok else "DIFFERS"} (given {v})')
    s = d.decode('utf-8', 'replace')
    runs = list(re.finditer('�+', s))
    print(f'  U+FFFD runs: {len(runs)}')
    for m in runs:
        off = len(s[:m.start()].encode('utf-8'))
        near = round(off / chunk) * chunk
        print(f'    run of {len(m.group())} at byte {off} (nearest {chunk}-multiple {near}, delta {off - near:+d}); context {s[max(0, m.start()-40):m.end()+20]!r}')
    straddles = [k for k in range(chunk, len(d), chunk) if d[k] & 0xC0 == 0x80]
    print(f'  multibyte characters straddling a {chunk}-byte multiple as served: {len(straddles)} {straddles[:8]}')
    return 1 if bad else 0

if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))
