. "$BST_LIB" # xxHash is a non-cryptographic hash: XXH32 and XXH64 are the classic one-shot # and streaming digests, XXH3 is the newer 64/128-bit family. The distro is # static-only, so the shipped payload is two headers plus a pkg-config file # (-dev) and the archive libxxhash.a (-staticdev). # # Unlike the libraries this image ships purely for future consumers, xxhash # brings its OWN consumer: the xxhsum CLI links the library statically and is # installed beside it. So this suite asserts in two registers -- the payload is # real (below), and the algorithms actually RUN and produce the right numbers # (the second half). The second half is what no header-and-symbols check can # establish. inc=/usr/include/xxhash.h inc3=/usr/include/xxh3.h pc=/usr/lib/pkgconfig/libxxhash.pc lib=/usr/lib/libxxhash.a check xxhash/header-present test -f "$inc" check xxhash/header-nonempty test -s "$inc" check_out xxhash/header-guard "#define XXHASH_H_5627135585666179" cat "$inc" # xxh3.h is a compatibility shim upstream keeps until v0.9.0; it is part of the # -dev payload, so its absence would mean a partial install rather than a # deliberate drop. check xxhash/xxh3-header-present test -f "$inc3" # The header separates a macro name from its value with runs of spaces, so the # version assertions match the whole line whitespace-agnostically rather than a # fixed-spacing literal. This is 0.8.2, the version the pkg-config file states. check xxhash/ver-major grep -qE "^#define[[:space:]]+XXH_VERSION_MAJOR[[:space:]]+0" "$inc" check xxhash/ver-minor grep -qE "^#define[[:space:]]+XXH_VERSION_MINOR[[:space:]]+8" "$inc" check xxhash/ver-release grep -qE "^#define[[:space:]]+XXH_VERSION_RELEASE[[:space:]]+2" "$inc" check xxhash/ver-number grep -qE "^#define[[:space:]]+XXH_VERSION_NUMBER" "$inc" # The error enum every streaming call returns. check_out xxhash/type-errorcode "} XXH_errorcode;" cat "$inc" check xxhash/enum-ok grep -qE "XXH_OK[[:space:]]*=[[:space:]]*0" "$inc" check_out xxhash/enum-error "XXH_ERROR" cat "$inc" # The width-carrying result types, the opaque streaming states, and the # canonical (endian-fixed) forms a caller serialises a digest through. check_out xxhash/type-hash32 "typedef uint32_t XXH32_hash_t;" cat "$inc" check_out xxhash/type-hash64 "typedef uint64_t XXH64_hash_t;" cat "$inc" check_out xxhash/type-hash128 "} XXH128_hash_t;" cat "$inc" check_out xxhash/type-state32 "XXH32_state_t;" cat "$inc" check_out xxhash/type-state64 "XXH64_state_t;" cat "$inc" check_out xxhash/type-state3 "XXH3_state_t;" cat "$inc" check_out xxhash/type-canon32 "} XXH32_canonical_t;" cat "$inc" check_out xxhash/type-canon64 "XXH64_canonical_t;" cat "$inc" check_out xxhash/type-canon128 "XXH128_canonical_t;" cat "$inc" # XXH32: the one-shot entry point plus the full streaming lifecycle, and the # canonical round-trip pair. for f in XXH32 XXH32_createState XXH32_freeState XXH32_reset XXH32_update \ XXH32_digest XXH32_copyState XXH32_canonicalFromHash XXH32_hashFromCanonical; do check_out "xxhash/api-$f" "$f(" cat "$inc" done # XXH64: the same shape at 64 bits. for f in XXH64 XXH64_createState XXH64_freeState XXH64_reset XXH64_update \ XXH64_digest XXH64_copyState XXH64_canonicalFromHash XXH64_hashFromCanonical; do check_out "xxhash/api-$f" "$f(" cat "$inc" done # XXH3 64-bit: one-shot, seeded and secret-keyed variants, and its own streaming # lifecycle. The secret arm is what distinguishes XXH3 from its predecessors. for f in XXH3_64bits XXH3_64bits_withSeed XXH3_64bits_withSecret \ XXH3_64bits_reset XXH3_64bits_reset_withSeed XXH3_64bits_reset_withSecret \ XXH3_64bits_update XXH3_64bits_digest XXH3_createState XXH3_freeState \ XXH3_copyState XXH3_generateSecret; do check_out "xxhash/api-$f" "$f(" cat "$inc" done # XXH3 128-bit, plus the 128-bit comparison/serialisation helpers a caller needs # because XXH128_hash_t is a struct and not an integer. for f in XXH3_128bits XXH3_128bits_withSeed XXH3_128bits_withSecret \ XXH3_128bits_reset XXH3_128bits_update XXH3_128bits_digest \ XXH128 XXH128_isEqual XXH128_cmp XXH128_canonicalFromHash XXH128_hashFromCanonical; do check_out "xxhash/api-$f" "$f(" cat "$inc" done # Declared "XXH_versionNumber (void)" -- the one public entry point in this # header with a space before the paren, so it needs the whitespace-agnostic form. check xxhash/api-versionNumber grep -qE "XXH_versionNumber[[:space:]]*\(" "$inc" # The secret-size contract a caller must honour when using the withSecret arm. check xxhash/const-secret-min grep -qE "^#define[[:space:]]+XXH3_SECRET_SIZE_MIN[[:space:]]+136" "$inc" check xxhash/const-secret-default grep -qE "^#define[[:space:]]+XXH3_SECRET_DEFAULT_SIZE[[:space:]]+192" "$inc" check_out xxhash/pc-name "Name: xxhash" cat "$pc" check_out xxhash/pc-version "Version: 0.8.2" cat "$pc" check_out xxhash/pc-libs "-lxxhash" cat "$pc" check_out xxhash/pc-cflags "-I\${includedir}" cat "$pc" check_out xxhash/pc-libdir "libdir=\${exec_prefix}/lib" cat "$pc" check xxhash/lib-present test -f "$lib" check xxhash/lib-nonempty test -s "$lib" check_out xxhash/lib-archive "!" head -c 8 "$lib" # Flatten the archive to printable text once: GNU grep suppresses matches inside # binary data, and busybox tr does not handle [:print:] under -c, so the octal # range is the formulation both greps and both trs agree on. flat=${TMPDIR:-/tmp}/bst-xxhash-flat.$$ tr -c '\40-\176\n' ' ' < "$lib" > "$flat" 2>/dev/null # Upstream compiles the whole library from a single translation unit, so one # member is the correct shape here -- not a partial build. check xxhash/member-xxhash.o grep -q "xxhash.o" "$flat" # Exported symbols, sampled across all four algorithm families, so the archive's # index is evidence the wasm32 compile really produced each one. for s in XXH32 XXH32_createState XXH32_update XXH32_digest XXH32_canonicalFromHash \ XXH64 XXH64_createState XXH64_update XXH64_digest XXH64_hashFromCanonical \ XXH3_64bits XXH3_64bits_withSeed XXH3_64bits_withSecret XXH3_64bits_update \ XXH3_128bits XXH3_128bits_digest XXH128 XXH128_isEqual XXH128_cmp \ XXH3_createState XXH3_generateSecret XXH_versionNumber; do check "xxhash/sym-$s" grep -q "$s" "$flat" done # No shared object and no libtool archive: static-only is distro policy, and # upstream's Makefile builds libxxhash.so as part of its default `lib` target, # so a stray .so here would mean this recipe's static-only override was lost. check xxhash/no-shared sh -c 'set -- /usr/lib/libxxhash.so*; [ ! -e "$1" ]' check xxhash/no-libtool sh -c 'set -- /usr/lib/libxxhash.la; [ ! -e "$1" ]' rm -f "$flat" # --- the algorithms running, through the CLI this package ships -------------- # # xxhsum links libxxhash statically and is installed by this image, so every # case below is xxHash executing on wasm32. The expected digests were read off a # native build of the SAME source (SRCREV bbb27a5, 0.8.2) before this suite was # written, and the empty-input values are additionally the canonical published # xxHash constants -- so a wrong answer here is a real miscompile, not a # self-consistent tautology. # # xxhsum writes a \r-and-spaces progress eraser to STDERR while hashing a file, # so every capture below takes stdout only; folding stderr in would corrupt the # checksum files that -c reads back. require xxhsum xxhash/cli-present tmp=${TMPDIR:-/tmp}/bst-xxhash.$$ mkdir -p "$tmp" || bst_notok xxhash/tmpdir "cannot create $tmp" printf 'brintos xxhash test vector\n' > "$tmp/vec.txt" : > "$tmp/empty.txt" # GNU-form output is " "; XXH3 has no GNU form and always prints the # BSD "XXH3 () = " shape, even without --tag. Two extractors, so the # XXH3 cases are not silently reading a filename. gnuhash() { xxhsum "$@" 2>/dev/null | awk '{print $1}'; } bsdhash() { xxhsum "$@" 2>/dev/null | awk '{print $NF}'; } check_out xxhash/cli-version "xxhsum 0.8.2" xxhsum --version # Known-answer vectors over a fixed 27-byte input, one per algorithm. check_eq xxhash/xxh32-vector cf7fdec0 "$(gnuhash -H0 "$tmp/vec.txt")" check_eq xxhash/xxh64-vector 47875da50ed4c1f5 "$(gnuhash -H1 "$tmp/vec.txt")" check_eq xxhash/xxh128-vector 59c67451305015094eb2e5f02b4ac995 "$(gnuhash -H2 "$tmp/vec.txt")" check_eq xxhash/xxh3-vector d53a02b07a3c528d "$(bsdhash -H3 "$tmp/vec.txt")" # The canonical published constants for the empty input -- the values every # conforming xxHash implementation must produce. check_eq xxhash/xxh32-empty 02cc5d05 "$(gnuhash -H0 "$tmp/empty.txt")" check_eq xxhash/xxh64-empty ef46db3751d8e999 "$(gnuhash -H1 "$tmp/empty.txt")" check_eq xxhash/xxh128-empty 99aa06d3014798d86001c324468d497f "$(gnuhash -H2 "$tmp/empty.txt")" check_eq xxhash/xxh3-empty 2d06800538d394c2 "$(bsdhash -H3 "$tmp/empty.txt")" # -H1 is the documented default, so a bare invocation must equal the XXH64 vector. check_eq xxhash/default-algo 47875da50ed4c1f5 "$(gnuhash "$tmp/vec.txt")" # stdin names the stream "stdin" and takes the streaming path rather than the # file path, so it is a distinct route to the same digest. check_eq xxhash/stdin-xxh64 47875da50ed4c1f5 "$(xxhsum < "$tmp/vec.txt" 2>/dev/null | awk '{print $1}')" check_eq xxhash/stdin-name stdin "$(xxhsum < "$tmp/vec.txt" 2>/dev/null | awk '{print $2}')" # Digests are printed big-endian (canonical) by default; --little-endian must # byte-swap the same value rather than compute a different one. check_eq xxhash/little-endian c0de7fcf "$(gnuhash -H0 --little-endian "$tmp/vec.txt")" # BSD --tag names the algorithm on each line; XXH32_LE is the tag the # little-endian convention carries, which is how a reader tells the two apart. check_out xxhash/tag-xxh32 "XXH32 (" xxhsum -H0 --tag "$tmp/vec.txt" check_out xxhash/tag-xxh64 "XXH64 (" xxhsum -H1 --tag "$tmp/vec.txt" check_out xxhash/tag-xxh128 "XXH128 (" xxhsum -H2 --tag "$tmp/vec.txt" check_out xxhash/tag-le "XXH32_LE (" xxhsum -H0 --little-endian --tag "$tmp/vec.txt" # Multi-block streaming: XSUM_hashFile reads in 64 KB blocks, so a 160000-byte # input drives XXH*_update across three of them. The payload is generated on # target from busybox yes+head so the bytes are reproducible without shipping a # data file, and the file and stdin routes must agree on the result. yes brintos | head -n 20000 > "$tmp/big.txt" check_eq xxhash/big-size 160000 "$(wc -c < "$tmp/big.txt" | tr -d ' ')" check_eq xxhash/big-xxh32 f5688b5d "$(gnuhash -H0 "$tmp/big.txt")" check_eq xxhash/big-xxh64 3117ed2ec7922d7d "$(gnuhash -H1 "$tmp/big.txt")" check_eq xxhash/big-xxh128 b358eda26e3ab4bb37fbc7d3ae04a784 "$(gnuhash -H2 "$tmp/big.txt")" check_eq xxhash/big-xxh3 37fbc7d3ae04a784 "$(bsdhash -H3 "$tmp/big.txt")" check_eq xxhash/big-stdin-eq 3117ed2ec7922d7d "$(xxhsum < "$tmp/big.txt" 2>/dev/null | awk '{print $1}')" # argv[0] dispatch: the three installed symlinks select the algorithm by the name # they are invoked under, so each must match its -H equivalent above. check_eq xxhash/alias-32 cf7fdec0 "$(xxh32sum "$tmp/vec.txt" 2>/dev/null | awk '{print $1}')" check_eq xxhash/alias-64 47875da50ed4c1f5 "$(xxh64sum "$tmp/vec.txt" 2>/dev/null | awk '{print $1}')" check_eq xxhash/alias-128 59c67451305015094eb2e5f02b4ac995 "$(xxh128sum "$tmp/vec.txt" 2>/dev/null | awk '{print $1}')" # -c round trip: xxhsum must read back its own output. Capturing stdout only is # what makes this work -- the stderr eraser would make the file unparseable. xxhsum "$tmp/vec.txt" 2>/dev/null > "$tmp/sums.txt" check_out xxhash/check-ok "OK" xxhsum -c "$tmp/sums.txt" check xxhash/check-rc xxhsum -c "$tmp/sums.txt" # -c must also accept the BSD --tag form it can emit. xxhsum --tag "$tmp/vec.txt" 2>/dev/null > "$tmp/bsd.txt" check xxhash/check-tag-rc xxhsum -c "$tmp/bsd.txt" # Corruption must be detected, not merely reported: rc 1 and a FAILED line. This # is the case that proves -c compares rather than echoes. printf 'brintos xxhash test vector CORRUPT\n' > "$tmp/vec.txt" check_rc xxhash/check-corrupt-rc 1 xxhsum -c "$tmp/sums.txt" # check_out is not usable here: it also demands rc 0, and a detected mismatch # correctly exits 1. The assertion is that the mismatch is reported as FAILED # rather than the run dying for some unrelated reason. _cout=$(xxhsum -c "$tmp/sums.txt" 2>&1) case "$_cout" in *FAILED*) bst_ok xxhash/check-corrupt-msg ;; *) bst_notok xxhash/check-corrupt-msg "$(_bst_1line "$_cout")" ;; esac check_rc xxhash/check-status-rc 1 xxhsum -c --status "$tmp/sums.txt" # Error paths: an out-of-range algorithm and an unreadable file both exit 1. check_rc xxhash/err-badalgo 1 xxhsum -H9 "$tmp/vec.txt" check_rc xxhash/err-nofile 1 xxhsum "$tmp/does-not-exist" rm -rf "$tmp"