add output versions of deflate and memory streams
This commit is contained in:
+12
-14
@@ -7,15 +7,9 @@
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
|
||||
class deflate_istream : public std::istream {
|
||||
class deflate_istreambuf : public std::streambuf {
|
||||
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) {
|
||||
explicit deflate_istreambuf(std::istream& src) : src(src) {
|
||||
zstream.zalloc = Z_NULL;
|
||||
zstream.zfree = Z_NULL;
|
||||
zstream.opaque = Z_NULL;
|
||||
@@ -28,12 +22,12 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
~deflate_streambuf() {
|
||||
~deflate_istreambuf() {
|
||||
inflateEnd(&zstream);
|
||||
}
|
||||
|
||||
deflate_streambuf(const deflate_streambuf&) = delete;
|
||||
deflate_streambuf& operator=(const deflate_streambuf&) = delete;
|
||||
deflate_istreambuf(const deflate_istreambuf&) = delete;
|
||||
deflate_istreambuf& operator=(const deflate_istreambuf&) = delete;
|
||||
|
||||
protected:
|
||||
int_type underflow() override {
|
||||
@@ -80,12 +74,10 @@ private:
|
||||
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;
|
||||
|
||||
@@ -96,6 +88,12 @@ private:
|
||||
bool eof = false;
|
||||
};
|
||||
|
||||
class deflate_istream : public std::istream {
|
||||
public:
|
||||
explicit deflate_istream(std::unique_ptr<std::istream> src)
|
||||
: std::istream(&buffer), source(std::move(src)), buffer(*source) {}
|
||||
|
||||
private:
|
||||
std::unique_ptr<std::istream> source;
|
||||
deflate_streambuf buf;
|
||||
deflate_istreambuf buffer;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
#include <iostream>
|
||||
#include <streambuf>
|
||||
#include <memory>
|
||||
#include <zlib.h>
|
||||
|
||||
class deflate_ostreambuf : public std::streambuf {
|
||||
public:
|
||||
deflate_ostreambuf(std::ostream& dest, int level = Z_DEFAULT_COMPRESSION)
|
||||
: dest(dest) {
|
||||
zstream.zalloc = Z_NULL;
|
||||
zstream.zfree = Z_NULL;
|
||||
zstream.opaque = Z_NULL;
|
||||
int ret = deflateInit2(
|
||||
&zstream, level, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY
|
||||
);
|
||||
if (ret != Z_OK) {
|
||||
throw std::runtime_error("zlib deflate initialization failed");
|
||||
}
|
||||
inBuffer = std::make_unique<char[]>(bufferSize);
|
||||
outBuffer = std::make_unique<char[]>(outBufferSize);
|
||||
|
||||
setp(inBuffer.get(), inBuffer.get() + bufferSize - 1);
|
||||
}
|
||||
|
||||
~deflate_ostreambuf() {
|
||||
try {
|
||||
close();
|
||||
} catch (...) {
|
||||
std::cerr << "error in zlib output stream finalization" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool close() {
|
||||
overflow(EOF);
|
||||
|
||||
// Finalize the deflate stream
|
||||
zstream.avail_in = 0;
|
||||
zstream.next_in = nullptr;
|
||||
int ret;
|
||||
do {
|
||||
zstream.avail_out = outBufferSize;
|
||||
zstream.next_out = reinterpret_cast<Bytef*>(outBuffer.get());
|
||||
ret = deflate(&zstream, Z_FINISH);
|
||||
if (ret == Z_STREAM_ERROR) {
|
||||
break;
|
||||
}
|
||||
size_t compressed_size = outBufferSize - zstream.avail_out;
|
||||
dest.write(outBuffer.get(), compressed_size);
|
||||
} while (ret != Z_STREAM_END);
|
||||
|
||||
deflateEnd(&zstream);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
int overflow(int c) override {
|
||||
if (c != EOF) {
|
||||
*pptr() = static_cast<char>(c);
|
||||
pbump(1);
|
||||
}
|
||||
|
||||
if (process_input() == EOF) {
|
||||
return EOF;
|
||||
}
|
||||
|
||||
return c != EOF ? 0 : EOF;
|
||||
}
|
||||
|
||||
int sync() override {
|
||||
if (process_input(Z_SYNC_FLUSH) == EOF) {
|
||||
return -1;
|
||||
}
|
||||
dest.flush();
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static const size_t bufferSize = 512;
|
||||
static const size_t outBufferSize = bufferSize * 2;
|
||||
|
||||
std::ostream& dest;
|
||||
z_stream zstream {};
|
||||
std::unique_ptr<char[]> inBuffer;
|
||||
std::unique_ptr<char[]> outBuffer;
|
||||
|
||||
int process_input(int flush = Z_NO_FLUSH) {
|
||||
size_t input_size = pptr() - pbase();
|
||||
zstream.avail_in = static_cast<uInt>(input_size);
|
||||
zstream.next_in = reinterpret_cast<Bytef*>(pbase());
|
||||
|
||||
int ret;
|
||||
do {
|
||||
zstream.avail_out = outBufferSize;
|
||||
zstream.next_out = reinterpret_cast<Bytef*>(outBuffer.get());
|
||||
ret = deflate(&zstream, flush);
|
||||
if (ret == Z_STREAM_ERROR) {
|
||||
return EOF;
|
||||
}
|
||||
size_t compressed_size = outBufferSize - zstream.avail_out;
|
||||
dest.write(outBuffer.get(), compressed_size);
|
||||
if (!dest) {
|
||||
return EOF;
|
||||
}
|
||||
} while (zstream.avail_out == 0);
|
||||
|
||||
setp(inBuffer.get(), inBuffer.get() + bufferSize - 1);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class deflate_ostream : public std::ostream {
|
||||
public:
|
||||
explicit deflate_ostream(std::ostream& dest, int level = Z_DEFAULT_COMPRESSION)
|
||||
: std::ostream(&buffer), buffer(dest, level) {}
|
||||
|
||||
private:
|
||||
deflate_ostreambuf buffer;
|
||||
};
|
||||
@@ -3,12 +3,6 @@
|
||||
#include <istream>
|
||||
#include "util/Buffer.hpp"
|
||||
|
||||
class memory_istream : public std::istream {
|
||||
public:
|
||||
explicit memory_istream(util::Buffer<char> buffer)
|
||||
: std::istream(&buf), buf(std::move(buffer)) {}
|
||||
|
||||
private:
|
||||
class memory_streambuf : public std::streambuf {
|
||||
public:
|
||||
explicit memory_streambuf(util::Buffer<char> buffer)
|
||||
@@ -30,5 +24,11 @@ private:
|
||||
util::Buffer<char> buffer;
|
||||
};
|
||||
|
||||
class memory_istream : public std::istream {
|
||||
public:
|
||||
explicit memory_istream(util::Buffer<char> buffer)
|
||||
: std::istream(&buf), buf(std::move(buffer)) {}
|
||||
|
||||
private:
|
||||
memory_streambuf buf;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
#include <iostream>
|
||||
#include <streambuf>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
#include "util/Buffer.hpp"
|
||||
|
||||
class memory_buffer : public std::streambuf {
|
||||
public:
|
||||
explicit memory_buffer(size_t initial_size = 64)
|
||||
: capacity(initial_size),
|
||||
buffer(std::make_unique<char[]>(initial_size)) {
|
||||
setp(buffer.get(), buffer.get() + initial_size);
|
||||
}
|
||||
|
||||
std::string_view view() const {
|
||||
return std::string_view(pbase(), pptr() - pbase());
|
||||
}
|
||||
|
||||
util::Buffer<char> release() {
|
||||
return {std::move(buffer), size()};
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return pptr()-pbase();
|
||||
}
|
||||
protected:
|
||||
int_type overflow(int_type c) override {
|
||||
if (c == traits_type::eof())
|
||||
return traits_type::eof();
|
||||
|
||||
const size_t data_size = pptr() - pbase();
|
||||
const size_t new_capacity = std::max(capacity * 2, data_size + 1);
|
||||
auto new_buffer = std::make_unique<char[]>(new_capacity);
|
||||
|
||||
std::memcpy(new_buffer.get(), pbase(), data_size);
|
||||
|
||||
buffer = std::move(new_buffer);
|
||||
capacity = new_capacity;
|
||||
|
||||
setp(buffer.get(), buffer.get() + new_capacity);
|
||||
pbump(data_size);
|
||||
|
||||
*pptr() = traits_type::to_char_type(c);
|
||||
pbump(1);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
std::streamsize xsputn(const char* s, std::streamsize count) override {
|
||||
const std::streamsize avail = epptr() - pptr();
|
||||
|
||||
if (avail >= count) {
|
||||
std::memcpy(pptr(), s, count);
|
||||
pbump(count);
|
||||
return count;
|
||||
}
|
||||
|
||||
std::streamsize written = 0;
|
||||
if (avail > 0) {
|
||||
std::memcpy(pptr(), s, avail);
|
||||
written += avail;
|
||||
s += avail;
|
||||
count -= avail;
|
||||
pbump(avail);
|
||||
}
|
||||
|
||||
const size_t data_size = pptr() - pbase();
|
||||
const size_t required_capacity = data_size + count;
|
||||
const size_t new_capacity = std::max(capacity * 2, required_capacity);
|
||||
auto new_buffer = std::make_unique<char[]>(new_capacity);
|
||||
|
||||
std::memcpy(new_buffer.get(), pbase(), data_size);
|
||||
std::memcpy(new_buffer.get() + data_size, s, count);
|
||||
|
||||
buffer = std::move(new_buffer);
|
||||
capacity = new_capacity;
|
||||
|
||||
setp(buffer.get(), buffer.get() + new_capacity);
|
||||
pbump(data_size + count);
|
||||
written += count;
|
||||
|
||||
return written;
|
||||
}
|
||||
private:
|
||||
std::unique_ptr<char[]> buffer;
|
||||
size_t capacity;
|
||||
};
|
||||
|
||||
class memory_ostream : public std::ostream {
|
||||
public:
|
||||
explicit memory_ostream(size_t initialCapacity = 64)
|
||||
: std::ostream(&buffer), buffer(initialCapacity) {}
|
||||
|
||||
std::string_view view() const {
|
||||
return buffer.view();
|
||||
}
|
||||
|
||||
util::Buffer<char> release() {
|
||||
return buffer.release();
|
||||
}
|
||||
private:
|
||||
memory_buffer buffer;
|
||||
};
|
||||
Reference in New Issue
Block a user