This is an automated email from the git hooks/post-receive script. sebastic pushed a commit to branch master in repository libosmium.
commit eec33478f11b5535dcf3587595aa1e3ea2b8bb02 Author: Bas Couwenberg <sebas...@xs4all.nl> Date: Wed Nov 18 22:36:47 2015 +0100 Imported Upstream version 2.5.3 --- CHANGELOG.md | 21 ++- CMakeLists.txt | 2 +- include/osmium/area/multipolygon_collector.hpp | 22 --- include/osmium/diff_handler.hpp | 3 +- include/osmium/diff_iterator.hpp | 35 ++-- include/osmium/diff_visitor.hpp | 2 +- include/osmium/io/output_iterator.hpp | 13 +- include/osmium/memory/buffer.hpp | 231 +++++++++++++++++++------ include/osmium/memory/item_iterator.hpp | 16 +- include/osmium/osm/diff_object.hpp | 140 ++++++++++++--- include/osmium/osm/item_type.hpp | 8 + include/osmium/osm/node_ref.hpp | 74 +++++++- include/osmium/osm/node_ref_list.hpp | 29 ++-- include/osmium/osm/timestamp.hpp | 66 +++++-- include/osmium/osm/types_from_string.hpp | 67 +++++++ include/osmium/relations/collector.hpp | 159 +++++++++-------- include/osmium/util/data_file.hpp | 194 --------------------- include/osmium/util/memory_mapping.hpp | 25 ++- include/osmium/util/options.hpp | 66 +++++-- include/osmium/visitor.hpp | 7 +- test/CMakeLists.txt | 1 - test/t/basic/test_object_comparisons.cpp | 44 ++--- test/t/basic/test_timestamp.cpp | 17 +- test/t/buffer/test_buffer_node.cpp | 44 ++++- test/t/util/test_data_file.cpp | 81 --------- test/t/util/test_options.cpp | 30 +++- 26 files changed, 813 insertions(+), 584 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 781a04a..70d77bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,24 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +## [2.5.3] - 2015-11-17 + +### Added + +- osmium::make_diff_iterator() helper function. + +### Changed + +- Deprecated osmium::Buffer::set_full_callback(). +- Removed DataFile class which was never used anywhere. +- Removed unused and obscure Buffer::value_type typedef. + +### Fixed + +- Possible overrun in Buffer when using the full-callback. +- Incorrect swapping of Buffer. + + ## [2.5.2] - 2015-11-06 # Fixed @@ -196,7 +214,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). Doxygen (up to version 1.8.8). This version contains a workaround to fix this. -[unreleased]: https://github.com/osmcode/libosmium/compare/v2.5.2...HEAD +[unreleased]: https://github.com/osmcode/libosmium/compare/v2.5.3...HEAD +[2.5.3]: https://github.com/osmcode/libosmium/compare/v2.5.1...v2.5.3 [2.5.2]: https://github.com/osmcode/libosmium/compare/v2.5.1...v2.5.2 [2.5.1]: https://github.com/osmcode/libosmium/compare/v2.5.0...v2.5.1 [2.5.0]: https://github.com/osmcode/libosmium/compare/v2.4.1...v2.5.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index f28e2aa..fc3e97e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ project(libosmium) set(LIBOSMIUM_VERSION_MAJOR 2) set(LIBOSMIUM_VERSION_MINOR 5) -set(LIBOSMIUM_VERSION_PATCH 2) +set(LIBOSMIUM_VERSION_PATCH 3) set(LIBOSMIUM_VERSION "${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}") diff --git a/include/osmium/area/multipolygon_collector.hpp b/include/osmium/area/multipolygon_collector.hpp index c4155db..d3ca10f 100644 --- a/include/osmium/area/multipolygon_collector.hpp +++ b/include/osmium/area/multipolygon_collector.hpp @@ -177,28 +177,6 @@ namespace osmium { } catch (osmium::invalid_location&) { // XXX ignore } - - // clear member metas - for (const auto& member : relation.members()) { - if (member.ref() != 0) { - auto& mmv = this->member_meta(member.type()); - auto range = std::equal_range(mmv.begin(), mmv.end(), osmium::relations::MemberMeta(member.ref())); - assert(range.first != range.second); - - // if this is the last time this object was needed - // then mark it as removed - if (osmium::relations::count_not_removed(range.first, range.second) == 1) { - this->get_member(range.first->buffer_offset()).set_removed(true); - } - - for (auto it = range.first; it != range.second; ++it) { - if (!it->removed() && relation.id() == this->get_relation(it->relation_pos()).id()) { - it->remove(); - break; - } - } - } - } } void flush() { diff --git a/include/osmium/diff_handler.hpp b/include/osmium/diff_handler.hpp index 4f9b3a1..06d8a93 100644 --- a/include/osmium/diff_handler.hpp +++ b/include/osmium/diff_handler.hpp @@ -46,8 +46,7 @@ namespace osmium { public: - DiffHandler() { - } + DiffHandler() = default; void node(const osmium::DiffNode&) const { } diff --git a/include/osmium/diff_iterator.hpp b/include/osmium/diff_iterator.hpp index ae80afb..6725fec 100644 --- a/include/osmium/diff_iterator.hpp +++ b/include/osmium/diff_iterator.hpp @@ -43,6 +43,11 @@ namespace osmium { class OSMObject; + /** + * An input iterator wrapping any iterator over OSMObjects. When + * dereferenced it will yield DiffObject objects pointing to the + * underlying OSMObjects. + */ template <typename TBasicIterator> class DiffIterator : public std::iterator<std::input_iterator_tag, const osmium::DiffObject> { @@ -56,7 +61,7 @@ namespace osmium { mutable osmium::DiffObject m_diff; - void set_diff() const { + void set_diff() const noexcept { assert(m_curr != m_end); bool use_curr_for_prev = m_prev->type() != m_curr->type() || m_prev->id() != m_curr->id(); @@ -71,19 +76,14 @@ namespace osmium { public: - explicit DiffIterator(TBasicIterator begin, TBasicIterator end) : + DiffIterator(TBasicIterator begin, TBasicIterator end) : m_prev(begin), m_curr(begin), m_next(begin == end ? begin : ++begin), - m_end(std::move(end)) { + m_end(std::move(end)), + m_diff() { } - DiffIterator(const DiffIterator&) = default; - DiffIterator& operator=(const DiffIterator&) = default; - - DiffIterator(DiffIterator&&) = default; - DiffIterator& operator=(DiffIterator&&) = default; - DiffIterator& operator++() { m_prev = std::move(m_curr); m_curr = m_next; @@ -101,26 +101,35 @@ namespace osmium { return tmp; } - bool operator==(const DiffIterator& rhs) const { + bool operator==(const DiffIterator& rhs) const noexcept { return m_curr == rhs.m_curr && m_end == rhs.m_end; } - bool operator!=(const DiffIterator& rhs) const { + bool operator!=(const DiffIterator& rhs) const noexcept { return !(*this == rhs); } - reference operator*() const { + reference operator*() const noexcept { set_diff(); return m_diff; } - pointer operator->() const { + pointer operator->() const noexcept { set_diff(); return &m_diff; } }; // class DiffIterator + /** + * Create a DiffIterator based on the given iterators. + */ + template <typename TBasicIterator> + inline DiffIterator<TBasicIterator> make_diff_iterator(TBasicIterator begin, + TBasicIterator end) { + return DiffIterator<TBasicIterator>{begin, end}; + } + } // namespace osmium #endif // OSMIUM_DIFF_ITERATOR_HPP diff --git a/include/osmium/diff_visitor.hpp b/include/osmium/diff_visitor.hpp index e7dc576..ac16a8e 100644 --- a/include/osmium/diff_visitor.hpp +++ b/include/osmium/diff_visitor.hpp @@ -70,7 +70,7 @@ namespace osmium { template <typename TIterator, typename... THandlers> inline void apply_diff(TIterator it, TIterator end, THandlers&... handlers) { - typedef osmium::DiffIterator<TIterator> diff_iterator; + using diff_iterator = osmium::DiffIterator<TIterator>; diff_iterator dit(it, end); diff_iterator dend(end, end); diff --git a/include/osmium/io/output_iterator.hpp b/include/osmium/io/output_iterator.hpp index 3d60fe6..1cf1d1d 100644 --- a/include/osmium/io/output_iterator.hpp +++ b/include/osmium/io/output_iterator.hpp @@ -62,7 +62,8 @@ namespace osmium { } /** - * Warning! Use of buffer size argument on OutputIterator + * @deprecated + * Use of buffer size argument on OutputIterator * constructor is deprecated. Call Writer::set_buffer_size() * instead if you want to change the default. */ @@ -80,7 +81,8 @@ namespace osmium { ~OutputIterator() = default; /** - * Warning! Calling OutputIterator<Writer>::flush() is usually not + * @deprecated + * Calling OutputIterator<Writer>::flush() is usually not * needed any more. Call flush() on the Writer instead if needed. */ OSMIUM_DEPRECATED void flush() { @@ -116,9 +118,10 @@ namespace osmium { } /** - * Warning! Use of buffer size argument on make_output_iterator is - * deprecated. Call Writer::set_buffer_size() instead if you want to - * change the default. + * @deprecated + * Use of buffer size argument on make_output_iterator is deprecated. + * Call Writer::set_buffer_size() instead if you want to change the + * default. */ template <typename TDest> OSMIUM_DEPRECATED OutputIterator<TDest> make_output_iterator(TDest& destination, const size_t buffer_size) { diff --git a/include/osmium/memory/buffer.hpp b/include/osmium/memory/buffer.hpp index 11a4c97..ce8c587 100644 --- a/include/osmium/memory/buffer.hpp +++ b/include/osmium/memory/buffer.hpp @@ -46,6 +46,7 @@ DEALINGS IN THE SOFTWARE. #include <osmium/memory/item.hpp> #include <osmium/memory/item_iterator.hpp> #include <osmium/osm/entity.hpp> +#include <osmium/util/compatibility.hpp> namespace osmium { @@ -89,7 +90,9 @@ namespace osmium { * * 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. + * 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 { @@ -112,12 +115,13 @@ namespace osmium { public: - typedef Item value_type; - /** - * The constructor without any parameters creates a non-initialized + * 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(), @@ -128,12 +132,14 @@ namespace osmium { } /** - * Constructs an externally memory-managed buffer using the given - * memory and size. + * 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 When the size isn't a multiple of the alignment. + * + * @throws std::invalid_argument if the size isn't a multiple of + * the alignment. */ explicit Buffer(unsigned char* data, size_t size) : m_memory(), @@ -147,13 +153,15 @@ namespace osmium { } /** - * Constructs an externally memory-managed buffer with the given - * capacity that already contains 'committed' bytes of data. + * 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 When the capacity or committed isn't a multiple of the alignment. + * + * @throws std::invalid_argument if the capacity or committed isn't + * a multiple of the alignment. */ explicit Buffer(unsigned char* data, size_t capacity, size_t committed) : m_memory(), @@ -170,10 +178,18 @@ namespace osmium { } /** - * Create an internally memory-managed buffer with the given capacity. - * different in that it internally gets dynamic memory of the - * required size. The dynamic memory will be automatically - * freed when the Buffer is destroyed. + * 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. + * @param auto_grow Should this buffer automatically grow when it + * becomes to small? + * + * @throws std::invalid_argument if the capacity isn't a multiple + * of the alignment. */ explicit Buffer(size_t capacity, auto_grow auto_grow = auto_grow::yes) : m_memory(capacity), @@ -199,6 +215,8 @@ namespace osmium { /** * Return a pointer to data inside the buffer. + * + * @pre The buffer must be valid. */ unsigned char* data() const noexcept { assert(m_data); @@ -206,8 +224,8 @@ namespace osmium { } /** - * Returns the capacity of the buffer, ie how many bytes it can contain. - * Always returns 0 on invalid buffers. + * Returns the capacity of the buffer, ie how many bytes it can + * contain. Always returns 0 on invalid buffers. */ size_t capacity() const noexcept { return m_capacity; @@ -234,8 +252,7 @@ namespace osmium { * This tests if the current state of the buffer is aligned * properly. Can be used for asserts. * - * The behaviour is undefined if you call this on an invalid - * buffer. + * @pre The buffer must be valid. */ bool is_aligned() const noexcept { assert(m_data); @@ -248,8 +265,21 @@ namespace osmium { * * 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 + * that the used size of the buffer (committed()) is small enough + * compared to the capacity (for instance small than 90% of the + * capacity) before adding anything to the Buffer. If the buffer + * is initialized with auto_grow::yes, it will still grow in the + * rare case that a very large object will be added taking more + * than the difference between committed() and capacity(). */ - void set_full_callback(std::function<void(Buffer&)> full) { + OSMIUM_DEPRECATED void set_full_callback(std::function<void(Buffer&)> full) { assert(m_data); m_full = full; } @@ -257,13 +287,18 @@ namespace osmium { /** * Grow capacity of this buffer to the given size. * This works only with internally memory-managed buffers. - * If the given size is not larger than the current capacity, nothing is done. + * If the given size is not larger than the current capacity, + * nothing is done. * Already written but not committed data is discarded. * - * The behaviour is undefined if you call this on an invalid - * buffer. + * @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::invalid_argument if the size isn't a multiple + * of the alignment. */ void grow(size_t size) { assert(m_data); @@ -283,10 +318,12 @@ namespace osmium { /** * Mark currently written bytes in the buffer as committed. * - * The behaviour is undefined if you call this on an invalid - * buffer. + * @pre The buffer must be valid and aligned properly (as indicated + * by is_aligned(). * - * @returns Last number of committed bytes before this commit. + * @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. */ size_t commit() { assert(m_data); @@ -300,8 +337,7 @@ namespace osmium { /** * Roll back changes in buffer to last committed state. * - * The behaviour is undefined if you call this on an invalid - * buffer. + * @pre The buffer must be valid. */ void rollback() { assert(m_data); @@ -325,11 +361,12 @@ namespace osmium { /** * Get the data in the buffer at the given offset. * - * The behaviour is undefined if you call this on an invalid - * buffer. + * @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. + * + * @returns Reference of given type pointing to the data in the + * buffer. */ template <typename T> T& get(const size_t offset) const { @@ -350,27 +387,35 @@ namespace osmium { * * * 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(). + * 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. * - * The behaviour is undefined if you call this on an invalid - * buffer. + * @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 Might be thrown if the buffer is full. + * 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 size_t size) { assert(m_data); + // 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_full) { - m_full(*this); - } else if (!m_memory.empty() && (m_auto_grow == auto_grow::yes)) { + if (!m_memory.empty() && (m_auto_grow == auto_grow::yes)) { // double buffer size until there is enough space size_t new_capacity = m_capacity * 2; while (m_written + size > new_capacity) { @@ -393,11 +438,12 @@ namespace osmium { * Note that you have to eventually call commit() to actually * commit this data. * - * The behaviour is undefined if you call this on an invalid - * buffer. + * @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> @@ -411,24 +457,26 @@ namespace osmium { /** * Add committed contents of the given buffer to this buffer. * - * The behaviour is undefined if you call this on an invalid - * buffer. + * @pre The buffer must be valid. * * 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); + assert(m_data && buffer); unsigned char* target = reserve_space(buffer.committed()); - std::copy_n(reinterpret_cast<const unsigned char*>(buffer.data()), buffer.committed(), target); + 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. * - * The behaviour is undefined if you call this on an invalid - * buffer. + * @pre The buffer must be valid. + * + * @param item The item to be added. */ void push_back(const osmium::memory::Item& item) { assert(m_data); @@ -437,46 +485,109 @@ namespace osmium { } /** - * These iterators can be used to iterate over all items in - * a buffer. + * 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>; - typedef t_iterator<osmium::OSMEntity> iterator; - typedef t_const_iterator<osmium::OSMEntity> const_iterator; + /** + * 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>; + /** + * 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); 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); 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(size_t offset) { assert(m_data); 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(size_t offset) { assert(m_data); 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); 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); return iterator(m_data + m_committed, m_data + m_committed); @@ -534,7 +645,7 @@ namespace osmium { } /** - * In a bool context any initialized buffer is true. + * In a bool context any valid buffer is true. */ explicit operator bool() const { return m_data != nullptr; @@ -548,6 +659,8 @@ namespace osmium { swap(lhs.m_capacity, rhs.m_capacity); swap(lhs.m_written, rhs.m_written); swap(lhs.m_committed, rhs.m_committed); + swap(lhs.m_auto_grow, rhs.m_auto_grow); + swap(lhs.m_full, rhs.m_full); } /** @@ -555,8 +668,8 @@ namespace osmium { * 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. + * 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 @@ -564,8 +677,7 @@ namespace osmium { * be and is now, respectively. This call can be used to update any * indexes. * - * The behaviour is undefined if you call this on an invalid - * buffer. + * @pre The buffer must be valid. */ template <typename TCallbackClass> void purge_removed(TCallbackClass* callback) { @@ -599,6 +711,13 @@ namespace osmium { }; // class Buffer + /** + * 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; diff --git a/include/osmium/memory/item_iterator.hpp b/include/osmium/memory/item_iterator.hpp index c6b4205..5751698 100644 --- a/include/osmium/memory/item_iterator.hpp +++ b/include/osmium/memory/item_iterator.hpp @@ -38,24 +38,12 @@ DEALINGS IN THE SOFTWARE. #include <iosfwd> #include <type_traits> +#include <osmium/fwd.hpp> #include <osmium/memory/item.hpp> #include <osmium/osm/item_type.hpp> namespace osmium { - class Node; - class Way; - class Relation; - class Area; - class Changeset; - class OSMObject; - class OSMEntity; - class TagList; - class WayNodeList; - class RelationMemberList; - class InnerRing; - class OuterRing; - namespace memory { namespace detail { @@ -217,7 +205,7 @@ namespace osmium { } explicit operator bool() const { - return m_data != nullptr; + return bool(m_data) && (m_data != m_end); } template <typename TChar, typename TTraits> diff --git a/include/osmium/osm/diff_object.hpp b/include/osmium/osm/diff_object.hpp index 96e07bc..d9872ea 100644 --- a/include/osmium/osm/diff_object.hpp +++ b/include/osmium/osm/diff_object.hpp @@ -33,6 +33,8 @@ DEALINGS IN THE SOFTWARE. */ +#include <cassert> + #include <osmium/fwd.hpp> #include <osmium/osm/item_type.hpp> #include <osmium/osm/object.hpp> @@ -41,71 +43,158 @@ DEALINGS IN THE SOFTWARE. namespace osmium { + /** + * A DiffObject holds pointers to three OSMObjects, the current object, + * the previous, and the next. They always have the same type (Node, Way, + * or Relation) and the same ID, but may have different versions. + * + * It is used when iterating over OSM files with history data to make + * working with versioned OSM objects easier. Because you have access to + * the previous and next objects as well as the current one, comparisons + * between object versions is easy. + * + * If the current object is the first version available, the previous + * pointer must be the same as the current one. If the current object is + * the last version available, the next pointer must be the same as the + * current one. + * + * DiffObjects are immutable. + */ class DiffObject { - protected: - - osmium::OSMObject* m_prev; - osmium::OSMObject* m_curr; - osmium::OSMObject* m_next; + const osmium::OSMObject* m_prev; + const osmium::OSMObject* m_curr; + const osmium::OSMObject* m_next; public: + /** + * Default construct an empty DiffObject. Most methods of this class + * can not be called on empty DiffObjects. + */ DiffObject() noexcept : m_prev(nullptr), m_curr(nullptr), m_next(nullptr) { } - explicit DiffObject(osmium::OSMObject& prev, osmium::OSMObject& curr, osmium::OSMObject& next) noexcept : + /** + * Construct a non-empty DiffObject from the given OSMObjects. All + * OSMObjects must be of the same type (Node, Way, or Relation) and + * have the same ID. + */ + DiffObject(const osmium::OSMObject& prev, const osmium::OSMObject& curr, const osmium::OSMObject& next) noexcept : m_prev(&prev), m_curr(&curr), m_next(&next) { + assert(prev.type() == curr.type() && curr.type() == next.type()); + assert(prev.id() == curr.id() && curr.id() == next.id()); } - DiffObject(const DiffObject&) = default; - DiffObject& operator=(const DiffObject&) = default; - - DiffObject(DiffObject&&) = default; - DiffObject& operator=(DiffObject&&) = default; + /** + * Check whether the DiffObject was created empty. + */ + bool empty() const noexcept { + return m_prev == nullptr; + } + /** + * Get the previous object stored. + * + * @pre DiffObject must not be empty. + */ const osmium::OSMObject& prev() const noexcept { + assert(m_prev && m_curr && m_next); return *m_prev; } + /** + * Get the current object stored. + * + * @pre DiffObject must not be empty. + */ const osmium::OSMObject& curr() const noexcept { + assert(m_prev && m_curr && m_next); return *m_curr; } + /** + * Get the next object stored. + * + * @pre DiffObject must not be empty. + */ const osmium::OSMObject& next() const noexcept { + assert(m_prev && m_curr && m_next); return *m_next; } + /** + * Is the current object version the first (with this type and ID)? + * + * @pre DiffObject must not be empty. + */ bool first() const noexcept { + assert(m_prev && m_curr && m_next); return m_prev == m_curr; } + /** + * Is the current object version the last (with this type and ID)? + * + * @pre DiffObject must not be empty. + */ bool last() const noexcept { + assert(m_prev && m_curr && m_next); return m_curr == m_next; } + /** + * Return the type of the current object. + * + * @pre DiffObject must not be empty. + */ osmium::item_type type() const noexcept { + assert(m_prev && m_curr && m_next); return m_curr->type(); } + /** + * Return the ID of the current object. + * + * @pre DiffObject must not be empty. + */ osmium::object_id_type id() const noexcept { + assert(m_prev && m_curr && m_next); return m_curr->id(); } + /** + * Return the version of the current object. + * + * @pre DiffObject must not be empty. + */ osmium::object_version_type version() const noexcept { + assert(m_prev && m_curr && m_next); return m_curr->version(); } + /** + * Return the changeset ID of the current object. + * + * @pre DiffObject must not be empty. + */ osmium::changeset_id_type changeset() const noexcept { + assert(m_prev && m_curr && m_next); return m_curr->changeset(); } + /** + * Return the timestamp when the current object version was created. + * + * @pre DiffObject must not be empty. + */ const osmium::Timestamp start_time() const noexcept { + assert(m_prev && m_curr && m_next); return m_curr->timestamp(); } @@ -115,8 +204,11 @@ namespace osmium { * is valid. If this is the last version of the object, this will * return a special "end of time" timestamp that is guaranteed to * be larger than any normal timestamp. + * + * @pre DiffObject must not be empty. */ const osmium::Timestamp end_time() const noexcept { + assert(m_prev && m_curr && m_next); return last() ? osmium::end_of_time() : m_next->timestamp(); } @@ -126,8 +218,11 @@ namespace osmium { * * This is a bit more complex than you'd think, because we have to * handle the case properly where the start_time() == end_time(). + * + * @pre DiffObject must not be empty. */ bool is_between(const osmium::Timestamp& from, const osmium::Timestamp& to) const noexcept { + assert(m_prev && m_curr && m_next); return start_time() < to && ((start_time() != end_time() && end_time() > from) || (start_time() == end_time() && end_time() >= from)); @@ -135,8 +230,11 @@ namespace osmium { /** * Current object version is visible at the given timestamp. + * + * @pre DiffObject must not be empty. */ bool is_visible_at(const osmium::Timestamp& timestamp) const noexcept { + assert(m_prev && m_curr && m_next); return start_time() <= timestamp && end_time() > timestamp && m_curr->visible(); } @@ -147,33 +245,27 @@ namespace osmium { public: - DiffObjectDerived(T& prev, T& curr, T& next) noexcept : + DiffObjectDerived(const T& prev, const T& curr, const T& next) noexcept : DiffObject(prev, curr, next) { } - DiffObjectDerived(const DiffObjectDerived&) = default; - DiffObjectDerived& operator=(const DiffObjectDerived&) = default; - - DiffObjectDerived(DiffObjectDerived&&) = default; - DiffObjectDerived& operator=(DiffObjectDerived&&) = default; - const T& prev() const noexcept { - return *static_cast<const T*>(m_prev); + return static_cast<const T&>(DiffObject::prev()); } const T& curr() const noexcept { - return *static_cast<const T*>(m_curr); + return static_cast<const T&>(DiffObject::curr()); } const T& next() const noexcept { - return *static_cast<const T*>(m_next); + return static_cast<const T&>(DiffObject::next()); } }; // class DiffObjectDerived - typedef DiffObjectDerived<osmium::Node> DiffNode; - typedef DiffObjectDerived<osmium::Way> DiffWay; - typedef DiffObjectDerived<osmium::Relation> DiffRelation; + using DiffNode = DiffObjectDerived<osmium::Node>; + using DiffWay = DiffObjectDerived<osmium::Way>; + using DiffRelation = DiffObjectDerived<osmium::Relation>; } // namespace osmium diff --git a/include/osmium/osm/item_type.hpp b/include/osmium/osm/item_type.hpp index d8f5296..95826bc 100644 --- a/include/osmium/osm/item_type.hpp +++ b/include/osmium/osm/item_type.hpp @@ -61,6 +61,10 @@ namespace osmium { /** * Return item_type for index: * 0 -> node, 1 -> way, 2 -> relation + * + * @param i Index. Must be between 0 and 2. + * + * @returns Item type. */ inline item_type nwr_index_to_item_type(unsigned int i) noexcept { assert(i <= 2); @@ -70,6 +74,10 @@ namespace osmium { /** * Return index for item_type: * node -> 0, way -> 1, relation -> 2 + * + * @param type Item type. Must be node, way, or relation. + * + * @returns Index. */ inline unsigned int item_type_to_nwr_index(item_type type) noexcept { unsigned int i = static_cast<unsigned int>(type); diff --git a/include/osmium/osm/node_ref.hpp b/include/osmium/osm/node_ref.hpp index 72359cd..def63b2 100644 --- a/include/osmium/osm/node_ref.hpp +++ b/include/osmium/osm/node_ref.hpp @@ -59,10 +59,16 @@ namespace osmium { m_location(location) { } + /** + * Get reference ID of this NodeRef. + */ osmium::object_id_type ref() const noexcept { return m_ref; } + /** + * Get absolute value of the reference ID of this NodeRef. + */ osmium::unsigned_object_id_type positive_ref() const noexcept { return static_cast<osmium::unsigned_object_id_type>(std::abs(m_ref)); } @@ -74,31 +80,60 @@ namespace osmium { return m_location; } + /** + * Get location of this NodeRef. + */ osmium::Location location() const noexcept { return m_location; } + /** + * Get longitude of the location in this NodeRef. + * + * @throws osmium::invalid_location if the location is not set. + */ double lon() const { return m_location.lon(); } + /** + * Get latitude of the location in this NodeRef. + * + * @throws osmium::invalid_location if the location is not set. + */ double lat() const { return m_location.lat(); } + /** + * Get internal x value of the location in this NodeRef. + */ int32_t x() const noexcept { return m_location.x(); } + /** + * Get internal y value of the location in this NodeRef. + */ int32_t y() const noexcept { return m_location.y(); } + /** + * Set the referenced ID. + * + * @returns Reference to this NodeRef for chaining calls. + */ NodeRef& set_ref(const osmium::object_id_type ref) noexcept { m_ref = ref; return *this; } + /** + * Set the location. + * + * @returns Reference to this NodeRef for chaining calls. + */ NodeRef& set_location(const osmium::Location& location) noexcept { m_location = location; return *this; @@ -106,26 +141,49 @@ namespace osmium { }; // class NodeRef + /** + * Compare two NodeRefs. They are equal if they reference the same Node ID. + */ inline bool operator==(const NodeRef& lhs, const NodeRef& rhs) noexcept { return lhs.ref() == rhs.ref(); } + /** + * Compare two NodeRefs. They are not equal if they reference different + * Node IDs. + */ inline bool operator!=(const NodeRef& lhs, const NodeRef& rhs) noexcept { return ! (lhs == rhs); } + /** + * Compare two NodeRefs. NodeRefs are ordered according to the Node ID + * they reference. + */ inline bool operator<(const NodeRef& lhs, const NodeRef& rhs) noexcept { return lhs.ref() < rhs.ref(); } + /** + * Compare two NodeRefs. NodeRefs are ordered according to the Node ID + * they reference. + */ inline bool operator>(const NodeRef& lhs, const NodeRef& rhs) noexcept { return rhs < lhs; } + /** + * Compare two NodeRefs. NodeRefs are ordered according to the Node ID + * they reference. + */ inline bool operator<=(const NodeRef& lhs, const NodeRef& rhs) noexcept { return ! (rhs < lhs); } + /** + * Compare two NodeRefs. NodeRefs are ordered according to the Node ID + * they reference. + */ inline bool operator>=(const NodeRef& lhs, const NodeRef& rhs) noexcept { return ! (lhs < rhs); } @@ -139,7 +197,7 @@ namespace osmium { } /** - * Functor to compare NodeRefs by Location instead of id. + * Functor to compare NodeRefs by Location instead of ID. */ struct location_equal { @@ -147,14 +205,14 @@ namespace osmium { return lhs.location() == rhs.location(); } - typedef NodeRef first_argument_type; - typedef NodeRef second_argument_type; - typedef bool result_type; + using first_argument_type = NodeRef; + using second_argument_type = NodeRef; + using result_type = bool; }; // struct location_equal /** - * Functor to compare NodeRefs by Location instead of id. + * Functor to compare NodeRefs by Location instead of ID. */ struct location_less { @@ -162,9 +220,9 @@ namespace osmium { return lhs.location() < rhs.location(); } - typedef NodeRef first_argument_type; - typedef NodeRef second_argument_type; - typedef bool result_type; + using first_argument_type = NodeRef; + using second_argument_type = NodeRef; + using result_type = bool; }; // struct location_less diff --git a/include/osmium/osm/node_ref_list.hpp b/include/osmium/osm/node_ref_list.hpp index c6c4213..e46a66d 100644 --- a/include/osmium/osm/node_ref_list.hpp +++ b/include/osmium/osm/node_ref_list.hpp @@ -44,8 +44,8 @@ DEALINGS IN THE SOFTWARE. namespace osmium { /** - * A vector of NodeRef objects. Usually this is not instantiated directly, - * but one of its subclasses are used. + * An ordered collection of NodeRef objects. Usually this is not + * instantiated directly, but one of its subclasses are used. */ class NodeRefList : public osmium::memory::Item { @@ -56,14 +56,14 @@ namespace osmium { } /** - * Checks whether the node list is empty. + * Checks whether the collection is empty. */ bool empty() const noexcept { return sizeof(NodeRefList) == byte_size(); } /** - * Returns the number of nodes in the list. + * Returns the number of NodeRefs in the collection. */ size_t size() const noexcept { auto size_node_refs = byte_size() - sizeof(NodeRefList); @@ -74,8 +74,9 @@ namespace osmium { /** * Access specified element. * - * @param n Get this element of the list. * @pre @code n < size() @endcode + * + * @param n Get the n-th element of the collection. */ const NodeRef& operator[](size_t n) const noexcept { assert(n < size()); @@ -104,16 +105,18 @@ namespace osmium { } /** - * Checks whether the first and last node in the list have the same ID. + * Checks whether the first and last node in the collection have the + * same ID. The locations are not checked. * * @pre @code !empty() @endcode */ bool is_closed() const noexcept { - return front().ref() == back().ref(); + return ends_have_same_id(); } /** - * Checks whether the first and last node in the list have the same ID. + * Checks whether the first and last node in the collection have the + * same ID. The locations are not checked. * * @pre @code !empty() @endcode */ @@ -122,8 +125,8 @@ namespace osmium { } /** - * Checks whether the first and last node in the list have the same - * location. The ID is not checked. + * Checks whether the first and last node in the collection have the + * same location. The IDs are not checked. * * @pre @code !empty() @endcode * @pre @code front().location() && back().location() @endcode @@ -133,9 +136,9 @@ namespace osmium { return front().location() == back().location(); } - typedef NodeRef* iterator; - typedef const NodeRef* const_iterator; - typedef std::reverse_iterator<const NodeRef*> const_reverse_iterator; + using iterator = NodeRef*; + using const_iterator = const NodeRef*; + using const_reverse_iterator = std::reverse_iterator<const NodeRef*>; /// Returns an iterator to the beginning. iterator begin() noexcept { diff --git a/include/osmium/osm/timestamp.hpp b/include/osmium/osm/timestamp.hpp index 2145fcb..9de727f 100644 --- a/include/osmium/osm/timestamp.hpp +++ b/include/osmium/osm/timestamp.hpp @@ -54,10 +54,8 @@ namespace osmium { // length of ISO timestamp string yyyy-mm-ddThh:mm:ssZ\0 static constexpr int timestamp_length = 20 + 1; - /** - * The timestamp format for OSM timestamps in strftime(3) format. - * This is the ISO-Format yyyy-mm-ddThh:mm:ssZ - */ + // The timestamp format for OSM timestamps in strftime(3) format. + // This is the ISO-Format "yyyy-mm-ddThh:mm:ssZ". static const char* timestamp_format() { static const char f[timestamp_length] = "%Y-%m-%dT%H:%M:%SZ"; return f; @@ -67,27 +65,29 @@ namespace osmium { public: + /** + * Default construct an invalid Timestamp. + */ constexpr Timestamp() noexcept : m_timestamp(0) { } - // Not "explicit" so that conversions from time_t work - // like in node.timestamp(123); - constexpr Timestamp(time_t timestamp) noexcept : - m_timestamp(static_cast<uint32_t>(timestamp)) { - } - /** - * Returns true if this timestamp is valid (ie set to something other - * than 0). + * Construct a Timestamp from a time_t containing the seconds since + * the epoch. + * + * The constructor is not declared "explicit" so that conversions + * like @code node.set_timestamp(123); @endcode work. */ - bool valid() const noexcept { - return m_timestamp != 0; + constexpr Timestamp(time_t timestamp) noexcept : + m_timestamp(static_cast<uint32_t>(timestamp)) { } /** - * Construct timestamp from ISO date/time string. - * Throws std::invalid_argument, if the timestamp can not be parsed. + * Construct timestamp from ISO date/time string in the format + * "yyyy-mm-ddThh:mm:ssZ". + * + * @throws std::invalid_argument if the timestamp can not be parsed. */ explicit Timestamp(const char* timestamp) { #ifndef _WIN32 @@ -113,14 +113,35 @@ namespace osmium { #endif } + /** + * Construct timestamp from ISO date/time string in the format + * "yyyy-mm-ddThh:mm:ssZ". + * + * @throws std::invalid_argument if the timestamp can not be parsed. + */ + explicit Timestamp(const std::string& timestamp) : + Timestamp(timestamp.c_str()) { + } + + /** + * Returns true if this timestamp is valid (ie set to something other + * than 0). + */ + bool valid() const noexcept { + return m_timestamp != 0; + } + + /// Explicit conversion into time_t. constexpr time_t seconds_since_epoch() const noexcept { return static_cast<time_t>(m_timestamp); } + /// Implicit conversion into time_t. constexpr operator time_t() const noexcept { return static_cast<time_t>(m_timestamp); } + /// Explicit conversion into uint32_t. explicit constexpr operator uint32_t() const noexcept { return m_timestamp; } @@ -136,7 +157,8 @@ namespace osmium { } /** - * Return UTC Unix time as string in ISO date/time format. + * Return UTC Unix time as string in ISO date/time + * ("yyyy-mm-ddThh:mm:ssZ") format. */ std::string to_iso() const { std::string s; @@ -164,12 +186,20 @@ namespace osmium { }; // class Timestamp + /** + * A special Timestamp guaranteed to be ordered before any other valid + * Timestamp. + */ inline OSMIUM_CONSTEXPR Timestamp start_of_time() noexcept { return Timestamp(1); } + /** + * A special Timestamp guaranteed to be ordered after any other valid + * Timestamp. + */ inline OSMIUM_CONSTEXPR Timestamp end_of_time() noexcept { - return Timestamp(std::numeric_limits<time_t>::max()); + return Timestamp(std::numeric_limits<uint32_t>::max()); } template <typename TChar, typename TTraits> diff --git a/include/osmium/osm/types_from_string.hpp b/include/osmium/osm/types_from_string.hpp index b0e22a7..67ab2c1 100644 --- a/include/osmium/osm/types_from_string.hpp +++ b/include/osmium/osm/types_from_string.hpp @@ -47,6 +47,15 @@ DEALINGS IN THE SOFTWARE. namespace osmium { + /** + * Convert string with object id to object_id_type. + * + * @pre input must not be nullptr. + * + * @param input Input string. + * + * @throws std::range_error if the value is out of range. + */ inline object_id_type string_to_object_id(const char* input) { assert(input); if (*input != '\0' && !std::isspace(*input)) { @@ -59,6 +68,19 @@ namespace osmium { throw std::range_error(std::string("illegal id: '") + input + "'"); } + /** + * Parse string with object type identifier followed by object id. This + * reads strings like "n1234" and "w10". + * + * @pre input must not be nullptr. + * + * @param input Input string. + * @param types Allowed types. Must not be osmium::osm_entity_bits::nothing. + * + * @returns std::pair of type and id. + * + * @throws std::range_error if the value is out of range. + */ inline std::pair<osmium::item_type, osmium::object_id_type> string_to_object_id(const char* input, osmium::osm_entity_bits::type types) { assert(input); assert(types != osmium::osm_entity_bits::nothing); @@ -89,16 +111,43 @@ namespace osmium { } // namespace detail + /** + * Convert string with object version to object_version_type. + * + * @pre input must not be nullptr. + * + * @param input Input string. + * + * @throws std::range_error if the value is out of range. + */ inline object_version_type string_to_object_version(const char* input) { assert(input); return static_cast_with_assert<object_version_type>(detail::string_to_ulong(input, "version")); } + /** + * Convert string with object version to object_version_type. + * + * @pre input must not be nullptr. + * + * @param input Input string. + * + * @throws std::range_error if the value is out of range. + */ inline changeset_id_type string_to_changeset_id(const char* input) { assert(input); return static_cast_with_assert<changeset_id_type>(detail::string_to_ulong(input, "changeset")); } + /** + * Convert string with user id to signed_user_id_type. + * + * @pre input must not be nullptr. + * + * @param input Input string. + * + * @throws std::range_error if the value is out of range. + */ inline signed_user_id_type string_to_user_id(const char* input) { assert(input); if (input[0] == '-' && input[1] == '1' && input[2] == '\0') { @@ -107,11 +156,29 @@ namespace osmium { return static_cast_with_assert<signed_user_id_type>(detail::string_to_ulong(input, "user id")); } + /** + * Convert string with number of changes to num_changes_type. + * + * @pre input must not be nullptr. + * + * @param input Input string. + * + * @throws std::range_error if the value is out of range. + */ inline num_changes_type string_to_num_changes(const char* input) { assert(input); return static_cast_with_assert<num_changes_type>(detail::string_to_ulong(input, "value for num changes")); } + /** + * Convert string with number of comments to num_comments_type. + * + * @pre input must not be nullptr. + * + * @param input Input string. + * + * @throws std::range_error if the value is out of range. + */ inline num_comments_type string_to_num_comments(const char* input) { assert(input); return static_cast_with_assert<num_comments_type>(detail::string_to_ulong(input, "value for num comments")); diff --git a/include/osmium/relations/collector.hpp b/include/osmium/relations/collector.hpp index e7f76a2..7d27b33 100644 --- a/include/osmium/relations/collector.hpp +++ b/include/osmium/relations/collector.hpp @@ -122,82 +122,15 @@ namespace osmium { TCollector& m_collector; - /** - * This variable is initialized with the number of different - * kinds of OSM objects we are interested in. If we only need - * way members (for instance for the multipolygon collector) - * it is intialized with 1 for instance. If node and way - * members are needed, it is initialized with 2. - * - * In the after_* methods of this handler, it is decremented - * and once it reaches 0, we know we have all members available - * that we are ever going to get. - */ - int m_want_types; - - /** - * Find this object in the member vectors and add it to all - * relations that need it. - * - * @returns true if the member was added to at least one - * relation and false otherwise - */ - bool find_and_add_object(const osmium::OSMObject& object) { - auto& mmv = m_collector.member_meta(object.type()); - auto range = std::equal_range(mmv.begin(), mmv.end(), MemberMeta(object.id())); - - if (osmium::relations::count_not_removed(range.first, range.second) == 0) { - // nothing found - return false; - } - - { - m_collector.members_buffer().add_item(object); - const size_t member_offset = m_collector.members_buffer().commit(); - - for (auto it = range.first; it != range.second; ++it) { - it->set_buffer_offset(member_offset); - } - } - - for (auto it = range.first; it != range.second; ++it) { - MemberMeta& member_meta = *it; - if (member_meta.removed()) { - break; - } - assert(member_meta.member_id() == object.id()); - assert(member_meta.relation_pos() < m_collector.m_relations.size()); - RelationMeta& relation_meta = m_collector.m_relations[member_meta.relation_pos()]; -// std::cerr << " => " << member_meta.member_pos() << " < " << m_collector.get_relation(relation_meta).members().size() << " (id=" << m_collector.get_relation(relation_meta).id() << ")\n"; - assert(member_meta.member_pos() < m_collector.get_relation(relation_meta).members().size()); -// std::cerr << " add way " << member_meta.member_id() << " to rel " << m_collector.get_relation(relation_meta).id() << " at pos " << member_meta.member_pos() << "\n"; - relation_meta.got_one_member(); - if (relation_meta.has_all_members()) { - const size_t relation_offset = member_meta.relation_pos(); - m_collector.complete_relation(relation_meta); - m_collector.m_relations[relation_offset] = RelationMeta(); - m_collector.possibly_purge_removed_members(); - } - } - - // Remove MemberMetas that were marked as removed. - mmv.erase(std::remove_if(mmv.begin(), mmv.end(), [](MemberMeta& mm) { - return mm.removed(); - }), mmv.end()); - - return true; - } - public: HandlerPass2(TCollector& collector) noexcept : - m_collector(collector), - m_want_types((TNodes?1:0) + (TWays?1:0) + (TRelations?1:0)) { + m_collector(collector) { } void node(const osmium::Node& node) { if (TNodes) { - if (! find_and_add_object(node)) { + if (! m_collector.find_and_add_object(node)) { m_collector.node_not_in_any_relation(node); } } @@ -205,7 +138,7 @@ namespace osmium { void way(const osmium::Way& way) { if (TWays) { - if (! find_and_add_object(way)) { + if (! m_collector.find_and_add_object(way)) { m_collector.way_not_in_any_relation(way); } } @@ -213,7 +146,7 @@ namespace osmium { void relation(const osmium::Relation& relation) { if (TRelations) { - if (! find_and_add_object(relation)) { + if (! m_collector.find_and_add_object(relation)) { m_collector.relation_not_in_any_relation(relation); } } @@ -225,6 +158,8 @@ namespace osmium { }; // class HandlerPass2 + private: + HandlerPass2 m_handler_pass2; // All relations we are interested in will be kept in this buffer @@ -373,6 +308,8 @@ namespace osmium { return m_members_buffer.get<osmium::OSMObject>(offset); } + private: + /** * Tell the Collector that you are interested in this relation * and want it kept until all members have been assembled and @@ -422,6 +359,84 @@ namespace osmium { std::sort(m_member_meta[2].begin(), m_member_meta[2].end()); } + /** + * Find this object in the member vectors and add it to all + * relations that need it. + * + * @returns true if the member was added to at least one + * relation and false otherwise + */ + bool find_and_add_object(const osmium::OSMObject& object) { + auto& mmv = member_meta(object.type()); + auto range = std::equal_range(mmv.begin(), mmv.end(), MemberMeta(object.id())); + + if (osmium::relations::count_not_removed(range.first, range.second) == 0) { + // nothing found + return false; + } + + { + members_buffer().add_item(object); + const size_t member_offset = members_buffer().commit(); + + for (auto it = range.first; it != range.second; ++it) { + it->set_buffer_offset(member_offset); + } + } + + for (auto it = range.first; it != range.second; ++it) { + MemberMeta& member_meta = *it; + if (member_meta.removed()) { + break; + } + assert(member_meta.member_id() == object.id()); + assert(member_meta.relation_pos() < m_relations.size()); + RelationMeta& relation_meta = m_relations[member_meta.relation_pos()]; +// std::cerr << " => " << member_meta.member_pos() << " < " << get_relation(relation_meta).members().size() << " (id=" << get_relation(relation_meta).id() << ")\n"; + assert(member_meta.member_pos() < get_relation(relation_meta).members().size()); +// std::cerr << " add way " << member_meta.member_id() << " to rel " << get_relation(relation_meta).id() << " at pos " << member_meta.member_pos() << "\n"; + relation_meta.got_one_member(); + if (relation_meta.has_all_members()) { + const size_t relation_offset = member_meta.relation_pos(); + static_cast<TCollector*>(this)->complete_relation(relation_meta); + clear_member_metas(relation_meta); + m_relations[relation_offset] = RelationMeta(); + possibly_purge_removed_members(); + } + } + + // Remove MemberMetas that were marked as removed. + mmv.erase(std::remove_if(mmv.begin(), mmv.end(), [](MemberMeta& mm) { + return mm.removed(); + }), mmv.end()); + + return true; + } + + void clear_member_metas(const osmium::relations::RelationMeta& relation_meta) { + const osmium::Relation& relation = get_relation(relation_meta); + for (const auto& member : relation.members()) { + if (member.ref() != 0) { + auto& mmv = member_meta(member.type()); + auto range = std::equal_range(mmv.begin(), mmv.end(), MemberMeta(member.ref())); + assert(range.first != range.second); + + // if this is the last time this object was needed + // then mark it as removed + if (osmium::relations::count_not_removed(range.first, range.second) == 1) { + get_member(range.first->buffer_offset()).set_removed(true); + } + + for (auto it = range.first; it != range.second; ++it) { + if (!it->removed() && relation.id() == get_relation(it->relation_pos()).id()) { + it->remove(); + break; + } + } + } + } + } + public: uint64_t used_memory() const { @@ -488,7 +503,7 @@ namespace osmium { void moving_in_buffer(size_t old_offset, size_t new_offset) { const osmium::OSMObject& object = m_members_buffer.get<osmium::OSMObject>(old_offset); auto& mmv = member_meta(object.type()); - auto range = std::equal_range(mmv.begin(), mmv.end(), osmium::relations::MemberMeta(object.id())); + auto range = std::equal_range(mmv.begin(), mmv.end(), MemberMeta(object.id())); for (auto it = range.first; it != range.second; ++it) { assert(it->buffer_offset() == old_offset); it->set_buffer_offset(new_offset); diff --git a/include/osmium/util/data_file.hpp b/include/osmium/util/data_file.hpp deleted file mode 100644 index 53bb81c..0000000 --- a/include/osmium/util/data_file.hpp +++ /dev/null @@ -1,194 +0,0 @@ -#ifndef OSMIUM_UTIL_DATA_FILE_HPP -#define OSMIUM_UTIL_DATA_FILE_HPP - -/* - -This file is part of Osmium (http://osmcode.org/libosmium). - -Copyright 2013-2015 Jochen Topf <joc...@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 <cerrno> -#include <cstddef> -#include <cstdio> -#include <stdexcept> -#include <string> -#include <system_error> - -#ifdef _WIN32 -# include <io.h> -# include <windows.h> -#endif - -#include <osmium/util/file.hpp> - -namespace osmium { - - namespace util { - - /** - * Class wrapper for convenient access to some low-level file - * functions. - */ - class DataFile { - - FILE* m_file; - - public: - - /** - * Create and open a temporary file. It is removed after opening. - * - * @throws std::system_error if something went wrong. - */ - DataFile() : - m_file(::tmpfile()) { - if (!m_file) { - throw std::system_error(errno, std::system_category(), "tmpfile failed"); - } - } - - /** - * Create and open a temporary file with the specified size. It - * is removed after opening. - * - * @throws std::system_error if something went wrong. - */ - explicit DataFile(size_t size) : - DataFile() { - grow(size); - } - - /** - * Create and open a named file. - * - * @param filename the name of the file - * @param writable should the file be writable? - * @throws std::system_error if something went wrong. - */ - DataFile(const char* filename, bool writable) : - m_file(::fopen(filename, writable ? "wb+" : "rb" )) { - if (!m_file) { - throw std::system_error(errno, std::system_category(), "fopen failed"); - } - } - - /** - * Create and open a named file. - * - * @param filename the name of the file - * @param writable should the file be writable? - * @throws std::system_error if something went wrong. - */ - DataFile(const std::string& filename, bool writable) : - DataFile(filename.c_str(), writable) { - } - - /** - * In boolean context the DataFile class returns true if the file - * is open. - */ - operator bool() const noexcept { - return m_file != nullptr; - } - - /** - * Close the file. - * - * Does nothing if the file is already closed. - * - * @throws std::system_error if file could not be closed - */ - void close() { - if (m_file) { - if (::fclose(m_file) != 0) { - throw std::system_error(errno, std::system_category(), "fclose failed"); - } - m_file = nullptr; - } - } - - ~DataFile() noexcept { - try { - close(); - } catch (std::system_error&) { - // Ignore any exceptions because destructor must not throw. - } - } - - /** - * Get file descriptor of underlying file. - * - * @throws std::runtime_errro if file is not open - * @throws std::system_error if fileno(3) call failed - */ - int fd() const { - if (!m_file) { - throw std::runtime_error("no open file"); - } - - int fd = ::fileno(m_file); - - if (fd == -1) { - throw std::system_error(errno, std::system_category(), "fileno failed"); - } - - return fd; - } - - /** - * Ask the operating system for the size of this file. - * - * @throws std::system_error if fstat(2) call failed - */ - size_t size() const { - return osmium::util::file_size(fd()); - } - - /** - * Grow file to given size. - * - * If the file is large enough already, nothing is done. - * The file is never shrunk. - * - * @throws std::system_error if ftruncate(2) call failed - */ - void grow(size_t new_size) const { - if (size() < new_size) { - osmium::util::resize_file(fd(), new_size); - } - } - - }; // class DataFile - - } // namespace util - -} // namespace osmium - - -#endif // OSMIUM_UTIL_DATA_FILE_HPP diff --git a/include/osmium/util/memory_mapping.hpp b/include/osmium/util/memory_mapping.hpp index d5a057d..48da13a 100644 --- a/include/osmium/util/memory_mapping.hpp +++ b/include/osmium/util/memory_mapping.hpp @@ -38,6 +38,7 @@ DEALINGS IN THE SOFTWARE. #include <stdexcept> #include <system_error> +#include <osmium/util/compatibility.hpp> #include <osmium/util/file.hpp> #ifndef _WIN32 @@ -173,7 +174,8 @@ private: * created, otherwise a mapping based on the file descriptor will * be created. * - * @pre size > 0 or mode == write_shared oder write_private + * @pre @code size > 0 @endcode or + * @code mode == write_shared || mode == write_private @endcode * * @param size Size of the mapping in bytes * @param mode Mapping mode: readonly, or writable (shared or private) @@ -183,8 +185,12 @@ private: */ MemoryMapping(size_t size, mapping_mode mode, int fd=-1, off_t offset=0); - /// DEPRECATED: For backwards compatibility - MemoryMapping(size_t size, bool writable=true, int fd=-1, off_t offset=0) : + /** + * @deprecated + * For backwards compatibility only. Use the constructor taking + * a mapping_mode as second argument instead. + */ + OSMIUM_DEPRECATED MemoryMapping(size_t size, bool writable=true, int fd=-1, off_t offset=0) : MemoryMapping(size, writable ? mapping_mode::write_shared : mapping_mode::readonly, fd, offset) { } @@ -232,8 +238,9 @@ private: * systems it will unmap and remap the memory. This can only be * done for file-based mappings, not anonymous mappings! * - * @param new_size Number of bytes to resize to - * @throws std::system_error if the remapping fails + * @param new_size Number of bytes to resize to (must be > 0). + * + * @throws std::system_error if the remapping fails. */ void resize(size_t new_size); @@ -353,8 +360,12 @@ private: m_mapping(sizeof(T) * size, mode, fd, sizeof(T) * offset) { } - /// DEPRECATED: For backwards compatibility - TypedMemoryMapping(size_t size, bool writable, int fd, off_t offset = 0) : + /** + * @deprecated + * For backwards compatibility only. Use the constructor taking + * a mapping_mode as second argument instead. + */ + OSMIUM_DEPRECATED TypedMemoryMapping(size_t size, bool writable, int fd, off_t offset = 0) : m_mapping(sizeof(T) * size, writable ? MemoryMapping::mapping_mode::write_shared : MemoryMapping::mapping_mode::readonly, fd, sizeof(T) * offset) { } diff --git a/include/osmium/util/options.hpp b/include/osmium/util/options.hpp index 24c0918..1019c8b 100644 --- a/include/osmium/util/options.hpp +++ b/include/osmium/util/options.hpp @@ -47,46 +47,65 @@ namespace osmium { * as a base class. Options are stored and retrieved by key using the * different set() and get() methods. * + * Both keys and values are stored as strings. The values "true", + * "yes", "false", and "no" are interpreted as boolean values in some + * functions. + * * You can iterate over all set options. Dereferencing an iterator * yields a std::pair of the key and value strings. */ class Options { - typedef std::map<std::string, std::string> option_map; + using option_map = std::map<std::string, std::string>; option_map m_options; public: - typedef option_map::iterator iterator; - typedef option_map::const_iterator const_iterator; - typedef option_map::value_type value_type; + using iterator = option_map::iterator; + using const_iterator = option_map::const_iterator; + using value_type = option_map::value_type; + /** + * Construct empty option set. + */ Options() = default; + /** + * Construct option set from initializer list: + * @code + * Options options{ { "foo", "true" }, { "bar", "17" } }; + * @endcode + */ explicit Options(const std::initializer_list<value_type>& values) : m_options(values) { } - Options(const Options&) = default; - Options& operator=(const Options&) = default; - - Options(Options&&) = default; - Options& operator=(Options&&) = default; - - ~Options() = default; - + /** + * Set option 'key' to 'value'. + */ void set(const std::string& key, const std::string& value) { m_options[key] = value; } + /** + * Set option 'key' to 'value'. + */ void set(const std::string& key, const char* value) { m_options[key] = value; } + /** + * Set option 'key' to 'value'. + */ void set(const std::string& key, bool value) { m_options[key] = value ? "true" : "false"; } + /** + * Set option from string in the form 'key=value'. If the string + * contains no equal sign, the whole string is the key and it will + * be set to "true". + */ void set(std::string data) { size_t pos = data.find_first_of('='); if (pos == std::string::npos) { @@ -99,7 +118,7 @@ namespace osmium { } /** - * Get value of "key" option. If not set the default_value (or + * Get value of "key" option. If not set, the default_value (or * empty string) is returned. */ std::string get(const std::string& key, const std::string& default_value="") const noexcept { @@ -128,30 +147,51 @@ namespace osmium { return !(value == "false" || value == "no"); } + /** + * The number of options set. + */ size_t size() const noexcept { return m_options.size(); } + /** + * Returns an iterator to the beginning. + */ iterator begin() noexcept { return m_options.begin(); } + /** + * Returns an iterator to the end. + */ iterator end() noexcept { return m_options.end(); } + /** + * Returns an iterator to the beginning. + */ const_iterator begin() const noexcept { return m_options.cbegin(); } + /** + * Returns an iterator to the end. + */ const_iterator end() const noexcept { return m_options.cend(); } + /** + * Returns an iterator to the beginning. + */ const_iterator cbegin() const noexcept { return m_options.cbegin(); } + /** + * Returns a iterator to the end. + */ const_iterator cend() const noexcept { return m_options.cend(); } diff --git a/include/osmium/visitor.hpp b/include/osmium/visitor.hpp index 35fcb4e..48ce45d 100644 --- a/include/osmium/visitor.hpp +++ b/include/osmium/visitor.hpp @@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE. #include <type_traits> +#include <osmium/fwd.hpp> #include <osmium/io/reader_iterator.hpp> // IWYU pragma: keep #include <osmium/memory/buffer.hpp> #include <osmium/osm.hpp> @@ -43,12 +44,6 @@ DEALINGS IN THE SOFTWARE. namespace osmium { - class TagList; - class WayNodeList; - class RelationMemberList; - class OuterRing; - class InnerRing; - namespace memory { class Item; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b9f02ee..f574161 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -151,7 +151,6 @@ add_unit_test(tags test_tag_list) add_unit_test(thread test_pool ENABLE_IF ${Threads_FOUND} LIBS ${CMAKE_THREAD_LIBS_INIT}) add_unit_test(util test_cast_with_assert) -add_unit_test(util test_data_file) add_unit_test(util test_delta) add_unit_test(util test_double) add_unit_test(util test_file) diff --git a/test/t/basic/test_object_comparisons.cpp b/test/t/basic/test_object_comparisons.cpp index 2bfdcad..ec9e6fa 100644 --- a/test/t/basic/test_object_comparisons.cpp +++ b/test/t/basic/test_object_comparisons.cpp @@ -31,20 +31,20 @@ TEST_CASE("Object_Comparisons") { node1.set_version(1); node2.set_id(15); node2.set_version(2); - REQUIRE(true == (node1 < node2)); - REQUIRE(false == (node1 > node2)); + REQUIRE(node1 < node2); + REQUIRE_FALSE(node1 > node2); node1.set_id(20); node1.set_version(1); node2.set_id(20); node2.set_version(2); - REQUIRE(true == (node1 < node2)); - REQUIRE(false == (node1 > node2)); + REQUIRE(node1 < node2); + REQUIRE_FALSE(node1 > node2); node1.set_id(-10); node1.set_version(2); node2.set_id(-15); node2.set_version(1); - REQUIRE(true == (node1 < node2)); - REQUIRE(false == (node1 > node2)); + REQUIRE(node1 < node2); + REQUIRE_FALSE(node1 > node2); } SECTION("order_types") { @@ -122,26 +122,26 @@ TEST_CASE("Object_Comparisons") { const osmium::Way& way = static_cast<const osmium::Way&>(*(++it)); const osmium::Relation& relation = static_cast<const osmium::Relation&>(*(++it)); - REQUIRE(true == (node1 < node2)); - REQUIRE(true == (node2 < way)); - REQUIRE(false == (node2 > way)); - REQUIRE(true == (way < relation)); - REQUIRE(true == (node1 < relation)); + REQUIRE(node1 < node2); + REQUIRE(node2 < way); + REQUIRE_FALSE(node2 > way); + REQUIRE(way < relation); + REQUIRE(node1 < relation); - REQUIRE(true == osmium::object_order_type_id_version()(node1, node2)); - REQUIRE(true == osmium::object_order_type_id_reverse_version()(node2, node1)); - REQUIRE(true == osmium::object_order_type_id_version()(node1, way)); - REQUIRE(true == osmium::object_order_type_id_reverse_version()(node1, way)); + REQUIRE(osmium::object_order_type_id_version()(node1, node2)); + REQUIRE(osmium::object_order_type_id_reverse_version()(node2, node1)); + REQUIRE(osmium::object_order_type_id_version()(node1, way)); + REQUIRE(osmium::object_order_type_id_reverse_version()(node1, way)); - REQUIRE(false == osmium::object_equal_type_id_version()(node1, node2)); - REQUIRE(true == osmium::object_equal_type_id_version()(node2, node3)); + REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, node2)); + REQUIRE(osmium::object_equal_type_id_version()(node2, node3)); - REQUIRE(true == osmium::object_equal_type_id()(node1, node2)); - REQUIRE(true == osmium::object_equal_type_id()(node2, node3)); + REQUIRE(osmium::object_equal_type_id()(node1, node2)); + REQUIRE(osmium::object_equal_type_id()(node2, node3)); - REQUIRE(false == osmium::object_equal_type_id_version()(node1, way)); - REQUIRE(false == osmium::object_equal_type_id_version()(node1, relation)); - REQUIRE(false == osmium::object_equal_type_id()(node1, relation)); + REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, way)); + REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, relation)); + REQUIRE_FALSE(osmium::object_equal_type_id()(node1, relation)); } } diff --git a/test/t/basic/test_timestamp.cpp b/test/t/basic/test_timestamp.cpp index f015730..f5c4eba 100644 --- a/test/t/basic/test_timestamp.cpp +++ b/test/t/basic/test_timestamp.cpp @@ -10,23 +10,34 @@ TEST_CASE("Timestamp") { osmium::Timestamp t; REQUIRE(0 == t); REQUIRE("" == t.to_iso()); + REQUIRE_FALSE(t.valid()); } SECTION("invalid value is zero") { osmium::Timestamp t(static_cast<time_t>(0)); REQUIRE(0 == t); REQUIRE("" == t.to_iso()); + REQUIRE_FALSE(t.valid()); } SECTION("can be initialized from time_t") { osmium::Timestamp t(static_cast<time_t>(1)); REQUIRE(1 == t); REQUIRE("1970-01-01T00:00:01Z" == t.to_iso()); + REQUIRE(t.valid()); } - SECTION("can be initialized from string") { + SECTION("can be initialized from const char*") { osmium::Timestamp t("2000-01-01T00:00:00Z"); REQUIRE("2000-01-01T00:00:00Z" == t.to_iso()); + REQUIRE(t.valid()); + } + + SECTION("can be initialized from string") { + std::string s = "2000-01-01T00:00:00Z"; + osmium::Timestamp t(s); + REQUIRE("2000-01-01T00:00:00Z" == t.to_iso()); + REQUIRE(t.valid()); } SECTION("throws if initialized from bad string") { @@ -50,6 +61,10 @@ TEST_CASE("Timestamp") { osmium::Timestamp t1(10); osmium::Timestamp t2(50); REQUIRE(t1 < t2); + REQUIRE(t1 > osmium::start_of_time()); + REQUIRE(t2 > osmium::start_of_time()); + REQUIRE(t1 < osmium::end_of_time()); + REQUIRE(t2 < osmium::end_of_time()); } SECTION("can be written to stream") { diff --git a/test/t/buffer/test_buffer_node.cpp b/test/t/buffer/test_buffer_node.cpp index 9bc8f70..e985009 100644 --- a/test/t/buffer/test_buffer_node.cpp +++ b/test/t/buffer/test_buffer_node.cpp @@ -56,13 +56,14 @@ void check_node_2(osmium::Node& node) { REQUIRE(2 == n); } -TEST_CASE("Buffer_Node") { +TEST_CASE("Node in Buffer") { - SECTION("buffer_node") { - constexpr size_t buffer_size = 10000; - unsigned char data[buffer_size]; + constexpr size_t buffer_size = 10000; + unsigned char data[buffer_size]; - osmium::memory::Buffer buffer(data, buffer_size, 0); + osmium::memory::Buffer buffer(data, buffer_size, 0); + + SECTION("Add node to buffer") { { // add node 1 @@ -132,4 +133,37 @@ TEST_CASE("Buffer_Node") { } + SECTION("Add buffer to another one") { + + { + // add node 1 + osmium::builder::NodeBuilder node_builder(buffer); + osmium::Node& node = node_builder.object(); + REQUIRE(osmium::item_type::node == node.type()); + + node.set_id(1); + node.set_version(3); + node.set_visible(true); + node.set_changeset(333); + node.set_uid(21); + node.set_timestamp(123); + node.set_location(osmium::Location(3.5, 4.7)); + + node_builder.add_user("testuser"); + + buffer.commit(); + } + + osmium::memory::Buffer buffer2(buffer_size, osmium::memory::Buffer::auto_grow::yes); + + buffer2.add_buffer(buffer); + buffer2.commit(); + + REQUIRE(buffer.committed() == buffer2.committed()); + const osmium::Node& node = buffer2.get<osmium::Node>(0); + REQUIRE(node.id() == 1); + REQUIRE(node.timestamp() == 123); + } + } + diff --git a/test/t/util/test_data_file.cpp b/test/t/util/test_data_file.cpp deleted file mode 100644 index 792cedf..0000000 --- a/test/t/util/test_data_file.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "catch.hpp" - -#include <cstring> - -#include <osmium/util/data_file.hpp> - -TEST_CASE("temporary file") { - - SECTION("create/open") { - osmium::util::DataFile file; - - REQUIRE(!!file); - int fd = file.fd(); - - REQUIRE(fd > 0); - - const char buf[] = "foobar"; - REQUIRE(::write(fd, buf, sizeof(buf)) == sizeof(buf)); - - file.close(); - - REQUIRE(!file); - } - -} - -TEST_CASE("named file") { - - SECTION("create/open") { - { - osmium::util::DataFile file("test.data", true); - - REQUIRE(!!file); - int fd = file.fd(); - - REQUIRE(fd > 0); - - REQUIRE(file.size() == 0); - - const char buf[] = "foobar"; - REQUIRE(::write(fd, buf, sizeof(buf) - 1) == sizeof(buf) - 1); - - file.close(); - - REQUIRE(!file); - } - { - osmium::util::DataFile file("test.data", false); - - REQUIRE(!!file); - int fd = file.fd(); - - REQUIRE(fd > 0); - - REQUIRE(file.size() == 6); - - char buf[10]; - auto len = ::read(fd, buf, sizeof(buf)); - - REQUIRE(len == 6); - REQUIRE(!strncmp(buf, "foobar", 6)); - - file.close(); - - REQUIRE(!file); - REQUIRE(unlink("test.data") == 0); - } - } - - SECTION("grow file") { - osmium::util::DataFile file("test.data", true); - - REQUIRE(!!file); - - REQUIRE(file.size() == 0); - file.grow(10); - REQUIRE(file.size() == 10); - } - -} - diff --git a/test/t/util/test_options.cpp b/test/t/util/test_options.cpp index c1e13bd..8cba095 100644 --- a/test/t/util/test_options.cpp +++ b/test/t/util/test_options.cpp @@ -8,7 +8,7 @@ TEST_CASE("Options") { osmium::util::Options o; - SECTION("set_simple") { + SECTION("set a single value from string") { o.set("foo", "bar"); REQUIRE("bar" == o.get("foo")); REQUIRE("" == o.get("empty")); @@ -23,7 +23,7 @@ TEST_CASE("Options") { REQUIRE(1 == o.size()); } - SECTION("set_from_bool") { + SECTION("set values from booleans") { o.set("t", true); o.set("f", false); REQUIRE("true" == o.get("t")); @@ -39,13 +39,13 @@ TEST_CASE("Options") { REQUIRE(2 == o.size()); } - SECTION("set_from_single_string_with_equals") { + SECTION("set value from string with equal sign") { o.set("foo=bar"); REQUIRE("bar" == o.get("foo")); REQUIRE(1 == o.size()); } - SECTION("set_from_single_string_without_equals") { + SECTION("set value from string without equal sign") { o.set("foo"); REQUIRE("true" == o.get("foo")); @@ -57,3 +57,25 @@ TEST_CASE("Options") { } +TEST_CASE("Options with initializer list") { + + osmium::util::Options o{ { "foo", "true" }, { "bar", "17" } }; + + REQUIRE(o.get("foo") == "true"); + REQUIRE(o.get("bar") == "17"); + REQUIRE(o.is_true("foo")); + REQUIRE_FALSE(o.is_true("bar")); + REQUIRE(o.size() == 2); + + SECTION("Change existing value") { + o.set("foo", "false"); + REQUIRE_FALSE(o.is_true("foo")); + } + + SECTION("Add new value") { + o.set("new", "something"); + REQUIRE_FALSE(o.is_true("new")); + REQUIRE(o.get("new") == "something"); + } +} + -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/libosmium.git _______________________________________________ Pkg-grass-devel mailing list Pkg-grass-devel@lists.alioth.debian.org http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel