hare

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

commit 398e4c23008c4a1f3ac90be31f1c0727984d053f
parent 37a484635f9ba6467d10750a2c6293184f544dcb
Author: Drew DeVault <sir@cmpwn.com>
Date:   Fri, 26 Feb 2021 18:09:18 -0500

hash::fnv: new module

Diffstat:
Mendian/host+aarch64.ha | 2+-
Mendian/host+x86_64.ha | 2+-
Ahash/fnv/fnv.ha | 184+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 186 insertions(+), 2 deletions(-)

diff --git a/endian/host+aarch64.ha b/endian/host+aarch64.ha @@ -1,2 +1,2 @@ // The [endian] functions which map to the host architecture. -export const host = &little; +export const host: *endian = &little; diff --git a/endian/host+x86_64.ha b/endian/host+x86_64.ha @@ -1,2 +1,2 @@ // The [endian] functions which map to the host architecture. -export const host = &little; +export const host: *endian = &little; diff --git a/hash/fnv/fnv.ha b/hash/fnv/fnv.ha @@ -0,0 +1,184 @@ +// Implements the Fowler–Noll–Vo (FNV) hash function. This hash is recommended +// for hash map keys and similar applications. It is a non-cryptographic hash. +use endian; +use hash; +use io; +use strings; +use fmt; + +def prime32: u32 = 16777619; +def prime64: u64 = 1099511628211; +def basis32: u32 = 2166136261; +def basis64: u64 = 14695981039346656037; + +type state32 = struct { + hash: hash::hash, + v: u32, +}; + +type state64 = struct { + hash: hash::hash, + v: u64, +}; + +// Creates a [hash::hash] which computes the FNV-1 32-bit hash function. +// +// 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, + }, + 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, + }, + v = basis32, +}): *hash::hash; + +// Creates a [hash::hash] which computes the FNV-1 64-bit hash function. +// +// 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, + }, + 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, + }, + v = basis64, +}): *hash::hash; + +fn fnv_close(s: *io::stream) void = free(s); + +fn fnv32_write(s: *io::stream, buf: const []u8) (size | io::error) = { + let s = s: *state32; + for (let i = 0z; i < len(buf); i += 1) { + s.v *= prime32; + s.v ^= buf[i]; + }; + return len(buf); +}; + +fn fnv32a_write(s: *io::stream, buf: const []u8) (size | io::error) = { + let s = s: *state32; + for (let i = 0z; i < len(buf); i += 1) { + s.v ^= buf[i]; + s.v *= prime32; + }; + return len(buf); +}; + +fn fnv32_reset(h: *hash::hash) void = { + let h = h: *state32; + h.v = basis32; +}; + +fn fnv32_sum(h: *hash::hash) []u8 = { + let h = h: *state32; + let buf: [4]u8 = [0...]; + endian::host.putu32(buf, h.v); + let sl: []u8 = alloc([], 4); + append(sl, ...buf); + return sl; +}; + +fn fnv64_write(s: *io::stream, buf: const []u8) (size | io::error) = { + let s = s: *state64; + for (let i = 0z; i < len(buf); i += 1) { + s.v *= prime64; + s.v ^= buf[i]; + }; + return len(buf); +}; + +fn fnv64a_write(s: *io::stream, buf: const []u8) (size | io::error) = { + let s = s: *state64; + for (let i = 0z; i < len(buf); i += 1) { + s.v ^= buf[i]; + s.v *= prime64; + }; + return len(buf); +}; + +fn fnv64_reset(h: *hash::hash) void = { + let h = h: *state64; + h.v = basis64; +}; + +fn fnv64_sum(h: *hash::hash) []u8 = { + let h = h: *state64; + let buf: [8]u8 = [0...]; + endian::host.putu64(buf, h.v); + let sl: []u8 = alloc([], 8); + append(sl, ...buf); + return sl; +}; + +// Returns the sum of a 32-bit FNV hash. +export fn sum32(h: *hash::hash) u32 = { + assert(h.reset == &fnv32_reset); + let h = h: *state32; + return h.v; +}; + +// Returns the sum of a 64-bit FNV hash. +export fn sum64(h: *hash::hash) u64 = { + assert(h.reset == &fnv64_reset); + let h = h: *state64; + return h.v; +}; + +@test fn fnv32() void = { + // TODO: Expand these tests + // I am too tired + const vectors: [_](str, u32) = [ + ("", 2166136261), + ("hello world", 1418570095), + ("Hare is a cool language", 2663852071), + ("'UNIX was not designed to stop its users from doing stupid things, as that would also stop them from doing clever things' - Doug Gwyn", 1203174417), + ("'Life is too short to run proprietary software' - Bdale Garbee", 493463614), + ("'The central enemy of reliability is complexity.' - Geer et al", 3263526736), + ("'A language that doesn’t have everything is actually easier to program in than some that do.' - Dennis Ritchie", 3069348265), + ]; + let hash = fnv32(); + defer hash::close(hash); + for (let i = 0z; i < len(vectors); i += 1) { + let vec = vectors[i]; + hash::reset(hash); + hash::write(hash, strings::to_utf8(vec.0)); + assert(sum32(hash) == vec.1); + }; +};