1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef vm_Compression_h
8 #define vm_Compression_h
9 
10 #include <zlib.h>
11 
12 #include "jstypes.h"
13 
14 #include "js/AllocPolicy.h"
15 #include "js/Vector.h"
16 
17 namespace js {
18 
19 struct CompressedDataHeader {
20   uint32_t compressedBytes;
21 };
22 
23 class Compressor {
24  public:
25   // After compressing CHUNK_SIZE bytes, we will do a full flush so we can
26   // start decompression at that point.
27   static constexpr size_t CHUNK_SIZE = 64 * 1024;
28 
29  private:
30   // Number of bytes we should hand to zlib each compressMore() call.
31   static constexpr size_t MAX_INPUT_SIZE = 2 * 1024;
32 
33   z_stream zs;
34   const unsigned char* inp;
35   size_t inplen;
36   size_t outbytes;
37   bool initialized;
38   bool finished;
39 
40   // The number of uncompressed bytes written for the current chunk. When this
41   // reaches CHUNK_SIZE, we finish the current chunk and start a new chunk.
42   uint32_t currentChunkSize;
43 
44   // At the end of each chunk (and the end of the uncompressed data if it's
45   // not a chunk boundary), we record the offset in the compressed data.
46   js::Vector<uint32_t, 8, SystemAllocPolicy> chunkOffsets;
47 
48  public:
49   enum Status { MOREOUTPUT, DONE, CONTINUE, OOM };
50 
51   Compressor(const unsigned char* inp, size_t inplen);
52   ~Compressor();
53   bool init();
54   void setOutput(unsigned char* out, size_t outlen);
55   /* Compress some of the input. Return true if it should be called again. */
56   Status compressMore();
sizeOfChunkOffsets()57   size_t sizeOfChunkOffsets() const {
58     return chunkOffsets.length() * sizeof(chunkOffsets[0]);
59   }
60 
61   // Returns the number of bytes needed to store the data currently written +
62   // the chunk offsets.
63   size_t totalBytesNeeded() const;
64 
65   // Append the chunk offsets to |dest|.
66   void finish(char* dest, size_t destBytes);
67 
rangeToChunkAndOffset(size_t uncompressedStart,size_t uncompressedLimit,size_t * firstChunk,size_t * firstChunkOffset,size_t * firstChunkSize,size_t * lastChunk,size_t * lastChunkSize)68   static void rangeToChunkAndOffset(size_t uncompressedStart,
69                                     size_t uncompressedLimit,
70                                     size_t* firstChunk,
71                                     size_t* firstChunkOffset,
72                                     size_t* firstChunkSize, size_t* lastChunk,
73                                     size_t* lastChunkSize) {
74     *firstChunk = uncompressedStart / CHUNK_SIZE;
75     *firstChunkOffset = uncompressedStart % CHUNK_SIZE;
76     *firstChunkSize = CHUNK_SIZE - *firstChunkOffset;
77 
78     MOZ_ASSERT(uncompressedStart < uncompressedLimit,
79                "subtraction below requires a non-empty range");
80 
81     *lastChunk = (uncompressedLimit - 1) / CHUNK_SIZE;
82     *lastChunkSize = ((uncompressedLimit - 1) % CHUNK_SIZE) + 1;
83   }
84 
chunkSize(size_t uncompressedBytes,size_t chunk)85   static size_t chunkSize(size_t uncompressedBytes, size_t chunk) {
86     MOZ_ASSERT(uncompressedBytes > 0, "must have uncompressed data to chunk");
87 
88     size_t startOfChunkBytes = chunk * CHUNK_SIZE;
89     MOZ_ASSERT(startOfChunkBytes < uncompressedBytes,
90                "chunk must refer to bytes not exceeding "
91                "|uncompressedBytes|");
92 
93     size_t remaining = uncompressedBytes - startOfChunkBytes;
94     return remaining < CHUNK_SIZE ? remaining : CHUNK_SIZE;
95   }
96 };
97 
98 /*
99  * Decompress a string. The caller must know the length of the output and
100  * allocate |out| to a string of that length.
101  */
102 bool DecompressString(const unsigned char* inp, size_t inplen,
103                       unsigned char* out, size_t outlen);
104 
105 /*
106  * Decompress a single chunk of at most Compressor::CHUNK_SIZE bytes.
107  * |chunk| is the chunk index. The caller must know the length of the output
108  * (the uncompressed chunk) and allocate |out| to a string of that length.
109  */
110 bool DecompressStringChunk(const unsigned char* inp, size_t chunk,
111                            unsigned char* out, size_t outlen);
112 
113 } /* namespace js */
114 
115 #endif /* vm_Compression_h */
116