// hash — the general-purpose hashing-function interface. Port of // ref/hare/hash/hash.ha. A hash is an io-write-only stream you feed // data to, plus sum()/reset()/sz()/bsz(). Concrete hashes (crypto/sha256 // and, eventually, the lib/hash/* checksums) embed [[hash]] as their // first field so a `*state` is castable to `*hash` for these dispatchers. // // The existing lib/hash/* submodules (crc32, siphash, …) are reduced // one-shot forms WITHOUT this interface; this is the first module to // ship it, riding the proven io.stream + inline-vtable shape. // // DIVERGENCE (inline vtable). Hare's `hash` carries `stream: io::stream` // — and Hare's `io::stream` is itself `*vtable` (ref/hare/io/stream.ha:33), // pointing at a separate module-level const vtable. ww's io collapse // (#94) makes the io dispatchers take the vtable pointer directly // (handle's stream arm is `stream` = `*vtable`, NOT `*stream`; see // lib/io/types.ww:53), so the dispatch arg the callbacks receive must BE // the outer state pointer. That requires the vtable embedded INLINE at // offset 0, exactly as lib/encoding/base64, lib/memio, and lib/io's own // callers do it. So `hash` opens with `vt: io.vtable` (inline) rather // than a `*vtable` field; otherwise the port is 1:1. package hash; import io; // ref/hare/hash/hash.ha:8-25. `vt` (inline io.vtable) replaces Hare's // `stream: io::stream` per the header divergence — it MUST stay the // first field so `*state -> *hash -> &h.vt` all alias offset 0. `reset` // is nullable: Hare's `nullable *fn(...)`; ww spells it `(*fn(...) | void)` // (#192, the Nullable-kept ruling). `sum`/`reset` are inline `*fn(...)` // fields, the same shape as lib/errors `strerror` (errors.ww:66). export type hash = struct { vt: io.vtable, sum: *fn(h: *hash, buf: []u8) void, reset: (*fn(h: *hash) void | void), sz: size, bsz: size, }; // write — feed `buf` to the hash. ref/hare/hash/hash.ha:28-29. Hare's // `io::write(h, buf) as size`; ww has the same `as` but the io.error arm // is impossible by contract (a hash stream never errors — its writer // only buffers), so the mismatch aborts rather than propagating. export fn write(h: *hash, buf: []u8) size = { match (io.write(&h.vt, buf)) { case let n: size => return n; case let e: io.error => abort("hash.write: hash stream returned an io error"); }; }; // close — discard the running checksum and release resources. // ref/hare/hash/hash.ha:32. Hare's `io::close(h)!`. export fn close(h: *hash) void = { match (io.close(&h.vt)) { case void => void; case let e: io.error => abort("hash.close: hash stream returned an io error"); }; }; // sum — write the current digest into `buf`, which must be at least // [[sz]] bytes. ref/hare/hash/hash.ha:35-38. ww spells Hare's `h.sum(...)` // as the explicit fn-ptr call `(*h.sum)(...)` (#193). export fn sum(h: *hash, buf: []u8) void = { assert((buf.len: size) >= h.sz, "hash.sum buffer does not meet minimum required size for this hash function"); (*h.sum)(h, buf); }; // reset — return the hash to its initial state. // ref/hare/hash/hash.ha:41-48. export fn reset(h: *hash) void = { match (h.reset) { case let f: *fn(h: *hash) void => (*f)(h); case void => abort("this hash cannot be reset"); }; }; // sz — digest size in bytes, independent of hash state. // ref/hare/hash/hash.ha:52. export fn sz(h: *hash) size = { return h.sz; }; // bsz — internal block size in bytes. ref/hare/hash/hash.ha:55. export fn bsz(h: *hash) size = { return h.bsz; };