hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 5720c45f454e4f86d647ac841415a38bbc4e8414
parent 3ab51bad39f641116c83998d273546d0a3cd4144
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat,  4 Sep 2021 07:50:50 +0200

hash::hash: refactor with subtyping

This also updates the interface for hash::fnv to avoid any allocations.
Nice!

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mcmd/hare/schedule.ha | 7+++----
Mcrypto/blake2b/blake2b.ha | 21++++++++-------------
Mcrypto/md5/md5.ha | 26++++++++++----------------
Mcrypto/sha1/sha1.ha | 28+++++++++++-----------------
Mcrypto/sha256/sha256.ha | 28+++++++++++-----------------
Mcrypto/sha512/sha512.ha | 31++++++++++++-------------------
Mfs/mem/util.ha | 5++---
Mhare/module/scan.ha | 2+-
Mhare/types/hash.ha | 45++++++++++++++++++++++-----------------------
Mhare/unit/scope.ha | 7+++----
Mhash/adler32/adler32.ha | 19+++++++------------
Mhash/crc16/crc16.ha | 19+++++++------------
Mhash/crc32/crc32.ha | 19+++++++------------
Mhash/crc64/crc64.ha | 19+++++++------------
Mhash/fnv/fnv.ha | 109++++++++++++++++++++++++++++++++++++-------------------------------------------
Mhash/hash.ha | 11++++-------
16 files changed, 165 insertions(+), 231 deletions(-)

