24 #ifndef STATICLIB_COMPRESS_ZIP_SINK_HPP
25 #define STATICLIB_COMPRESS_ZIP_SINK_HPP
34 #include "staticlib/config.hpp"
35 #include "staticlib/io.hpp"
36 #include "staticlib/endian.hpp"
52 uint16_t compression_method;
55 uint32_t compressed_size = 0;
56 uint32_t uncompressed_size = 0;
61 Header(std::string filename, uint16_t compression_method) :
62 filename(std::move(filename)),
63 compression_method(compression_method) { }
65 template <
typename Sink>
67 namespace en = staticlib::endian;
69 en::write_32_le(sink, 0x04034b50);
71 en::write_16_le(sink, 10);
73 en::write_16_le(sink, 8);
75 en::write_16_le(sink, compression_method);
77 en::write_16_le(sink, 0);
79 en::write_16_le(sink, 0);
81 en::write_32_le(sink, 0);
83 en::write_32_le(sink, 0);
85 en::write_32_le(sink, 0);
87 en::write_16_le(sink, filename.length());
89 en::write_16_le(sink, 0);
91 io::write_all(sink, {filename.data(), filename.length()});
93 this->offset = offset;
96 template <
typename Sink>
98 namespace en = staticlib::endian;
100 en::write_32_le(sink, 0x08074b50);
102 en::write_32_le(sink, crc);
104 en::write_32_le(sink, compressed_size);
106 en::write_32_le(sink, uncompressed_size);
108 this->compressed_size = compressed_size;
109 this->uncompressed_size = uncompressed_size;
114 template <
typename Sink>
116 namespace en = staticlib::endian;
118 en::write_32_le(sink, 0x02014b50);
120 en::write_16_le(sink, 10);
122 en::write_16_le(sink, 10);
124 en::write_16_le(sink, 8);
126 en::write_16_le(sink, compression_method);
128 en::write_16_le(sink, 0);
130 en::write_16_le(sink, 0);
132 en::write_32_le(sink, crc);
134 en::write_32_le(sink, compressed_size);
136 en::write_32_le(sink, uncompressed_size);
138 en::write_16_le(sink, filename.length());
140 en::write_16_le(sink, 0);
142 en::write_16_le(sink, 0);
144 en::write_16_le(sink, 0);
146 en::write_16_le(sink, 0);
148 en::write_32_le(sink, 0);
150 en::write_32_le(sink, offset);
152 io::write_all(sink, {filename.data(), filename.length()});
157 template <
typename Sink>
158 void write_eocd(Sink& sink, uint16_t files_count, uint32_t cd_offset, uint32_t cd_length) {
159 namespace en = staticlib::endian;
161 en::write_32_le(sink, 0x06054b50);
163 en::write_16_le(sink, 0);
165 en::write_16_le(sink, 0);
167 en::write_16_le(sink, files_count);
169 en::write_16_le(sink, files_count);
171 en::write_32_le(sink, cd_length);
173 en::write_32_le(sink, cd_offset);
175 en::write_16_le(sink, 0);
184 template <
typename Sink>
188 using sink_ref_type = sl::io::reference_sink<sl::io::counting_sink<Sink>>;
189 using entry_counter_ref_type = sl::io::reference_sink<sl::io::counting_sink<sink_ref_type>>;
190 using deflater_type = sl::io::counting_sink<deflate_sink<entry_counter_ref_type>>;
196 sl::io::counting_sink<Sink> sink;
197 std::vector<detail::Header> headers;
198 bool cd_written =
false;
200 sl::io::counting_sink<sink_ref_type> entry_counter;
201 std::unique_ptr<deflater_type> entry_deflater;
202 uint32_t entry_crc = 0;
212 sink(sl::io::make_counting_sink(std::move(sink))),
213 entry_counter(sl::io::make_counting_sink(this->sink)),
214 entry_deflater(nullptr) { }
248 sink(std::move(other.sink)),
249 headers(std::move(other.headers)),
250 cd_written(other.cd_written),
251 entry_counter(std::move(other.entry_counter)),
252 entry_deflater(std::move(other.entry_deflater)),
253 entry_crc(other.entry_crc) { }
262 sink = std::move(other.sink);
263 headers = std::move(other.headers);
264 cd_written = other.cd_written;
265 entry_counter = std::move(other.entry_counter);
266 entry_deflater = std::move(other.entry_deflater);
267 entry_crc = other.entry_crc;
277 std::streamsize
write(sl::io::span<const char> span) {
278 if (
nullptr != entry_deflater.get()) {
279 size_t count_before = entry_deflater->get_count();
280 entry_deflater->write(span);
281 size_t count = entry_deflater->get_count() - count_before;
282 this->entry_crc = ::crc32(entry_crc, reinterpret_cast<const Bytef*>(span.data()), static_cast<uInt>(count));
283 return static_cast<std::streamsize
>(count);
285 throw compress_exception(TRACEMSG(
"Invalid ZIP sink state: add ZIP entry before writing the data"));
304 if (filename.empty())
throw compress_exception(TRACEMSG(
"Invalid empty entry name specified"));
306 "Invalid entry add attempt for finalized ZIP stream"));
307 if (headers.size() > 0) {
308 write_entry_data_descriptor();
311 headers.emplace_back(std::string(filename.data(), filename.length()), static_cast<uint16_t>(method));
312 headers.back().write_local_file_header(sink, static_cast<uint32_t>(sink.get_count()));
314 entry_crc = ::crc32(0L, Z_NULL, 0);
323 if (!cd_written && headers.size() > 0) {
324 write_entry_data_descriptor();
325 uint32_t cd_offset =
static_cast<uint32_t
>(sink.get_count());
327 he.write_cd_file_header(sink);
329 size_t cd_len = sink.get_count() - cd_offset;
330 detail::write_eocd(sink, static_cast<uint16_t>(headers.size()), cd_offset, static_cast<uint32_t>(cd_len));
336 void write_entry_data_descriptor() {
338 uint32_t uncompressed_size =
static_cast<uint32_t
>(entry_deflater->get_count());
339 entry_deflater.reset(
nullptr);
341 uint32_t compressed_size =
static_cast<uint32_t
>(entry_counter.get_count());
342 entry_counter = sl::io::make_counting_sink(sink);
343 headers.back().write_data_descriptor(sink, compressed_size, uncompressed_size, entry_crc);
355 template <
typename Sink,
356 class =
typename std::enable_if<!std::is_lvalue_reference<Sink>::value>::type>
368 template <
typename Sink>
371 sl::io::make_reference_sink(sink));