• src/hash/crc32.h src/sbbs3/zmodem.c zmodem.h

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Mon Aug 24 20:32:03 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/d0a694d3e1d9c9f12f3fbe7f
    Modified Files:
    src/hash/crc32.h src/sbbs3/zmodem.c zmodem.h
    Log Message:
    zmodem: add an optional bulk receive callback

    The receive path called back into the transport once per byte, so nothing
    above it could be batched: no span copy, no hoisted loop invariants, and a
    CRC that had to run a byte at a time. Measured against lrzsz, zmodem.c's
    three receive functions cost 82.6 instructions per received byte where lrz's entire receiver, transport included, costs 24.1.

    A consumer may now supply recv_span, which copies bytes while the plain-byte table says they need no zmodem_rx() handling and stops before the first one that does, leaving it unconsumed. zmodem_recv_data32() takes those runs
    whole and CRCs each as a block via the new ucrc32_span(), which is the slicing-by-4 the send path has used since 2026-07-24. On random data the
    runs average 36 bytes and cover 97% of the stream.

    The callback is optional and deliberately not a zmodem_init() parameter: zmodem_init() memsets the struct, so it is NULL for every existing consumer with no source change, and NULL keeps the byte-at-a-time path exactly as it was. Consumers opt in one at a time.

    Passing the table to the callback, rather than letting the transport decide where a run ends, keeps protocol knowledge out of the transport and avoids needing to hand bytes back when a run stops early. It also means the two receive paths share one definition of which bytes are plain and cannot
    disagree about it.

    Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

    ---
    þ Synchronet þ Vertrauen þ Home of Synchronet þ [vert/cvs/bbs].synchro.net