bin_hash_object() is XXH3 of the body of R's XDR serialisation of x,
format version 2 or 3: everything serialize() writes after its header.
It is streamed through the hasher, so the serialised bytes are never
allocated. Skipping the header, which records the R version that wrote it
(and, for version = 3, the native encoding), keeps the digest from
changing with those; it stays the same for as long as R writes the same
body for x, which R does not promise across its versions (see
serialize()), least of all for closures, environments and ALTREP
objects. Objects with the same serialisation have the same digest; the
converse holds only up to the chance of a collision.
Usage
bin_hash_object(x, algo = c("xxh3_64", "xxh3_128"), version = 2L, seed = 0)Details
It is a fingerprint, not a cryptographic digest and not a durable content address: fast, and well distributed, but not a defence against someone choosing inputs to collide.
The digest is of the bytes R writes, not of the values they stand for, so
identical(x, y) does not imply equal digests:
A double is its 64 bits.
0and-0differ, as do NaNs of different sign or payload, andNAonce arithmetic has set its quiet bit (c(NA, 1) + 0), thoughidentical()treats each pair as the same. How NaN andNApropagate through arithmetic depends on the platform (see NA), so the same computation may hash differently on two machines; and on 32-bit x86 even a doubleNAwritten as a literal has different bits.A string is its bytes and its encoding mark, so the same non-ASCII text hashes differently when it is marked UTF-8, latin1 or not at all; and unmarked text is in the session's native encoding, which depends on the locale. Apply
enc2utf8()to the strings first for a digest that does not depend on either.
Apart from these, the digest of an object is the same on every platform R runs on.
version = 2, the default, writes compact sequences such as 1:10 as the
vectors they are; version = 3 writes their compact form, so the same
values may then hash differently.