4. Hashing Multiple Values Together
Questa sezione conserva il testo RFC su FNV, includendo FNV primes, offset_basis, endianism, XOR folding, constants, non-cryptographic security guidance, source code, test code, Makefile e confronti con SHA-1 and SHA-256.
Testo RFC originale
4. Hashing Multiple Values Together
Sometimes, there are multiple different component values, say three
strings X, Y, and Z, where a hash over all of them is desired. The
simplest thing to do is to concatenate them in a fixed order and
compute the hash of that concatenation, as in
hash ( X | Y | Z )
where the vertical bar character ("|") represents string
concatenation. If the components being combined are of variable
length, some information is lost by simple concatenation. For
example, X = "12" and Y = "345" would not be distinguished from X =
"123" and Y = "45". To preserve that information, each component
should be preceded by an encoding of its length or should end with
some sequence that cannot occur within the component, or some similar
technique should be used.
For FNV, the same hash results if 1) X, Y, and Z are actually
concatenated and the FNV hash is applied to the resulting string or
2) FNV is calculated on an initial substring and the result is used
as the offset_basis when calculating the FNV hash of the remainder of
the string. This can be done several times. Assuming that
FNVoffset_basis ( v, w ) is the FNV of w using v as the offset_basis,
then in the example above, fnvx = FNV ( X ) could be calculated and
then fnvxy = FNVoffset_basis ( fnvx, Y ), and finally fnvxyz =
FNVoffset_basis ( fnvxy, Z ). The resulting fnvxyz would be the same
as FNV ( X | Y | Z ).
This means that if you have the value of FNV ( X ) and you want to
calculate FNV ( X | Y ), you do not need to find X. You can simply
calculate FNVoffset_basis ( FNV ( X ), Y ) and thereby get FNV ( X |
Y ).
Sometimes, such a hash needs to be repeatedly calculated; the
component values vary, but some vary more frequently than others.
For example, assume that some sort of computer network traffic flow
ID, such as the IPv6 Flow Label [RFC6437], is to be calculated for
network packets based on the source and destination IPv6 addresses
and the Traffic Class [RFC8200]. If the Flow Label is calculated in
the originating host, the source IPv6 address would likely always be
the same or would perhaps assume one of a very small number of
values. By placing this quasi-constant IPv6 source address first in
the string being FNV-hashed, FNV ( IPv6source ) could be calculated
and used as the offset_basis for calculating the FNV of the IPv6
destination address and Traffic Class for each packet. As a result,
the per-packet hash would be over 17 bytes rather than over 33 bytes,
saving computational resources. The source code in this document
includes functions facilitating the use of a non-standard
offset_basis.
An alternative method of hashing multiple values is to concatenate
the hashes of those values and then hash the concatenation -- that
is, compute something like
hash ( hash (X) | hash (Y) | hash (Z) )
This will involve more computation than simply computing the hash of
the concatenation of the values and thus, unless parallel
computational resources are available, greater latency; however, if
parallel computational resources are available and the values being
hashed together are long enough to overcome any initial/final hash
function overhead, which is very small for FNV, latency can be
reduced by hashing the concatenation of the hashes of the values.
For another example of a similar technique, assume a desire to use
FNV-N to hash a byte string of length L. Let B = N/8, the number of
bytes of FNV-N output. If that string is divided into k successive
substrings of equal length and assuming, for simplicity, that L is an
integer multiple of k, hashing the substrings and then hashing the
concatenation of their hashes will hash a total of L + k*B bytes,
clearly more than the initial string size L. However, if sufficient
parallel computational resources are available to hash all the
substrings simultaneously, the elapsed time can be changed
approximately from on the order of L to on the order of L/k + k*B.
For sufficiently large L, this parallelization will reduce the
elapsed time to produce the overall hash.