lib/hash: add hash::hash interface (vtable + sum/reset/sz/bsz)
Port of ref/hare/hash/hash.ha — the general-purpose hashing-function interface that crypto/sha256 (and, later, the lib/hash/* checksums) embed as their first field. Rides the proven io.stream + inline-vtable shape: a hash is an io-write-only stream plus sum()/reset()/sz()/bsz(). One divergence (documented at-site): Hare's `stream: io::stream` field becomes an inline `vt: io.vtable` at offset 0. ww's io collapse (#94) makes the dispatchers take the vtable pointer directly, so the vtable must be embedded inline for a *state to recover from the dispatch arg — the base64/memio/io convention.
This commit is contained in:
89
lib/hash/hash.ww
Normal file
89
lib/hash/hash.ww
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// 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;
|
||||||
|
import os;
|
||||||
|
|
||||||
|
// 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 = {
|
||||||
|
os.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;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user