staticlib_compress  1.2.0
 All Classes Namespaces Files Functions Enumerations
lzma_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: lzma_sink.hpp
19  * Author: alex
20  *
21  * Created on January 29, 2016, 9:21 PM
22  */
23 
24 #ifndef STATICLIB_COMPRESS_LZMA_SINK_HPP
25 #define STATICLIB_COMPRESS_LZMA_SINK_HPP
26 
27 #include <cstdint>
28 #include <cstdlib>
29 #include <cstring>
30 #include <ios>
31 #include <memory>
32 #include <type_traits>
33 
34 #include "lzma.h"
35 
36 #include "staticlib/config.hpp"
37 #include "staticlib/io.hpp"
38 #include "staticlib/support.hpp"
39 
41 
42 namespace staticlib {
43 namespace compress {
44 
48 template <typename Sink, int compression_level = 6, std::size_t buf_size = 4096 >
49 class lzma_sink {
53  Sink sink;
57  std::array<char, buf_size> buf;
61  lzma_stream* strm;
62 
63 public:
64 
70  lzma_sink(Sink&& sink) :
71  sink(std::move(sink)),
72  strm([] {
73  lzma_stream* stream = static_cast<lzma_stream*> (std::malloc(sizeof(lzma_stream)));
74  if (nullptr == stream) throw compress_exception(TRACEMSG(
75  "Error creating lzma stream: 'malloc' failed"));
76  *stream = LZMA_STREAM_INIT;
77  auto err = ::lzma_easy_encoder(stream, compression_level, LZMA_CHECK_CRC64);
78  if (LZMA_OK != err) throw compress_exception(TRACEMSG(
79  "Error initializing LZMA stream, code: [" + sl::support::to_string(err) + "]"));
80  return stream;
81  }()) { }
82 
83  ~lzma_sink() STATICLIB_NOEXCEPT {
84  if (nullptr == strm) return;
85  auto deferred = sl::support::defer([this]() STATICLIB_NOEXCEPT {
86  ::lzma_end(this->strm);
87  ::free(this->strm);
88  });
89  // finish encoding
90  strm->next_in = nullptr;
91  strm->avail_in = 0;
92  strm->next_out = reinterpret_cast<uint8_t*>(buf.data());
93  strm->avail_out = buf.size();
94  // call code
95  bool coding = true;
96  while (coding) {
97  auto err = ::lzma_code(strm, LZMA_FINISH);
98  switch (err) {
99  case LZMA_OK:
100  sl::io::write_all(sink, {buf.data(), buf.size() - strm->avail_out});
101  strm->next_out = reinterpret_cast<uint8_t*>(buf.data());
102  strm->avail_out = buf.size();
103  // not finished
104  break;
105  case LZMA_STREAM_END:
106  sl::io::write_all(sink, {buf.data(), buf.size() - strm->avail_out});
107  // finished
108  coding = false;
109  break;
110  default:
111  // finish
112  coding = false;
113  }
114  }
115  }
116 
122  lzma_sink(const lzma_sink&) = delete;
123 
130  lzma_sink& operator=(const lzma_sink&) = delete;
131 
137  lzma_sink(lzma_sink&& other) :
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 lzma stream
166  strm->next_in = reinterpret_cast<const uint8_t*>(span.data());
167  strm->avail_in = static_cast<size_t> (span.size());
168  strm->next_out = reinterpret_cast<uint8_t*>(buf.data());
169  strm->avail_out = buf.size();
170  // call code
171  while (strm->avail_in > 0) {
172  auto err = ::lzma_code(strm, LZMA_RUN);
173  switch (err) {
174  case LZMA_OK:
175  sl::io::write_all(sink, {buf.data(), buf.size() - strm->avail_out});
176  strm->next_out = reinterpret_cast<uint8_t*>(buf.data());
177  strm->avail_out = buf.size();
178  break;
179  default: throw compress_exception(TRACEMSG(
180  "LZMA error code: [" + sl::support::to_string(err) + "]"));
181  }
182  }
183  return span.size_signed();
184  }
185 
191  std::streamsize flush() {
192  // note: maybe it is better to enable LZMA_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 
214 template <typename Sink,
215  class = typename std::enable_if<!std::is_lvalue_reference<Sink>::value>::type>
217  return lzma_sink<Sink>(std::move(sink));
218 }
219 
227 template <typename Sink>
230  sl::io::make_reference_sink(sink));
231 }
232 
233 } // namespace
234 }
235 
236 
237 #endif /* STATICLIB_COMPRESS_LZMA_SINK_HPP */
238