blob: 1ef17947ce248e772eb72b0496437c49121e0c7e [file]
/**
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file GzipInflateTransformation.cc
*/
#include <cstring>
#include <vector>
#include <zlib.h>
#include <cinttypes>
#include <string_view>
#include "tscpp/api/TransformationPlugin.h"
#include "tscpp/api/GzipInflateTransformation.h"
#include "logging_internal.h"
using namespace atscppapi::transformations;
using std::vector;
namespace
{
const int WINDOW_BITS = 31; // Always use 31 for gzip.
unsigned int INFLATE_SCALE_FACTOR = 6;
} // namespace
/**
* @private
*/
struct atscppapi::transformations::GzipInflateTransformationState : noncopyable {
z_stream z_stream_;
bool z_stream_initialized_;
int64_t bytes_produced_;
TransformationPlugin::Type transformation_type_;
GzipInflateTransformationState(TransformationPlugin::Type type)
: z_stream_initialized_(false), bytes_produced_(0), transformation_type_(type)
{
memset(&z_stream_, 0, sizeof(z_stream_));
int err = inflateInit2(&z_stream_, WINDOW_BITS);
if (Z_OK != err) {
LOG_ERROR("inflateInit2 failed with error code '%d'.", err);
} else {
z_stream_initialized_ = true;
}
};
~GzipInflateTransformationState()
{
if (z_stream_initialized_) {
int err = inflateEnd(&z_stream_);
if (Z_OK != err && Z_STREAM_END != err) {
LOG_ERROR("Unable to inflateEnd(), returned error code '%d'", err);
}
}
};
};
GzipInflateTransformation::GzipInflateTransformation(Transaction &transaction, TransformationPlugin::Type type)
: TransformationPlugin(transaction, type)
{
state_ = std::make_unique<GzipInflateTransformationState>(type);
}
GzipInflateTransformation::~GzipInflateTransformation() {}
void
GzipInflateTransformation::consume(std::string_view data)
{
if (data.size() == 0) {
return;
}
if (!state_->z_stream_initialized_) {
LOG_ERROR("Unable to inflate output because the z_stream was not initialized.");
return;
}
int err = Z_OK;
int iteration = 0;
int inflate_block_size = INFLATE_SCALE_FACTOR * data.size();
vector<char> buffer(inflate_block_size);
// Setup the compressed input
state_->z_stream_.next_in = reinterpret_cast<unsigned char *>(const_cast<char *>(data.data()));
state_->z_stream_.avail_in = data.length();
// Loop while we have more data to inflate
while (state_->z_stream_.avail_in > 0 && err != Z_STREAM_END) {
LOG_DEBUG("Iteration %d: Gzip has %d bytes to inflate", ++iteration, state_->z_stream_.avail_in);
// Setup where the decompressed output will go
// next_out needs to be set to nullptr before we return since it points to a local buffer
// coverity[WRAPPER_ESCAPE: FALSE]
state_->z_stream_.next_out = reinterpret_cast<unsigned char *>(&buffer[0]);
state_->z_stream_.avail_out = inflate_block_size;
// Uncompress the data
err = inflate(&state_->z_stream_, Z_SYNC_FLUSH);
if (err != Z_OK && err != Z_STREAM_END) {
LOG_ERROR("Iteration %d: Inflate failed with error '%d'", iteration, err);
state_->z_stream_.next_out = nullptr;
return;
}
LOG_DEBUG("Iteration %d: Gzip inflated a total of %d bytes, producingOutput...", iteration,
(inflate_block_size - state_->z_stream_.avail_out));
produce(std::string_view(&buffer[0], (inflate_block_size - state_->z_stream_.avail_out)));
state_->bytes_produced_ += (inflate_block_size - state_->z_stream_.avail_out);
}
state_->z_stream_.next_out = nullptr;
}
void
GzipInflateTransformation::handleInputComplete()
{
int64_t bytes_written = setOutputComplete();
if (state_->bytes_produced_ != bytes_written) {
LOG_ERROR("Gzip bytes produced sanity check failed, inflated bytes = %" PRId64 " != written bytes = %" PRId64,
state_->bytes_produced_, bytes_written);
}
}