diff --git a/include/osmium/builder/osm_object_builder.hpp b/include/osmium/builder/osm_object_builder.hpp index 7e49535d..e6d18043 100644 --- a/include/osmium/builder/osm_object_builder.hpp +++ b/include/osmium/builder/osm_object_builder.hpp @@ -324,7 +324,7 @@ namespace osmium { class ChangesetDiscussionBuilder : public Builder { - osmium::ChangesetComment* m_comment = nullptr; + std::size_t m_comment_offset = std::numeric_limits::max(); void add_user(osmium::ChangesetComment& comment, const char* user, const std::size_t length) { if (length > osmium::max_osm_string_length) { @@ -343,6 +343,20 @@ namespace osmium { add_padding(true); } + bool has_open_comment() const noexcept { + return m_comment_offset != std::numeric_limits::max(); + } + + // Get current comment pointer (recalculated each time to handle buffer reallocation) + osmium::ChangesetComment* get_comment_ptr() { + if (!has_open_comment()) { + return nullptr; + } + return reinterpret_cast( + buffer().data() + buffer().committed() + m_comment_offset + ); + } + public: explicit ChangesetDiscussionBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) : @@ -362,32 +376,46 @@ namespace osmium { ChangesetDiscussionBuilder& operator=(ChangesetDiscussionBuilder&&) = delete; ~ChangesetDiscussionBuilder() { - assert(!m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!"); + assert(!has_open_comment() && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!"); add_padding(); } void add_comment(osmium::Timestamp date, osmium::user_id_type uid, const char* user) { - assert(!m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!"); - m_comment = reserve_space_for(); - new (m_comment) osmium::ChangesetComment{date, uid}; + assert(!has_open_comment() && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!"); + + // Store offset instead of pointer to handle buffer reallocation + m_comment_offset = buffer().written() - buffer().committed(); + + auto* comment = reserve_space_for(); + new (comment) osmium::ChangesetComment{date, uid}; add_size(sizeof(ChangesetComment)); - add_user(*m_comment, user, std::strlen(user)); + + add_user(*comment, user, std::strlen(user)); } void add_comment_text(const char* text) { - assert(m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!"); - osmium::ChangesetComment& comment = *m_comment; - m_comment = nullptr; - add_text(comment, text, std::strlen(text)); + assert(has_open_comment() && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!"); + + // Get fresh pointer each time to handle buffer reallocation + auto* comment = get_comment_ptr(); + + // Invalidate offset to ensure right adding order + m_comment_offset = std::numeric_limits::max(); + + add_text(*comment, text, std::strlen(text)); } void add_comment_text(const std::string& text) { - assert(m_comment && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!"); - osmium::ChangesetComment& comment = *m_comment; - m_comment = nullptr; - add_text(comment, text.c_str(), text.size()); + assert(has_open_comment() && "You have to always call both add_comment() and then add_comment_text() in that order for each comment!"); + + // Get fresh pointer each time to handle buffer reallocation + auto* comment = get_comment_ptr(); + + // Invalidate offset to ensure right adding order + m_comment_offset = std::numeric_limits::max(); + + add_text(*comment, text.c_str(), text.size()); } - }; // class ChangesetDiscussionBuilder #define OSMIUM_FORWARD(setter) \ diff --git a/include/osmium/io/bzip2_compression.hpp b/include/osmium/io/bzip2_compression.hpp index 99c50cc2..a4a6137a 100644 --- a/include/osmium/io/bzip2_compression.hpp +++ b/include/osmium/io/bzip2_compression.hpp @@ -313,7 +313,18 @@ namespace osmium { throw bzip2_error{"bzip2 error: read open failed", bzerror}; } } else { - m_stream_end = true; + // Close current stream and try to open a new one for multi-stream files + ::BZ2_bzReadClose(&bzerror, m_bzfile); + if (bzerror != BZ_OK) { + throw bzip2_error{"bzip2 error: read close failed", bzerror}; + } + // Try to open a new stream - there might be more bzip2 streams concatenated + m_bzfile = ::BZ2_bzReadOpen(&bzerror, m_file.file(), 0, 0, nullptr, 0); + if (!m_bzfile || bzerror != BZ_OK) { + // If we can't open a new stream, we've truly reached the end + m_stream_end = true; + m_bzfile = nullptr; + } } } else { m_stream_end = true;