Code change:
2.2.1. <del> Given: </del> <ins> Given the following inputs: </ins> <del> "digest-value", an array of bits </del> <ins> "URL" a string corresponding to the Effective Request URI (RFC7230, Section 5.5) of a cached response RFC7234. </ins> <del> "URL", an array of characters </del> <ins> "ETag" a string corresponding to the entity-tag RFC7232 of a cached response RFC7234 (if the ETag is available; otherwise, null). </ins> <del> "ETag", an array of characters </del> <ins> "digest-value", an array of bits. Let "f" be the value of the first byte of "digest-value". Let "b" be the bucket size, defined as 4. </ins> <del> "validators", a boolean </del> <ins> Let "N" be the value of the second to fifth bytes of "digest- value" in big endian form. </ins> <del> we can determine whether there is a match in the digest using the following algorithm: </del> <ins> Let "key" be the return value of key with "URL" and "ETag" as inputs. </ins> <del> Read the first 5 bits of "digest-value" as an integer; let "N" be two raised to the power of that value. </del> <ins> Let "h1" be the return value of hash with "key" and "N" as inputs. </ins> <del> Read the next 5 bits of "digest-value" as an integer; let "P" be two raised to the power of that value. </del> <ins> Let "fingerprint" be the return value of fingerprint with "key" and "f" as inputs. </ins> <del> Let "hash-value" be the result of computing a hash value (hash). </del> <ins> Let "fingerprint-string" be the value of "fingerprint" in base 10, expressed as a string. </ins> <del> Let "C" be -1. </del> <ins> Let "h2" be the return value of hash with "fingerprint" and "N" as inputs, XORed with "h1". </ins> <del> Read '0' bits from "digest-value" until a '1' bit is found; let "Q" be the number of '0' bits.  Discard the '1'. </del> <ins> Let "h" be "h1". </ins> <del> Read log2("P") bits from "digest-value" after the '1' as an integer; let "R" be its value. </del> <ins> Let "position_start" be 40 + "h" * "f" * "b". </ins> <del> Let "D" be "Q" * "P" + "R". </del> <ins> Let "position_end" be "position_start" + "f" * "b". </ins> <del> Increment "C" by "D" + 1. </del> <ins> While "position_start" < "position_end": </ins> <del> If "C" is equal to "hash-value", return 'true'. </del> <ins> Let "bits" be "f" bits from "digest_value" starting at "position_start". </ins> <del> Otherwise, return to step 5 and continue processing; if no match is found before "digest-value" is exhausted, return 'false'. </del> <ins> If "bits" is "fingerprint", return true Add "f" to "position_start". Return false. </ins> 3. <ins> A Client SHOULD notify its support for CACHE_DIGEST frames by sending the SENDING_CACHE_DIGEST (0xXXX) SETTINGS parameter. The value of the parameter is a bit-field of which the following bits are defined: DIGEST_PENDING (0x1): When set it indicates that the client has a digest to send, and the server may choose to wait for a digest in order to make server push decisions. Rest of the bits MUST be ignored and MUST be left unset when sending. The initial value of the parameter is zero (0x0) meaning that the client has no digest to send the server. 4. </ins> A server can notify its support for CACHE_DIGEST frame by sending the ACCEPT_CACHE_DIGEST (0x7) SETTINGS parameter.  If the server is tempted to making optimizations based on CACHE_DIGEST frames, it

Related discussion:
text: I've got an incomplete initial reference implementation at URL It doesn't yet include removal and querying (that's what I'll be adding next), but I did run it on a list of ~3250 URL (which I got out of my main profile chrome://cache/) and it seems to be creating reasonable sized digests. One more advantage, the digests seem to be highly compressible when sparse. Results so far: Digest with 1021 entries (so room for ~4K URLs): 5621 in-memory, 5233 gzipped (when filled with 3250 URLs). Digest with 2503 entries (so room for ~10K URLs): 13772 in-memory, 6879 gzipped (same 3250 URLs). Digest with 7919 entries (so room for ~31K URLs): 43560 in-memory, 9984 gzipped (same 3250 URLs). In practice, I think ~1000 entries is most probably enough, but it's good to know we can increase the digest size (to avoid having to recreate it), without significant over-the-wire penalty.
OK, I now have a complete reference implementation and it seems to be working fine. It also exposed an issue with the initial algorithm, forcing table allocation to accommodate a power of 2 number of entries. Latest results for 3250 URLs taken from my cache: One note: the 1021 entries table had 35 collisions, so it seems like it's insufficient for that number of URLs, unless we're willing to absorb extra pushes for ~1% of the resources.
NAME Interesting! It's good to know that we have numbers now. What is the value of P (the false positive ratio) that you used?
P=8 (so 1/256 false positive)
Note that the numbers here may be possible to further optimize. One example is semi-sorting of the buckets which the Cuckoo-Filters paper mentions, and which I have not yet implemented. It adds some runtime complexity, but can reduce the fingerprint size per resource by a full bit, so could have resulted in ~9% smaller digests in this case.
NAME Awesome! Me not being familiar with these data structures (despite reading Wikipedia article ), why does the 1/256 probability (~4/1000) result in 35 collisions?