From 5deae85b18c2e305debcccd9b634847616c52cda Mon Sep 17 00:00:00 2001 From: Murat Toprak Date: Tue, 8 Jul 2025 13:44:08 +0300 Subject: [PATCH] Handle allocator propagation in basic_memory_buffer::move Update `basic_memory_buffer::move` to respect `propagate_on_container_move_assignment`allocator trait. If the allocator should not propagate and differs from the target's allocator, fallback to copying the buffer instead of transferring ownership. This avoids potential allocator mismatch issues and ensures exception safety. --- include/fmt/format.h | 21 ++++++++++++++++++++- test/mock-allocator.h | 11 +++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 1cbd3df4..591a28af 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -751,6 +751,14 @@ template struct allocator : private std::decay { } void deallocate(T* p, size_t) { std::free(p); } + + friend bool operator==(const allocator&, const allocator&) noexcept { + return true; // All instances of this allocator are equivalent. + } + + friend bool operator!=(const allocator& a, const allocator& b) noexcept { + return !(a == b); + } }; } // namespace detail @@ -828,9 +836,20 @@ class basic_memory_buffer : public detail::buffer { private: // Move data from other to this buffer. FMT_CONSTEXPR20 void move(basic_memory_buffer& other) { - alloc_ = std::move(other.alloc_); + using alloc_traits = std::allocator_traits; T* data = other.data(); size_t size = other.size(), capacity = other.capacity(); + if constexpr (alloc_traits::propagate_on_container_move_assignment::value) { + alloc_ = std::move(other.alloc_); + } else { + if (alloc_ != other.alloc_) { + this->reserve(capacity); + detail::copy(data, data + size, this->data()); + this->resize(size); + return; + } + } + if (data == other.store_) { this->set(store_, capacity); detail::copy(other.store_, other.store_ + size, store_); diff --git a/test/mock-allocator.h b/test/mock-allocator.h index 32c4caae..168d02a6 100644 --- a/test/mock-allocator.h +++ b/test/mock-allocator.h @@ -72,6 +72,17 @@ template class allocator_ref { return std::allocator_traits::allocate(*alloc_, n); } void deallocate(value_type* p, size_t n) { alloc_->deallocate(p, n); } + + friend bool operator==(const allocator_ref& a, const allocator_ref& b) noexcept { + if (a.alloc_ == b.alloc_) return true; + if (a.alloc_ == nullptr || b.alloc_ == nullptr) return false; + + return *a.alloc_ == *b.alloc_; + } + + friend bool operator!=(const allocator_ref& a, const allocator_ref& b) noexcept { + return !(a == b); + } }; #endif // FMT_MOCK_ALLOCATOR_H_