diff --git a/cmd/hare/schedule.ha b/cmd/hare/schedule.ha @@ -13,12 +13,11 @@ use strio; fn ident_hash(ident: ast::ident) u32 = { let hash = fnv::fnv32(); - defer hash::close(hash); for (let i = 0z; i < len(ident); i += 1) { - hash::write(hash, strings::toutf8(ident[i])); - hash::write(hash, [0]); + hash::write(&hash, strings::toutf8(ident[i])); + hash::write(&hash, [0]); }; - return fnv::sum32(hash); + return fnv::sum32(&hash); }; fn sched_module(plan: *plan, ident: ast::ident, link: *[]*task) *task = { diff --git a/crypto/blake2b/blake2b.ha b/crypto/blake2b/blake2b.ha @@ -33,7 +33,7 @@ const sigma: [12][16]u8 = [ ]; type digest = struct { - hash: hash::hash, + hash::hash, h: [8]u64, tlow: u64, thigh: u64, @@ -54,16 +54,11 @@ export fn blake2b(key: []u8, sz: size) *hash::hash = { let keyblock: [BSIZE]u8 = [0...]; keyblock[..len(key)] = key; return alloc(digest { - hash = hash::hash { - stream = io::stream { - writer = &write, - closer = &close, - }, - sum = &sum, - reset = &reset, - sz = sz, - ... - }, + writer = &write, + closer = &close, + sum = &sum, + reset = &reset, + sz = sz, h = h, tlow = 0, thigh = 0, @@ -118,7 +113,7 @@ fn sum(h: *hash::hash, buf: []u8) void = { compress(&h.h, &h.block, h.tlow, h.thigh, true); - for (let i = 0z; i < h.hash.sz / 8; i += 1) { + for (let i = 0z; i < h.sz / 8; i += 1) { endian::leputu64(buf[i * 8..i * 8 + 8], h.h[i]); }; }; @@ -126,7 +121,7 @@ fn sum(h: *hash::hash, buf: []u8) void = { fn reset(h: *hash::hash) void = { let h = h: *digest; h.h = iv; - h.h[0] ^= 0x01010000 ^ (h.keylen << 8) ^ h.hash.sz; + h.h[0] ^= 0x01010000 ^ (h.keylen << 8) ^ h.sz; h.tlow = 0; h.thigh = 0; h.block = h.key; diff --git a/crypto/md5/md5.ha b/crypto/md5/md5.ha @@ -13,7 +13,7 @@ def init2: u32 = 0x98BADCFE; def init3: u32 = 0x10325476; type digest = struct { - hash: hash::hash, + hash::hash, h: [4]u32, x: [chunk]u8, nx: size, @@ -26,21 +26,15 @@ type digest = struct { // [[crypto::sha256]] or [[crypto::sha512]] instead. export fn md5() *hash::hash = { let md5 = alloc(digest { - hash = hash::hash { - stream = io::stream { - writer = &write, - closer = &close, - ... - }, - sum = &sum, - reset = &reset, - sz = SIZE, - ... - }, + writer = &write, + closer = &close, + sum = &sum, + reset = &reset, + sz = SIZE, + ... }); - let hash = &md5.hash; - hash::reset(hash); - return hash; + hash::reset(md5); + return md5; }; fn write(st: *io::stream, buf: const []u8) (size | io::error) = { @@ -97,7 +91,7 @@ fn sum(h: *hash::hash, buf: []u8) void = { let tmp: [1 + 63 + 8]u8 = [0x80, 0...]; const pad = (55 - ln) % 64; endian::leputu32(tmp[1+pad..], (ln << 3) : u32); - write(&h.hash.stream, tmp[..1+pad+8])!; // append the length in bits + write(h, tmp[..1+pad+8])!; // append the length in bits assert(h.nx == 0); diff --git a/crypto/sha1/sha1.ha b/crypto/sha1/sha1.ha @@ -16,7 +16,7 @@ def init3: u32 = 0x10325476; def init4: u32 = 0xC3D2E1F0; type digest = struct { - hash: hash::hash, + hash::hash, h: [5]u32, x: [chunk]u8, nx: size, @@ -28,21 +28,15 @@ type digest = struct { // encouraged to use [[crypto::sha256]] or [[crypto::sha512]] instead. export fn sha1() *hash::hash = { let sha = alloc(digest { - hash = hash::hash { - stream = io::stream { - writer = &write, - closer = &close, - ... - }, - sum = &sum, - reset = &reset, - sz = SIZE, - ... - }, + writer = &write, + closer = &close, + sum = &sum, + reset = &reset, + sz = SIZE, + ... }); - let hash = &sha.hash; - hash::reset(hash); - return hash; + hash::reset(sha); + return sha; }; fn write(st: *io::stream, buf: const []u8) (size | io::error) = { @@ -102,12 +96,12 @@ fn sum(h: *hash::hash, buf: []u8) void = { let tmp: [64]u8 = [0x80, 0...]; const pad = if ((ln % 64z) < 56z) 56z - ln % 64z else 64 + 56z - ln % 64z; - write(&h.hash.stream, tmp[..pad])!; + write(h, tmp[..pad])!; // Length in bits. ln <<= 3; endian::beputu64(tmp, ln: u64); - write(&h.hash.stream, tmp[..8])!; + write(h, tmp[..8])!; assert(h.nx == 0); diff --git a/crypto/sha256/sha256.ha b/crypto/sha256/sha256.ha @@ -32,7 +32,7 @@ const k: [_]u32 = [ ]; type state = struct { - hash: hash::hash, + hash::hash, h: [8]u32, x: [chunk]u8, nx: size, @@ -42,21 +42,15 @@ type state = struct { // Creates a [[hash::hash]] which computes a SHA-256 hash. export fn sha256() *hash::hash = { let sha = alloc(state { - hash = hash::hash { - stream = io::stream { - writer = &write, - closer = &close, - ... - }, - sum = &sum, - reset = &reset, - sz = SIZE, - ... - }, + writer = &write, + closer = &close, + sum = &sum, + reset = &reset, + sz = SIZE, + ... }); - let hash = &sha.hash; - hash::reset(hash); - return hash; + hash::reset(sha); + return sha; }; fn reset(h: *hash::hash) void = { @@ -118,11 +112,11 @@ fn sum(h: *hash::hash, buf: []u8) void = { tmp[0] = 0x80; const n = if ((ln % 64z) < 56z) 56z - ln % 64z else 64z + 56z - ln % 64z; - write(&h.hash.stream, tmp[..n])!; + write(h, tmp[..n])!; ln <<= 3; endian::beputu64(tmp, ln: u64); - write(&h.hash.stream, tmp[..8])!; + write(h, tmp[..8])!; assert(h.nx == 0); diff --git a/crypto/sha512/sha512.ha b/crypto/sha512/sha512.ha @@ -57,7 +57,7 @@ def init6_384: u64 = 0xdb0c2e0d64f98fa7; def init7_384: u64 = 0x47b5481dbefa4fa4; type digest = struct { - hash: hash::hash, + hash::hash, h: [8]u64, x: [chunk]u8, nx: size, @@ -80,23 +80,16 @@ export fn sha384() *hash::hash = init(variant::SHA384, SIZE384); // Internal initialization function fn init(var: variant, sz: size) *hash::hash = { let sha = alloc(digest { - hash = hash::hash { - stream = io::stream { - writer = &write, - closer = &close, - ... - }, - sum = &sum, - reset = &reset, - sz = sz, - ... - }, + writer = &write, + closer = &close, + sum = &sum, + reset = &reset, + sz = sz, var = var, }); - let hash = &sha.hash; - hash::reset(hash); - return hash; + hash::reset(sha); + return sha; }; fn write(st: *io::stream, buf: const []u8) (size | io::error) = { @@ -143,17 +136,17 @@ fn sum(h: *hash::hash, buf: []u8) void = { let tmp: [chunk]u8 = [0x80, 0...]; if ((ln % 128) < 112) { const n = 112 - (ln % 128); - write(&d.hash.stream, tmp[..n])!; + write(d, tmp[..n])!; } else { const n = 128 + 112 - (ln % 128); - write(&d.hash.stream, tmp[..n])!; + write(d, tmp[..n])!; }; // Length in bits ln <<= 3; endian::beputu64(tmp, 0u64); // upper 64 bits are always zero endian::beputu64(tmp[8..], ln : u64); - write(&d.hash.stream, tmp[..16])!; + write(d, tmp[..16])!; assert(d.nx == 0); @@ -171,7 +164,7 @@ fn sum(h: *hash::hash, buf: []u8) void = { // We only copy the necessary bytes from fixed-size array into the // returned slice. The size is already found in the inner hash struct. - buf[..] = dig[..d.hash.sz]; + buf[..] = dig[..d.sz]; }; fn reset(h: *hash::hash) void = { diff --git a/fs/mem/util.ha b/fs/mem/util.ha @@ -119,8 +119,7 @@ fn empty_dir() directory = directory { fn hash_of(name: str) u64 = { let h = hash::fnv::fnv64a(); - defer hash::close(h); - hash::write(h, strings::toutf8(name)); - return hash::fnv::sum64(h); + hash::write(&h, strings::toutf8(name)); + return hash::fnv::sum64(&h); }; diff --git a/hare/module/scan.ha b/hare/module/scan.ha @@ -320,7 +320,7 @@ fn scan_file( hash::write(sha, strings::toutf8(path)); - let tee = io::tee(f, hash::writer(sha)); + let tee = io::tee(f, sha); defer io::close(tee); let lexer = lex::init(tee, path); diff --git a/hare/types/hash.ha b/hare/types/hash.ha @@ -73,57 +73,56 @@ export fn hash(t: *_type) u32 = { // Note that this function should produce the same hashes as harec; see // bootstrap harec:src/types.c:type_hash let id = fnv::fnv32a(); - defer hash::close(id); - write8(id, type_storage(t)); - write8(id, t.flags); + write8(&id, type_storage(t)); + write8(&id, t.flags); match (t.repr) { a: alias => for (let i = len(a.id); i > 0; i -= 1) { - hash::write(id, strings::toutf8(a.id[i - 1])); - write8(id, 0); + hash::write(&id, strings::toutf8(a.id[i - 1])); + write8(&id, 0); }, a: array => { - write32(id, hash(a.member)); + write32(&id, hash(a.member)); static assert(size(u64) == size(size)); // TODO - write64(id, a.length); + write64(&id, a.length); }, builtin => void, e: _enum => { - write8(id, builtin_storage(e.storage)); + write8(&id, builtin_storage(e.storage)); for (let i = 0z; i < len(e.values); i += 1) { - hash::write(id, strings::toutf8(e.values[i].0)); - write64(id, e.values[i].1); + hash::write(&id, strings::toutf8(e.values[i].0)); + write64(&id, e.values[i].1); }; }, f: func => { - write32(id, hash(f.result)); - write8(id, f.variadism: u8); - write8(id, f.flags: u8); + write32(&id, hash(f.result)); + write8(&id, f.variadism: u8); + write8(&id, f.flags: u8); for (let i = 0z; i < len(f.params); i += 1) { - write32(id, hash(f.params[i])); + write32(&id, hash(f.params[i])); }; }, p: pointer => { - write8(id, p.flags); - write32(id, hash(p.referent)); + write8(&id, p.flags); + write32(&id, hash(p.referent)); }, - s: slice => write32(id, hash(s)), + s: slice => write32(&id, hash(s)), st: _struct => for (let i = 0z; i < len(st.fields); i += 1) { const field = st.fields[i]; - hash::write(id, strings::toutf8(field.name)); - write32(id, hash(field._type)); + hash::write(&id, strings::toutf8(field.name)); + write32(&id, hash(field._type)); static assert(size(u64) == size(size)); // TODO - write64(id, field.offs); + write64(&id, field.offs); }, tu: tuple => for (let i = 0z; i < len(tu); i += 1) { - write32(id, hash(tu[i]._type)); + write32(&id, hash(tu[i]._type)); }, ta: tagged => for (let i = 0z; i < len(ta); i += 1) { - write32(id, hash(ta[i])); + write32(&id, hash(ta[i])); }, }; - return fnv::sum32(id); + return fnv::sum32(&id); }; @test fn hash() void = { diff --git a/hare/unit/scope.ha b/hare/unit/scope.ha @@ -67,13 +67,12 @@ fn scope_pop(ctx: *context) *scope = { fn ident_hash(ident: ast::ident) u64 = { let hash = fnv::fnv64a(); - defer hash::close(hash); const zerobuf = [0u8]; for (let i = len(ident); i > 0; i -= 1) { - hash::write(hash, strings::toutf8(ident[i - 1])); - hash::write(hash, zerobuf[..]); + hash::write(&hash, strings::toutf8(ident[i - 1])); + hash::write(&hash, zerobuf[..]); }; - return fnv::sum64(hash); + return fnv::sum64(&hash); }; fn scope_insert(ctx: *context, obj: object) *object = { diff --git a/hash/adler32/adler32.ha b/hash/adler32/adler32.ha @@ -7,26 +7,21 @@ use strings; export def SIZE: size = 4; type state = struct { - hash: hash::hash, + hash::hash, a: u32, b: u32, }; // Creates a [[hash::hash]] which computes the Adler-32 checksum algorithm. export fn adler32() *hash::hash = alloc(state { - hash = hash::hash { - stream = io::stream { - writer = &write, - closer = &close, - ... - }, - sum = &sum, - reset = &reset, - sz = 4, - }, + writer = &write, + closer = &close, + sum = &sum, + reset = &reset, + sz = 4, a = 1, b = 0, -}): *hash::hash; +}); fn close(s: *io::stream) void = free(s); diff --git a/hash/crc16/crc16.ha b/hash/crc16/crc16.ha @@ -172,7 +172,7 @@ export fn memoize(polynomial: u16, buf: *[256]u16) void = { }; type state = struct { - hash: hash::hash, + hash::hash, table: *[256]u16, cval: u16, }; @@ -183,19 +183,14 @@ type state = struct { // [[ccitt_table]], [[dect_table]], or [[ansi_table]]); a table for a // custom polynomial, populated by [[memoize]] function, may also be used. export fn crc16(table: *[256]u16) *hash::hash = alloc(state { - hash = hash::hash { - stream = io::stream { - writer = &write, - closer = &close, - ... - }, - sum = &sum, - reset = &reset, - sz = SIZE, - }, + writer = &write, + closer = &close, + sum = &sum, + reset = &reset, + sz = SIZE, table = table, cval = ~0u16, -}): *hash::hash; +}); fn close(s: *io::stream) void = free(s); diff --git a/hash/crc32/crc32.ha b/hash/crc32/crc32.ha @@ -179,7 +179,7 @@ export fn memoize(polynomial: u32, buf: *[256]u32) void = { }; type state = struct { - hash: hash::hash, + hash::hash, table: *[256]u32, cval: u32, }; @@ -190,19 +190,14 @@ type state = struct { // [[ieee_table]], [[castagnoli_table]], or [[koopman_table]]); a table for a // custom polynomial, populated by [[memoize]] function, may also be used. export fn crc32(table: *[256]u32) *hash::hash = alloc(state { - hash = hash::hash { - stream = io::stream { - writer = &write, - closer = &close, - ... - }, - sum = &sum, - reset = &reset, - sz = SIZE, - }, + writer = &write, + closer = &close, + sum = &sum, + reset = &reset, + sz = SIZE, table = table, cval = ~0u32, -}): *hash::hash; +}); fn close(s: *io::stream) void = free(s); diff --git a/hash/crc64/crc64.ha b/hash/crc64/crc64.ha @@ -211,7 +211,7 @@ export fn memoize(polynomial: u64, buf: *[256]u64) void = { }; type state = struct { - hash: hash::hash, + hash::hash, table: *[256]u64, cval: u64, }; @@ -222,19 +222,14 @@ type state = struct { // [[iso_table]] or [[ecma_table]]); a table for a custom polynomial, populated // by [[memoize]] function, may also be used. export fn crc64(table: *[256]u64) *hash::hash = alloc(state { - hash = hash::hash { - stream = io::stream { - writer = &write, - closer = &close, - ... - }, - sum = &sum, - reset = &reset, - sz = SIZE, - }, + writer = &write, + closer = &close, + sum = &sum, + reset = &reset, + sz = SIZE, table = table, cval = ~0u64, -}): *hash::hash; +}); fn close(s: *io::stream) void = free(s); diff --git a/hash/fnv/fnv.ha b/hash/fnv/fnv.ha @@ -8,75 +8,67 @@ def prime64: u64 = 1099511628211; def basis32: u32 = 2166136261; def basis64: u64 = 14695981039346656037; -type state32 = struct { - hash: hash::hash, +export type state32 = struct { + hash::hash, v: u32, }; -type state64 = struct { - hash: hash::hash, +export type state64 = struct { + hash::hash, v: u64, }; -// Creates a [[hash::hash]] which computes the FNV-1 32-bit hash function. +// Creates a [[hash::hash]] which computes the FNV-1 32-bit hash function. This +// hash does not allocate any state, so you do not need to call [[hash::close]] +// when you're done with it. // // Unless you have a reason to use this, [[fnv32a]] is recommended instead. -export fn fnv32() *hash::hash = alloc(state32 { - hash = hash::hash { - stream = io::stream { - writer = &fnv32_write, - closer = &fnv_close, - }, - sum = &fnv32_sum, - reset = &fnv32_reset, - sz = 4, - }, +export fn fnv32() state32 = state32 { + writer = &fnv32_write, + closer = &fnv_close, + sum = &fnv32_sum, + reset = &fnv32_reset, + sz = 4, v = basis32, -}): *hash::hash; - -// Creates a [[hash::hash]] which computes the FNV-1a 32-bit hash function. -export fn fnv32a() *hash::hash = alloc(state32 { - hash = hash::hash { - stream = io::stream { - writer = &fnv32a_write, - closer = &fnv_close, - }, - sum = &fnv32_sum, - reset = &fnv32_reset, - sz = 4, - }, +}; + +// Creates a [[hash::hash]] which computes the FNV-1a 32-bit hash function. This +// hash does not allocate any state, so you do not need to call [[hash::close]] +// when you're done with it. +export fn fnv32a() state32 = state32 { + writer = &fnv32a_write, + closer = &fnv_close, + sum = &fnv32_sum, + reset = &fnv32_reset, + sz = 4, v = basis32, -}): *hash::hash; +}; -// Creates a [[hash::hash]] which computes the FNV-1 64-bit hash function. +// Creates a [[hash::hash]] which computes the FNV-1 64-bit hash function. This +// hash does not allocate any state, so you do not need to call [[hash::close]] +// when you're done with it. // // Unless you have a reason to use this, [[fnv64a]] is recommended instead. -export fn fnv64() *hash::hash = alloc(state64 { - hash = hash::hash { - stream = io::stream { - writer = &fnv64_write, - closer = &fnv_close, - }, - sum = &fnv64_sum, - reset = &fnv64_reset, - sz = 8, - }, +export fn fnv64() state64 = state64 { + writer = &fnv64_write, + closer = &fnv_close, + sum = &fnv64_sum, + reset = &fnv64_reset, + sz = 8, v = basis64, -}): *hash::hash; - -// Creates a [[hash::hash]] which computes the FNV-1a 64-bit hash function. -export fn fnv64a() *hash::hash = alloc(state64 { - hash = hash::hash { - stream = io::stream { - writer = &fnv64a_write, - closer = &fnv_close, - }, - sum = &fnv64_sum, - reset = &fnv64_reset, - sz = 8, - }, +}; + +// Creates a [[hash::hash]] which computes the FNV-1a 64-bit hash function. This +// hash does not allocate any state, so you do not need to call [[hash::close]] +// when you're done with it. +export fn fnv64a() state64 = state64 { + writer = &fnv64a_write, + closer = &fnv_close, + sum = &fnv64_sum, + reset = &fnv64_reset, + sz = 8, v = basis64, -}): *hash::hash; +}; fn fnv_close(s: *io::stream) void = free(s); @@ -164,17 +156,16 @@ export fn sum64(h: *hash::hash) u64 = { ]; let hash = fnv32(); - defer hash::close(hash); let s: [4]u8 = [0...]; for (let i = 0z; i < len(vectors); i += 1) { let vec = vectors[i]; - hash::reset(hash); - hash::write(hash, strings::toutf8(vec.0)); - hash::sum(hash, s); + hash::reset(&hash); + hash::write(&hash, strings::toutf8(vec.0)); + hash::sum(&hash, s); assert(endian::host.getu32(s) == vec.1); - assert(sum32(hash) == vec.1); + assert(sum32(&hash) == vec.1); }; }; diff --git a/hash/hash.ha b/hash/hash.ha @@ -4,7 +4,7 @@ use fmt; // The general purpose interface for a hashing function. export type hash = struct { // A stream which only supports writes and never returns errors. - stream: io::stream, + io::stream, // Returns the current hash. sum: *fn(hash: *hash, buf: []u8) void, @@ -16,23 +16,20 @@ export type hash = struct { sz: size, }; -// Returns a writable [[io::stream]] for a given hash. -export fn writer(h: *hash) *io::stream = &h.stream; - // Writes an input to the hash function. export fn write(h: *hash, buf: const []u8) size = - io::write(&h.stream, buf) as size; + io::write(h, buf) as size; // Finalizes the hash, frees resources associated with the hash, and populate // buf with the sum. export fn finish(h: *hash, buf: []u8) void = { sum(h, buf); - io::close(&h.stream); + io::close(h); return buf; }; // Closes a hash, freeing its resources and discarding the checksum. -export fn close(h: *hash) void = io::close(&h.stream); +export fn close(h: *hash) void = io::close(h); // Populates the user-provided buffer with the current sum. export fn sum(h: *hash, buf: []u8) void = {