Bug 1059797 - Pre-allocate zlib inflate buffers in faulty.lib. r=froydnj, a=lmandel
authorMike Hommey <mh+mozilla@glandium.org>
Tue, 28 Oct 2014 16:45:17 +0900
changeset 225869 3554e60ef779
parent 225868 30ebfff46e63
child 225870 49069150dab1
push id4046
push userryanvm@gmail.com
push date2014-10-30 13:09 +0000
treeherdermozilla-beta@3554e60ef779 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj, lmandel
bugs1059797
milestone34.0
Bug 1059797 - Pre-allocate zlib inflate buffers in faulty.lib. r=froydnj, a=lmandel 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;
     }