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