Libosmium  2.17.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;
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) {
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) {
246  osmium::io::detail::remove_buffered_pages(m_fd);
247 #ifdef _MSC_VER
248  osmium::detail::disable_invalid_parameter_handler diph;
249 #endif
250  const int result = ::gzclose_r(m_gzfile);
251  m_gzfile = nullptr;
252  if (result != Z_OK) {
253  throw gzip_error{"gzip error: read close failed", result};
254  }
255  }
256  }
257 
258  }; // class GzipDecompressor
259 
260  class GzipBufferDecompressor final : public Decompressor {
261 
262  const char* m_buffer;
263  std::size_t m_buffer_size;
264  z_stream m_zstream;
265 
266  public:
267 
268  GzipBufferDecompressor(const char* buffer, const std::size_t size) :
269  m_buffer(buffer),
270  m_buffer_size(size),
271  m_zstream() {
272  m_zstream.next_in = reinterpret_cast<unsigned char*>(const_cast<char*>(buffer));
273  assert(size < std::numeric_limits<unsigned int>::max());
274  m_zstream.avail_in = static_cast<unsigned int>(size);
275  const int result = inflateInit2(&m_zstream, MAX_WBITS | 32); // NOLINT(hicpp-signed-bitwise)
276  if (result != Z_OK) {
277  std::string message{"gzip error: decompression init failed: "};
278  if (m_zstream.msg) {
279  message.append(m_zstream.msg);
280  }
281  throw osmium::gzip_error{message, result};
282  }
283  }
284 
287 
290 
291  ~GzipBufferDecompressor() noexcept override {
292  try {
293  close();
294  } catch (...) {
295  // Ignore any exceptions because destructor must not throw.
296  }
297  }
298 
299  std::string read() override {
300  std::string output;
301 
302  if (m_buffer) {
303  const std::size_t buffer_size = 10240;
304  output.append(buffer_size, '\0');
305  m_zstream.next_out = reinterpret_cast<unsigned char*>(&*output.begin());
306  m_zstream.avail_out = buffer_size;
307  const int result = inflate(&m_zstream, Z_SYNC_FLUSH);
308 
309  if (result != Z_OK) {
310  m_buffer = nullptr;
311  m_buffer_size = 0;
312  }
313 
314  if (result != Z_OK && result != Z_STREAM_END) {
315  std::string message{"gzip error: inflate failed: "};
316  if (m_zstream.msg) {
317  message.append(m_zstream.msg);
318  }
319  throw osmium::gzip_error{message, result};
320  }
321 
322  output.resize(static_cast<std::size_t>(m_zstream.next_out - reinterpret_cast<const unsigned char*>(output.data())));
323  }
324 
325  return output;
326  }
327 
328  void close() override {
329  inflateEnd(&m_zstream);
330  }
331 
332  }; // class GzipBufferDecompressor
333 
334  namespace detail {
335 
336  // we want the register_compression() function to run, setting
337  // the variable is only a side-effect, it will never be used
339  [](const int fd, const fsync sync) { return new osmium::io::GzipCompressor{fd, sync}; },
340  [](const int fd) { return new osmium::io::GzipDecompressor{fd}; },
341  [](const char* buffer, const std::size_t size) { return new osmium::io::GzipBufferDecompressor{buffer, size}; }
342  );
343 
344  // dummy function to silence the unused variable warning from above
345  inline bool get_registered_gzip_compression() noexcept {
346  return registered_gzip_compression;
347  }
348 
349  } // namespace detail
350 
351  } // namespace io
352 
353 } // namespace osmium
354 
355 #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:189
static CompressionFactory & instance()
Definition: compression.hpp:184
Definition: compression.hpp:57
bool do_fsync() const noexcept
Definition: compression.hpp:63
Definition: compression.hpp:91
@ input_buffer_size
Definition: compression.hpp:99
std::size_t offset() const noexcept
Definition: compression.hpp:124
void set_offset(const std::size_t offset) noexcept
Definition: compression.hpp:128
Definition: gzip_compression.hpp:260
std::size_t m_buffer_size
Definition: gzip_compression.hpp:263
GzipBufferDecompressor(GzipBufferDecompressor &&)=delete
GzipBufferDecompressor & operator=(GzipBufferDecompressor &&)=delete
GzipBufferDecompressor(const GzipBufferDecompressor &)=delete
const char * m_buffer
Definition: gzip_compression.hpp:262
z_stream m_zstream
Definition: gzip_compression.hpp:264
std::string read() override
Definition: gzip_compression.hpp:299
GzipBufferDecompressor & operator=(const GzipBufferDecompressor &)=delete
~GzipBufferDecompressor() noexcept override
Definition: gzip_compression.hpp:291
GzipBufferDecompressor(const char *buffer, const std::size_t size)
Definition: gzip_compression.hpp:268
void close() override
Definition: gzip_compression.hpp:328
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:44