1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef SRC_TRACE_PROCESSOR_UTIL_GZIP_UTILS_H_ 18 #define SRC_TRACE_PROCESSOR_UTIL_GZIP_UTILS_H_ 19 20 #include <cstdint> 21 #include <memory> 22 #include <vector> 23 24 struct z_stream_s; 25 26 namespace perfetto { 27 namespace trace_processor { 28 namespace util { 29 30 // Returns whether gzip related functioanlity is supported with the current 31 // build flags. 32 bool IsGzipSupported(); 33 34 // Usage: To decompress in a streaming way, there are two ways of using it: 35 // 1. [Commonly used] - Feed the sequence of mem-blocks in 'FeedAndExtract' one 36 // by one. Output will be produced in given output_consumer, which is simply 37 // a callback. On each 'FeedAndExtract', output_consumer could get invoked 38 // any number of times, based on how much partial output is available. 39 40 // 2. [Uncommon ; Discouraged] - Feed the sequence of mem-blocks one by one, by 41 // calling 'Feed'. For each time 'Feed' is called, client should call 42 // 'ExtractOutput' again and again to extrat the partially available output, 43 // until there in no more output to extract. Also see 'ResultCode' enum. 44 class GzipDecompressor { 45 public: 46 enum class ResultCode { 47 // 'kOk' means nothing bad happened so far, but continue doing what you 48 // were doing. 49 kOk, 50 // While calling 'ExtractOutput' repeatedly, if we get 'kEof', it means 51 // we have extracted all the partially available data and we are also 52 // done, i.e. there is no need to feed more input. 53 kEof, 54 // Some error. Possibly invalid compressed stream or corrupted data. 55 kError, 56 // While calling 'ExtractOutput' repeatedly, if we get 'kNeedsMoreInput', 57 // it means we have extracted all the partially available data, but we are 58 // not done yet. We need to call the 'Feed' to feed the next input 59 // mem-block and go through the ExtractOutput loop again. 60 kNeedsMoreInput, 61 }; 62 struct Result { 63 // The return code of the decompression. 64 ResultCode ret; 65 66 // The amount of bytes written to output. 67 // Valid in all cases except |ResultCode::kError|. 68 size_t bytes_written; 69 }; 70 enum class InputMode { 71 // The input stream contains a gzip header. This is for the common case of 72 // decompressing .gz files. 73 kGzip = 0, 74 75 // A raw deflate stream. This is for the case of uncompressing files from 76 // a .zip archive, where the compression type is specified in the zip file 77 // entry, rather than in the stream header. 78 kRawDeflate = 1, 79 }; 80 81 explicit GzipDecompressor(InputMode = InputMode::kGzip); 82 ~GzipDecompressor(); 83 GzipDecompressor(const GzipDecompressor&) = delete; 84 GzipDecompressor& operator=(const GzipDecompressor&) = delete; 85 86 // Feed the next mem-block. 87 void Feed(const uint8_t* data, size_t size); 88 89 // Feed the next mem-block and extract output in the callback consumer. 90 // callback can get invoked multiple times if there are multiple 91 // mem-blocks to output. 92 template <typename Callback = void(const uint8_t* ptr, size_t size)> FeedAndExtract(const uint8_t * data,size_t size,const Callback & output_consumer)93 ResultCode FeedAndExtract(const uint8_t* data, 94 size_t size, 95 const Callback& output_consumer) { 96 Feed(data, size); 97 uint8_t buffer[4096]; 98 Result result; 99 do { 100 result = ExtractOutput(buffer, sizeof(buffer)); 101 if (result.ret != ResultCode::kError && result.bytes_written > 0) { 102 output_consumer(buffer, result.bytes_written); 103 } 104 } while (result.ret == ResultCode::kOk); 105 return result.ret; 106 } 107 108 // Extract the newly available partial output. On each 'Feed', this method 109 // should be called repeatedly until there is no more data to output 110 // i.e. (either 'kEof' or 'kNeedsMoreInput'). 111 Result ExtractOutput(uint8_t* out, size_t out_capacity); 112 113 // Sets the state of the decompressor to reuse with other gzip streams. 114 // This is almost like constructing a new 'GzipDecompressor' object 115 // but without paying the cost of internal memory allocation. 116 void Reset(); 117 118 // Decompress the entire mem-block and return decompressed mem-block. 119 // This is used for decompressing small strings or small files 120 // which doesn't require streaming decompression. 121 static std::vector<uint8_t> DecompressFully(const uint8_t* data, size_t len); 122 123 private: 124 std::unique_ptr<z_stream_s> z_stream_; 125 }; 126 127 } // namespace util 128 } // namespace trace_processor 129 } // namespace perfetto 130 131 #endif // SRC_TRACE_PROCESSOR_UTIL_GZIP_UTILS_H_ 132