byteme
Read/write bytes from various sources
Loading...
Searching...
No Matches
ZlibBufferReader.hpp
Go to the documentation of this file.
1#ifndef BYTEME_ZLIB_BUFFER_READER_HPP
2#define BYTEME_ZLIB_BUFFER_READER_HPP
3
4#include <stdexcept>
5#include <vector>
6#include <cstddef>
7#include <optional>
8
9#include "zlib.h"
11
12#include "Reader.hpp"
13#include "magic_numbers.hpp"
14#include "utils.hpp"
15
22namespace byteme {
23
32 std::optional<ZlibCompressionMode> mode;
33
39};
40
46class ZlibBufferReader final : public Reader {
47private:
48 struct ZStream {
49 ZStream(std::optional<ZlibCompressionMode> mode) {
50 /* allocate inflate state */
51 strm.zalloc = Z_NULL;
52 strm.zfree = Z_NULL;
53 strm.opaque = Z_NULL;
54 strm.avail_in = 0;
55 strm.next_in = Z_NULL;
56
57 /* See:
58 * https://stackoverflow.com/questions/1838699/how-can-i-decompress-a-gzip-stream-with-zlib
59 * https://stackoverflow.com/questions/29003909/why-is-a-different-zlib-window-bits-value-required-for-extraction-compared-with
60 */
61 int ret;
62 if (mode.has_value()) {
63 switch (*mode) {
64 case ZlibCompressionMode::DEFLATE:
65 ret = inflateInit2(&strm, -MAX_WBITS);
66 break;
67 case ZlibCompressionMode::ZLIB:
68 ret = inflateInit2(&strm, MAX_WBITS);
69 break;
70 case ZlibCompressionMode::GZIP:
71 ret = inflateInit2(&strm, 16 + MAX_WBITS);
72 break;
73 default:
74 throw std::runtime_error("unknown Zlib compression mode");
75 }
76 } else {
77 // Autodetection of Gzip or Zlib.
78 ret = inflateInit2(&strm, 32 + MAX_WBITS);
79 }
80
81 if (ret != Z_OK) {
82 throw std::runtime_error(strm.msg);
83 }
84 }
85
86 ~ZStream() {
87 inflateEnd(&strm);
88 return;
89 }
90
91 public:
92 // Delete the remaining constructors.
93 ZStream(const ZStream&) = delete;
94 ZStream(ZStream&&) = delete;
95 ZStream& operator=(const ZStream&) = delete;
96 ZStream& operator=(ZStream&&) = delete;
97
98 public:
99 z_stream strm;
100 };
101
102 typedef I<decltype(I<decltype(ZStream::strm)>::avail_out)> ZoutSize;
103
104public:
110 ZlibBufferReader(const unsigned char* buffer, std::size_t length, const ZlibBufferReaderOptions& options) :
111 my_zstr(options.mode),
112 // Cap it for both allocation safety and to avoid problems from overflow of the avail_out member..
113 my_buffer(sanisizer::cap<I<decltype(my_buffer.size())> >(sanisizer::cap<ZoutSize>(options.buffer_size))),
114 my_input(buffer),
115 my_length(length)
116 {}
117
118public:
119 std::size_t read(unsigned char* buffer, std::size_t n) {
120 return read0<ZoutSize>(buffer, n);
121 }
122
126 // This is meant to be an internal function, only exported here for tests.
127 // Specifically we want to check that the overflow protection works correctly for the input size.
128 template<typename InSize>
129 std::size_t read0(unsigned char* buffer, std::size_t n) {
130 return safe_read<ZoutSize>(buffer, n, [this](unsigned char* buffer, ZoutSize n) -> ZoutSize {
131 return this->read_internal<InSize>(buffer, n);
132 });
133 }
138private:
139 template<typename InSize_>
140 ZoutSize read_internal(unsigned char* buffer, ZoutSize n) {
141 auto remaining = n;
142
143 if (my_cached_length) {
144 const auto limit = std::min(remaining, my_cached_length);
145 std::copy_n(my_buffer.data() + my_cached_start, limit, buffer);
146 buffer += limit;
147 remaining -= limit;
148 my_cached_start += limit;
149 my_cached_length -= limit;
150 }
151
152 if (remaining) {
153 while (!my_finished) {
154 if (my_zstr.strm.avail_in == 0) {
155 const auto to_use = sanisizer::cap<InSize_>(my_length);
156 my_zstr.strm.avail_in = to_use;
157 my_length -= to_use;
158 my_zstr.strm.next_in = const_cast<unsigned char*>(my_input); // cast is purely for C compatibility.
159 my_input += to_use;
160 }
161
162 /* This function is stolen from the loop in 'inf()' at
163 * http://www.zlib.net/zpipe.c, with some shuffling of code to make it
164 * a bit more C++-like.
165 */
166 my_zstr.strm.avail_out = my_buffer.size();
167 my_zstr.strm.next_out = my_buffer.data();
168 auto ret = inflate(&(my_zstr.strm), Z_NO_FLUSH);
169
170 switch (ret) {
171 case Z_STREAM_ERROR:
172 case Z_NEED_DICT:
173 case Z_DATA_ERROR:
174 case Z_MEM_ERROR:
175 throw std::runtime_error("zlib error");
176 case Z_STREAM_END:
177 my_finished = true;
178 break;
179 }
180
181 const ZoutSize nread = my_buffer.size() - my_zstr.strm.avail_out;
182 if (nread < remaining) {
183 std::copy_n(my_buffer.data(), nread, buffer);
184 buffer += nread;
185 remaining -= nread;
186 } else {
187 std::copy_n(my_buffer.data(), remaining, buffer);
188 my_cached_start = remaining;
189 my_cached_length = nread - remaining;
190 remaining = 0;
191 break;
192 }
193 }
194 }
195
196 return n - remaining;
197 }
198
199private:
200 ZStream my_zstr;
201 std::vector<unsigned char> my_buffer;
202 const unsigned char* my_input;
203 std::size_t my_length;
204 ZoutSize my_cached_start = 0, my_cached_length = 0;
205 bool my_finished = false;
206};
207
208}
209
210#endif
Read an input source.
Virtual class for reading bytes from a source.
Definition Reader.hpp:17
Read and decompress bytes from a Zlib-compressed buffer.
Definition ZlibBufferReader.hpp:46
ZlibBufferReader(const unsigned char *buffer, std::size_t length, const ZlibBufferReaderOptions &options)
Definition ZlibBufferReader.hpp:110
std::size_t read(unsigned char *buffer, std::size_t n)
Definition ZlibBufferReader.hpp:119
Magic numbers for various compression algorithms.
Simple byte readers and writers.
Definition BufferedReader.hpp:21
constexpr Dest_ cap(Value_ x)
Options for the ZlibBufferReader constructor.
Definition ZlibBufferReader.hpp:27
std::optional< ZlibCompressionMode > mode
Definition ZlibBufferReader.hpp:32
std::size_t buffer_size
Definition ZlibBufferReader.hpp:38