| 147 | } |
| 148 | |
| 149 | Status GzipDecompressor::ProcessBlock(bool output_preallocated, int64_t input_length, |
| 150 | const uint8_t* input, int64_t* output_length, uint8_t** output) { |
| 151 | int64_t output_length_local = *output_length; |
| 152 | *output_length = 0; |
| 153 | if (UNLIKELY(output_preallocated && output_length_local == 0)) { |
| 154 | // The zlib library does not allow *output to be nullptr, even when output_length is 0 |
| 155 | // (inflate() will return Z_STREAM_ERROR). We don't consider this an error, so bail |
| 156 | // early if no output is expected. Note that we don't signal an error if the input |
| 157 | // actually contains compressed data. |
| 158 | return Status::OK(); |
| 159 | } |
| 160 | |
| 161 | bool use_temp = false; |
| 162 | if (!output_preallocated) { |
| 163 | if (!reuse_buffer_ || out_buffer_ == nullptr) { |
| 164 | // guess that we will need 2x the input length. |
| 165 | buffer_length_ = input_length * 2; |
| 166 | out_buffer_ = temp_memory_pool_->TryAllocate(buffer_length_); |
| 167 | if (UNLIKELY(out_buffer_ == nullptr)) { |
| 168 | string details = Substitute(DECOMPRESSOR_MEM_LIMIT_EXCEEDED, "Gzip", |
| 169 | buffer_length_); |
| 170 | return temp_memory_pool_->mem_tracker()->MemLimitExceeded( |
| 171 | nullptr, details, buffer_length_); |
| 172 | } |
| 173 | } |
| 174 | use_temp = true; |
| 175 | *output = out_buffer_; |
| 176 | output_length_local = buffer_length_; |
| 177 | } |
| 178 | |
| 179 | // Reset the stream for this block |
| 180 | int ret = inflateReset(&stream_); |
| 181 | if (ret != Z_OK) { |
| 182 | return Status(TErrorCode::COMPRESSED_FILE_DECOMPRESSOR_ERROR, "Gzip", |
| 183 | "inflateReset()", ret); |
| 184 | } |
| 185 | |
| 186 | // We only support the non-streaming use case where we present it the entire |
| 187 | // compressed input and a buffer big enough to contain the entire decompressed |
| 188 | // output. In the case where we don't know the output, we just make a bigger |
| 189 | // buffer and try the non-streaming mode from the beginning again. |
| 190 | // TODO: IMPALA-3073 Verify if compressed block could be multistream. If yes, we need |
| 191 | // to support it and shouldn't stop decompressing while ret == Z_STREAM_END. |
| 192 | while (ret != Z_STREAM_END) { |
| 193 | stream_.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(input)); |
| 194 | stream_.avail_in = input_length; |
| 195 | stream_.next_out = reinterpret_cast<Bytef*>(*output); |
| 196 | stream_.avail_out = output_length_local; |
| 197 | |
| 198 | if (use_temp) { |
| 199 | // We don't know the output size, so this might fail. |
| 200 | ret = inflate(&stream_, Z_PARTIAL_FLUSH); |
| 201 | } else { |
| 202 | // We know the output size. In this case, we can use Z_FINISH |
| 203 | // which is more efficient. |
| 204 | ret = inflate(&stream_, Z_FINISH); |
| 205 | } |
| 206 | if (ret == Z_STREAM_END || ret != Z_OK) break; |
nothing calls this directly
no test coverage detected