staticlib_compress  1.2.0
 All Classes Namespaces Files Functions Enumerations
deflate_sink.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2016, alex at staticlibs.net
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * File: deflate_sink.hpp
19  * Author: alex
20  *
21  * Created on January 29, 2016, 9:19 PM
22  */
23 
24 #ifndef STATICLIB_COMPRESS_DEFLATE_SINK_HPP
25 #define STATICLIB_COMPRESS_DEFLATE_SINK_HPP
26 
27 #include <cstdlib>
28 #include <cstring>
29 #include <ios>
30 #include <memory>
31 #include <type_traits>
32 
33 #include "zlib.h"
34 
35 #include "staticlib/config.hpp"
36 #include "staticlib/io.hpp"
37 #include "staticlib/support.hpp"
38 
40 
41 namespace staticlib {
42 namespace compress {
43 
47 template <typename Sink, int compression_level = 6, std::size_t buf_size = 4096>
48 class deflate_sink {
52  Sink sink;
56  std::array<char, buf_size> buf;
60  z_stream* strm;
61 
62 public:
63 
69  deflate_sink(Sink&& sink) :
70  sink(std::move(sink)),
71  strm([] {
72  z_stream* stream = static_cast<z_stream*> (std::malloc(sizeof(z_stream)));
73  if (nullptr == stream) throw compress_exception(TRACEMSG(
74  "Error creating deflate stream: 'malloc' failed"));
75  std::memset(stream, 0, sizeof (z_stream));
76  auto err = deflateInit2(stream, compression_level, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
77  if (Z_OK != err) throw compress_exception(TRACEMSG(
78  "Error initializing deflate stream: [" + ::zError(err) + "]"));
79  return stream;
80  }()) { }
81 
82  ~deflate_sink() STATICLIB_NOEXCEPT {
83  if (nullptr == strm) return;
84  auto deferred = sl::support::defer([this]() STATICLIB_NOEXCEPT {
85  ::deflateEnd(this->strm);
86  ::free(this->strm);
87  });
88  // finish encoding
89  strm->next_in = nullptr;
90  strm->avail_in = 0;
91  strm->next_out = reinterpret_cast<unsigned char*> (buf.data());
92  strm->avail_out = static_cast<uInt> (buf.size());
93  // call deflate
94  bool deflating = true;
95  while(deflating) {
96  auto err = ::deflate(strm, Z_FINISH);
97  // cannot report any error safely - we are in destructor
98  switch (err) {
99  case Z_OK:
100  sl::io::write_all(sink, {buf.data(), buf.size() - strm->avail_out});
101  strm->next_out = reinterpret_cast<unsigned char*> (buf.data());
102  strm->avail_out = static_cast<uInt> (buf.size());
103  // still not finished
104  break;
105  case Z_STREAM_END:
106  sl::io::write_all(sink, {buf.data(), buf.size() - strm->avail_out});
107  // finished
108  deflating = false;
109  break;
110  default:
111  // finish
112  deflating = false;
113  }
114  }
115  }
116 
122  deflate_sink(const deflate_sink&) = delete;
123 
130  deflate_sink& operator=(const deflate_sink&) = delete;
131 
138  sink(std::move(other.sink)),
139  buf(std::move(other.buf)),
140  strm(other.strm) {
141  other.strm = nullptr;
142  }
143 
151  sink = std::move(other.sink);
152  buf = std::move(other.buf);
153  strm = other.strm;
154  other.strm = nullptr;
155  return *this;
156  }
157 
164  std::streamsize write(sl::io::span<const char> span) {
165  // prepare zlib stream
166  strm->next_in = reinterpret_cast<const unsigned char*> (span.data());
167  strm->avail_in = static_cast<uInt> (span.size());
168  strm->next_out = reinterpret_cast<unsigned char*> (buf.data());
169  strm->avail_out = static_cast<uInt> (buf.size());
170  // call deflate
171  while(strm->avail_in > 0) {
172  auto err = ::deflate(strm, Z_NO_FLUSH);
173  switch (err) {
174  case Z_OK:
175  sl::io::write_all(sink, {buf.data(), buf.size() - strm->avail_out});
176  strm->next_out = reinterpret_cast<unsigned char*> (buf.data());
177  strm->avail_out = static_cast<uInt> (buf.size());
178  break;
179  default: throw compress_exception(TRACEMSG(
180  "Deflate error: [" + ::zError(err) + "]"));
181  }
182  }
183  return span.size_signed();
184  }
185 
191  std::streamsize flush() {
192  // note: maybe it is better to enable Z_SYNC_FLUSH support,
193  // but it will make output result non-deterministic
194  return sink.flush();
195  }
196 
202  Sink& get_sink() {
203  return sink;
204  }
205 
206 };
207 
215 template <typename Sink,
216  class = typename std::enable_if<!std::is_lvalue_reference<Sink>::value>::type>
218  return deflate_sink<Sink>(std::move(sink));
219 }
220 
228 template <typename Sink>
231  sl::io::make_reference_sink(sink));
232 }
233 
234 } // namespace
235 }
236 
237 #endif /* STATICLIB_COMPRESS_DEFLATE_SINK_HPP */
238