diff options
author | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-15 19:00:00 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-15 19:00:00 +0000 |
commit | 9523c88db869fc1df3b95e29a6d6a8e2fa8bad1f (patch) | |
tree | 60c99bc8c88aa5bb8b34f0ba261b3b5ac2205cfb | |
parent | aa928cc483032481b155ffeab439b25039056621 (diff) | |
download | chromium_src-9523c88db869fc1df3b95e29a6d6a8e2fa8bad1f.zip chromium_src-9523c88db869fc1df3b95e29a6d6a8e2fa8bad1f.tar.gz chromium_src-9523c88db869fc1df3b95e29a6d6a8e2fa8bad1f.tar.bz2 |
net: workaround compression leaks
(This is a reland of r151502, which was reverted in r151517 because it
broke the Linux Official build.)
This may break the Official build for a brief window while a two-sided
patch lands.
BUG=139744
Review URL: https://chromiumcodereview.appspot.com/10837057
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@151720 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | net/spdy/buffered_spdy_framer.cc | 5 | ||||
-rw-r--r-- | net/spdy/buffered_spdy_framer.h | 1 | ||||
-rw-r--r-- | net/spdy/spdy_framer.cc | 178 | ||||
-rw-r--r-- | net/spdy/spdy_framer.h | 10 | ||||
-rw-r--r-- | net/spdy/spdy_framer_test.cc | 80 | ||||
-rw-r--r-- | third_party/zlib/README.chromium | 3 | ||||
-rw-r--r-- | third_party/zlib/deflate.c | 250 | ||||
-rw-r--r-- | third_party/zlib/deflate.h | 5 | ||||
-rw-r--r-- | third_party/zlib/mixed-source.patch | 491 | ||||
-rw-r--r-- | third_party/zlib/zlib.gyp | 8 | ||||
-rw-r--r-- | third_party/zlib/zlib.h | 12 |
11 files changed, 956 insertions, 87 deletions
diff --git a/net/spdy/buffered_spdy_framer.cc b/net/spdy/buffered_spdy_framer.cc index 08012c9..bafb5ce 100644 --- a/net/spdy/buffered_spdy_framer.cc +++ b/net/spdy/buffered_spdy_framer.cc @@ -296,11 +296,6 @@ bool BufferedSpdyFramer::IsCompressible(const SpdyFrame& frame) const { return spdy_framer_.IsCompressible(frame); } -SpdyControlFrame* BufferedSpdyFramer::CompressControlFrame( - const SpdyControlFrame& frame) { - return spdy_framer_.CompressControlFrame(frame); -} - // static void BufferedSpdyFramer::set_enable_compression_default(bool value) { g_enable_compression_default = value; diff --git a/net/spdy/buffered_spdy_framer.h b/net/spdy/buffered_spdy_framer.h index e13df30..39a2d0e 100644 --- a/net/spdy/buffered_spdy_framer.h +++ b/net/spdy/buffered_spdy_framer.h @@ -177,7 +177,6 @@ class NET_EXPORT_PRIVATE BufferedSpdyFramer SpdyDataFlags flags); SpdyPriority GetHighestPriority() const; bool IsCompressible(const SpdyFrame& frame) const; - SpdyControlFrame* CompressControlFrame(const SpdyControlFrame& frame); // Specify if newly created SpdySessions should have compression enabled. static void set_enable_compression_default(bool value); diff --git a/net/spdy/spdy_framer.cc b/net/spdy/spdy_framer.cc index 1d14714..fc1bb06 100644 --- a/net/spdy/spdy_framer.cc +++ b/net/spdy/spdy_framer.cc @@ -661,6 +661,155 @@ void SpdyFramer::WriteHeaderBlock(SpdyFrameBuilder* frame, } } +// These constants are used by zlib to differentiate between normal data and +// cookie data. Cookie data is handled specially by zlib when compressing. +enum ZDataClass { + // kZStandardData is compressed normally, save that it will never match + // against any other class of data in the window. + kZStandardData = Z_CLASS_STANDARD, + // kZCookieData is compressed in its own Huffman blocks and only matches in + // its entirety and only against other kZCookieData blocks. Any matches must + // be preceeded by a kZStandardData byte, or a semicolon to prevent matching + // a suffix. It's assumed that kZCookieData ends in a semicolon to prevent + // prefix matches. + kZCookieData = Z_CLASS_COOKIE, + // kZHuffmanOnlyData is only Huffman compressed - no matches are performed + // against the window. + kZHuffmanOnlyData = Z_CLASS_HUFFMAN_ONLY, +}; + +// WriteZ writes |data| to the deflate context |out|. WriteZ will flush as +// needed when switching between classes of data. +static void WriteZ(const base::StringPiece& data, + ZDataClass clas, + z_stream* out) { + int rv; + + // If we are switching from standard to non-standard data then we need to end + // the current Huffman context to avoid it leaking between them. + if (out->clas == kZStandardData && + clas != kZStandardData) { + out->avail_in = 0; + rv = deflate(out, Z_PARTIAL_FLUSH); + DCHECK_EQ(Z_OK, rv); + DCHECK_EQ(0u, out->avail_in); + DCHECK_LT(0u, out->avail_out); + } + + out->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data.data())); + out->avail_in = data.size(); + out->clas = clas; + if (clas == kZStandardData) { + rv = deflate(out, Z_NO_FLUSH); + } else { + rv = deflate(out, Z_PARTIAL_FLUSH); + } + DCHECK_EQ(Z_OK, rv); + DCHECK_EQ(0u, out->avail_in); + DCHECK_LT(0u, out->avail_out); +} + +// WriteLengthZ writes |n| as a |length|-byte, big-endian number to |out|. +static void WriteLengthZ(size_t n, + unsigned length, + ZDataClass clas, + z_stream* out) { + char buf[4]; + DCHECK_LE(length, sizeof(buf)); + for (unsigned i = 1; i <= length; i++) { + buf[length - i] = n; + n >>= 8; + } + WriteZ(base::StringPiece(buf, length), clas, out); +} + +// WriteHeaderBlockToZ serialises |headers| to the deflate context |z| in a +// manner that resists the length of the compressed data from compromising +// cookie data. +void SpdyFramer::WriteHeaderBlockToZ(const SpdyHeaderBlock* headers, + z_stream* z) const { + unsigned length_length = 4; + if (spdy_version_ < 3) + length_length = 2; + + WriteLengthZ(headers->size(), length_length, kZStandardData, z); + + std::map<std::string, std::string>::const_iterator it; + for (it = headers->begin(); it != headers->end(); ++it) { + WriteLengthZ(it->first.size(), length_length, kZStandardData, z); + WriteZ(it->first, kZStandardData, z); + + if (it->first == "cookie") { + // We require the cookie values to end with a semi-colon and (save for + // the first) to start with one too. The values are already separated by + // semicolons in the header, but there's usually whitespace in there too. + // So we accumulate the values without whitespace in |cookie_values| and + // write them out, along with a final semicolon to terminate the last + // cookie. + + std::string last_cookie; + std::vector<base::StringPiece> cookie_values; + size_t cookie_length = 0; + base::StringPiece cookie_data(it->second); + + for (;;) { + while (!cookie_data.empty() && + (cookie_data[0] == ' ' || cookie_data[0] == '\t')) { + cookie_data.remove_prefix(1); + } + if (cookie_data.empty()) + break; + + size_t i; + for (i = 0; i < cookie_data.size(); i++) { + if (cookie_data[i] == ';') + break; + } + if (i < cookie_data.size()) { + cookie_values.push_back(cookie_data.substr(0, i+1)); + cookie_length += i+1; + cookie_data.remove_prefix(i + 1); + } else { + last_cookie = cookie_data.as_string() + ";"; + cookie_values.push_back(last_cookie); + cookie_length += last_cookie.size(); + cookie_data.remove_prefix(i); + } + } + + WriteLengthZ(cookie_length, length_length, kZStandardData, z); + for (std::vector<base::StringPiece>::const_iterator + i = cookie_values.begin(); i != cookie_values.end(); i++) { + WriteZ(*i, kZCookieData, z); + } + } else if (it->first == "accept" || + it->first == "accept-charset" || + it->first == "accept-encoding" || + it->first == "accept-language" || + it->first == "host" || + it->first == "version" || + it->first == "method" || + it->first == "scheme" || + it->first == ":host" || + it->first == ":version" || + it->first == ":method" || + it->first == ":scheme" || + it->first == "user-agent") { + WriteLengthZ(it->second.size(), length_length, kZStandardData, z); + WriteZ(it->second, kZStandardData, z); + } else { + // Non-whitelisted headers are Huffman compressed in their own block, but + // don't match against the window. + WriteLengthZ(it->second.size(), length_length, kZStandardData, z); + WriteZ(it->second, kZHuffmanOnlyData, z); + } + } + + z->avail_in = 0; + int rv = deflate(z, Z_SYNC_FLUSH); + DCHECK_EQ(Z_OK, rv); + z->clas = kZStandardData; +} size_t SpdyFramer::ProcessControlFrameBeforeHeaderBlock(const char* data, size_t len) { @@ -1122,7 +1271,7 @@ SpdySynStreamControlFrame* SpdyFramer::CreateSynStream( reinterpret_cast<SpdySynStreamControlFrame*>(frame.take())); if (compressed) { return reinterpret_cast<SpdySynStreamControlFrame*>( - CompressControlFrame(*syn_frame.get())); + CompressControlFrame(*syn_frame.get(), headers)); } return syn_frame.release(); } @@ -1155,7 +1304,7 @@ SpdySynReplyControlFrame* SpdyFramer::CreateSynReply( reinterpret_cast<SpdySynReplyControlFrame*>(frame.take())); if (compressed) { return reinterpret_cast<SpdySynReplyControlFrame*>( - CompressControlFrame(*reply_frame.get())); + CompressControlFrame(*reply_frame.get(), headers)); } return reply_frame.release(); } @@ -1251,7 +1400,7 @@ SpdyHeadersControlFrame* SpdyFramer::CreateHeaders( reinterpret_cast<SpdyHeadersControlFrame*>(frame.take())); if (compressed) { return reinterpret_cast<SpdyHeadersControlFrame*>( - CompressControlFrame(*headers_frame.get())); + CompressControlFrame(*headers_frame.get(), headers)); } return headers_frame.release(); } @@ -1431,7 +1580,8 @@ bool SpdyFramer::GetFrameBoundaries(const SpdyFrame& frame, } SpdyControlFrame* SpdyFramer::CompressControlFrame( - const SpdyControlFrame& frame) { + const SpdyControlFrame& frame, + const SpdyHeaderBlock* headers) { z_stream* compressor = GetHeaderCompressor(); if (!compressor) return NULL; @@ -1452,6 +1602,10 @@ SpdyControlFrame* SpdyFramer::CompressControlFrame( // Create an output frame. int compressed_max_size = deflateBound(compressor, payload_length); + // Since we'll be performing lots of flushes when compressing the data, + // zlib's lower bounds may be insufficient. + compressed_max_size *= 2; + size_t new_frame_size = header_length + compressed_max_size; if ((frame.type() == SYN_REPLY || frame.type() == HEADERS) && spdy_version_ < 3) { @@ -1462,24 +1616,10 @@ SpdyControlFrame* SpdyFramer::CompressControlFrame( memcpy(new_frame->data(), frame.data(), frame.length() + SpdyFrame::kHeaderSize); - compressor->next_in = reinterpret_cast<Bytef*>(const_cast<char*>(payload)); - compressor->avail_in = payload_length; compressor->next_out = reinterpret_cast<Bytef*>(new_frame->data()) + header_length; compressor->avail_out = compressed_max_size; - - // Make sure that all the data we pass to zlib is defined. - // This way, all Valgrind reports on the compressed data are zlib's fault. - (void)VALGRIND_CHECK_MEM_IS_DEFINED(compressor->next_in, - compressor->avail_in); - - int rv = deflate(compressor, Z_SYNC_FLUSH); - if (rv != Z_OK) { // How can we know that it compressed everything? - // This shouldn't happen, right? - LOG(WARNING) << "deflate failure: " << rv; - return NULL; - } - + WriteHeaderBlockToZ(headers, compressor); int compressed_size = compressed_max_size - compressor->avail_out; // We trust zlib. Also, we can't do anything about it. diff --git a/net/spdy/spdy_framer.h b/net/spdy/spdy_framer.h index 1f82876..93f60e7 100644 --- a/net/spdy/spdy_framer.h +++ b/net/spdy/spdy_framer.h @@ -417,9 +417,6 @@ class NET_EXPORT_PRIVATE SpdyFramer { // Returns true if a frame could be compressed. bool IsCompressible(const SpdyFrame& frame) const; - // Returns a new SpdyControlFrame with the compressed payload of |frame|. - SpdyControlFrame* CompressControlFrame(const SpdyControlFrame& frame); - // Get the minimum size of the control frame for the given control frame // type. This is useful for validating frame blocks. static size_t GetMinimumControlFrameSize(int version, SpdyControlType type); @@ -528,6 +525,9 @@ class NET_EXPORT_PRIVATE SpdyFramer { void WriteHeaderBlock(SpdyFrameBuilder* frame, const SpdyHeaderBlock* headers) const; + void WriteHeaderBlockToZ(const SpdyHeaderBlock* headers, + z_stream* out) const; + // Set the error code and moves the framer into the error state. void set_error(SpdyError error); @@ -536,6 +536,10 @@ class NET_EXPORT_PRIVATE SpdyFramer { bool GetFrameBoundaries(const SpdyFrame& frame, int* payload_length, int* header_length, const char** payload) const; + // Returns a new SpdyControlFrame with the compressed payload of |frame|. + SpdyControlFrame* CompressControlFrame(const SpdyControlFrame& frame, + const SpdyHeaderBlock* headers); + // The size of the control frame buffer. // Since this is only used for control frame headers, the maximum control // frame header size (SYN_STREAM) is sufficient; all remaining control diff --git a/net/spdy/spdy_framer_test.cc b/net/spdy/spdy_framer_test.cc index fc1edde..fc78e20 100644 --- a/net/spdy/spdy_framer_test.cc +++ b/net/spdy/spdy_framer_test.cc @@ -1684,31 +1684,39 @@ TEST_P(SpdyFramerTest, CreateSynStreamCompressed) { const SpdyPriority priority = IsSpdy2() ? 2 : 4; const unsigned char kV2FrameData[] = { 0x80, spdy_version_, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x25, + 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x38, 0xea, 0xdf, 0xa2, 0x51, 0xb2, 0x62, 0x60, 0x62, 0x60, 0x4e, 0x4a, 0x2c, 0x62, - 0x60, 0x4e, 0xcb, 0xcf, - 0x87, 0x12, 0x40, 0x2e, - 0x00, 0x00, 0x00, 0xff, - 0xff + 0x60, 0x06, 0x08, 0xa0, + 0xb4, 0xfc, 0x7c, 0x80, + 0x00, 0x62, 0x60, 0x4e, + 0xcb, 0xcf, 0x67, 0x60, + 0x06, 0x08, 0xa0, 0xa4, + 0xc4, 0x22, 0x80, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0xff, 0xff, }; const unsigned char kV3FrameData[] = { 0x80, spdy_version_, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x27, + 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x38, 0xEA, 0xE3, 0xC6, 0xA7, 0xC2, 0x02, 0xE5, 0x0E, 0x50, 0xC2, 0x4B, 0x4A, 0x04, - 0xE5, 0x0B, 0xE6, 0xB4, - 0xFC, 0x7C, 0x24, 0x0A, - 0x28, 0x08, 0x00, 0x00, - 0x00, 0xFF, 0xFF + 0xE5, 0x0B, 0x66, 0x80, + 0x00, 0x4A, 0xCB, 0xCF, + 0x07, 0x08, 0x20, 0x10, + 0x95, 0x96, 0x9F, 0x0F, + 0xA2, 0x00, 0x02, 0x28, + 0x29, 0xB1, 0x08, 0x20, + 0x80, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0xFF, }; scoped_ptr<SpdyFrame> frame( framer.CreateSynStream(1, // stream id @@ -1862,29 +1870,37 @@ TEST_P(SpdyFramerTest, CreateSynReplyCompressed) { const unsigned char kV2FrameData[] = { 0x80, spdy_version_, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x21, + 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x38, 0xea, 0xdf, 0xa2, 0x51, 0xb2, 0x62, 0x60, 0x62, 0x60, 0x4e, 0x4a, 0x2c, 0x62, - 0x60, 0x4e, 0xcb, 0xcf, - 0x87, 0x12, 0x40, 0x2e, - 0x00, 0x00, 0x00, 0xff, - 0xff + 0x60, 0x06, 0x08, 0xa0, + 0xb4, 0xfc, 0x7c, 0x80, + 0x00, 0x62, 0x60, 0x4e, + 0xcb, 0xcf, 0x67, 0x60, + 0x06, 0x08, 0xa0, 0xa4, + 0xc4, 0x22, 0x80, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0xff, 0xff, }; const unsigned char kV3FrameData[] = { 0x80, spdy_version_, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x21, + 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x01, 0x38, 0xea, 0xe3, 0xc6, 0xa7, 0xc2, 0x02, 0xe5, 0x0e, 0x50, 0xc2, 0x4b, 0x4a, 0x04, 0xe5, 0x0b, - 0xe6, 0xb4, 0xfc, 0x7c, - 0x24, 0x0a, 0x28, 0x08, + 0x66, 0x80, 0x00, 0x4a, + 0xcb, 0xcf, 0x07, 0x08, + 0x20, 0x10, 0x95, 0x96, + 0x9f, 0x0f, 0xa2, 0x00, + 0x02, 0x28, 0x29, 0xb1, + 0x08, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0xff, - 0xff + 0xff, }; scoped_ptr<SpdyFrame> frame(framer.CreateSynReply( 1, CONTROL_FLAG_NONE, true, &headers)); @@ -2246,29 +2262,37 @@ TEST_P(SpdyFramerTest, CreateHeadersCompressed) { const unsigned char kV2FrameData[] = { 0x80, spdy_version_, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x21, + 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x38, 0xea, 0xdf, 0xa2, 0x51, 0xb2, 0x62, 0x60, 0x62, 0x60, 0x4e, 0x4a, 0x2c, 0x62, - 0x60, 0x4e, 0xcb, 0xcf, - 0x87, 0x12, 0x40, 0x2e, - 0x00, 0x00, 0x00, 0xff, - 0xff + 0x60, 0x06, 0x08, 0xa0, + 0xb4, 0xfc, 0x7c, 0x80, + 0x00, 0x62, 0x60, 0x4e, + 0xcb, 0xcf, 0x67, 0x60, + 0x06, 0x08, 0xa0, 0xa4, + 0xc4, 0x22, 0x80, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0xff, 0xff, }; const unsigned char kV3FrameData[] = { 0x80, spdy_version_, 0x00, 0x08, - 0x00, 0x00, 0x00, 0x21, + 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x01, 0x38, 0xea, 0xe3, 0xc6, 0xa7, 0xc2, 0x02, 0xe5, 0x0e, 0x50, 0xc2, 0x4b, 0x4a, 0x04, 0xe5, 0x0b, - 0xe6, 0xb4, 0xfc, 0x7c, - 0x24, 0x0a, 0x28, 0x08, + 0x66, 0x80, 0x00, 0x4a, + 0xcb, 0xcf, 0x07, 0x08, + 0x20, 0x10, 0x95, 0x96, + 0x9f, 0x0f, 0xa2, 0x00, + 0x02, 0x28, 0x29, 0xb1, + 0x08, 0x20, 0x80, 0x00, 0x00, 0x00, 0x00, 0xff, - 0xff + 0xff, }; scoped_ptr<SpdyFrame> frame(framer.CreateHeaders( 1, CONTROL_FLAG_NONE, true, &headers)); diff --git a/third_party/zlib/README.chromium b/third_party/zlib/README.chromium index aa5d4a5..81d20ef 100644 --- a/third_party/zlib/README.chromium +++ b/third_party/zlib/README.chromium @@ -16,3 +16,6 @@ A few minor changes, all marked with "Google": - Added 'mozzconf.h' to mangle the function names. - Added an #ifdef to prevent zlib.h from mangling its functions. The 'google.patch' file represents our changes from the original zlib-1.2.5. + +A more significant change to support mixed-source data compression. See +crbug.com/139744 and mixed-source.patch. diff --git a/third_party/zlib/deflate.c b/third_party/zlib/deflate.c index 5c4022f..02d1516 100644 --- a/third_party/zlib/deflate.c +++ b/third_party/zlib/deflate.c @@ -70,14 +70,15 @@ typedef enum { finish_done /* finish done, accept no more input or output */ } block_state; -typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +typedef block_state (*compress_func) OF((deflate_state *s, int flush, + int clas)); /* Compression function. Returns the block state after the call. */ local void fill_window OF((deflate_state *s)); -local block_state deflate_stored OF((deflate_state *s, int flush)); -local block_state deflate_fast OF((deflate_state *s, int flush)); +local block_state deflate_stored OF((deflate_state *s, int flush, int clas)); +local block_state deflate_fast OF((deflate_state *s, int flush, int clas)); #ifndef FASTEST -local block_state deflate_slow OF((deflate_state *s, int flush)); +local block_state deflate_slow OF((deflate_state *s, int flush, int clas)); #endif local block_state deflate_rle OF((deflate_state *s, int flush)); local block_state deflate_huff OF((deflate_state *s, int flush)); @@ -87,9 +88,9 @@ local void flush_pending OF((z_streamp strm)); local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); #ifdef ASMV void match_init OF((void)); /* asm code initialization */ - uInt longest_match OF((deflate_state *s, IPos cur_match)); + uInt longest_match OF((deflate_state *s, IPos cur_match, int clas)); #else -local uInt longest_match OF((deflate_state *s, IPos cur_match)); +local uInt longest_match OF((deflate_state *s, IPos cur_match, int clas)); #endif #ifdef DEBUG @@ -281,6 +282,9 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + s->class_bitmap = NULL; + zmemzero(&s->cookie_locations, sizeof(s->cookie_locations)); + strm->clas = 0; s->high_water = 0; /* nothing written to s->window yet */ @@ -367,6 +371,8 @@ int ZEXPORT deflateReset (strm) s = (deflate_state *)strm->state; s->pending = 0; s->pending_out = s->pending_buf; + TRY_FREE(strm, s->class_bitmap); + s->class_bitmap = NULL; if (s->wrap < 0) { s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ @@ -817,9 +823,26 @@ int ZEXPORT deflate (strm, flush) (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { block_state bstate; - bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : - (s->strategy == Z_RLE ? deflate_rle(s, flush) : - (*(configuration_table[s->level].func))(s, flush)); + if (strm->clas && s->class_bitmap == NULL) { + /* This is the first time that we have seen alternative class + * data. All data up till this point has been standard class. */ + s->class_bitmap = (Bytef*) ZALLOC(strm, s->w_size/4, sizeof(Byte)); + zmemzero(s->class_bitmap, s->w_size/4); + } + + if (strm->clas && s->strategy == Z_RLE) { + /* We haven't patched deflate_rle. */ + ERR_RETURN(strm, Z_BUF_ERROR); + } + + if (s->strategy == Z_HUFFMAN_ONLY) { + bstate = deflate_huff(s, flush); + } else if (s->strategy == Z_RLE) { + bstate = deflate_rle(s, flush); + } else { + bstate = (*(configuration_table[s->level].func)) + (s, flush, strm->clas); + } if (bstate == finish_started || bstate == finish_done) { s->status = FINISH_STATE; @@ -915,6 +938,7 @@ int ZEXPORT deflateEnd (strm) TRY_FREE(strm, strm->state->head); TRY_FREE(strm, strm->state->prev); TRY_FREE(strm, strm->state->window); + TRY_FREE(strm, strm->state->class_bitmap); ZFREE(strm, strm->state); strm->state = Z_NULL; @@ -1046,6 +1070,57 @@ local void lm_init (s) #endif } +/* class_set sets bits [offset,offset+len) in s->class_bitmap to either 1 (if + * class != 0) or 0 (otherwise). */ +local void class_set(s, offset, len, clas) + deflate_state *s; + IPos offset; + uInt len; + int clas; +{ + IPos byte = offset >> 3; + IPos bit = offset & 7; + Bytef class_byte_value = clas ? 0xff : 0x00; + Bytef class_bit_value = clas ? 1 : 0; + static const Bytef mask[8] = {0xfe, 0xfd, 0xfb, 0xf7, + 0xef, 0xdf, 0xbf, 0x7f}; + + if (bit) { + while (len) { + s->class_bitmap[byte] &= mask[bit]; + s->class_bitmap[byte] |= class_bit_value << bit; + bit++; + len--; + if (bit == 8) { + bit = 0; + byte++; + break; + } + } + } + + while (len >= 8) { + s->class_bitmap[byte++] = class_byte_value; + len -= 8; + } + + while (len) { + s->class_bitmap[byte] &= mask[bit]; + s->class_bitmap[byte] |= class_bit_value << bit; + bit++; + len--; + } +} + +local int class_at(s, window_offset) + deflate_state *s; + IPos window_offset; +{ + IPos byte = window_offset >> 3; + IPos bit = window_offset & 7; + return (s->class_bitmap[byte] >> bit) & 1; +} + #ifndef FASTEST /* =========================================================================== * Set match_start to the longest match starting at the given string and @@ -1060,9 +1135,10 @@ local void lm_init (s) /* For 80x86 and 680x0, an optimized version will be provided in match.asm or * match.S. The code will be functionally equivalent. */ -local uInt longest_match(s, cur_match) +local uInt longest_match(s, cur_match, clas) deflate_state *s; IPos cur_match; /* current match */ + int clas; { unsigned chain_length = s->max_chain_length;/* max hash chain length */ register Bytef *scan = s->window + s->strstart; /* current string */ @@ -1110,6 +1186,9 @@ local uInt longest_match(s, cur_match) do { Assert(cur_match < s->strstart, "no future"); match = s->window + cur_match; + /* If the matched data is in the wrong class, skip it. */ + if (s->class_bitmap && class_at(s, cur_match) != clas) + continue; /* Skip to next match if the match length cannot increase * or if the match length is less than 2. Note that the checks below @@ -1152,6 +1231,8 @@ local uInt longest_match(s, cur_match) len = (MAX_MATCH - 1) - (int)(strend-scan); scan = strend - (MAX_MATCH-1); +#error "UNALIGNED_OK hasn't been patched." + #else /* UNALIGNED_OK */ if (match[best_len] != scan_end || @@ -1168,15 +1249,23 @@ local uInt longest_match(s, cur_match) scan += 2, match++; Assert(*scan == *match, "match[2]?"); - /* We check for insufficient lookahead only every 8th comparison; - * the 256th check will be made at strstart+258. - */ - do { - } while (*++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - *++scan == *++match && *++scan == *++match && - scan < strend); + if (!s->class_bitmap) { + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + } else { + /* We have to be mindful of the class of the data and not stray. */ + do { + } while (*++scan == *++match && + class_at(s, match - s->window) == clas && + scan < strend); + } Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); @@ -1204,20 +1293,67 @@ local uInt longest_match(s, cur_match) } #endif /* ASMV */ +/* cookie_match is a replacement for longest_match in the case of cookie data. + * Here we only wish to match the entire value so trying the partial matches in + * longest_match is both wasteful and often fails to find the correct match. + * + * So we take the djb2 hash of the cookie and look up the last position for a + * match in a special hash table. */ +local uInt cookie_match(s, start, len) + deflate_state *s; + IPos start; + unsigned len; +{ + unsigned hash = 5381; + Bytef *str = s->window + start; + unsigned i; + IPos cookie_location; + + if (len >= MAX_MATCH) + return 0; + + for (i = 0; i < len; i++) + hash = ((hash << 5) + hash) + str[i]; + + hash &= Z_COOKIE_HASH_MASK; + cookie_location = s->cookie_locations[hash]; + s->cookie_locations[hash] = start; + s->match_start = 0; + if (cookie_location && + (start - cookie_location) > len && + (start - cookie_location) < MAX_DIST(s) && + len <= s->lookahead) { + for (i = 0; i < len; i++) { + if (s->window[start+i] != s->window[cookie_location+i] || + class_at(s, cookie_location+i) != 1) { + return 0; + } + } + s->match_start = cookie_location; + return len; + } + + return 0; +} + + #else /* FASTEST */ /* --------------------------------------------------------------------------- * Optimized version for FASTEST only */ -local uInt longest_match(s, cur_match) +local uInt longest_match(s, cur_match, clas) deflate_state *s; IPos cur_match; /* current match */ + int clas; { register Bytef *scan = s->window + s->strstart; /* current string */ register Bytef *match; /* matched string */ register int len; /* length of current match */ register Bytef *strend = s->window + s->strstart + MAX_MATCH; +#error "This code not patched" + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. * It is easy to get rid of this optimization if necessary. */ @@ -1360,6 +1496,21 @@ local void fill_window(s) */ } while (--n); #endif + + for (n = 0; n < Z_COOKIE_HASH_SIZE; n++) { + if (s->cookie_locations[n] > wsize) { + s->cookie_locations[n] -= wsize; + } else { + s->cookie_locations[n] = 0; + } + } + + if (s->class_bitmap) { + zmemcpy(s->class_bitmap, s->class_bitmap + s->w_size/8, + s->w_size/8); + zmemzero(s->class_bitmap + s->w_size/8, s->w_size/8); + } + more += wsize; } if (s->strm->avail_in == 0) return; @@ -1378,6 +1529,9 @@ local void fill_window(s) Assert(more >= 2, "more < 2"); n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + if (s->class_bitmap != NULL) { + class_set(s, s->strstart + s->lookahead, n, s->strm->clas); + } s->lookahead += n; /* Initialize the hash value now that we have some input: */ @@ -1459,9 +1613,10 @@ local void fill_window(s) * NOTE: this function should be optimized to avoid extra copying from * window to pending_buf. */ -local block_state deflate_stored(s, flush) +local block_state deflate_stored(s, flush, clas) deflate_state *s; int flush; + int clas; { /* Stored blocks are limited to 0xffff bytes, pending_buf is limited * to pending_buf_size, and each stored block has a 5 byte header: @@ -1517,13 +1672,19 @@ local block_state deflate_stored(s, flush) * new strings in the dictionary only for unmatched strings or for short * matches. It is used only for the fast compression options. */ -local block_state deflate_fast(s, flush) +local block_state deflate_fast(s, flush, clas) deflate_state *s; int flush; + int clas; { IPos hash_head; /* head of the hash chain */ int bflush; /* set if current block must be flushed */ + if (clas != 0) { + /* We haven't patched this code for alternative class data. */ + return Z_BUF_ERROR; + } + for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes @@ -1554,7 +1715,7 @@ local block_state deflate_fast(s, flush) * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ - s->match_length = longest_match (s, hash_head); + s->match_length = longest_match (s, hash_head, clas); /* longest_match() sets match_start */ } if (s->match_length >= MIN_MATCH) { @@ -1613,12 +1774,25 @@ local block_state deflate_fast(s, flush) * evaluation for matches: a match is finally adopted only if there is * no better match at the next window position. */ -local block_state deflate_slow(s, flush) +local block_state deflate_slow(s, flush, clas) deflate_state *s; int flush; + int clas; { IPos hash_head; /* head of hash chain */ int bflush; /* set if current block must be flushed */ + uInt input_length ; + int first = 1; /* first says whether this is the first iteration + of the loop, below. */ + + if (clas == Z_CLASS_COOKIE) { + if (s->lookahead) { + /* Alternative class data must always be presented at the beginning + * of a block. */ + return Z_BUF_ERROR; + } + input_length = s->strm->avail_in; + } /* Process the input block. */ for (;;) { @@ -1648,13 +1822,18 @@ local block_state deflate_slow(s, flush) s->prev_length = s->match_length, s->prev_match = s->match_start; s->match_length = MIN_MATCH-1; - if (hash_head != NIL && s->prev_length < s->max_lazy_match && - s->strstart - hash_head <= MAX_DIST(s)) { + if (clas == Z_CLASS_COOKIE && first) { + s->match_length = cookie_match(s, s->strstart, input_length); + } else if (clas == Z_CLASS_STANDARD && + hash_head != NIL && + s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { /* To simplify the code, we prevent matches with the string * of window index 0 (in particular we have to avoid a match * of the string with itself at the start of the input file). */ - s->match_length = longest_match (s, hash_head); + s->match_length = longest_match (s, hash_head, clas); + /* longest_match() sets match_start */ if (s->match_length <= 5 && (s->strategy == Z_FILTERED @@ -1673,7 +1852,22 @@ local block_state deflate_slow(s, flush) /* If there was a match at the previous step and the current * match is not better, output the previous match: */ - if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + first = 0; + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length && + /* We will only accept an exact match for Z_CLASS_COOKIE data and + * we won't match Z_CLASS_HUFFMAN_ONLY data at all. */ + (clas == Z_CLASS_STANDARD || (clas == Z_CLASS_COOKIE && + s->prev_length == input_length && + s->prev_match > 0 && + /* We require that a Z_CLASS_COOKIE match be + * preceded by either a semicolon (which cannot be + * part of a cookie), or non-cookie data. This is + * to prevent + * a cookie from being a prefix of another. + * spdy_framer.cc ensures that cookies are always + * terminated by a semicolon. */ + (class_at(s, s->prev_match-1) == Z_CLASS_STANDARD || + *(s->window + s->prev_match-1) == ';')))) { uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; /* Do not insert strings in hash table beyond this. */ diff --git a/third_party/zlib/deflate.h b/third_party/zlib/deflate.h index cbf0d1e..83b4602 100644 --- a/third_party/zlib/deflate.h +++ b/third_party/zlib/deflate.h @@ -91,6 +91,9 @@ typedef unsigned IPos; * save space in the various tables. IPos is used only for parameter passing. */ +#define Z_COOKIE_HASH_SIZE 64 +#define Z_COOKIE_HASH_MASK (Z_COOKIE_HASH_SIZE-1) + typedef struct internal_state { z_streamp strm; /* pointer back to this zlib stream */ int status; /* as the name implies */ @@ -139,6 +142,8 @@ typedef struct internal_state { uInt hash_mask; /* hash_size-1 */ uInt hash_shift; + Bytef *class_bitmap; /* bitmap of class for each byte in window */ + IPos cookie_locations[Z_COOKIE_HASH_SIZE]; /* Number of bits by which ins_h must be shifted at each input * step. It must be such that after MIN_MATCH steps, the oldest * byte no longer takes part in the hash key, that is: diff --git a/third_party/zlib/mixed-source.patch b/third_party/zlib/mixed-source.patch new file mode 100644 index 0000000..bd55ef4 --- /dev/null +++ b/third_party/zlib/mixed-source.patch @@ -0,0 +1,491 @@ +diff --git a/third_party/zlib/deflate.c b/third_party/zlib/deflate.c +index 5c4022f..02d1516 100644 +--- a/third_party/zlib/deflate.c ++++ b/third_party/zlib/deflate.c +@@ -70,14 +70,15 @@ typedef enum { + finish_done /* finish done, accept no more input or output */ + } block_state; + +-typedef block_state (*compress_func) OF((deflate_state *s, int flush)); ++typedef block_state (*compress_func) OF((deflate_state *s, int flush, ++ int clas)); + /* Compression function. Returns the block state after the call. */ + + local void fill_window OF((deflate_state *s)); +-local block_state deflate_stored OF((deflate_state *s, int flush)); +-local block_state deflate_fast OF((deflate_state *s, int flush)); ++local block_state deflate_stored OF((deflate_state *s, int flush, int clas)); ++local block_state deflate_fast OF((deflate_state *s, int flush, int clas)); + #ifndef FASTEST +-local block_state deflate_slow OF((deflate_state *s, int flush)); ++local block_state deflate_slow OF((deflate_state *s, int flush, int clas)); + #endif + local block_state deflate_rle OF((deflate_state *s, int flush)); + local block_state deflate_huff OF((deflate_state *s, int flush)); +@@ -87,9 +88,9 @@ local void flush_pending OF((z_streamp strm)); + local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); + #ifdef ASMV + void match_init OF((void)); /* asm code initialization */ +- uInt longest_match OF((deflate_state *s, IPos cur_match)); ++ uInt longest_match OF((deflate_state *s, IPos cur_match, int clas)); + #else +-local uInt longest_match OF((deflate_state *s, IPos cur_match)); ++local uInt longest_match OF((deflate_state *s, IPos cur_match, int clas)); + #endif + + #ifdef DEBUG +@@ -281,6 +282,9 @@ int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); ++ s->class_bitmap = NULL; ++ zmemzero(&s->cookie_locations, sizeof(s->cookie_locations)); ++ strm->clas = 0; + + s->high_water = 0; /* nothing written to s->window yet */ + +@@ -367,6 +371,8 @@ int ZEXPORT deflateReset (strm) + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; ++ TRY_FREE(strm, s->class_bitmap); ++ s->class_bitmap = NULL; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ +@@ -817,9 +823,26 @@ int ZEXPORT deflate (strm, flush) + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + +- bstate = s->strategy == Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : +- (s->strategy == Z_RLE ? deflate_rle(s, flush) : +- (*(configuration_table[s->level].func))(s, flush)); ++ if (strm->clas && s->class_bitmap == NULL) { ++ /* This is the first time that we have seen alternative class ++ * data. All data up till this point has been standard class. */ ++ s->class_bitmap = (Bytef*) ZALLOC(strm, s->w_size/4, sizeof(Byte)); ++ zmemzero(s->class_bitmap, s->w_size/4); ++ } ++ ++ if (strm->clas && s->strategy == Z_RLE) { ++ /* We haven't patched deflate_rle. */ ++ ERR_RETURN(strm, Z_BUF_ERROR); ++ } ++ ++ if (s->strategy == Z_HUFFMAN_ONLY) { ++ bstate = deflate_huff(s, flush); ++ } else if (s->strategy == Z_RLE) { ++ bstate = deflate_rle(s, flush); ++ } else { ++ bstate = (*(configuration_table[s->level].func)) ++ (s, flush, strm->clas); ++ } + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; +@@ -915,6 +938,7 @@ int ZEXPORT deflateEnd (strm) + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); ++ TRY_FREE(strm, strm->state->class_bitmap); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; +@@ -1046,6 +1070,57 @@ local void lm_init (s) + #endif + } + ++/* class_set sets bits [offset,offset+len) in s->class_bitmap to either 1 (if ++ * class != 0) or 0 (otherwise). */ ++local void class_set(s, offset, len, clas) ++ deflate_state *s; ++ IPos offset; ++ uInt len; ++ int clas; ++{ ++ IPos byte = offset >> 3; ++ IPos bit = offset & 7; ++ Bytef class_byte_value = clas ? 0xff : 0x00; ++ Bytef class_bit_value = clas ? 1 : 0; ++ static const Bytef mask[8] = {0xfe, 0xfd, 0xfb, 0xf7, ++ 0xef, 0xdf, 0xbf, 0x7f}; ++ ++ if (bit) { ++ while (len) { ++ s->class_bitmap[byte] &= mask[bit]; ++ s->class_bitmap[byte] |= class_bit_value << bit; ++ bit++; ++ len--; ++ if (bit == 8) { ++ bit = 0; ++ byte++; ++ break; ++ } ++ } ++ } ++ ++ while (len >= 8) { ++ s->class_bitmap[byte++] = class_byte_value; ++ len -= 8; ++ } ++ ++ while (len) { ++ s->class_bitmap[byte] &= mask[bit]; ++ s->class_bitmap[byte] |= class_bit_value << bit; ++ bit++; ++ len--; ++ } ++} ++ ++local int class_at(s, window_offset) ++ deflate_state *s; ++ IPos window_offset; ++{ ++ IPos byte = window_offset >> 3; ++ IPos bit = window_offset & 7; ++ return (s->class_bitmap[byte] >> bit) & 1; ++} ++ + #ifndef FASTEST + /* =========================================================================== + * Set match_start to the longest match starting at the given string and +@@ -1060,9 +1135,10 @@ local void lm_init (s) + /* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +-local uInt longest_match(s, cur_match) ++local uInt longest_match(s, cur_match, clas) + deflate_state *s; + IPos cur_match; /* current match */ ++ int clas; + { + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ +@@ -1110,6 +1186,9 @@ local uInt longest_match(s, cur_match) + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; ++ /* If the matched data is in the wrong class, skip it. */ ++ if (s->class_bitmap && class_at(s, cur_match) != clas) ++ continue; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below +@@ -1152,6 +1231,8 @@ local uInt longest_match(s, cur_match) + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + ++#error "UNALIGNED_OK hasn't been patched." ++ + #else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || +@@ -1168,15 +1249,23 @@ local uInt longest_match(s, cur_match) + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + +- /* We check for insufficient lookahead only every 8th comparison; +- * the 256th check will be made at strstart+258. +- */ +- do { +- } while (*++scan == *++match && *++scan == *++match && +- *++scan == *++match && *++scan == *++match && +- *++scan == *++match && *++scan == *++match && +- *++scan == *++match && *++scan == *++match && +- scan < strend); ++ if (!s->class_bitmap) { ++ /* We check for insufficient lookahead only every 8th comparison; ++ * the 256th check will be made at strstart+258. ++ */ ++ do { ++ } while (*++scan == *++match && *++scan == *++match && ++ *++scan == *++match && *++scan == *++match && ++ *++scan == *++match && *++scan == *++match && ++ *++scan == *++match && *++scan == *++match && ++ scan < strend); ++ } else { ++ /* We have to be mindful of the class of the data and not stray. */ ++ do { ++ } while (*++scan == *++match && ++ class_at(s, match - s->window) == clas && ++ scan < strend); ++ } + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + +@@ -1204,20 +1293,67 @@ local uInt longest_match(s, cur_match) + } + #endif /* ASMV */ + ++/* cookie_match is a replacement for longest_match in the case of cookie data. ++ * Here we only wish to match the entire value so trying the partial matches in ++ * longest_match is both wasteful and often fails to find the correct match. ++ * ++ * So we take the djb2 hash of the cookie and look up the last position for a ++ * match in a special hash table. */ ++local uInt cookie_match(s, start, len) ++ deflate_state *s; ++ IPos start; ++ unsigned len; ++{ ++ unsigned hash = 5381; ++ Bytef *str = s->window + start; ++ unsigned i; ++ IPos cookie_location; ++ ++ if (len >= MAX_MATCH) ++ return 0; ++ ++ for (i = 0; i < len; i++) ++ hash = ((hash << 5) + hash) + str[i]; ++ ++ hash &= Z_COOKIE_HASH_MASK; ++ cookie_location = s->cookie_locations[hash]; ++ s->cookie_locations[hash] = start; ++ s->match_start = 0; ++ if (cookie_location && ++ (start - cookie_location) > len && ++ (start - cookie_location) < MAX_DIST(s) && ++ len <= s->lookahead) { ++ for (i = 0; i < len; i++) { ++ if (s->window[start+i] != s->window[cookie_location+i] || ++ class_at(s, cookie_location+i) != 1) { ++ return 0; ++ } ++ } ++ s->match_start = cookie_location; ++ return len; ++ } ++ ++ return 0; ++} ++ ++ + #else /* FASTEST */ + + /* --------------------------------------------------------------------------- + * Optimized version for FASTEST only + */ +-local uInt longest_match(s, cur_match) ++local uInt longest_match(s, cur_match, clas) + deflate_state *s; + IPos cur_match; /* current match */ ++ int clas; + { + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + ++#error "This code not patched" ++ + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ +@@ -1360,6 +1496,21 @@ local void fill_window(s) + */ + } while (--n); + #endif ++ ++ for (n = 0; n < Z_COOKIE_HASH_SIZE; n++) { ++ if (s->cookie_locations[n] > wsize) { ++ s->cookie_locations[n] -= wsize; ++ } else { ++ s->cookie_locations[n] = 0; ++ } ++ } ++ ++ if (s->class_bitmap) { ++ zmemcpy(s->class_bitmap, s->class_bitmap + s->w_size/8, ++ s->w_size/8); ++ zmemzero(s->class_bitmap + s->w_size/8, s->w_size/8); ++ } ++ + more += wsize; + } + if (s->strm->avail_in == 0) return; +@@ -1378,6 +1529,9 @@ local void fill_window(s) + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); ++ if (s->class_bitmap != NULL) { ++ class_set(s, s->strstart + s->lookahead, n, s->strm->clas); ++ } + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ +@@ -1459,9 +1613,10 @@ local void fill_window(s) + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +-local block_state deflate_stored(s, flush) ++local block_state deflate_stored(s, flush, clas) + deflate_state *s; + int flush; ++ int clas; + { + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: +@@ -1517,13 +1672,19 @@ local block_state deflate_stored(s, flush) + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +-local block_state deflate_fast(s, flush) ++local block_state deflate_fast(s, flush, clas) + deflate_state *s; + int flush; ++ int clas; + { + IPos hash_head; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + ++ if (clas != 0) { ++ /* We haven't patched this code for alternative class data. */ ++ return Z_BUF_ERROR; ++ } ++ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes +@@ -1554,7 +1715,7 @@ local block_state deflate_fast(s, flush) + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +- s->match_length = longest_match (s, hash_head); ++ s->match_length = longest_match (s, hash_head, clas); + /* longest_match() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { +@@ -1613,12 +1774,25 @@ local block_state deflate_fast(s, flush) + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +-local block_state deflate_slow(s, flush) ++local block_state deflate_slow(s, flush, clas) + deflate_state *s; + int flush; ++ int clas; + { + IPos hash_head; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ ++ uInt input_length ; ++ int first = 1; /* first says whether this is the first iteration ++ of the loop, below. */ ++ ++ if (clas == Z_CLASS_COOKIE) { ++ if (s->lookahead) { ++ /* Alternative class data must always be presented at the beginning ++ * of a block. */ ++ return Z_BUF_ERROR; ++ } ++ input_length = s->strm->avail_in; ++ } + + /* Process the input block. */ + for (;;) { +@@ -1648,13 +1822,18 @@ local block_state deflate_slow(s, flush) + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + +- if (hash_head != NIL && s->prev_length < s->max_lazy_match && +- s->strstart - hash_head <= MAX_DIST(s)) { ++ if (clas == Z_CLASS_COOKIE && first) { ++ s->match_length = cookie_match(s, s->strstart, input_length); ++ } else if (clas == Z_CLASS_STANDARD && ++ hash_head != NIL && ++ s->prev_length < s->max_lazy_match && ++ s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +- s->match_length = longest_match (s, hash_head); ++ s->match_length = longest_match (s, hash_head, clas); ++ + /* longest_match() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +@@ -1673,7 +1852,22 @@ local block_state deflate_slow(s, flush) + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ +- if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { ++ first = 0; ++ if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length && ++ /* We will only accept an exact match for Z_CLASS_COOKIE data and ++ * we won't match Z_CLASS_HUFFMAN_ONLY data at all. */ ++ (clas == Z_CLASS_STANDARD || (clas == Z_CLASS_COOKIE && ++ s->prev_length == input_length && ++ s->prev_match > 0 && ++ /* We require that a Z_CLASS_COOKIE match be ++ * preceded by either a semicolon (which cannot be ++ * part of a cookie), or non-cookie data. This is ++ * to prevent ++ * a cookie from being a prefix of another. ++ * spdy_framer.cc ensures that cookies are always ++ * terminated by a semicolon. */ ++ (class_at(s, s->prev_match-1) == Z_CLASS_STANDARD || ++ *(s->window + s->prev_match-1) == ';')))) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + +diff --git a/third_party/zlib/deflate.h b/third_party/zlib/deflate.h +index cbf0d1e..83b4602 100644 +--- a/third_party/zlib/deflate.h ++++ b/third_party/zlib/deflate.h +@@ -91,6 +91,9 @@ typedef unsigned IPos; + * save space in the various tables. IPos is used only for parameter passing. + */ + ++#define Z_COOKIE_HASH_SIZE 64 ++#define Z_COOKIE_HASH_MASK (Z_COOKIE_HASH_SIZE-1) ++ + typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ +@@ -139,6 +142,8 @@ typedef struct internal_state { + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; ++ Bytef *class_bitmap; /* bitmap of class for each byte in window */ ++ IPos cookie_locations[Z_COOKIE_HASH_SIZE]; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: +diff --git a/third_party/zlib/zlib.h b/third_party/zlib/zlib.h +index 4d54af9..da7e971 100644 +--- a/third_party/zlib/zlib.h ++++ b/third_party/zlib/zlib.h +@@ -101,6 +101,7 @@ typedef struct z_stream_s { + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ ++ int clas; + } z_stream; + + typedef z_stream FAR *z_streamp; +@@ -207,6 +208,10 @@ typedef gz_header FAR *gz_headerp; + + #define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + ++#define Z_CLASS_STANDARD 0 ++#define Z_CLASS_COOKIE 1 ++#define Z_CLASS_HUFFMAN_ONLY 2 ++ + #define zlib_version zlibVersion() + /* for compatibility with versions < 1.0.2 */ + +@@ -1587,6 +1592,13 @@ ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); ++# else ++ ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); ++ ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); ++ ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); ++ ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); ++ ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); ++ ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + # endif + #else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); diff --git a/third_party/zlib/zlib.gyp b/third_party/zlib/zlib.gyp index 5e4c200..d856897 100644 --- a/third_party/zlib/zlib.gyp +++ b/third_party/zlib/zlib.gyp @@ -5,12 +5,12 @@ { 'variables': { 'conditions': [ - [ 'os_posix == 1 and OS != "mac" and OS != "ios" and OS != "openbsd"', { - # Link to system .so since we already use it due to GTK. + [ 'OS=="none"', { + # Because we have a patched zlib, we cannot use the system libz. # TODO(pvalchev): OpenBSD is purposefully left out, as the system # zlib brings up an incompatibility that breaks rendering. 'use_system_zlib%': 1, - }, { # os_posix != 1 or OS == "mac" or OS == "ios" or OS == "openbsd" + }, { 'use_system_zlib%': 0, }], ], @@ -70,6 +70,8 @@ 'sources!': [ 'contrib/minizip/iowin32.c' ], + }], ['OS=="android"', { + 'toolsets': ['target', 'host'], }], ], }, { diff --git a/third_party/zlib/zlib.h b/third_party/zlib/zlib.h index 4d54af9..da7e971 100644 --- a/third_party/zlib/zlib.h +++ b/third_party/zlib/zlib.h @@ -101,6 +101,7 @@ typedef struct z_stream_s { int data_type; /* best guess about the data type: binary or text */ uLong adler; /* adler32 value of the uncompressed data */ uLong reserved; /* reserved for future use */ + int clas; } z_stream; typedef z_stream FAR *z_streamp; @@ -207,6 +208,10 @@ typedef gz_header FAR *gz_headerp; #define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ +#define Z_CLASS_STANDARD 0 +#define Z_CLASS_COOKIE 1 +#define Z_CLASS_HUFFMAN_ONLY 2 + #define zlib_version zlibVersion() /* for compatibility with versions < 1.0.2 */ @@ -1587,6 +1592,13 @@ ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, ZEXTERN z_off_t ZEXPORT gzoffset64 OF((gzFile)); ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); +# else + ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); + ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile, z_off_t, int)); + ZEXTERN z_off_t ZEXPORT gztell OF((gzFile)); + ZEXTERN z_off_t ZEXPORT gzoffset OF((gzFile)); + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); # endif #else ZEXTERN gzFile ZEXPORT gzopen OF((const char *, const char *)); |