commit 098920432e447c4e288aab3b56962bb21cc529ea
parent 002cdf70cf43e0b8e8452187afe6d3e3f606fd9d
Author: Eyal Sawady <ecs@d2evs.net>
Date: Mon, 29 Mar 2021 17:50:44 -0400
hash::adler32: new module
Diffstat:
3 files changed, 119 insertions(+), 0 deletions(-)
diff --git a/hash/adler32/adler32.ha b/hash/adler32/adler32.ha
@@ -0,0 +1,85 @@
+// Implements the Adler-32 checksum algorithm. It is a non-cryptographic
+// checksum.
+
+use endian;
+use hash;
+use io;
+use strings;
+
+type state = struct {
+ 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,
+ },
+ a = 1,
+ b = 0,
+}): *hash::hash;
+
+fn close(s: *io::stream) void = free(s);
+
+fn write(s: *io::stream, buf: const []u8) (size | io::error) = {
+ let s = s: *state;
+ for (let i = 0z; i < len(buf); i += 1) {
+ s.a = (s.a + buf[i]) % 65521;
+ s.b = (s.b + s.a) % 65521;
+ };
+ return len(buf);
+};
+
+fn reset(h: *hash::hash) void = {
+ let h = h: *state;
+ h.a = 1;
+ h.b = 0;
+};
+
+fn sum(h: *hash::hash) []u8 = {
+ let h = h: *state;
+ let buf: [4]u8 = [0...];
+ // RFC 1950 specifies that Adler-32 checksums are stored in network
+ // order.
+ endian::beputu32(buf, (h.b << 16) | h.a);
+ return alloc(buf);
+};
+
+export fn sum32(h: *hash::hash) u32 = {
+ assert(h.reset == &reset);
+ let h = h: *state;
+ return h.b << 16 | h.a;
+};
+
+@test fn adler32() void = {
+ const vectors: [](str, u32) = [
+ ("", 1),
+ ("hello world", 436929629),
+ ("Hare is a cool language", 1578567727),
+ ("'UNIX was not designed to stop its users from doing stupid things, as that would also stop them from doing clever things' - Doug Gwyn", 2140876731),
+ ("'Life is too short to run proprietary software' - Bdale Garbee", 3135706652),
+ ("'The central enemy of reliability is complexity.' - Geer et al", 3170309588),
+ ("'A language that doesn’t have everything is actually easier to program in than some that do.' - Dennis Ritchie", 1148528899),
+
+ ];
+ let hash = adler32();
+ defer hash::close(hash);
+ for (let i = 0z; i < len(vectors); i += 1) {
+ let vec = vectors[i];
+ hash::reset(hash);
+ hash::write(hash, strings::toutf8(vec.0));
+ let s = hash::sum(hash);
+ defer free(s);
+ assert(endian::begetu32(s) == vec.1);
+ assert(sum32(hash) == vec.1);
+ };
+};
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -346,6 +346,13 @@ hash() {
gen_ssa hash io
}
+hash_adler32() {
+ printf '# hash::adler32\n'
+ gen_srcs hash::adler32 \
+ adler32.ha
+ gen_ssa hash::adler32 endian hash io strings
+}
+
hash_fnv() {
printf '# hash::fnv\n'
gen_srcs hash::fnv \
@@ -603,6 +610,7 @@ hare_module
hare_parse
hare_unparse
hash
+hash_adler32
hash_fnv
io
linux
diff --git a/stdlib.mk b/stdlib.mk
@@ -135,6 +135,9 @@ hare_stdlib_deps+=$(stdlib_hare_unparse)
stdlib_hash=$(HARECACHE)/hash/hash.o
hare_stdlib_deps+=$(stdlib_hash)
+stdlib_hash_adler32=$(HARECACHE)/hash/adler32/hash_adler32.o
+hare_stdlib_deps+=$(stdlib_hash_adler32)
+
stdlib_hash_fnv=$(HARECACHE)/hash/fnv/hash_fnv.o
hare_stdlib_deps+=$(stdlib_hash_fnv)
@@ -455,6 +458,16 @@ $(HARECACHE)/hash/hash.ssa: $(stdlib_hash_srcs) $(stdlib_rt) $(stdlib_io)
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nhash \
-t$(HARECACHE)/hash/hash.td $(stdlib_hash_srcs)
+# hash::adler32
+stdlib_hash_adler32_srcs= \
+ $(STDLIB)/hash/adler32/adler32.ha
+
+$(HARECACHE)/hash/adler32/hash_adler32.ssa: $(stdlib_hash_adler32_srcs) $(stdlib_rt) $(stdlib_endian) $(stdlib_hash) $(stdlib_io) $(stdlib_strings)
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(HARECACHE)/hash/adler32
+ @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nhash::adler32 \
+ -t$(HARECACHE)/hash/adler32/hash_adler32.td $(stdlib_hash_adler32_srcs)
+
# hash::fnv
stdlib_hash_fnv_srcs= \
$(STDLIB)/hash/fnv/fnv.ha
@@ -839,6 +852,9 @@ hare_testlib_deps+=$(testlib_hare_unparse)
testlib_hash=$(TESTCACHE)/hash/hash.o
hare_testlib_deps+=$(testlib_hash)
+testlib_hash_adler32=$(TESTCACHE)/hash/adler32/hash_adler32.o
+hare_testlib_deps+=$(testlib_hash_adler32)
+
testlib_hash_fnv=$(TESTCACHE)/hash/fnv/hash_fnv.o
hare_testlib_deps+=$(testlib_hash_fnv)
@@ -1164,6 +1180,16 @@ $(TESTCACHE)/hash/hash.ssa: $(testlib_hash_srcs) $(testlib_rt) $(testlib_io)
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nhash \
-t$(TESTCACHE)/hash/hash.td $(testlib_hash_srcs)
+# hash::adler32
+testlib_hash_adler32_srcs= \
+ $(STDLIB)/hash/adler32/adler32.ha
+
+$(TESTCACHE)/hash/adler32/hash_adler32.ssa: $(testlib_hash_adler32_srcs) $(testlib_rt) $(testlib_endian) $(testlib_hash) $(testlib_io) $(testlib_strings)
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(TESTCACHE)/hash/adler32
+ @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nhash::adler32 \
+ -t$(TESTCACHE)/hash/adler32/hash_adler32.td $(testlib_hash_adler32_srcs)
+
# hash::fnv
testlib_hash_fnv_srcs= \
$(STDLIB)/hash/fnv/fnv.ha