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