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:
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 = {