FNV-1a Hash CLI with Official Vectors
A 32-bit FNV-1a hash implementation exposed as a CLI, verified against the official reference vectors for empty, short, and known input strings.
Project
node-fnv-hash-cli
Entry point: fnv.mjs
Install
node --versionRun
node fnv.mjs foobarTest
node --test fnv.test.mjsExpected output
Running with the argument foobar prints 0xbf9cf968, the official FNV-1a 32-bit digest of that string.Dependencies
- node >=20
What it does
Computes the 32-bit FNV-1a hash of each argument using Math.imul for exact 32-bit multiply semantics, printing 0x-prefixed hex digests.
const FNV_OFFSET = 0x811c9dc5;
const FNV_PRIME = 0x01000193;
export function fnv1a(input) {
let hash = FNV_OFFSET;
for (let i = 0; i < input.length; i++) {
hash ^= input.charCodeAt(i);
hash = Math.imul(hash, FNV_PRIME) >>> 0;
}
return hash >>> 0;
}
export function fnv1aHex(input) {
return '0x' + fnv1a(input).toString(16).padStart(8, '0');
}
for (const arg of process.argv.slice(2)) {
console.log(fnv1aHex(arg));
}
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { fnv1aHex } from './fnv.mjs';
test('official FNV-1a 32-bit reference vectors', () => {
assert.equal(fnv1aHex(''), '0x811c9dc5');
assert.equal(fnv1aHex('a'), '0xe40c292c');
assert.equal(fnv1aHex('foobar'), '0xbf9cf968');
});
test('hashes are stable and order-sensitive', () => {
assert.equal(fnv1aHex('ab'), fnv1aHex('ab'));
assert.notEqual(fnv1aHex('ab'), fnv1aHex('ba'));
});
test('longer inputs stay within 32 bits', () => {
assert.match(fnv1aHex('the quick brown fox jumps over the lazy dog'), /^0x[0-9a-f]{8}$/);
});
Run and verify
node --test fnv.test.mjs
node fnv.mjs foobar
Run the tests against the official reference vectors, then hash foobar on the command line.
Expected Output
The test run should print three passing tests, with the first one asserting the official FNV-1a 32-bit reference vectors from the IETF draft. The CLI’s console.log output should print one 0x-prefixed digest per argument; running node fnv.mjs foobar should print:
0xbf9cf968
Multiple arguments each get their own line — node fnv.mjs a foobar prints 0xe40c292c then 0xbf9cf968 — and Math.imul plus >>> 0 keep the multiply arithmetic exactly within 32 bits so the digests match the published vectors rather than floating-point drift.
Limitations
FNV-1a is a fast non-cryptographic hash: never use it for signatures or adversarial inputs, only for checksums and hash tables.