lib/libz/0004-Backport-Unconditional-refill-for-literals-with-fast.patch
$ cat 0004-Backport-Unconditional-refill-for-literals-with-fast.patch
From bc8d6761c35275f96916238bd450296a9106528d Mon Sep 17 00:00:00 2001
From: Felix Hanau <felix@cloudflare.com>
Date: Fri, 8 Sep 2023 17:58:50 -0400
Subject: [PATCH 4/4] Backport "Unconditional refill for literals with
fastpath"
This PR avoids branching before a potential bit buffer refill during
decompression as the buffer is filled once in every iteration, allowing
up to two literals to be decoded. See https://chromium-review.googlesource.com/c/chromium/src/+/4247146
for base commit, rationale and performance metrics. Patch based on
Dougall Johnson's inflate improvements.
Also drops superfluous return statement.
---
inffast_chunk.c | 69 +++++++++++++++++++++++++++++++------------------
inffast_chunk.h | 17 ++++++++----
2 files changed, 56 insertions(+), 30 deletions(-)
diff --git a/inffast_chunk.c b/inffast_chunk.c
index 39eff71..7a158d7 100644
--- a/inffast_chunk.c
+++ b/inffast_chunk.c
@@ -22,6 +22,7 @@
* jloup@gzip.org madler@alumni.caltech.edu
*
* Copyright (C) 1995-2017 Mark Adler
+ * Copyright 2023 The Chromium Authors
* For conditions of distribution and use, see copyright notice in zlib.h
*/
@@ -46,8 +47,8 @@
Entry assumptions:
state->mode == LEN
- strm->avail_in >= INFLATE_FAST_MIN_INPUT (6 or 8 bytes)
- strm->avail_out >= INFLATE_FAST_MIN_OUTPUT (258 bytes)
+ strm->avail_in >= INFLATE_FAST_MIN_INPUT (6 or 8 bytes + 7 bytes)
+ strm->avail_out >= INFLATE_FAST_MIN_OUTPUT (258 bytes + 2 bytes)
start >= strm->avail_out
state->bits < 8
strm->next_out[0..strm->avail_out] does not overlap with
@@ -63,7 +64,7 @@
Notes:
- INFLATE_FAST_MIN_INPUT: 6 or 8 bytes
+ INFLATE_FAST_MIN_INPUT: 6 or 8 bytes + 7 bytes
- The maximum input bits used by a length/distance pair is 15 bits for the
length code, 5 bits for the length extra, 15 bits for the distance code,
@@ -85,10 +86,10 @@
(state->hold >> state->bits) == 0
- INFLATE_FAST_MIN_OUTPUT: 258 bytes
+ INFLATE_FAST_MIN_OUTPUT: 258 bytes + 2 bytes for literals = 260 bytes
- The maximum bytes that a single length/distance pair can output is 258
bytes, which is the maximum length that can be coded. inflate_fast()
- requires strm->avail_out >= 258 for each loop to avoid checking for
+ requires strm->avail_out >= 260 for each loop to avoid checking for
available output space while decoding.
*/
void ZLIB_INTERNAL inflate_fast_chunk_(strm, start)
@@ -144,22 +145,50 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */
lmask = (1U << state->lenbits) - 1;
dmask = (1U << state->distbits) - 1;
+#ifdef INFLATE_CHUNK_READ_64LE
+#define REFILL() do { \
+ Assert(bits < 64, "too many bits in inflate_fast_chunk_"); \
+ hold |= read64le(in) << bits; \
+ in += 7; \
+ in -= bits >> 3; \
+ bits |= 56; \
+ } while (0)
+#endif
+
/* decode literals and length/distances until end-of-block or not enough
input data or output space */
do {
- if (bits < 15) {
#ifdef INFLATE_CHUNK_READ_64LE
- hold |= read64le(in) << bits;
- in += 6;
- bits += 48;
+ REFILL();
#else
+ if (bits < 15) {
hold += (unsigned long)(*in++) << bits;
bits += 8;
hold += (unsigned long)(*in++) << bits;
bits += 8;
-#endif
}
+#endif
here = lcode[hold & lmask];
+#ifdef INFLATE_CHUNK_READ_64LE
+ if (here.op == 0) { /* literal */
+ Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+ "inflate: literal '%c'\n" :
+ "inflate: literal 0x%02x\n", here.val));
+ *out++ = (unsigned char)(here.val);
+ hold >>= here.bits;
+ bits -= here.bits;
+ here = lcode[hold & lmask];
+ if (here.op == 0) { /* literal */
+ Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
+ "inflate: 2nd literal '%c'\n" :
+ "inflate: 2nd literal 0x%02x\n", here.val));
+ *out++ = (unsigned char)(here.val);
+ hold >>= here.bits;
+ bits -= here.bits;
+ here = lcode[hold & lmask];
+ }
+ }
+#endif
dolen:
op = (unsigned)(here.bits);
hold >>= op;
@@ -175,33 +204,25 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */
len = (unsigned)(here.val);
op &= 15; /* number of extra bits */
if (op) {
+#ifndef INFLATE_CHUNK_READ_64LE
if (bits < op) {
-#ifdef INFLATE_CHUNK_READ_64LE
- hold |= read64le(in) << bits;
- in += 6;
- bits += 48;
-#else
hold += (unsigned long)(*in++) << bits;
bits += 8;
-#endif
}
+#endif
len += (unsigned)hold & ((1U << op) - 1);
hold >>= op;
bits -= op;
}
Tracevv((stderr, "inflate: length %u\n", len));
+#ifndef INFLATE_CHUNK_READ_64LE
if (bits < 15) {
-#ifdef INFLATE_CHUNK_READ_64LE
- hold |= read64le(in) << bits;
- in += 6;
- bits += 48;
-#else
hold += (unsigned long)(*in++) << bits;
bits += 8;
hold += (unsigned long)(*in++) << bits;
bits += 8;
-#endif
}
+#endif
here = dcode[hold & dmask];
dodist:
op = (unsigned)(here.bits);
@@ -213,9 +234,7 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */
op &= 15; /* number of extra bits */
if (bits < op) {
#ifdef INFLATE_CHUNK_READ_64LE
- hold |= read64le(in) << bits;
- in += 6;
- bits += 48;
+ REFILL();
#else
hold += (unsigned long)(*in++) << bits;
bits += 8;
diff --git a/inffast_chunk.h b/inffast_chunk.h
index 5d6025a..aadfd70 100644
--- a/inffast_chunk.h
+++ b/inffast_chunk.h
@@ -33,16 +33,23 @@
#include "inffast.h"
-/* INFLATE_FAST_MIN_INPUT: the minimum number of input bytes needed so that
- we can safely call inflate_fast() with only one up-front bounds check. One
+/* INFLATE_FAST_MIN_INPUT:
+ The minimum number of input bytes needed so that we can safely call
+ inflate_fast() with only one up-front bounds check. One
length/distance code pair (15 bits for the length code, 5 bits for length
extra, 15 bits for the distance code, 13 bits for distance extra) requires
- reading up to 48 input bits (6 bytes). The wide input data reading option
- requires a little endian machine, and reads 64 input bits (8 bytes).
+ reading up to 48 input bits. Additionally, in the same iteration, we may
+ decode two literals from the root table (requiring 258 + 2 output bytes).
+
+ The wide refill reads eight bytes and advances the input pointer by up to
+ seven bytes, maintaining at least 56 bits in the bit buffer. In the worst
+ case it refills twice in one iteration, requiring 8 + 7 input bytes.
*/
#ifdef INFLATE_CHUNK_READ_64LE
#undef INFLATE_FAST_MIN_INPUT
-#define INFLATE_FAST_MIN_INPUT 8
+#define INFLATE_FAST_MIN_INPUT 15
+#undef INFLATE_FAST_MIN_OUTPUT
+#define INFLATE_FAST_MIN_OUTPUT 260
#endif
void ZLIB_INTERNAL inflate_fast_chunk_(z_streamp strm, unsigned start);
--
2.55.0
