The flat checker scope makes ANY decl named assert/abort anywhere in the combined unit disable the builtin unit-wide (the #45 shadow shape: scope_lookup_prefer's cross-module fallback finds it). lib carried three colliding @symbol("rt_abort") shims (os, time, strconv/stof) plus the os.assert wrapper, so a bare assert(cond) in ANY program importing os mis-bound os.assert and failed arity — a hard blocker for regex fold-5 (regex.ha:660/670 bring builtin-assert mass). Ruled respell-now per the recurrence test (#45 -> #58). Delete the shims and the os.assert wrapper; every bare abort(msg) caller (regex, strings, utf8, hash, getopt, encoding/*, time, stof) now lands on the builtin, and the ~40 os.assert(c, m) sites respell to the builtin assert(c, m) — restoring the exact Hare spelling the lib ports diverged from (e.g. ref/hare/bytes/tokenize.ha:23). os.assert had no Hare counterpart (Hare's assert is a language builtin); rule-9 wrapper removed. temp/dirs/bufio already use the non-colliding rtabort spelling and keep it. Now-dead 'import os;' lines kept (pre-existing precedent: lib/strconv/strconv.ww carries one); a tree-wide dead-import sweep is a separate concern. regex.ww's if+abort workarounds citing #58 stay for the fold-5 owner to fold back into assert. combined.ww regenerated for all five selfhost tools + the smoke fixture via make.
90 lines
3.5 KiB
Plaintext
90 lines
3.5 KiB
Plaintext
// 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 = {
|
|
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;
|
|
};
|