Loading...
Searching...
No Matches
reader.hpp
Go to the documentation of this file.
#ifndef OSMIUM_IO_READER_HPP
#define OSMIUM_IO_READER_HPP
/*
This file is part of Osmium (https://osmcode.org/libosmium).
Copyright 2013-2023 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <osmium/io/compression.hpp>
#include <osmium/io/detail/input_format.hpp>
#include <osmium/io/detail/queue_util.hpp>
#include <osmium/io/detail/read_thread.hpp>
#include <osmium/io/detail/read_write.hpp>
#include <osmium/io/error.hpp>
#include <osmium/io/file.hpp>
#include <osmium/io/header.hpp>
#include <osmium/memory/buffer.hpp>
#include <osmium/osm/entity_bits.hpp>
#include <osmium/thread/pool.hpp>
#include <osmium/thread/util.hpp>
#include <osmium/util/config.hpp>
#include <cerrno>
#include <cstdlib>
#include <fcntl.h>
#include <future>
#include <memory>
#include <string>
#include <system_error>
#include <thread>
#include <utility>
#ifndef _WIN32
# include <sys/wait.h>
#endif
#ifndef _MSC_VER
# include <unistd.h>
#endif
namespace osmium {
namespace io {
namespace detail {
inline std::size_t get_input_queue_size() noexcept {
return osmium::config::get_max_queue_size("INPUT", 20);
}
inline std::size_t get_osmdata_queue_size() noexcept {
return osmium::config::get_max_queue_size("OSMDATA", 20);
}
} // namespace detail
class Reader {
// The Reader::read() function reads from a queue of buffers which
// can contain nested buffers. These nested buffers will be in
// here, because read() can only return a single unnested buffer.
osmium::memory::Buffer m_back_buffers{};
osmium::io::File m_file;
osmium::thread::Pool* m_pool = nullptr;
std::atomic<std::size_t> m_offset{0};
detail::ParserFactory::create_parser_type m_creator;
enum class status {
okay = 0, // normal reading
error = 1, // some error occurred while reading
closed = 2, // close() called
eof = 3 // eof of file was reached without error
} m_status = status::okay;
int m_childpid = 0;
detail::future_string_queue_type m_input_queue;
int m_fd = -1;
std::size_t m_file_size = 0;
std::unique_ptr<osmium::io::Decompressor> m_decompressor;
osmium::io::detail::ReadThreadManager m_read_thread_manager;
detail::future_buffer_queue_type m_osmdata_queue;
detail::queue_wrapper<osmium::memory::Buffer> m_osmdata_queue_wrapper;
std::future<osmium::io::Header> m_header_future{};
osmium::io::Header m_header{};
osmium::thread::thread_handler m_thread{};
osmium::osm_entity_bits::type m_read_which_entities = osmium::osm_entity_bits::all;
osmium::io::read_meta m_read_metadata = osmium::io::read_meta::yes;
osmium::io::buffers_type m_buffers_kind = osmium::io::buffers_type::any;
void set_option(osmium::thread::Pool& pool) noexcept {
m_pool = &pool;
}
void set_option(osmium::osm_entity_bits::type value) noexcept {
m_read_which_entities = value;
}
void set_option(osmium::io::read_meta value) noexcept {
// Ignore this setting if we have a history/change file,
// because if this is set to "no", we don't see the difference
// between visible and deleted objects.
if (!m_file.has_multiple_object_versions()) {
m_read_metadata = value;
}
}
void set_option(osmium::io::buffers_type value) noexcept {
m_buffers_kind = value;
}
// This function will run in a separate thread.
static void parser_thread(osmium::thread::Pool& pool,
int fd,
const detail::ParserFactory::create_parser_type& creator,
detail::future_string_queue_type& input_queue,
detail::future_buffer_queue_type& osmdata_queue,
std::promise<osmium::io::Header>&& header_promise,
std::atomic<std::size_t>* offset_ptr,
osmium::osm_entity_bits::type read_which_entities,
osmium::io::read_meta read_metadata,
osmium::io::buffers_type buffers_kind,
bool want_buffered_pages_removed) {
std::promise<osmium::io::Header> promise{std::move(header_promise)};
osmium::io::detail::parser_arguments args = {
pool,
fd,
input_queue,
osmdata_queue,
promise,
offset_ptr,
read_which_entities,
read_metadata,
buffers_kind,
want_buffered_pages_removed};
creator(args)->parse();
}
#ifndef _WIN32
static int execute(const std::string& command, const std::string& filename, int* childpid) {
int pipefd[2];
if (pipe(pipefd) < 0) {
throw std::system_error{errno, std::system_category(), "opening pipe failed"};
}
const pid_t pid = fork();
if (pid < 0) {
throw std::system_error{errno, std::system_category(), "fork failed"};
}
if (pid == 0) { // child
// close all file descriptors except one end of the pipe
for (int i = 0; i < 32; ++i) {
if (i != pipefd[1]) {
::close(i);
}
}
if (dup2(pipefd[1], 1) < 0) { // put end of pipe as stdout/stdin
std::exit(1); // NOLINT(concurrency-mt-unsafe)
}
::open("/dev/null", O_RDONLY); // stdin
::open("/dev/null", O_WRONLY); // stderr
// hack: -g switches off globbing in curl which allows [] to be used in file names
// this is important for XAPI URLs
// in theory this execute() function could be used for other commands, but it is
// only used for curl at the moment, so this is okay.
if (::execlp(command.c_str(), command.c_str(), "-g", filename.c_str(), nullptr) < 0) {
std::exit(1); // NOLINT(concurrency-mt-unsafe)
}
}
// parent
*childpid = pid;
::close(pipefd[1]);
return pipefd[0];
}
#endif
static int open_input_file_or_url(const std::string& filename, int* childpid) {
const std::string protocol{filename.substr(0, filename.find_first_of(':'))};
if (protocol == "http" || protocol == "https" || protocol == "ftp" || protocol == "file") {
#ifndef _WIN32
return execute("curl", filename, childpid);
#else
throw io_error{"Reading OSM files from the network currently not supported on Windows."};
#endif
}
const int fd = osmium::io::detail::open_for_reading(filename);
#if defined(__linux__) || defined(__FreeBSD__)
if (fd >= 0) {
// Tell the kernel we are going to read this file sequentially
::posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);
}
#endif
return fd;
}
static std::unique_ptr<Decompressor> make_decompressor(const osmium::io::File& file, int fd, std::atomic<std::size_t>* offset_ptr) {
const auto& factory = osmium::io::CompressionFactory::instance();
std::unique_ptr<Decompressor> decompressor;
if (file.buffer()) {
decompressor = factory.create_decompressor(file.compression(), file.buffer(), file.buffer_size());
} else if (file.format() == file_format::pbf) {
decompressor = std::unique_ptr<Decompressor>{new DummyDecompressor{}};
} else {
decompressor = factory.create_decompressor(file.compression(), fd);
}
decompressor->set_offset_ptr(offset_ptr);
return decompressor;
}
public:
template <typename... TArgs>
explicit Reader(const osmium::io::File& file, TArgs&&... args) :
m_file(file.check()),
m_creator(detail::ParserFactory::instance().get_creator_function(m_file)),
m_input_queue(detail::get_input_queue_size(), "raw_input"),
m_fd(m_file.buffer() ? -1 : open_input_file_or_url(m_file.filename(), &m_childpid)),
m_file_size(m_fd > 2 ? osmium::file_size(m_fd) : 0),
m_decompressor(make_decompressor(m_file, m_fd, &m_offset)),
m_read_thread_manager(*m_decompressor, m_input_queue),
m_osmdata_queue(detail::get_osmdata_queue_size(), "parser_results"),
m_osmdata_queue_wrapper(m_osmdata_queue) {
(void)std::initializer_list<int>{(set_option(args), 0)...};
if (!m_pool) {
m_pool = &thread::Pool::default_instance();
}
std::promise<osmium::io::Header> header_promise;
m_header_future = header_promise.get_future();
const auto cpc = osmium::config::clean_page_cache_after_read();
if (cpc >= 0) {
m_decompressor->set_want_buffered_pages_removed(true);
}
const int fd_for_parser = m_decompressor->is_real() ? -1 : m_fd;
m_thread = osmium::thread::thread_handler{parser_thread, std::ref(*m_pool), fd_for_parser, std::ref(m_creator),
std::ref(m_input_queue), std::ref(m_osmdata_queue),
std::move(header_promise), &m_offset, m_read_which_entities,
m_read_metadata, m_buffers_kind,
m_decompressor->want_buffered_pages_removed()};
}
template <typename... TArgs>
explicit Reader(const std::string& filename, TArgs&&... args) :
Reader(osmium::io::File(filename), std::forward<TArgs>(args)...) {
}
template <typename... TArgs>
explicit Reader(const char* filename, TArgs&&... args) :
Reader(osmium::io::File(filename), std::forward<TArgs>(args)...) {
}
Reader(const Reader&) = delete;
Reader& operator=(const Reader&) = delete;
Reader(Reader&&) = delete;
Reader& operator=(Reader&&) = delete;
~Reader() noexcept {
try {
close();
} catch (...) {
// Ignore any exceptions because destructor must not throw.
}
}
void close() {
m_status = status::closed;
m_read_thread_manager.stop();
m_osmdata_queue_wrapper.shutdown();
try {
m_read_thread_manager.close();
} catch (...) {
// Ignore any exceptions.
}
#ifndef _WIN32
if (m_childpid) {
int status = 0;
const pid_t pid = ::waitpid(m_childpid, &status, 0);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
if (pid < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) { // NOLINT(hicpp-signed-bitwise)
throw std::system_error{errno, std::system_category(), "subprocess returned error"};
}
#pragma GCC diagnostic pop
m_childpid = 0;
}
#endif
}
osmium::io::Header header() {
if (m_status == status::error) {
throw io_error{"Can not get header from reader when in status 'error'"};
}
try {
if (m_header_future.valid()) {
m_header = m_header_future.get();
}
} catch (...) {
close();
m_status = status::error;
throw;
}
return m_header;
}
osmium::memory::Buffer read() {
osmium::memory::Buffer buffer;
// If there are buffers on the stack, return those first.
if (m_back_buffers) {
if (m_back_buffers.has_nested_buffers()) {
buffer = std::move(*m_back_buffers.get_last_nested());
} else {
buffer = std::move(m_back_buffers);
m_back_buffers = osmium::memory::Buffer{};
}
return buffer;
}
if (m_status != status::okay) {
throw io_error{"Can not read from reader when in status 'closed', 'eof', or 'error'"};
}
if (m_read_which_entities == osmium::osm_entity_bits::nothing) {
m_status = status::eof;
return buffer;
}
try {
// m_input_format.read() can return an invalid buffer to signal EOF,
// or a valid buffer with or without data. A valid buffer
// without data is not an error, it just means we have to
// keep getting the next buffer until there is one with data.
while (true) {
buffer = m_osmdata_queue_wrapper.pop();
if (detail::at_end_of_data(buffer)) {
m_status = status::eof;
m_read_thread_manager.close();
return buffer;
}
if (buffer.has_nested_buffers()) {
m_back_buffers = std::move(buffer);
buffer = std::move(*m_back_buffers.get_last_nested());
}
if (buffer.committed() > 0) {
return buffer;
}
}
} catch (...) {
close();
m_status = status::error;
throw;
}
}
bool eof() const {
return m_status == status::eof || m_status == status::closed;
}
std::size_t file_size() const noexcept {
return m_file_size;
}
std::size_t offset() const noexcept {
return m_offset;
}
}; // class Reader
template <typename... TArgs>
osmium::memory::Buffer read_file(TArgs&&... args) {
osmium::memory::Buffer buffer{1024UL * 1024UL, osmium::memory::Buffer::auto_grow::yes};
Reader reader{std::forward<TArgs>(args)...};
while (auto read_buffer = reader.read()) {
buffer.add_buffer(read_buffer);
buffer.commit();
}
return buffer;
}
} // namespace io
} // namespace osmium
#endif // OSMIUM_IO_READER_HPP
buffer.hpp
osmium::io::CompressionFactory::instance
static CompressionFactory & instance()
Definition compression.hpp:191
osmium::io::Decompressor::set_offset_ptr
void set_offset_ptr(std::atomic< std::size_t > *offset_ptr) noexcept
Definition compression.hpp:121
osmium::io::DummyDecompressor
Definition compression.hpp:287
osmium::io::File
Definition file.hpp:72
osmium::io::File::buffer_size
size_t buffer_size() const noexcept
Definition file.hpp:147
osmium::io::File::has_multiple_object_versions
bool has_multiple_object_versions() const noexcept
Definition file.hpp:303
osmium::io::File::compression
file_compression compression() const noexcept
Definition file.hpp:294
osmium::io::File::format
file_format format() const noexcept
Definition file.hpp:285
osmium::io::File::buffer
const char * buffer() const noexcept
Definition file.hpp:143
osmium::io::Header
Definition header.hpp:68
osmium::io::Reader
Definition reader.hpp:90
osmium::io::Reader::read
osmium::memory::Buffer read()
Definition reader.hpp:444
osmium::io::Reader::m_buffers_kind
osmium::io::buffers_type m_buffers_kind
Definition reader.hpp:134
osmium::io::Reader::m_input_queue
detail::future_string_queue_type m_input_queue
Definition reader.hpp:114
osmium::io::Reader::m_back_buffers
osmium::memory::Buffer m_back_buffers
Definition reader.hpp:95
osmium::io::Reader::parser_thread
static void parser_thread(osmium::thread::Pool &pool, int fd, const detail::ParserFactory::create_parser_type &creator, detail::future_string_queue_type &input_queue, detail::future_buffer_queue_type &osmdata_queue, std::promise< osmium::io::Header > &&header_promise, std::atomic< std::size_t > *offset_ptr, osmium::osm_entity_bits::type read_which_entities, osmium::io::read_meta read_metadata, osmium::io::buffers_type buffers_kind, bool want_buffered_pages_removed)
Definition reader.hpp:158
osmium::io::Reader::operator=
Reader & operator=(Reader &&)=delete
osmium::io::Reader::m_childpid
int m_childpid
Definition reader.hpp:112
osmium::io::Reader::set_option
void set_option(osmium::io::read_meta value) noexcept
Definition reader.hpp:144
osmium::io::Reader::m_osmdata_queue
detail::future_buffer_queue_type m_osmdata_queue
Definition reader.hpp:124
osmium::io::Reader::make_decompressor
static std::unique_ptr< Decompressor > make_decompressor(const osmium::io::File &file, int fd, std::atomic< std::size_t > *offset_ptr)
Definition reader.hpp:260
osmium::io::Reader::m_file_size
std::size_t m_file_size
Definition reader.hpp:118
osmium::io::Reader::set_option
void set_option(osmium::thread::Pool &pool) noexcept
Definition reader.hpp:136
osmium::io::Reader::set_option
void set_option(osmium::io::buffers_type value) noexcept
Definition reader.hpp:153
osmium::io::Reader::operator=
Reader & operator=(const Reader &)=delete
osmium::io::Reader::execute
static int execute(const std::string &command, const std::string &filename, int *childpid)
Definition reader.hpp:196
osmium::io::Reader::m_status
enum osmium::io::Reader::status m_status
osmium::io::Reader::open_input_file_or_url
static int open_input_file_or_url(const std::string &filename, int *childpid)
Definition reader.hpp:241
osmium::io::Reader::m_decompressor
std::unique_ptr< osmium::io::Decompressor > m_decompressor
Definition reader.hpp:120
osmium::io::Reader::status
status
Definition reader.hpp:105
osmium::io::Reader::status::eof
@ eof
osmium::io::Reader::status::closed
@ closed
osmium::io::Reader::status::error
@ error
osmium::io::Reader::status::okay
@ okay
osmium::io::Reader::Reader
Reader(const char *filename, TArgs &&... args)
Definition reader.hpp:358
osmium::io::Reader::m_header
osmium::io::Header m_header
Definition reader.hpp:128
osmium::io::Reader::m_creator
detail::ParserFactory::create_parser_type m_creator
Definition reader.hpp:103
osmium::io::Reader::header
osmium::io::Header header()
Definition reader.hpp:418
osmium::io::Reader::m_osmdata_queue_wrapper
detail::queue_wrapper< osmium::memory::Buffer > m_osmdata_queue_wrapper
Definition reader.hpp:125
osmium::io::Reader::Reader
Reader(const osmium::io::File &file, TArgs &&... args)
Definition reader.hpp:319
osmium::io::Reader::file_size
std::size_t file_size() const noexcept
Definition reader.hpp:506
osmium::io::Reader::Reader
Reader(Reader &&)=delete
osmium::io::Reader::set_option
void set_option(osmium::osm_entity_bits::type value) noexcept
Definition reader.hpp:140
osmium::io::Reader::m_header_future
std::future< osmium::io::Header > m_header_future
Definition reader.hpp:127
osmium::io::Reader::m_read_thread_manager
osmium::io::detail::ReadThreadManager m_read_thread_manager
Definition reader.hpp:122
osmium::io::Reader::eof
bool eof() const
Definition reader.hpp:498
osmium::io::Reader::Reader
Reader(const Reader &)=delete
osmium::io::Reader::offset
std::size_t offset() const noexcept
Definition reader.hpp:524
osmium::io::Reader::m_offset
std::atomic< std::size_t > m_offset
Definition reader.hpp:101
osmium::io::Reader::m_thread
osmium::thread::thread_handler m_thread
Definition reader.hpp:130
osmium::io::Reader::close
void close()
Definition reader.hpp:384
osmium::io::Reader::~Reader
~Reader() noexcept
Definition reader.hpp:368
osmium::io::Reader::m_file
osmium::io::File m_file
Definition reader.hpp:97
osmium::io::Reader::m_read_which_entities
osmium::osm_entity_bits::type m_read_which_entities
Definition reader.hpp:132
osmium::io::Reader::Reader
Reader(const std::string &filename, TArgs &&... args)
Definition reader.hpp:353
osmium::io::Reader::m_read_metadata
osmium::io::read_meta m_read_metadata
Definition reader.hpp:133
osmium::io::Reader::m_fd
int m_fd
Definition reader.hpp:116
osmium::io::Reader::m_pool
osmium::thread::Pool * m_pool
Definition reader.hpp:99
osmium::thread::Pool
Definition pool.hpp:90
osmium::thread::Pool::default_instance
static Pool & default_instance()
Definition pool.hpp:186
osmium::thread::thread_handler
Definition util.hpp:91
compression.hpp
config.hpp
entity_bits.hpp
error.hpp
header.hpp
file.hpp
detail
Definition attr.hpp:342
osmium::config::get_max_queue_size
std::size_t get_max_queue_size(const char *queue_name, const std::size_t default_value) noexcept
Definition config.hpp:83
osmium::config::clean_page_cache_after_read
int8_t clean_page_cache_after_read() noexcept
Definition config.hpp:106
osmium::io::file_format::pbf
@ pbf
osmium::io::read_file
osmium::memory::Buffer read_file(TArgs &&... args)
Definition reader.hpp:539
osmium::io::buffers_type
buffers_type
Definition file_format.hpp:60
osmium::io::buffers_type::any
@ any
osmium::io::read_meta
read_meta
Definition file_format.hpp:55
osmium::io::read_meta::yes
@ yes
osmium::osm_entity_bits::type
type
Definition entity_bits.hpp:63
osmium::osm_entity_bits::all
@ all
object or changeset
Definition entity_bits.hpp:76
osmium::osm_entity_bits::nothing
@ nothing
Definition entity_bits.hpp:67
osmium
Namespace for everything in the Osmium library.
Definition assembler.hpp:53
std
Definition location.hpp:555
pool.hpp
osmium::io_error
Definition error.hpp:46
util.hpp