← Runnable Projects

阅读中文版本

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.

beginnerjavascriptunverified

Project

node-fnv-hash-cli

Entry point: fnv.mjs

Install

node --version

Run

node fnv.mjs foobar

Test

node --test fnv.test.mjs

Expected 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.