Libosmium  2.17.1
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;
127 
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  int m_fd;
188 
189  public:
190 
191  explicit GzipDecompressor(const int fd) : m_fd(fd) {
192 #ifdef _MSC_VER
193  osmium::detail::disable_invalid_parameter_handler diph;
194 #endif
195  m_gzfile = ::gzdopen(fd, "rb");
196  if (!m_gzfile) {
197  try {
198  osmium::io::detail::reliable_close(fd);
199  } catch (...) {
200  }
201  throw gzip_error{"gzip error: read initialization failed"};
202  }
203  }
204 
207 
210 
211  ~GzipDecompressor() noexcept override {
212  try {
213  close();
214  } catch (...) {
215  // Ignore any exceptions because destructor must not throw.
216  }
217  }
218 
219  std::string read() override {
220  assert(m_gzfile);
221 #ifdef _MSC_VER
222  osmium::detail::disable_invalid_parameter_handler diph;
223 #else
224 # if ZLIB_VERNUM >= 0x1240
225  const auto offset = ::gzoffset(m_gzfile);
226  if (offset > 0 && want_buffered_pages_removed()) {
227  osmium::io::detail::remove_buffered_pages(m_fd, static_cast<std::size_t>(offset));
228  }
229 # endif
230 #endif
231  std::string buffer(osmium::io::Decompressor::input_buffer_size, '\0');
232  assert(buffer.size() < std::numeric_limits<unsigned int>::max());
233  int nread = ::gzread(m_gzfile, &*buffer.begin(), static_cast<unsigned int>(buffer.size()));
234  if (nread < 0) {
235  detail::throw_gzip_error(m_gzfile, "read failed");
236  }
237  buffer.resize(static_cast<std::string::size_type>(nread));
238 #if ZLIB_VERNUM >= 0x1240
239  set_offset(static_cast<std::size_t>(::gzoffset(m_gzfile)));
240 #endif
241  return buffer;
242  }
243 
244  void close() override {
245  if (m_gzfile) {
247  osmium::io::detail::remove_buffered_pages(m_fd);
248  }
249 #ifdef _MSC_VER
250  osmium::detail::disable_invalid_parameter_handler diph;
251 #endif
252  const int result = ::gzclose_r(m_gzfile);
253  m_gzfile = nullptr;
254  if (result != Z_OK) {
255  throw gzip_error{"gzip error: read close failed", result};
256  }
257  }
258  }
259 
260  }; // class GzipDecompressor
261 
262  class GzipBufferDecompressor final : public Decompressor {
263 
264  const char* m_buffer;
265  std::size_t m_buffer_size;
266  z_stream m_zstream;
267 
268  public:
269 
270  GzipBufferDecompressor(const char* buffer, const std::size_t size) :
271  m_buffer(buffer),
272  m_buffer_size(size),
273  m_zstream() {
274  m_zstream.next_in = reinterpret_cast<unsigned char*>(const_cast<char*>(buffer));
275  assert(size < std::numeric_limits<unsigned int>::max());
276  m_zstream.avail_in = static_cast<unsigned int>(size);
277  const int result = inflateInit2(&m_zstream, MAX_WBITS | 32); // NOLINT(hicpp-signed-bitwise)
278  if (result != Z_OK) {
279  std::string message{"gzip error: decompression init failed: "};
280  if (m_zstream.msg) {
281  message.append(m_zstream.msg);
282  }
283  throw osmium::gzip_error{message, result};
284  }
285  }
286 
289 
292 
293  ~GzipBufferDecompressor() noexcept override {
294  try {
295  close();
296  } catch (...) {
297  // Ignore any exceptions because destructor must not throw.
298  }
299  }
300 
301  std::string read() override {
302  std::string output;
303 
304  if (m_buffer) {
305  const std::size_t buffer_size = 10240;
306  output.append(buffer_size, '\0');
307  m_zstream.next_out = reinterpret_cast<unsigned char*>(&*output.begin());
308  m_zstream.avail_out = buffer_size;
309  const int result = inflate(&m_zstream, Z_SYNC_FLUSH);
310 
311  if (result != Z_OK) {
312  m_buffer = nullptr;
313  m_buffer_size = 0;
314  }
315 
316  if (result != Z_OK && result != Z_STREAM_END) {
317  std::string message{"gzip error: inflate failed: "};
318  if (m_zstream.msg) {
319  message.append(m_zstream.msg);
320  }
321  throw osmium::gzip_error{message, result};
322  }
323 
324  output.resize(static_cast<std::size_t>(m_zstream.next_out - reinterpret_cast<const unsigned char*>(output.data())));
325  }
326 
327  return output;
328  }
329 
330  void close() override {
331  inflateEnd(&m_zstream);
332  }
333 
334  }; // class GzipBufferDecompressor
335 
336  namespace detail {
337 
338  // we want the register_compression() function to run, setting
339  // the variable is only a side-effect, it will never be used
341  [](const int fd, const fsync sync) { return new osmium::io::GzipCompressor{fd, sync}; },
342  [](const int fd) { return new osmium::io::GzipDecompressor{fd}; },
343  [](const char* buffer, const std::size_t size) { return new osmium::io::GzipBufferDecompressor{buffer, size}; }
344  );
345 
346  // dummy function to silence the unused variable warning from above
347  inline bool get_registered_gzip_compression() noexcept {
348  return registered_gzip_compression;
349  }
350 
351  } // namespace detail
352 
353  } // namespace io
354 
355 } // namespace osmium
356 
357 #endif // OSMIUM_IO_GZIP_COMPRESSION_HPP
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:196
static CompressionFactory & instance()
Definition: compression.hpp:191
Definition: compression.hpp:57
bool do_fsync() const noexcept
Definition: compression.hpp:63
Definition: compression.hpp:91
@ input_buffer_size
Definition: compression.hpp:100
void set_offset(const std::size_t offset) noexcept
Definition: compression.hpp:125
bool want_buffered_pages_removed() const noexcept
Definition: compression.hpp:131
Definition: gzip_compression.hpp:262
std::size_t m_buffer_size
Definition: gzip_compression.hpp:265
GzipBufferDecompressor(GzipBufferDecompressor &&)=delete
GzipBufferDecompressor & operator=(GzipBufferDecompressor &&)=delete
GzipBufferDecompressor(const GzipBufferDecompressor &)=delete
const char * m_buffer
Definition: gzip_compression.hpp:264
z_stream m_zstream
Definition: gzip_compression.hpp:266
std::string read() override
Definition: gzip_compression.hpp:301
GzipBufferDecompressor & operator=(const GzipBufferDecompressor &)=delete
~GzipBufferDecompressor() noexcept override
Definition: gzip_compression.hpp:293
GzipBufferDecompressor(const char *buffer, const std::size_t size)
Definition: gzip_compression.hpp:270
void close() override
Definition: gzip_compression.hpp:330
Definition: gzip_compression.hpp:105
gzFile m_gzfile
Definition: gzip_compression.hpp:109
GzipCompressor(GzipCompressor &&)=delete
std::size_t file_size() const override
Definition: gzip_compression.hpp:178
~GzipCompressor() noexcept override
Definition: gzip_compression.hpp:131
int m_fd
Definition: gzip_compression.hpp:108
GzipCompressor(const GzipCompressor &)=delete
std::size_t m_file_size
Definition: gzip_compression.hpp:107
GzipCompressor & operator=(const GzipCompressor &)=delete
void close() override
Definition: gzip_compression.hpp:153
void write(const std::string &data) override
Definition: gzip_compression.hpp:139
GzipCompressor & operator=(GzipCompressor &&)=delete
GzipCompressor(const int fd, const fsync sync)
Definition: gzip_compression.hpp:113
Definition: gzip_compression.hpp:184
GzipDecompressor & operator=(const GzipDecompressor &)=delete
GzipDecompressor & operator=(GzipDecompressor &&)=delete
GzipDecompressor(const GzipDecompressor &)=delete
GzipDecompressor(const int fd)
Definition: gzip_compression.hpp:191
int m_fd
Definition: gzip_compression.hpp:187
~GzipDecompressor() noexcept override
Definition: gzip_compression.hpp:211
gzFile m_gzfile
Definition: gzip_compression.hpp:186
std::string read() override
Definition: gzip_compression.hpp:219
GzipDecompressor(GzipDecompressor &&)=delete
void close() override
Definition: gzip_compression.hpp:244
Definition: attr.hpp:342
fsync
Definition: writer_options.hpp:51
std::size_t file_size(int fd)
Definition: file.hpp:109
Namespace for everything in the Osmium library.
Definition: assembler.hpp:53
Definition: gzip_compression.hpp:69
gzip_error(const std::string &what, const int error_code)
Definition: gzip_compression.hpp:78
int gzip_error_code
Definition: gzip_compression.hpp:71
gzip_error(const std::string &what)
Definition: gzip_compression.hpp:74
int system_errno
Definition: gzip_compression.hpp:72
Definition: error.hpp:46