• src/hash/fnv1a.c fnv1a.h fnv1a_test.c.gitignore src/doors/syncconquer/

    From Rob Swindell (on Debian Linux)@VERT to Git commit to main/sbbs/master on Fri Jul 31 21:43:31 2026
    https://gitlab.synchro.net/main/sbbs/-/commit/a607bbabdc95b262d2d29c26
    Added Files:
    src/hash/fnv1a.c fnv1a.h fnv1a_test.c
    Modified Files:
    .gitignore src/doors/syncconquer/door/soundio_termgfx.cpp src/doors/syncdoom/i_termmusic.c src/doors/syncduke/syncduke_io.c syncduke_stubs.c src/doors/syncmoo1/syncmoo1_audio.c src/doors/syncmoo1/tests/CMakeLists.txt test_audio.c src/doors/termgfx/CMakeLists.txt src/doors/termgfx/test/CMakeLists.txt src/doors/termgfx/text.c src/hash/CMakeLists.txt GNUmakefile objects.mk
    Removed Files:
    src/doors/termgfx/hash.c hash.h
    Log Message:
    hash: add FNV-1a, and collapse the doors' six copies onto it

    Six sites across the door tree had grown their own 32-bit FNV-1a: the
    termgfx text renderer's per-cell glyph signature, termgfx's own hash.c, SyncDOOM's term_hash(), two open-coded loops in SyncDuke, and
    SyncConquer's sa_fnv1a(). Four of the six were the same length-taking
    function byte-for-byte; one stopped at a NUL instead, and one folded its
    fields in one at a time.

    Duplication is the lesser problem. These hashes are promises: each one
    names a SyncTERM client-cache entry after the bytes it holds, so
    identical content has to produce an identical name across sessions,
    across hosts and across doors. A constant that drifts in one copy does
    not fail loudly, it silently stops hitting the cache - or lands two
    unrelated clips on one name.

    FNV-1a belongs beside crc16/crc32/md5/sha1/sha256 rather than in the
    door tree, so it lands in the hash library as fnv1a32(), a resumable fnv1a32_update() for callers whose input is not one contiguous buffer (SyncDuke's HUD signature folds a line count, then each row, then each
    row's text), and fnv1a32_str() for the NUL-terminated case, which the
    text renderer calls for every character cell of every frame. The 64-bit
    width comes along at the same price: 32 bits is more likely than not to
    collide somewhere past about 77,000 keys, which is ample for a door's
    handful of music lumps and thin for anything open-ended.

    Every replaced call site keeps its exact prior value, so nothing already
    in a client's cache is invalidated. The one exception is SyncDuke's HUD signature, which now folds its integers as bytes rather than as whole
    words; it is compared only against the previous frame's value inside a
    single process, and never stored.

    fnv1a_test.c pins the published vectors for both widths, checks that fnv1a32_str() agrees with the strlen() form, and checks that chaining is
    exact at every split of a test buffer. `gmake fnv1a_test` runs it, as
    does termgfx's opt-in CTest suite; SyncMOO1's test_audio asserts the same 32-bit vectors through its own path.

    termgfx compiles the hash library's fnv1a.c directly and re-exports its directory, rather than taking a dependency on the whole library, which
    would pull crc/md5/sha and their xpdev link into every door.

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

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