Merge commit '6eb4f090f98f6b17a23c57768c16b7716b6c9cbd' as 'third_party/libosmium'
This commit is contained in:
@@ -0,0 +1,788 @@
|
||||
#ifndef OSMIUM_MEMORY_BUFFER_HPP
|
||||
#define OSMIUM_MEMORY_BUFFER_HPP
|
||||
|
||||
#include <iostream>
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 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 <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/memory/item.hpp>
|
||||
#include <osmium/memory/item_iterator.hpp>
|
||||
#include <osmium/osm/entity.hpp>
|
||||
#include <osmium/util/compatibility.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* Exception thrown by the osmium::memory::Buffer class when somebody tries
|
||||
* to write data into a buffer and it doesn't fit. Buffers with internal
|
||||
* memory management will not throw this exception, but increase their size.
|
||||
*/
|
||||
struct buffer_is_full : public std::runtime_error {
|
||||
|
||||
buffer_is_full() :
|
||||
std::runtime_error{"Osmium buffer is full"} {
|
||||
}
|
||||
|
||||
}; // struct buffer_is_full
|
||||
|
||||
/**
|
||||
* @brief Memory management of items in buffers and iterators over this data.
|
||||
*/
|
||||
namespace memory {
|
||||
|
||||
/**
|
||||
* A memory area for storing OSM objects and other items. Each item stored
|
||||
* has a type and a length. See the Item class for details.
|
||||
*
|
||||
* Data can be added to a buffer piece by piece using reserve_space() and
|
||||
* add_item(). After all data that together forms an item is added, it must
|
||||
* be committed using the commit() call. Usually this is done through the
|
||||
* Builder class and its derived classes.
|
||||
*
|
||||
* You can iterate over all items in a buffer using the iterators returned
|
||||
* by begin(), end(), cbegin(), and cend().
|
||||
*
|
||||
* Buffers exist in two flavours, those with external memory management and
|
||||
* those with internal memory management. If you already have some memory
|
||||
* with data in it (for instance read from disk), you create a Buffer with
|
||||
* external memory management. It is your job then to free the memory once
|
||||
* the buffer isn't used any more. If you don't have memory already, you can
|
||||
* create a Buffer object and have it manage the memory internally. It will
|
||||
* dynamically allocate memory and free it again after use.
|
||||
*
|
||||
* By default, if a buffer gets full it will throw a buffer_is_full exception.
|
||||
* You can use the set_full_callback() method to set a callback functor
|
||||
* which will be called instead of throwing an exception. The full
|
||||
* callback functionality is deprecated and will be removed in the
|
||||
* future. See the documentation for set_full_callback() for alternatives.
|
||||
*/
|
||||
class Buffer {
|
||||
|
||||
public:
|
||||
|
||||
// This is needed so we can call std::back_inserter() on a Buffer.
|
||||
using value_type = Item;
|
||||
|
||||
enum class auto_grow : bool {
|
||||
yes = true,
|
||||
no = false
|
||||
}; // enum class auto_grow
|
||||
|
||||
private:
|
||||
|
||||
std::unique_ptr<unsigned char[]> m_memory;
|
||||
unsigned char* m_data;
|
||||
std::size_t m_capacity;
|
||||
std::size_t m_written;
|
||||
std::size_t m_committed;
|
||||
#ifndef NDEBUG
|
||||
uint8_t m_builder_count = 0;
|
||||
#endif
|
||||
auto_grow m_auto_grow{auto_grow::no};
|
||||
std::function<void(Buffer&)> m_full;
|
||||
|
||||
static std::size_t calculate_capacity(std::size_t capacity) noexcept {
|
||||
// The majority of all Nodes will fit into this size.
|
||||
constexpr static const std::size_t min_capacity = 64;
|
||||
if (capacity < min_capacity) {
|
||||
return min_capacity;
|
||||
}
|
||||
return padded_length(capacity);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* The constructor without any parameters creates an invalid,
|
||||
* buffer, ie an empty hull of a buffer that has no actual memory
|
||||
* associated with it. It can be used to signify end-of-data.
|
||||
*
|
||||
* Most methods of the Buffer class will not work with an invalid
|
||||
* buffer.
|
||||
*/
|
||||
Buffer() noexcept :
|
||||
m_memory(),
|
||||
m_data(nullptr),
|
||||
m_capacity(0),
|
||||
m_written(0),
|
||||
m_committed(0) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a valid externally memory-managed buffer using the
|
||||
* given memory and size.
|
||||
*
|
||||
* @param data A pointer to some already initialized data.
|
||||
* @param size The size of the initialized data.
|
||||
*
|
||||
* @throws std::invalid_argument if the size isn't a multiple of
|
||||
* the alignment.
|
||||
*/
|
||||
explicit Buffer(unsigned char* data, std::size_t size) :
|
||||
m_memory(),
|
||||
m_data(data),
|
||||
m_capacity(size),
|
||||
m_written(size),
|
||||
m_committed(size) {
|
||||
if (size % align_bytes != 0) {
|
||||
throw std::invalid_argument{"buffer size needs to be multiple of alignment"};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a valid externally memory-managed buffer with the
|
||||
* given capacity that already contains 'committed' bytes of data.
|
||||
*
|
||||
* @param data A pointer to some (possibly initialized) data.
|
||||
* @param capacity The size of the memory for this buffer.
|
||||
* @param committed The size of the initialized data. If this is 0, the buffer startes out empty.
|
||||
*
|
||||
* @throws std::invalid_argument if the capacity or committed isn't
|
||||
* a multiple of the alignment or if committed is larger
|
||||
* than capacity.
|
||||
*/
|
||||
explicit Buffer(unsigned char* data, std::size_t capacity, std::size_t committed) :
|
||||
m_memory(),
|
||||
m_data(data),
|
||||
m_capacity(capacity),
|
||||
m_written(committed),
|
||||
m_committed(committed) {
|
||||
if (capacity % align_bytes != 0) {
|
||||
throw std::invalid_argument{"buffer capacity needs to be multiple of alignment"};
|
||||
}
|
||||
if (committed % align_bytes != 0) {
|
||||
throw std::invalid_argument{"buffer parameter 'committed' needs to be multiple of alignment"};
|
||||
}
|
||||
if (committed > capacity) {
|
||||
throw std::invalid_argument{"buffer parameter 'committed' can not be larger than capacity"};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a valid internally memory-managed buffer with the
|
||||
* given capacity.
|
||||
* Will internally get dynamic memory of the required size.
|
||||
* The dynamic memory will be automatically freed when the Buffer
|
||||
* is destroyed.
|
||||
*
|
||||
* @param capacity The (initial) size of the memory for this buffer.
|
||||
* Actual capacity might be larger tue to alignment.
|
||||
* @param auto_grow Should this buffer automatically grow when it
|
||||
* becomes to small?
|
||||
*/
|
||||
explicit Buffer(std::size_t capacity, auto_grow auto_grow = auto_grow::yes) :
|
||||
m_memory(new unsigned char[calculate_capacity(capacity)]),
|
||||
m_data(m_memory.get()),
|
||||
m_capacity(calculate_capacity(capacity)),
|
||||
m_written(0),
|
||||
m_committed(0),
|
||||
m_auto_grow(auto_grow) {
|
||||
}
|
||||
|
||||
// buffers can not be copied
|
||||
Buffer(const Buffer&) = delete;
|
||||
Buffer& operator=(const Buffer&) = delete;
|
||||
|
||||
// buffers can be moved
|
||||
Buffer(Buffer&&) = default;
|
||||
Buffer& operator=(Buffer&&) = default;
|
||||
|
||||
~Buffer() = default;
|
||||
|
||||
#ifndef NDEBUG
|
||||
void increment_builder_count() noexcept {
|
||||
++m_builder_count;
|
||||
}
|
||||
|
||||
void decrement_builder_count() noexcept {
|
||||
assert(m_builder_count > 0);
|
||||
--m_builder_count;
|
||||
}
|
||||
|
||||
uint8_t builder_count() const noexcept {
|
||||
return m_builder_count;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Return a pointer to data inside the buffer.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
*/
|
||||
unsigned char* data() const noexcept {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return m_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the capacity of the buffer, ie how many bytes it can
|
||||
* contain. Always returns 0 on invalid buffers.
|
||||
*/
|
||||
std::size_t capacity() const noexcept {
|
||||
return m_capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes already filled in this buffer.
|
||||
* Always returns 0 on invalid buffers.
|
||||
*/
|
||||
std::size_t committed() const noexcept {
|
||||
return m_committed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of bytes currently filled in this buffer that
|
||||
* are not yet committed.
|
||||
* Always returns 0 on invalid buffers.
|
||||
*/
|
||||
std::size_t written() const noexcept {
|
||||
return m_written;
|
||||
}
|
||||
|
||||
/**
|
||||
* This tests if the current state of the buffer is aligned
|
||||
* properly. Can be used for asserts.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
*/
|
||||
bool is_aligned() const noexcept {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return (m_written % align_bytes == 0) && (m_committed % align_bytes == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set functor to be called whenever the buffer is full
|
||||
* instead of throwing buffer_is_full.
|
||||
*
|
||||
* The behaviour is undefined if you call this on an invalid
|
||||
* buffer.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
*
|
||||
* @deprecated
|
||||
* Callback functionality will be removed in the future. Either
|
||||
* detect the buffer_is_full exception or use a buffer with
|
||||
* auto_grow::yes. If you want to avoid growing buffers, check
|
||||
* the CallbackBuffer class.
|
||||
*/
|
||||
OSMIUM_DEPRECATED void set_full_callback(const std::function<void(Buffer&)>& full) {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
m_full = full;
|
||||
}
|
||||
|
||||
/**
|
||||
* Grow capacity of this buffer to the given size (which will be
|
||||
* rounded up to the alignment needed).
|
||||
* This works only with internally memory-managed buffers.
|
||||
* If the given size is not larger than the current capacity,
|
||||
* nothing is done.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
*
|
||||
* @param size New capacity.
|
||||
*
|
||||
* @throws std::logic_error if the buffer doesn't use internal
|
||||
* memory management.
|
||||
* @throws std::bad_alloc if there isn't enough memory available.
|
||||
*/
|
||||
void grow(std::size_t size) {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
if (!m_memory) {
|
||||
throw std::logic_error{"Can't grow Buffer if it doesn't use internal memory management."};
|
||||
}
|
||||
size = calculate_capacity(size);
|
||||
if (m_capacity < size) {
|
||||
std::unique_ptr<unsigned char[]> memory(new unsigned char[size]);
|
||||
std::copy_n(m_memory.get(), m_capacity, memory.get());
|
||||
using std::swap;
|
||||
swap(m_memory, memory);
|
||||
m_data = m_memory.get();
|
||||
m_capacity = size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark currently written bytes in the buffer as committed.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
* @pre The buffer must be aligned properly (as indicated
|
||||
* by is_aligned().
|
||||
* @pre No builder can be open on this buffer.
|
||||
*
|
||||
* @returns Number of committed bytes before this commit. Can be
|
||||
* used as an offset into the buffer to get to the
|
||||
* object being committed by this call.
|
||||
*/
|
||||
std::size_t commit() {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope");
|
||||
assert(is_aligned());
|
||||
|
||||
const std::size_t offset = m_committed;
|
||||
m_committed = m_written;
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Roll back changes in buffer to last committed state.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
* @pre No builder can be open on this buffer.
|
||||
*/
|
||||
void rollback() {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope");
|
||||
m_written = m_committed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the buffer.
|
||||
*
|
||||
* No-op on an invalid buffer.
|
||||
*
|
||||
* @pre No builder can be open on this buffer.
|
||||
*
|
||||
* @returns Number of bytes in the buffer before it was cleared.
|
||||
*/
|
||||
std::size_t clear() {
|
||||
assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope");
|
||||
const std::size_t committed = m_committed;
|
||||
m_written = 0;
|
||||
m_committed = 0;
|
||||
return committed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data in the buffer at the given offset.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
*
|
||||
* @tparam T Type we want to the data to be interpreted as.
|
||||
*
|
||||
* @returns Reference of given type pointing to the data in the
|
||||
* buffer.
|
||||
*/
|
||||
template <typename T>
|
||||
T& get(const std::size_t offset) const {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return *reinterpret_cast<T*>(&m_data[offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserve space of given size in buffer and return pointer to it.
|
||||
* This is the only way of adding data to the buffer. You reserve
|
||||
* the space and then fill it.
|
||||
*
|
||||
* Note that you have to eventually call commit() to actually
|
||||
* commit this data.
|
||||
*
|
||||
* If there isn't enough space in the buffer, one of three things
|
||||
* can happen:
|
||||
*
|
||||
* * If you have set a callback with set_full_callback(), it is
|
||||
* called. After the call returns, you must have either grown
|
||||
* the buffer or cleared it by calling buffer.clear(). (Usage
|
||||
* of the full callback is deprecated and this functionality
|
||||
* will be removed in the future. See the documentation for
|
||||
* set_full_callback() for alternatives.
|
||||
* * If no callback is defined and this buffer uses internal
|
||||
* memory management, the buffers capacity is grown, so that
|
||||
* the new data will fit.
|
||||
* * Else the buffer_is_full exception is thrown.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
*
|
||||
* @param size Number of bytes to reserve.
|
||||
*
|
||||
* @returns Pointer to reserved space. Note that this pointer is
|
||||
* only guaranteed to be valid until the next call to
|
||||
* reserve_space().
|
||||
*
|
||||
* @throws osmium::buffer_is_full if the buffer is full there is
|
||||
* no callback defined and the buffer isn't auto-growing.
|
||||
*/
|
||||
unsigned char* reserve_space(const std::size_t size) {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
// try to flush the buffer empty first.
|
||||
if (m_written + size > m_capacity && m_full) {
|
||||
m_full(*this);
|
||||
}
|
||||
// if there's still not enough space, then try growing the buffer.
|
||||
if (m_written + size > m_capacity) {
|
||||
if (m_memory && (m_auto_grow == auto_grow::yes)) {
|
||||
// double buffer size until there is enough space
|
||||
std::size_t new_capacity = m_capacity * 2;
|
||||
while (m_written + size > new_capacity) {
|
||||
new_capacity *= 2;
|
||||
}
|
||||
grow(new_capacity);
|
||||
} else {
|
||||
throw osmium::buffer_is_full{};
|
||||
}
|
||||
}
|
||||
unsigned char* data = &m_data[m_written];
|
||||
m_written += size;
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item to the buffer. The size of the item is stored inside
|
||||
* the item, so we know how much memory to copy.
|
||||
*
|
||||
* Note that you have to eventually call commit() to actually
|
||||
* commit this data.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
*
|
||||
* @tparam T Class of the item to be copied.
|
||||
*
|
||||
* @param item Reference to the item to be copied.
|
||||
*
|
||||
* @returns Reference to newly copied data in the buffer.
|
||||
*/
|
||||
template <typename T>
|
||||
T& add_item(const T& item) {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
unsigned char* target = reserve_space(item.padded_size());
|
||||
std::copy_n(reinterpret_cast<const unsigned char*>(&item), item.padded_size(), target);
|
||||
return *reinterpret_cast<T*>(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add committed contents of the given buffer to this buffer.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
* @pre No builder can be open on this buffer.
|
||||
*
|
||||
* Note that you have to eventually call commit() to actually
|
||||
* commit this data.
|
||||
*
|
||||
* @param buffer The source of the copy. Must be valid.
|
||||
*/
|
||||
void add_buffer(const Buffer& buffer) {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
assert(buffer && "Buffer parameter must be a valid buffer");
|
||||
assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope");
|
||||
unsigned char* target = reserve_space(buffer.committed());
|
||||
std::copy_n(buffer.data(), buffer.committed(), target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an item to the buffer. This function is provided so that
|
||||
* you can use std::back_inserter.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
* @pre No builder can be open on this buffer.
|
||||
*
|
||||
* @param item The item to be added.
|
||||
*/
|
||||
void push_back(const osmium::memory::Item& item) {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
assert(m_builder_count == 0 && "Make sure there are no Builder objects still in scope");
|
||||
add_item(item);
|
||||
commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* An iterator that can be used to iterate over all items of
|
||||
* type T in a buffer.
|
||||
*/
|
||||
template <typename T>
|
||||
using t_iterator = osmium::memory::ItemIterator<T>;
|
||||
|
||||
/**
|
||||
* A const iterator that can be used to iterate over all items of
|
||||
* type T in a buffer.
|
||||
*/
|
||||
template <typename T>
|
||||
using t_const_iterator = osmium::memory::ItemIterator<const T>;
|
||||
|
||||
/**
|
||||
* An iterator that can be used to iterate over all OSMEntity
|
||||
* objects in a buffer.
|
||||
*/
|
||||
using iterator = t_iterator<osmium::OSMEntity>;
|
||||
|
||||
/**
|
||||
* A const iterator that can be used to iterate over all OSMEntity
|
||||
* objects in a buffer.
|
||||
*/
|
||||
using const_iterator = t_const_iterator<osmium::OSMEntity>;
|
||||
|
||||
template <typename T>
|
||||
ItemIteratorRange<T> select() {
|
||||
return ItemIteratorRange<T>{m_data, m_data + m_committed};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ItemIteratorRange<const T> select() const {
|
||||
return ItemIteratorRange<const T>{m_data, m_data + m_committed};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get iterator for iterating over all items of type T in the
|
||||
* buffer.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
*
|
||||
* @returns Iterator to first item of type T in the buffer.
|
||||
*/
|
||||
template <typename T>
|
||||
t_iterator<T> begin() {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return t_iterator<T>(m_data, m_data + m_committed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get iterator for iterating over all objects of class OSMEntity
|
||||
* in the buffer.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
*
|
||||
* @returns Iterator to first OSMEntity in the buffer.
|
||||
*/
|
||||
iterator begin() {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return iterator(m_data, m_data + m_committed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get iterator for iterating over all items of type T in the
|
||||
* buffer.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
*
|
||||
* @returns Iterator to first item of type T after given offset
|
||||
* in the buffer.
|
||||
*/
|
||||
template <typename T>
|
||||
t_iterator<T> get_iterator(std::size_t offset) {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return t_iterator<T>(m_data + offset, m_data + m_committed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get iterator for iterating over all objects of class OSMEntity
|
||||
* in the buffer.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
*
|
||||
* @returns Iterator to first OSMEntity after given offset in the
|
||||
* buffer.
|
||||
*/
|
||||
iterator get_iterator(std::size_t offset) {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return iterator(m_data + offset, m_data + m_committed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get iterator for iterating over all items of type T in the
|
||||
* buffer.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
*
|
||||
* @returns End iterator.
|
||||
*/
|
||||
template <typename T>
|
||||
t_iterator<T> end() {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return t_iterator<T>(m_data + m_committed, m_data + m_committed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get iterator for iterating over all objects of class OSMEntity
|
||||
* in the buffer.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
*
|
||||
* @returns End iterator.
|
||||
*/
|
||||
iterator end() {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return iterator(m_data + m_committed, m_data + m_committed);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
t_const_iterator<T> cbegin() const {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return t_const_iterator<T>(m_data, m_data + m_committed);
|
||||
}
|
||||
|
||||
const_iterator cbegin() const {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return const_iterator(m_data, m_data + m_committed);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
t_const_iterator<T> get_iterator(std::size_t offset) const {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return t_const_iterator<T>(m_data + offset, m_data + m_committed);
|
||||
}
|
||||
|
||||
const_iterator get_iterator(std::size_t offset) const {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return const_iterator(m_data + offset, m_data + m_committed);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
t_const_iterator<T> cend() const {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return t_const_iterator<T>(m_data + m_committed, m_data + m_committed);
|
||||
}
|
||||
|
||||
const_iterator cend() const {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
return const_iterator(m_data + m_committed, m_data + m_committed);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
t_const_iterator<T> begin() const {
|
||||
return cbegin<T>();
|
||||
}
|
||||
|
||||
const_iterator begin() const {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
t_const_iterator<T> end() const {
|
||||
return cend<T>();
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* In a bool context any valid buffer is true.
|
||||
*/
|
||||
explicit operator bool() const noexcept {
|
||||
return m_data != nullptr;
|
||||
}
|
||||
|
||||
void swap(Buffer& other) {
|
||||
using std::swap;
|
||||
|
||||
swap(m_memory, other.m_memory);
|
||||
swap(m_data, other.m_data);
|
||||
swap(m_capacity, other.m_capacity);
|
||||
swap(m_written, other.m_written);
|
||||
swap(m_committed, other.m_committed);
|
||||
swap(m_auto_grow, other.m_auto_grow);
|
||||
swap(m_full, other.m_full);
|
||||
}
|
||||
|
||||
/**
|
||||
* Purge removed items from the buffer. This is done by moving all
|
||||
* non-removed items forward in the buffer overwriting removed
|
||||
* items and then correcting the m_written and m_committed numbers.
|
||||
*
|
||||
* Note that calling this function invalidates all iterators on
|
||||
* this buffer and all offsets in this buffer.
|
||||
*
|
||||
* For every non-removed item that moves its position, the function
|
||||
* 'moving_in_buffer' is called on the given callback object with
|
||||
* the old and new offsets in the buffer where the object used to
|
||||
* be and is now, respectively. This call can be used to update any
|
||||
* indexes.
|
||||
*
|
||||
* @pre The buffer must be valid.
|
||||
*/
|
||||
template <typename TCallbackClass>
|
||||
void purge_removed(TCallbackClass* callback) {
|
||||
assert(m_data && "This must be a valid buffer");
|
||||
if (begin() == end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
iterator it_write = begin();
|
||||
|
||||
iterator next;
|
||||
for (iterator it_read = begin(); it_read != end(); it_read = next) {
|
||||
next = std::next(it_read);
|
||||
if (!it_read->removed()) {
|
||||
if (it_read != it_write) {
|
||||
assert(it_read.data() >= data());
|
||||
assert(it_write.data() >= data());
|
||||
const auto old_offset = static_cast<std::size_t>(it_read.data() - data());
|
||||
const auto new_offset = static_cast<std::size_t>(it_write.data() - data());
|
||||
callback->moving_in_buffer(old_offset, new_offset);
|
||||
std::memmove(it_write.data(), it_read.data(), it_read->padded_size());
|
||||
}
|
||||
it_write.advance_once();
|
||||
}
|
||||
}
|
||||
|
||||
assert(it_write.data() >= data());
|
||||
m_written = static_cast<std::size_t>(it_write.data() - data());
|
||||
m_committed = m_written;
|
||||
}
|
||||
|
||||
}; // class Buffer
|
||||
|
||||
inline void swap(Buffer& lhs, Buffer& rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two buffers for equality.
|
||||
*
|
||||
* Buffers are equal if they are both invalid or if they are both
|
||||
* valid and have the same data pointer, capacity and committed
|
||||
* data.
|
||||
*/
|
||||
inline bool operator==(const Buffer& lhs, const Buffer& rhs) noexcept {
|
||||
if (!lhs || !rhs) {
|
||||
return !lhs && !rhs;
|
||||
}
|
||||
return lhs.data() == rhs.data() && lhs.capacity() == rhs.capacity() && lhs.committed() == rhs.committed();
|
||||
}
|
||||
|
||||
inline bool operator!=(const Buffer& lhs, const Buffer& rhs) noexcept {
|
||||
return ! (lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace memory
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_MEMORY_BUFFER_HPP
|
||||
@@ -0,0 +1,189 @@
|
||||
#ifndef OSMIUM_MEMORY_CALLBACK_BUFFER_HPP
|
||||
#define OSMIUM_MEMORY_CALLBACK_BUFFER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 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 <cstddef>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace memory {
|
||||
|
||||
/**
|
||||
* This is basically a wrapper around osmium::memory::Buffer with an
|
||||
* additional callback function that is called whenever the buffer is
|
||||
* full.
|
||||
*
|
||||
* The internal buffer is created with the `initial_buffer_size` set
|
||||
* in the constructor. When it grows beyond the `max_buffer_size` set
|
||||
* in the constructor, the callback function is called with the buffer
|
||||
* and a new, empty buffer is created internally.
|
||||
*
|
||||
* Note that the buffer can grow beyond the initial buffer size if
|
||||
* needed. This can happen if a new object doesn't fit into the rest
|
||||
* of the buffer available or if no callback function is set (yet).
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* CallbackBuffer cb;
|
||||
* cb.set_callback([&](osmium::memory::Buffer&& buffer) {
|
||||
* ...handle buffer...
|
||||
* }
|
||||
* osmium::builder::add_node(cb.buffer(), _id(9), ...);
|
||||
* osmium::builder::add_way(cb.buffer(), _id(27), ...);
|
||||
* @endcode
|
||||
*/
|
||||
class CallbackBuffer {
|
||||
|
||||
public:
|
||||
|
||||
/// The type for the callback function
|
||||
using callback_func_type = std::function<void(osmium::memory::Buffer&&)>;
|
||||
|
||||
private:
|
||||
|
||||
static constexpr const std::size_t default_initial_buffer_size = 1024 * 1024;
|
||||
static constexpr const std::size_t default_max_buffer_size = 800 * 1024;
|
||||
|
||||
osmium::memory::Buffer m_buffer;
|
||||
std::size_t m_initial_buffer_size;
|
||||
std::size_t m_max_buffer_size;
|
||||
callback_func_type m_callback;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a CallbackBuffer without a callback function. You
|
||||
* can later call set the callback with set_callback().
|
||||
*
|
||||
* @param initial_buffer_size The initial size of newly created
|
||||
* internal buffers.
|
||||
* @param max_buffer_size If the buffer grows beyond this size the
|
||||
* callback will be called.
|
||||
*/
|
||||
explicit CallbackBuffer(std::size_t initial_buffer_size = default_initial_buffer_size, std::size_t max_buffer_size = default_max_buffer_size) :
|
||||
m_buffer(initial_buffer_size, osmium::memory::Buffer::auto_grow::yes),
|
||||
m_initial_buffer_size(initial_buffer_size),
|
||||
m_max_buffer_size(max_buffer_size),
|
||||
m_callback(nullptr) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a CallbackBuffer with a callback function.
|
||||
*
|
||||
* @param callback The callback function. Must be of type
|
||||
* @code void(osmium::memory::Buffer&&) @endcode
|
||||
* @param initial_buffer_size The initial size of newly created
|
||||
* internal buffers.
|
||||
* @param max_buffer_size If the buffer grows beyond this size the
|
||||
* callback will be called.
|
||||
*/
|
||||
explicit CallbackBuffer(const callback_func_type& callback, std::size_t initial_buffer_size = default_initial_buffer_size, std::size_t max_buffer_size = default_max_buffer_size) :
|
||||
m_buffer(initial_buffer_size, osmium::memory::Buffer::auto_grow::yes),
|
||||
m_initial_buffer_size(initial_buffer_size),
|
||||
m_max_buffer_size(max_buffer_size),
|
||||
m_callback(callback) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Access the internal buffer. This is used to fill the buffer,
|
||||
* the CallbackBuffer still owns the buffer.
|
||||
*
|
||||
* Use read() or the callback if you need to own the buffer.
|
||||
*/
|
||||
osmium::memory::Buffer& buffer() noexcept {
|
||||
return m_buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the callback. The function must take a rvalue reference to
|
||||
* a buffer and return void.
|
||||
*
|
||||
* @param callback The callback function. Must be of type
|
||||
* @code void(osmium::memory::Buffer&&) @endcode
|
||||
*/
|
||||
void set_callback(const callback_func_type& callback = nullptr) noexcept {
|
||||
m_callback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the internal buffer regardless of how full it is. Calls
|
||||
* the callback with the buffer and creates an new empty internal
|
||||
* one.
|
||||
*
|
||||
* This will do nothing if no callback is set or if the buffer
|
||||
* is empty.
|
||||
*/
|
||||
void flush() {
|
||||
if (m_callback && m_buffer.committed() > 0) {
|
||||
m_callback(read());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the internal buffer if and only if it contains more than
|
||||
* the max_buffer_size set in the constructor. Calls the callback
|
||||
* with the buffer and creates an new empty internal one.
|
||||
*
|
||||
* This will do nothing if no callback is set or if the buffer
|
||||
* is empty.
|
||||
*/
|
||||
void possibly_flush() {
|
||||
if (m_buffer.committed() > m_max_buffer_size) {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the internal buffer and create a new empty internal one.
|
||||
* You can use this as an alternative access instead of using the
|
||||
* callback.
|
||||
*/
|
||||
osmium::memory::Buffer read() {
|
||||
osmium::memory::Buffer buffer{m_initial_buffer_size, osmium::memory::Buffer::auto_grow::yes};
|
||||
using std::swap;
|
||||
swap(buffer, m_buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}; // class CallbackBuffer
|
||||
|
||||
} // namespace memory
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_MEMORY_CALLBACK_BUFFER_HPP
|
||||
@@ -0,0 +1,186 @@
|
||||
#ifndef OSMIUM_MEMORY_COLLECTION_HPP
|
||||
#define OSMIUM_MEMORY_COLLECTION_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 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 <iosfwd>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#include <osmium/memory/item.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace memory {
|
||||
|
||||
template <typename TMember>
|
||||
class CollectionIterator {
|
||||
|
||||
// This data_type is either 'unsigned char*' or 'const unsigned
|
||||
// char*' depending on whether TMember is const. This allows this
|
||||
// class to be used as an iterator and as a const_iterator.
|
||||
using data_type = typename std::conditional<std::is_const<TMember>::value, const unsigned char*, unsigned char*>::type;
|
||||
|
||||
data_type m_data;
|
||||
|
||||
public:
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = TMember;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
CollectionIterator() noexcept :
|
||||
m_data(nullptr) {
|
||||
}
|
||||
|
||||
explicit CollectionIterator(data_type data) noexcept :
|
||||
m_data(data) {
|
||||
}
|
||||
|
||||
CollectionIterator<TMember>& operator++() {
|
||||
m_data = reinterpret_cast<TMember*>(m_data)->next();
|
||||
return *static_cast<CollectionIterator<TMember>*>(this);
|
||||
}
|
||||
|
||||
CollectionIterator<TMember> operator++(int) {
|
||||
CollectionIterator<TMember> tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const CollectionIterator<TMember>& rhs) const noexcept {
|
||||
return m_data == rhs.m_data;
|
||||
}
|
||||
|
||||
bool operator!=(const CollectionIterator<TMember>& rhs) const noexcept {
|
||||
return m_data != rhs.m_data;
|
||||
}
|
||||
|
||||
unsigned char* data() const noexcept {
|
||||
return m_data;
|
||||
}
|
||||
|
||||
TMember& operator*() const noexcept {
|
||||
return *reinterpret_cast<TMember*>(m_data);
|
||||
}
|
||||
|
||||
TMember* operator->() const noexcept {
|
||||
return reinterpret_cast<TMember*>(m_data);
|
||||
}
|
||||
|
||||
template <typename TChar, typename TTraits>
|
||||
void print(std::basic_ostream<TChar, TTraits>& out) const {
|
||||
out << static_cast<const void*>(m_data);
|
||||
}
|
||||
|
||||
}; // class CollectionIterator
|
||||
|
||||
template <typename TChar, typename TTraits, typename TMember>
|
||||
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const CollectionIterator<TMember>& iter) {
|
||||
iter.print(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename TMember, osmium::item_type TCollectionItemType>
|
||||
class Collection : public Item {
|
||||
|
||||
public:
|
||||
|
||||
using value_type = TMember;
|
||||
using reference = TMember&;
|
||||
using const_reference = const TMember&;
|
||||
using iterator = CollectionIterator<TMember>;
|
||||
using const_iterator = CollectionIterator<const TMember>;
|
||||
using size_type = size_t;
|
||||
|
||||
static constexpr osmium::item_type itemtype = TCollectionItemType;
|
||||
|
||||
constexpr static bool is_compatible_to(osmium::item_type t) noexcept {
|
||||
return t == itemtype;
|
||||
}
|
||||
|
||||
Collection() :
|
||||
Item(sizeof(Collection<TMember, TCollectionItemType>), TCollectionItemType) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this collection contain any items?
|
||||
*
|
||||
* Complexity: Constant.
|
||||
*/
|
||||
bool empty() const noexcept {
|
||||
return sizeof(Collection<TMember, TCollectionItemType>) == byte_size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of items in this collection.
|
||||
*
|
||||
* Complexity: Linear in the number of items.
|
||||
*/
|
||||
size_type size() const noexcept {
|
||||
return static_cast<size_type>(std::distance(begin(), end()));
|
||||
}
|
||||
|
||||
iterator begin() noexcept {
|
||||
return iterator(data() + sizeof(Collection<TMember, TCollectionItemType>));
|
||||
}
|
||||
|
||||
iterator end() noexcept {
|
||||
return iterator(data() + byte_size());
|
||||
}
|
||||
|
||||
const_iterator cbegin() const noexcept {
|
||||
return const_iterator(data() + sizeof(Collection<TMember, TCollectionItemType>));
|
||||
}
|
||||
|
||||
const_iterator cend() const noexcept {
|
||||
return const_iterator(data() + byte_size());
|
||||
}
|
||||
|
||||
const_iterator begin() const noexcept {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
const_iterator end() const noexcept {
|
||||
return cend();
|
||||
}
|
||||
|
||||
}; // class Collection
|
||||
|
||||
} // namespace memory
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_MEMORY_COLLECTION_HPP
|
||||
@@ -0,0 +1,203 @@
|
||||
#ifndef OSMIUM_MEMORY_ITEM_HPP
|
||||
#define OSMIUM_MEMORY_ITEM_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 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 <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include <osmium/util/cast.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
// forward declaration, see osmium/osm/item_type.hpp for declaration
|
||||
enum class item_type : uint16_t;
|
||||
|
||||
namespace builder {
|
||||
class Builder;
|
||||
} // namespace builder
|
||||
|
||||
enum class diff_indicator_type {
|
||||
none = 0,
|
||||
left = 1,
|
||||
right = 2,
|
||||
both = 3
|
||||
}; // diff_indicator_type
|
||||
|
||||
namespace memory {
|
||||
|
||||
using item_size_type = uint32_t;
|
||||
|
||||
// align datastructures to this many bytes
|
||||
constexpr const std::size_t align_bytes = 8;
|
||||
|
||||
inline constexpr std::size_t padded_length(std::size_t length) noexcept {
|
||||
return (length + align_bytes - 1) & ~(align_bytes - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Namespace for Osmium internal use
|
||||
*/
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* This class contains only a helper method used in several
|
||||
* other classes.
|
||||
*/
|
||||
class ItemHelper {
|
||||
|
||||
protected:
|
||||
|
||||
ItemHelper() = default;
|
||||
|
||||
~ItemHelper() = default;
|
||||
|
||||
ItemHelper(const ItemHelper&) = default;
|
||||
ItemHelper(ItemHelper&&) = default;
|
||||
|
||||
ItemHelper& operator=(const ItemHelper&) = default;
|
||||
ItemHelper& operator=(ItemHelper&&) = default;
|
||||
|
||||
public:
|
||||
|
||||
unsigned char* data() noexcept {
|
||||
return reinterpret_cast<unsigned char*>(this);
|
||||
}
|
||||
|
||||
const unsigned char* data() const noexcept {
|
||||
return reinterpret_cast<const unsigned char*>(this);
|
||||
}
|
||||
|
||||
}; // class ItemHelper
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class Item : public osmium::memory::detail::ItemHelper {
|
||||
|
||||
item_size_type m_size;
|
||||
item_type m_type;
|
||||
uint16_t m_removed : 1;
|
||||
uint16_t m_diff : 2;
|
||||
uint16_t m_padding : 13;
|
||||
|
||||
template <typename TMember>
|
||||
friend class CollectionIterator;
|
||||
|
||||
template <typename TMember>
|
||||
friend class ItemIterator;
|
||||
|
||||
friend class osmium::builder::Builder;
|
||||
|
||||
Item& add_size(const item_size_type size) noexcept {
|
||||
m_size += size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
explicit Item(item_size_type size = 0, item_type type = item_type()) noexcept :
|
||||
m_size(size),
|
||||
m_type(type),
|
||||
m_removed(false),
|
||||
m_diff(0),
|
||||
m_padding(0) {
|
||||
}
|
||||
|
||||
Item(const Item&) = delete;
|
||||
Item(Item&&) = delete;
|
||||
|
||||
Item& operator=(const Item&) = delete;
|
||||
Item& operator=(Item&&) = delete;
|
||||
|
||||
Item& set_type(const item_type item_type) noexcept {
|
||||
m_type = item_type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
constexpr static bool is_compatible_to(osmium::item_type /*t*/) noexcept {
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char* next() noexcept {
|
||||
return data() + padded_size();
|
||||
}
|
||||
|
||||
const unsigned char* next() const noexcept {
|
||||
return data() + padded_size();
|
||||
}
|
||||
|
||||
item_size_type byte_size() const noexcept {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
item_size_type padded_size() const {
|
||||
return static_cast_with_assert<item_size_type>(padded_length(m_size));
|
||||
}
|
||||
|
||||
item_type type() const noexcept {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
bool removed() const noexcept {
|
||||
return m_removed;
|
||||
}
|
||||
|
||||
void set_removed(bool removed) noexcept {
|
||||
m_removed = removed;
|
||||
}
|
||||
|
||||
diff_indicator_type diff() const noexcept {
|
||||
return diff_indicator_type(m_diff);
|
||||
}
|
||||
|
||||
char diff_as_char() const noexcept {
|
||||
static constexpr const char* diff_chars = "*-+ ";
|
||||
return diff_chars[m_diff];
|
||||
}
|
||||
|
||||
void set_diff(diff_indicator_type diff) noexcept {
|
||||
m_diff = uint16_t(diff);
|
||||
}
|
||||
|
||||
}; // class Item
|
||||
|
||||
static_assert(sizeof(Item) == 8, "Class osmium::Item has wrong size!");
|
||||
static_assert(sizeof(Item) % align_bytes == 0, "Class osmium::Item has wrong size to be aligned properly!");
|
||||
|
||||
} // namespace memory
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_MEMORY_ITEM_HPP
|
||||
@@ -0,0 +1,250 @@
|
||||
#ifndef OSMIUM_MEMORY_ITEM_ITERATOR_HPP
|
||||
#define OSMIUM_MEMORY_ITEM_ITERATOR_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2017 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 <cassert>
|
||||
#include <cstddef>
|
||||
#include <iosfwd>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#include <osmium/memory/item.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace memory {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
constexpr inline bool type_is_compatible(osmium::item_type t) noexcept {
|
||||
return T::is_compatible_to(t);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename TMember>
|
||||
class ItemIterator {
|
||||
|
||||
static_assert(std::is_base_of<osmium::memory::Item, TMember>::value, "TMember must derive from osmium::memory::Item");
|
||||
|
||||
// This data_type is either 'unsigned char*' or 'const unsigned char*' depending
|
||||
// on whether TMember is const. This allows this class to be used as an iterator and
|
||||
// as a const_iterator.
|
||||
using data_type = typename std::conditional<std::is_const<TMember>::value, const unsigned char*, unsigned char*>::type;
|
||||
|
||||
data_type m_data;
|
||||
data_type m_end;
|
||||
|
||||
void advance_to_next_item_of_right_type() noexcept {
|
||||
while (m_data != m_end &&
|
||||
!detail::type_is_compatible<TMember>(reinterpret_cast<const osmium::memory::Item*>(m_data)->type())) {
|
||||
m_data = reinterpret_cast<TMember*>(m_data)->next();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = TMember;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = value_type*;
|
||||
using reference = value_type&;
|
||||
|
||||
ItemIterator() noexcept :
|
||||
m_data(nullptr),
|
||||
m_end(nullptr) {
|
||||
}
|
||||
|
||||
ItemIterator(data_type data, data_type end) noexcept :
|
||||
m_data(data),
|
||||
m_end(end) {
|
||||
advance_to_next_item_of_right_type();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ItemIterator<T> cast() const noexcept {
|
||||
return ItemIterator<T>(m_data, m_end);
|
||||
}
|
||||
|
||||
ItemIterator<TMember>& operator++() noexcept {
|
||||
assert(m_data);
|
||||
assert(m_data != m_end);
|
||||
m_data = reinterpret_cast<TMember*>(m_data)->next();
|
||||
advance_to_next_item_of_right_type();
|
||||
return *static_cast<ItemIterator<TMember>*>(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like operator++() but will NOT skip items of unwanted
|
||||
* types. Do not use this unless you know what you are
|
||||
* doing.
|
||||
*/
|
||||
ItemIterator<TMember>& advance_once() noexcept {
|
||||
assert(m_data);
|
||||
assert(m_data != m_end);
|
||||
m_data = reinterpret_cast<TMember*>(m_data)->next();
|
||||
return *static_cast<ItemIterator<TMember>*>(this);
|
||||
}
|
||||
|
||||
ItemIterator<TMember> operator++(int) noexcept {
|
||||
ItemIterator<TMember> tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const ItemIterator<TMember>& rhs) const noexcept {
|
||||
return m_data == rhs.m_data && m_end == rhs.m_end;
|
||||
}
|
||||
|
||||
bool operator!=(const ItemIterator<TMember>& rhs) const noexcept {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
data_type data() noexcept {
|
||||
assert(m_data);
|
||||
return m_data;
|
||||
}
|
||||
|
||||
const unsigned char* data() const noexcept {
|
||||
assert(m_data);
|
||||
return m_data;
|
||||
}
|
||||
|
||||
TMember& operator*() const noexcept {
|
||||
assert(m_data);
|
||||
assert(m_data != m_end);
|
||||
return *reinterpret_cast<TMember*>(m_data);
|
||||
}
|
||||
|
||||
TMember* operator->() const noexcept {
|
||||
assert(m_data);
|
||||
assert(m_data != m_end);
|
||||
return reinterpret_cast<TMember*>(m_data);
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept {
|
||||
return (m_data != nullptr) && (m_data != m_end);
|
||||
}
|
||||
|
||||
template <typename TChar, typename TTraits>
|
||||
void print(std::basic_ostream<TChar, TTraits>& out) const {
|
||||
out << static_cast<const void*>(m_data);
|
||||
}
|
||||
|
||||
}; // class ItemIterator
|
||||
|
||||
template <typename TChar, typename TTraits, typename TMember>
|
||||
inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const ItemIterator<TMember>& iter) {
|
||||
iter.print(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class ItemIteratorRange {
|
||||
|
||||
static_assert(std::is_base_of<osmium::memory::Item, T>::value, "Template parameter must derive from osmium::memory::Item");
|
||||
|
||||
// This data_type is either 'unsigned char*' or
|
||||
// 'const unsigned char*' depending on whether T is const.
|
||||
using data_type = typename std::conditional<std::is_const<T>::value, const unsigned char*, unsigned char*>::type;
|
||||
|
||||
data_type m_begin;
|
||||
data_type m_end;
|
||||
|
||||
public:
|
||||
|
||||
using iterator = ItemIterator<T>;
|
||||
using const_iterator = ItemIterator<const T>;
|
||||
|
||||
ItemIteratorRange(data_type first, data_type last) noexcept :
|
||||
m_begin(first),
|
||||
m_end(last) {
|
||||
}
|
||||
|
||||
iterator begin() noexcept {
|
||||
return iterator{m_begin, m_end};
|
||||
}
|
||||
|
||||
iterator end() noexcept {
|
||||
return iterator{m_end, m_end};
|
||||
}
|
||||
|
||||
const_iterator cbegin() const noexcept {
|
||||
return const_iterator{m_begin, m_end};
|
||||
}
|
||||
|
||||
const_iterator cend() const noexcept {
|
||||
return const_iterator{m_end, m_end};
|
||||
}
|
||||
|
||||
const_iterator begin() const noexcept {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
const_iterator end() const noexcept {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of items in this range.
|
||||
*
|
||||
* Note that this methods has worst-case complexity O(n) with n
|
||||
* being the number of items in the underlying range.
|
||||
*/
|
||||
size_t size() const {
|
||||
if (m_begin == m_end) {
|
||||
return 0;
|
||||
}
|
||||
return std::distance(cbegin(), cend());
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this range empty?
|
||||
*
|
||||
* Note that this methods has worst-case complexity O(n) with n
|
||||
* being the number of items in the underlying range.
|
||||
*/
|
||||
bool empty() const {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
}; // class ItemIteratorRange
|
||||
|
||||
} // namespace memory
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_MEMORY_ITEM_ITERATOR_HPP
|
||||
Reference in New Issue
Block a user