Libosmium  2.16.0
Fast and flexible C++ library for working with OpenStreetMap data
gzip_compression.hpp
Go to the documentation of this file.
1 #ifndef OSMIUM_IO_GZIP_COMPRESSION_HPP
2 #define OSMIUM_IO_GZIP_COMPRESSION_HPP
3 
4 /*
5 
6 This file is part of Osmium (https://osmcode.org/libosmium).
7 
8 Copyright 2013-2021 Jochen Topf <jochen@topf.org> and others (see README).
9 
10 Boost Software License - Version 1.0 - August 17th, 2003
11 
12 Permission is hereby granted, free of charge, to any person or organization
13 obtaining a copy of the software and accompanying documentation covered by
14 this license (the "Software") to use, reproduce, display, distribute,
15 execute, and transmit the Software, and to prepare derivative works of the
16 Software, and to permit third-parties to whom the Software is furnished to
17 do so, all subject to the following:
18 
19 The copyright notices in the Software and this entire statement, including
20 the above license grant, this restriction and the following disclaimer,
21 must be included in all copies of the Software, in whole or in part, and
22 all derivative works of the Software, unless such copies or derivative
23 works are solely in the form of machine-executable object code generated by
24 a source language processor.
25 
26 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
29 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
30 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
31 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32 DEALINGS IN THE SOFTWARE.
33 
34 */
35 
46 #include <osmium/io/detail/read_write.hpp>
47 #include <osmium/io/error.hpp>
50 
51 #include <zlib.h>
52 
53 #include <cassert>
54 #include <cerrno>
55 #include <cstddef>
56 #include <limits>
57 #include <string>
58 
59 #ifndef _MSC_VER
60 # include <unistd.h>
61 #endif
62 
63 namespace osmium {
64 
69  struct gzip_error : public io_error {
70 
71  int gzip_error_code = 0;
72  int system_errno = 0;
73 
74  explicit gzip_error(const std::string& what) :
75  io_error(what) {
76  }
77 
78  gzip_error(const std::string& what, const int error_code) :
79  io_error(what),
80  gzip_error_code(error_code) {
81  if (error_code == Z_ERRNO) {
82  system_errno = errno;
83  }
84  }
85 
86  }; // struct gzip_error
87 
88  namespace io {
89 
90  namespace detail {
91 
92  [[noreturn]] inline void throw_gzip_error(gzFile gzfile, const char* msg) {
93  std::string error{"gzip error: "};
94  error += msg;
95  error += ": ";
96  int error_code = 0;
97  if (gzfile) {
98  error += ::gzerror(gzfile, &error_code);
99  }
100  throw osmium::gzip_error{error, error_code};
101  }
102 
103  } // namespace detail
104 
105  class GzipCompressor final : public Compressor {
106 
107  std::size_t m_file_size = 0;
108  int m_fd;
109  gzFile m_gzfile;
110 
111  public:
112 
113  explicit GzipCompressor(const int fd, const fsync sync) :
114  Compressor(sync),
115  m_fd(fd) {
116 #ifdef _MSC_VER
117  osmium::detail::disable_invalid_parameter_handler diph;
118 #endif
119  m_gzfile = ::gzdopen(osmium::io::detail::reliable_dup(fd), "wb");
120  if (!m_gzfile) {
121  throw gzip_error{"gzip error: write initialization failed"};
122  }
123  }
124 
125  GzipCompressor(const GzipCompressor&) = delete;
126  GzipCompressor& operator=(const GzipCompressor&) = delete;
127 
128  GzipCompressor(GzipCompressor&&) = delete;
130 
131  ~GzipCompressor() noexcept override {
132  try {
133  close();
134  } catch (...) {
135  // Ignore any exceptions because destructor must not throw.
136  }
137  }
138 
139  void write(const std::string& data) override {
140 #ifdef _MSC_VER
141  osmium::detail::disable_invalid_parameter_handler diph;
142 #endif
143  assert(m_gzfile);
144  assert(data.size() < std::numeric_limits<unsigned int>::max());
145  if (!data.empty()) {
146  const int nwrite = ::gzwrite(m_gzfile, data.data(), static_cast<unsigned int>(data.size()));
147  if (nwrite == 0) {
148  detail::throw_gzip_error(m_gzfile, "write failed");
149  }
150  }
151  }
152 
153  void close() override {
154  if (m_gzfile) {
155 #ifdef _MSC_VER
156  osmium::detail::disable_invalid_parameter_handler diph;
157 #endif
158  const int result = ::gzclose_w(m_gzfile);
159  m_gzfile = nullptr;
160  if (result != Z_OK) {
161  throw gzip_error{"gzip error: write close failed", result};
162  }
163 
164  // Do not sync or close stdout
165  if (m_fd == 1) {
166  return;
167  }
168 
170 
171  if (do_fsync()) {
172  osmium::io::detail::reliable_fsync(m_fd);
173  }
174  osmium::io::detail::reliable_close(m_fd);
175  }
176  }
177 
178  std::size_t file_size() const override {
179  return m_file_size;
180  }
181 
182  }; // class GzipCompressor
183 
184  class GzipDecompressor final : public Decompressor {
185 
186  gzFile m_gzfile = nullptr;
187 
188  public:
189 
190  explicit GzipDecompressor(const int fd) {
191 #ifdef _MSC_VER
192  osmium::detail::disable_invalid_parameter_handler diph;
193 #endif
194  m_gzfile = ::gzdopen(fd, "rb");
195  if (!m_gzfile) {
196  try {
197  osmium::io::detail::reliable_close(fd);
198  } catch (...) {
199  }
200  throw gzip_error{"gzip error: read initialization failed"};
201  }
202  }
203 
204  GzipDecompressor(const GzipDecompressor&) = delete;
205  GzipDecompressor& operator=(const GzipDecompressor&) = delete;
206 
209 
210  ~GzipDecompressor() noexcept override {
211  try {
212  close();
213  } catch (...) {
214  // Ignore any exceptions because destructor must not throw.
215  }
216  }
217 
218  std::string read() override {
219 #ifdef _MSC_VER
220  osmium::detail::disable_invalid_parameter_handler diph;
221 #endif
222  assert(m_gzfile);
223  std::string buffer(osmium::io::Decompressor::input_buffer_size, '\0');
224  assert(buffer.size() < std::numeric_limits<unsigned int>::max());
225  int nread = ::gzread(m_gzfile, &*buffer.begin(), static_cast<unsigned int>(buffer.size()));
226  if (nread < 0) {
227  detail::throw_gzip_error(m_gzfile, "read failed");
228  }
229  buffer.resize(static_cast<std::string::size_type>(nread));
230 #if ZLIB_VERNUM >= 0x1240
231  set_offset(static_cast<std::size_t>(::gzoffset(m_gzfile)));
232 #endif
233  return buffer;
234  }
235 
236  void close() override {
237  if (m_gzfile) {
238 #ifdef _MSC_VER
239  osmium::detail::disable_invalid_parameter_handler diph;
240 #endif
241  const int result = ::gzclose_r(m_gzfile);
242  m_gzfile = nullptr;
243  if (result != Z_OK) {
244  throw gzip_error{"gzip error: read close failed", result};
245  }
246  }
247  }
248 
249  }; // class GzipDecompressor
250 
251  class GzipBufferDecompressor final : public Decompressor {
252 
253  const char* m_buffer;
254  std::size_t m_buffer_size;
255  z_stream m_zstream;
256 
257  public:
258 
259  GzipBufferDecompressor(const char* buffer, const std::size_t size) :
260  m_buffer(buffer),
261  m_buffer_size(size),
262  m_zstream() {
263  m_zstream.next_in = reinterpret_cast<unsigned char*>(const_cast<char*>(buffer));
264  assert(size < std::numeric_limits<unsigned int>::max());
265  m_zstream.avail_in = static_cast<unsigned int>(size);
266  const int result = inflateInit2(&m_zstream, MAX_WBITS | 32); // NOLINT(hicpp-signed-bitwise)
267  if (result != Z_OK) {
268  std::string message{"gzip error: decompression init failed: "};
269  if (m_zstream.msg) {
270  message.append(m_zstream.msg);
271  }
272  throw osmium::gzip_error{message, result};
273  }
274  }
275 
278 
281 
282  ~GzipBufferDecompressor() noexcept override {
283  try {
284  close();
285  } catch (...) {
286  // Ignore any exceptions because destructor must not throw.
287  }
288  }
289 
290  std::string read() override {
291  std::string output;
292 
293  if (m_buffer) {
294  const std::size_t buffer_size = 10240;
295  output.append(buffer_size, '\0');
296  m_zstream.next_out = reinterpret_cast<unsigned char*>(&*output.begin());
297  m_zstream.avail_out = buffer_size;
298  const int result = inflate(&m_zstream, Z_SYNC_FLUSH);
299 
300  if (result != Z_OK) {
301  m_buffer = nullptr;
302  m_buffer_size = 0;
303  }
304 
305  if (result != Z_OK && result != Z_STREAM_END) {
306  std::string message{"gzip error: inflate failed: "};
307  if (m_zstream.msg) {
308  message.append(m_zstream.msg);
309  }
310  throw osmium::gzip_error{message, result};
311  }
312 
313  output.resize(static_cast<std::size_t>(m_zstream.next_out - reinterpret_cast<const unsigned char*>(output.data())));
314  }
315 
316  return output;
317  }
318 
319  void close() override {
320  inflateEnd(&m_zstream);
321  }
322 
323  }; // class GzipBufferDecompressor
324 
325  namespace detail {
326 
327  // we want the register_compression() function to run, setting
328  // the variable is only a side-effect, it will never be used
330  [](const int fd, const fsync sync) { return new osmium::io::GzipCompressor{fd, sync}; },
331  [](const int fd) { return new osmium::io::GzipDecompressor{fd}; },
332  [](const char* buffer, const std::size_t size) { return new osmium::io::GzipBufferDecompressor{buffer, size}; }
333  );
334 
335  // dummy function to silence the unused variable warning from above
336  inline bool get_registered_gzip_compression() noexcept {
337  return registered_gzip_compression;
338  }
339 
340  } // namespace detail
341 
342  } // namespace io
343 
344 } // namespace osmium
345 
346 #endif // OSMIUM_IO_GZIP_COMPRESSION_HPP
osmium::io::GzipBufferDecompressor::GzipBufferDecompressor
GzipBufferDecompressor(const char *buffer, const std::size_t size)
Definition: gzip_compression.hpp:259
osmium::io::GzipBufferDecompressor::close
void close() override
Definition: gzip_compression.hpp:319
osmium::io::GzipCompressor::close
void close() override
Definition: gzip_compression.hpp:153
writer_options.hpp
osmium::io::CompressionFactory::instance
static CompressionFactory & instance()
Definition: compression.hpp:184
compression.hpp
osmium::io::GzipDecompressor::GzipDecompressor
GzipDecompressor(const int fd)
Definition: gzip_compression.hpp:190
osmium::io::GzipCompressor::GzipCompressor
GzipCompressor(const int fd, const fsync sync)
Definition: gzip_compression.hpp:113
osmium::gzip_error
Definition: gzip_compression.hpp:69
osmium::io::GzipCompressor
Definition: gzip_compression.hpp:105
osmium::io::GzipDecompressor::m_gzfile
gzFile m_gzfile
Definition: gzip_compression.hpp:186
detail
Definition: attr.hpp:342
osmium::io::GzipDecompressor::operator=
GzipDecompressor & operator=(const GzipDecompressor &)=delete
osmium::io::Compressor
Definition: compression.hpp:57
osmium::io::GzipCompressor::operator=
GzipCompressor & operator=(const GzipCompressor &)=delete
osmium::io::file_compression::gzip
@ gzip
osmium::io::Decompressor
Definition: compression.hpp:91
osmium::io::GzipCompressor::~GzipCompressor
~GzipCompressor() noexcept override
Definition: gzip_compression.hpp:131
osmium::gzip_error::gzip_error
gzip_error(const std::string &what)
Definition: gzip_compression.hpp:74
osmium::io::GzipBufferDecompressor::~GzipBufferDecompressor
~GzipBufferDecompressor() noexcept override
Definition: gzip_compression.hpp:282
osmium::io::GzipCompressor::m_file_size
std::size_t m_file_size
Definition: gzip_compression.hpp:107
osmium::io::GzipBufferDecompressor::m_buffer
const char * m_buffer
Definition: gzip_compression.hpp:253
osmium
Namespace for everything in the Osmium library.
Definition: assembler.hpp:53
osmium::io::Decompressor::input_buffer_size
@ input_buffer_size
Definition: compression.hpp:99
osmium::gzip_error::gzip_error_code
int gzip_error_code
Definition: gzip_compression.hpp:71
osmium::io_error
Definition: error.hpp:44
osmium::io::GzipBufferDecompressor::operator=
GzipBufferDecompressor & operator=(const GzipBufferDecompressor &)=delete
osmium::util::file_size
std::size_t file_size(int fd)
Definition: file.hpp:109
osmium::io::GzipDecompressor::read
std::string read() override
Definition: gzip_compression.hpp:218
osmium::io::GzipCompressor::m_fd
int m_fd
Definition: gzip_compression.hpp:108
osmium::io::GzipCompressor::m_gzfile
gzFile m_gzfile
Definition: gzip_compression.hpp:109
osmium::io::GzipBufferDecompressor
Definition: gzip_compression.hpp:251
osmium::io::fsync
fsync
Definition: writer_options.hpp:51
osmium::io::GzipDecompressor::~GzipDecompressor
~GzipDecompressor() noexcept override
Definition: gzip_compression.hpp:210
osmium::gzip_error::system_errno
int system_errno
Definition: gzip_compression.hpp:72
osmium::io::GzipBufferDecompressor::m_zstream
z_stream m_zstream
Definition: gzip_compression.hpp:255
file_compression.hpp
osmium::io::Decompressor::set_offset
void set_offset(const std::size_t offset) noexcept
Definition: compression.hpp:128
osmium::io::CompressionFactory::register_compression
bool register_compression(osmium::io::file_compression compression, const create_compressor_type &create_compressor, const create_decompressor_type_fd &create_decompressor_fd, const create_decompressor_type_buffer &create_decompressor_buffer)
Definition: compression.hpp:189
osmium::io::GzipCompressor::write
void write(const std::string &data) override
Definition: gzip_compression.hpp:139
osmium::io::GzipBufferDecompressor::read
std::string read() override
Definition: gzip_compression.hpp:290
osmium::io::GzipDecompressor
Definition: gzip_compression.hpp:184
osmium::io::GzipDecompressor::close
void close() override
Definition: gzip_compression.hpp:236
osmium::io::Compressor::do_fsync
bool do_fsync() const noexcept
Definition: compression.hpp:63
osmium::gzip_error::gzip_error
gzip_error(const std::string &what, const int error_code)
Definition: gzip_compression.hpp:78
osmium::io::GzipCompressor::file_size
std::size_t file_size() const override
Definition: gzip_compression.hpp:178
error.hpp
osmium::io::GzipBufferDecompressor::m_buffer_size
std::size_t m_buffer_size
Definition: gzip_compression.hpp:254