Bug 1059797 - Pre-allocate zlib inflate buffers in faulty.lib. r=froydnj
authorMike Hommey <mh+mozilla@glandium.org>
Tue, 28 Oct 2014 16:45:17 +0900
changeset 212662 44c6d392bbc850ee299189c17da888bfd8b3fa91
parent 212661 48099863baecf556a76a607811064e06daa88671
child 212663 3bec46585a18ef6f25c335d9a44bc287ac97dae7
push id27722
push userryanvm@gmail.com
push dateTue, 28 Oct 2014 20:02:50 +0000
treeherdermozilla-central@17a2e10e3808 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1059797
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1059797 - Pre-allocate zlib inflate buffers in faulty.lib. r=froydnj Original patch from James Willcox <snorp@snorp.net>
mozglue/linker/Mappable.cpp
mozglue/linker/Mappable.h
mozglue/linker/SeekableZStream.cpp
mozglue/linker/Zip.h
--- a/mozglue/linker/Mappable.cpp
+++ b/mozglue/linker/Mappable.cpp
@@ -99,17 +99,17 @@ MappableExtractFile::Create(const char *
     /* Map the temporary file for use as inflate buffer */
     MappedPtr buffer(MemoryRange::mmap(nullptr, stream->GetUncompressedSize(),
                                        PROT_WRITE, MAP_SHARED, fd, 0));
     if (buffer == MAP_FAILED) {
       ERROR("Couldn't map %s to decompress library", file.get());
       return nullptr;
     }
 
-    z_stream zStream = stream->GetZStream(buffer);
+    zxx_stream zStream = stream->GetZStream(buffer);
 
     /* Decompress */
     if (inflateInit2(&zStream, -MAX_WBITS) != Z_OK) {
       ERROR("inflateInit failed: %s", zStream.msg);
       return nullptr;
     }
     if (inflate(&zStream, Z_FINISH) != Z_STREAM_END) {
       ERROR("inflate failed: %s", zStream.msg);
--- a/mozglue/linker/Mappable.h
+++ b/mozglue/linker/Mappable.h
@@ -175,17 +175,17 @@ private:
 
   /* Zip reference */
   mozilla::RefPtr<Zip> zip;
 
   /* Decompression buffer */
   mozilla::UniquePtr<_MappableBuffer> buffer;
 
   /* Zlib data */
-  z_stream zStream;
+  zxx_stream zStream;
 };
 
 /**
  * Mappable implementation for seekable zStreams.
  * Inflates the mapped bits in a temporary buffer, on demand.
  */
 class MappableSeekableZStream: public Mappable
 {
--- a/mozglue/linker/SeekableZStream.cpp
+++ b/mozglue/linker/SeekableZStream.cpp
@@ -67,18 +67,17 @@ SeekableZStream::DecompressChunk(void *w
 
   size_t chunkLen = isLastChunk ? lastChunkSize : chunkSize;
 
   if (length == 0 || length > chunkLen)
     length = chunkLen;
 
   DEBUG_LOG("DecompressChunk #%" PRIdSize " @%p (%" PRIdSize "/% " PRIdSize ")",
         chunk, where, length, chunkLen);
-  z_stream zStream;
-  memset(&zStream, 0, sizeof(zStream));
+  zxx_stream zStream;
   zStream.avail_in = (isLastChunk ? totalSize : uint32_t(offsetTable[chunk + 1]))
                      - uint32_t(offsetTable[chunk]);
   zStream.next_in = const_cast<Bytef *>(buffer + uint32_t(offsetTable[chunk]));
   zStream.avail_out = length;
   zStream.next_out = reinterpret_cast<Bytef *>(where);
 
   /* Decompress chunk */
   if (inflateInit2(&zStream, windowBits) != Z_OK) {
--- a/mozglue/linker/Zip.h
+++ b/mozglue/linker/Zip.h
@@ -5,17 +5,108 @@
 #ifndef Zip_h
 #define Zip_h
 
 #include <cstring>
 #include <stdint.h>
 #include <vector>
 #include <zlib.h>
 #include "Utils.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+
+/**
+ * Helper class wrapping z_stream to avoid malloc() calls during
+ * inflate. Do not use for deflate.
+ * inflateInit allocates two buffers:
+ * - one for its internal state, which is "approximately 10K bytes" according
+ *   to inflate.h from zlib.
+ * - one for the compression window, which depends on the window size passed
+ *   to inflateInit2, but is never greater than 32K (1 << MAX_WBITS).
+ * Those buffers are created at instantiation time instead of when calling
+ * inflateInit2. When inflateInit2 is called, it will call zxx_stream::Alloc
+ * to get both these buffers. zxx_stream::Alloc will choose one of the
+ * pre-allocated buffers depending on the requested size.
+ */
+class zxx_stream: public z_stream
+{
+public:
+  zxx_stream() {
+    memset(this, 0, sizeof(z_stream));
+    zalloc = Alloc;
+    zfree = Free;
+    opaque = this;
+  }
+
+private:
+  static void *Alloc(void *data, uInt items, uInt size)
+  {
+    size_t buf_size = items * size;
+    zxx_stream *zStream = reinterpret_cast<zxx_stream *>(data);
+
+    if (items == 1 && buf_size <= zStream->stateBuf.size) {
+      return zStream->stateBuf.get();
+    } else if (buf_size == zStream->windowBuf.size) {
+      return zStream->windowBuf.get();
+    } else {
+      MOZ_CRASH("No ZStreamBuf for allocation");
+    }
+  }
+
+  static void Free(void *data, void *ptr)
+  {
+    zxx_stream *zStream = reinterpret_cast<zxx_stream *>(data);
+
+    if (zStream->stateBuf.Equals(ptr)) {
+      zStream->stateBuf.Release();
+    } else if (zStream->windowBuf.Equals(ptr)) {
+      zStream->windowBuf.Release();
+    } else {
+      MOZ_CRASH("Pointer doesn't match a ZStreamBuf");
+    }
+  }
+
+  /**
+   * Helper class for each buffer.
+   */
+  template <size_t Size>
+  class ZStreamBuf
+  {
+  public:
+    ZStreamBuf() : buf(new char[Size]), inUse(false) { }
+
+    char *get()
+    {
+      if (!inUse) {
+        inUse = true;
+        return buf.get();
+      } else {
+        MOZ_CRASH("ZStreamBuf already in use");
+      }
+    }
+
+    void Release()
+    {
+      memset(buf.get(), 0, Size);
+      inUse = false;
+    }
+
+    bool Equals(const void *other) { return other == buf.get(); }
+
+    static const size_t size = Size;
+
+  private:
+    mozilla::UniquePtr<char[]> buf;
+    bool inUse;
+  };
+
+  ZStreamBuf<0x3000> stateBuf; // 0x3000 is an arbitrary size above 10K.
+  ZStreamBuf<1 << MAX_WBITS> windowBuf;
+};
 
 /**
  * Forward declaration
  */
 class ZipCollection;
 
 /**
  * Class to handle access to Zip archive streams. The Zip archive is mapped
@@ -82,24 +173,23 @@ public:
      * Getters
      */
     const void *GetBuffer() { return compressedBuf; }
     size_t GetSize() { return compressedSize; }
     size_t GetUncompressedSize() { return uncompressedSize; }
     Type GetType() { return type; }
 
     /**
-     * Returns a z_stream for use with inflate functions using the given
+     * Returns a zxx_stream for use with inflate functions using the given
      * buffer as inflate output. The caller is expected to allocate enough
      * memory for the Stream uncompressed size.
      */
-    z_stream GetZStream(void *buf)
+    zxx_stream GetZStream(void *buf)
     {
-      z_stream zStream;
-      memset(&zStream, 0, sizeof(zStream));
+      zxx_stream zStream;
       zStream.avail_in = compressedSize;
       zStream.next_in = reinterpret_cast<Bytef *>(
                         const_cast<void *>(compressedBuf));
       zStream.avail_out = uncompressedSize;
       zStream.next_out = static_cast<Bytef *>(buf);
       return zStream;
     }