From 0e81a358816ffdb4f9de79826e79d2b62db5a3de Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Fri, 14 Nov 2025 22:45:20 +0300 Subject: [PATCH] libutil: Make CanonPath a proper range This was we can use std::ranges algorithms on it. Requires making the iterator a proper forward iterator type as well. --- src/libutil/include/nix/util/canon-path.hh | 68 +++++++++++++++++++--- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/src/libutil/include/nix/util/canon-path.hh b/src/libutil/include/nix/util/canon-path.hh index b9b2fff25..2156b02fc 100644 --- a/src/libutil/include/nix/util/canon-path.hh +++ b/src/libutil/include/nix/util/canon-path.hh @@ -8,6 +8,7 @@ #include #include #include +#include #include @@ -122,33 +123,70 @@ public: return &cs[1]; } - struct Iterator + class Iterator { + /** + * Helper class with overloaded operator-> for "drill-down" behavior. + * This was a "temporary" string_view doesn't have to be stored anywhere. + */ + class PointerProxy + { + std::string_view segment; + + public: + PointerProxy(std::string_view segment_) + : segment(segment_) + { + } + + const std::string_view * operator->() const + { + return &segment; + } + }; + + public: + using value_type = std::string_view; + using reference_type = const std::string_view; + using pointer_type = PointerProxy; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + std::string_view remaining; size_t slash; + /** + * Dummy default constructor required for forward iterators. Doesn't return + * a usable iterator. + */ + Iterator() + : remaining() + , slash(0) + { + } + Iterator(std::string_view remaining) : remaining(remaining) , slash(remaining.find('/')) { } - bool operator!=(const Iterator & x) const - { - return remaining.data() != x.remaining.data(); - } - bool operator==(const Iterator & x) const { - return !(*this != x); + return remaining.data() == x.remaining.data(); } - const std::string_view operator*() const + reference_type operator*() const { return remaining.substr(0, slash); } - void operator++() + pointer_type operator->() const + { + return PointerProxy(**this); + } + + Iterator & operator++() { if (slash == remaining.npos) remaining = remaining.substr(remaining.size()); @@ -156,9 +194,19 @@ public: remaining = remaining.substr(slash + 1); slash = remaining.find('/'); } + return *this; + } + + Iterator operator++(int) + { + auto tmp = *this; + ++*this; + return tmp; } }; + static_assert(std::forward_iterator); + Iterator begin() const { return Iterator(rel()); @@ -265,6 +313,8 @@ public: friend std::size_t hash_value(const CanonPath &); }; +static_assert(std::ranges::forward_range); + std::ostream & operator<<(std::ostream & stream, const CanonPath & path); inline std::size_t hash_value(const CanonPath & path)