NAME
File::Raw::Hash - Cryptographic and integrity digests as a File::Raw plugin
VERSION
Version 0.07
SYNOPSIS
Loading the module registers one plugin (hash) with File::Raw. It is a passthrough on the data path: bytes flow through unchanged, and the digest is delivered through a caller-supplied scalar or hash reference passed via the into option.
use File::Raw::Hash;
use File::Raw qw(import);
# Single algorithm, scalar destination.
my $bytes = file_slurp("input.bin",
plugin => 'hash',
algo => 'sha256',
into => \my $digest,
);
# $digest is now the lowercase-hex SHA-256 of the file.
# Multiple algorithms in one pass; result lands in a hash.
my %digests;
file_slurp("input.bin",
plugin => 'hash',
algos => [qw(sha256 md5 crc32)],
into => \%digests,
);
# Streaming: each_line digests the file in chunks; the digest
# arrives in $d after iteration completes. RAM stays bounded
# regardless of file size.
each_line("huge.log", sub { ... },
plugin => 'hash',
algo => 'sha256',
into => \my $d,
);
# Same plugin, write side. The bytes spewed are the original
# payload; the digest of that payload lands in $d.
file_spew("output.bin", $payload,
plugin => 'hash',
algo => 'sha256',
into => \my $d,
);
OPTIONS
algo-
Single algorithm name. One of
sha256(default),sha512,sha1,md5,crc32,xxh64,blake3. Names are case-insensitive and tolerate dashes / underscores:SHA-256,sha_256,SHA256all resolve to the same algorithm. Mutually exclusive withalgos. algos-
Arrayref of algorithm names; one pass, all digests computed in lockstep. The result hash is keyed by canonical lowercase name (
sha256, notSHA-256). Mutually exclusive withalgo. into-
Required. Where the digest goes.
For single-algo mode (
algoor default): a scalar reference. The referent is overwritten with the formatted digest.For multi-algo mode (
algos): a hash reference. Existing keys are left alone; one entry is stored per requested algo.
format-
Output format. One of:
hmac_key-
If set, switches the algorithm to RFC 2104 HMAC mode. Available for
sha256,sha512,sha1, andmd5; rejected forcrc32,xxh64, andblake3. The key may be any byte string, including binary or empty; keys longer than the algorithm's block size are hashed down per the spec.my $mac; file_slurp("payload.bin", plugin => 'hash', algo => 'sha256', hmac_key => $secret, into => \$mac, ); xxh64_seed-
Seed for the
xxh64algorithm. Default0. Ignored by every other algorithm.
PHASES
read, write, stream, and record are all implemented.
The plugin is a passthrough on read and write - the byte stream is returned unchanged. record_fn behaves the same way: the original record is returned and a digest is appended to the caller-supplied arrayref. RECORD-phase output goes into an arrayref (one entry per record), regardless of single/multi algo:
my @per_line_digests;
# File::Raw 0.11+ does not yet expose a public per-record iterator
# API; the helper below dispatches a single record at a time and
# is the same path future high-level entry points will use.
File::Raw::Hash::_test_record_one("the line",
algo => 'sha256',
into => \@per_line_digests);
The plugin is a passthrough on read and write - the byte stream is returned unchanged. That makes it composable in a chain anywhere the caller wants to checksum a particular representation:
# Hash the wire bytes (the .gz file as it sits on disk):
my $payload = file_slurp("data.json.gz",
plugin => ['hash', 'gzip', 'json'],
hash => { algo => 'sha256', into => \my $disk_digest },
);
# Hash the decompressed payload (after gunzip, before JSON parse):
my $payload = file_slurp("data.json.gz",
plugin => ['gzip', 'hash', 'json'],
hash => { algo => 'sha256', into => \my $payload_digest },
);
(Plugin chains require File::Raw 0.10+.)
ALGORITHM CHOICE
sha256 - modern default. Cryptographically secure for new designs.
sha512 - same security family, faster on 64-bit hosts for large inputs because it processes 1024-bit blocks.
sha1 - kept for git/openssh/legacy interop. Cryptographically broken; do not use for new security designs.
md5 - kept for content fingerprinting and upstream interop (etags, checksum manifests). Cryptographically broken; do not use for security.
crc32 - IEEE 802.3 polynomial, the same CRC zlib/gzip/PNG/Ethernet use. Not a hash function; use only for integrity / dedup, never for authenticity.
xxh64 - non-cryptographic 64-bit hash by Yann Collet. Very fast; useful for content fingerprinting / dedup. Optional 64-bit seed via the
xxh64_seedoption (default 0). Not for security.blake3 - modern cryptographic hash (32-byte default output). Faster than SHA-2 family on most modern hardware. Sequential reference implementation (no SIMD) in v0.01; multi-threaded fan-out is a future enhancement.
IMPLEMENTATION
All algorithms are vendored, public-domain reference implementations. There is no external library dependency (no OpenSSL, no libsodium).
SHA-1 and SHA-256 use the CPU's SHA instructions where it has them (ARMv8 processors with the cryptography extension, which includes Apple silicon). The choice is made when the module loads, from what the running CPU reports, so one build runs everywhere; a CPU without the instructions uses the portable code, which gives the same digests.
implementation
my $impl = File::Raw::Hash::implementation();
# { sha256 => 'armv8', sha1 => 'armv8', md5 => 'portable', ... }
A hashref naming, per algorithm, the code in use: armv8, or portable.
FRH_PORTABLE
Set FRH_PORTABLE to a true value in the environment before the module loads to use the portable code for every algorithm, for example to compare the two paths.
THE C ABI
Other XS distributions can reach the digests, HMAC and the lockstep runner without linking, through the table returned by File::Raw::Hash::_abi_ptr(). The contract lives in frh_abi.h, installed via ExtUtils::Depends:
use ExtUtils::Depends;
my $pkg = ExtUtils::Depends->new('Your::Dist', 'File::Raw::Hash');
and resolved at runtime:
IV p = 0;
dSP;
PUSHMARK(SP); PUTBACK;
if (call_pv("File::Raw::Hash::_abi_ptr", G_SCALAR|G_EVAL) > 0) {
SPAGAIN; p = POPi; PUTBACK;
}
const frh_abi *H = p ? INT2PTR(const frh_abi *, p) : NULL;
#define MY_FRH_NEED 1
if (!H || H->version < MY_FRH_NEED) croak(...);
Check the version with >= against a constant naming what your code actually calls - never against the installed header's FRH_ABI_VERSION, and never with equality. The table is append-only, so a provider newer than your header is always safe; an equality check turns every append into a breaking change for consumers already shipped, and comparing against the installed header's constant means merely recompiling raises your runtime requirement.
No Perl types cross the seam: handles are opaque and released with runner_free, digests land in caller buffers sized from the registry, and raw output for the integer checksums (crc32, xxh64) is the big-endian rendering of the value, matching what the hex format prints.
The charter is hashing: digests, HMAC, cache keys, content addressing, checksums. For keys, signatures and JWS - anywhere libcrypto is the point rather than a cost - use Crypt::JWS and its jws_abi instead.
File::Raw::Hash::_abi_selftest exercises every member from the C side and is what t/15-abi.t runs.
SEE ALSO
File::Raw, Digest::SHA, Digest::MD5, Digest::CRC.
AUTHOR
LNATION <email@lnation.org>
LICENSE AND COPYRIGHT
This software is Copyright (c) 2026 by LNATION <email@lnation.org>.
This is free software, licensed under:
The Artistic License 2.0 (GPL Compatible)