add deflate_istream
This commit is contained in:
parent
7f4b074d70
commit
9389d63a5f
101
src/io/deflate_istream.hpp
Normal file
101
src/io/deflate_istream.hpp
Normal file
@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
|
||||
#define ZLIB_CONST
|
||||
#include <zlib.h>
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
|
||||
class deflate_istream : public std::istream {
|
||||
public:
|
||||
explicit deflate_istream(std::unique_ptr<std::istream> src)
|
||||
: std::istream(&buf), source(std::move(src)), buf(*source) {}
|
||||
|
||||
private:
|
||||
class deflate_streambuf : public std::streambuf {
|
||||
public:
|
||||
explicit deflate_streambuf(std::istream& src) : src(src) {
|
||||
zstream.zalloc = Z_NULL;
|
||||
zstream.zfree = Z_NULL;
|
||||
zstream.opaque = Z_NULL;
|
||||
zstream.avail_in = 0;
|
||||
zstream.next_in = Z_NULL;
|
||||
|
||||
int ret = inflateInit2(&zstream, -15);
|
||||
if (ret != Z_OK) {
|
||||
throw std::runtime_error("zlib init failed");
|
||||
}
|
||||
}
|
||||
|
||||
~deflate_streambuf() {
|
||||
inflateEnd(&zstream);
|
||||
}
|
||||
|
||||
deflate_streambuf(const deflate_streambuf&) = delete;
|
||||
deflate_streambuf& operator=(const deflate_streambuf&) = delete;
|
||||
|
||||
protected:
|
||||
int_type underflow() override {
|
||||
if (gptr() < egptr()) {
|
||||
return traits_type::to_int_type(*gptr());
|
||||
}
|
||||
|
||||
if (eof) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
zstream.next_out = reinterpret_cast<Bytef*>(outBuf.data());
|
||||
zstream.avail_out = outBuf.size();
|
||||
|
||||
do {
|
||||
if (zstream.avail_in == 0) {
|
||||
src.read(inBuf.data(), inBuf.size());
|
||||
zstream.avail_in = static_cast<uInt>(src.gcount());
|
||||
zstream.next_in = reinterpret_cast<Bytef*>(inBuf.data());
|
||||
|
||||
if (src.bad()) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
}
|
||||
|
||||
int ret = inflate(&zstream, Z_NO_FLUSH);
|
||||
if (ret == Z_STREAM_END) {
|
||||
eof = true;
|
||||
} else if (ret != Z_OK) {
|
||||
if (ret == Z_BUF_ERROR && zstream.avail_out == outBuf.size()) {
|
||||
continue;
|
||||
}
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
const auto decompressed = outBuf.size() - zstream.avail_out;
|
||||
if (decompressed > 0) {
|
||||
setg(outBuf.data(),
|
||||
outBuf.data(),
|
||||
outBuf.data() + decompressed);
|
||||
return traits_type::to_int_type(*gptr());
|
||||
}
|
||||
|
||||
if (eof) {
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
} while (zstream.avail_in > 0 || !src.eof());
|
||||
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t BUFFER_SIZE = 16384;
|
||||
|
||||
std::istream& src;
|
||||
z_stream zstream {};
|
||||
std::array<char, BUFFER_SIZE> inBuf {};
|
||||
std::array<char, BUFFER_SIZE> outBuf {};
|
||||
bool eof = false;
|
||||
};
|
||||
|
||||
std::unique_ptr<std::istream> source;
|
||||
deflate_streambuf buf;
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user