This is an automated email from the ASF dual-hosted git repository. amc pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push: new 61b27c3403 Update to libswoc 1.5.0 (#9830) 61b27c3403 is described below commit 61b27c340368e51e4879f82c1b7775b5000fd79e Author: Alan M. Carroll <a...@apache.org> AuthorDate: Thu Jun 15 08:59:01 2023 -0500 Update to libswoc 1.5.0 (#9830) --- lib/swoc/CMakeLists.txt | 2 +- lib/swoc/Makefile.am | 2 +- lib/swoc/include/swoc/DiscreteRange.h | 234 ++++++++---- lib/swoc/include/swoc/IPRange.h | 700 ++++++++++++++++++++++++++-------- lib/swoc/include/swoc/Lexicon.h | 2 +- lib/swoc/include/swoc/MemSpan.h | 23 +- lib/swoc/include/swoc/bwf_base.h | 28 +- lib/swoc/include/swoc/swoc_version.h | 6 +- lib/swoc/src/swoc_file.cc | 2 + lib/swoc/src/swoc_ip.cc | 96 +++-- proxy/ControlMatcher.cc | 2 +- 11 files changed, 798 insertions(+), 299 deletions(-) diff --git a/lib/swoc/CMakeLists.txt b/lib/swoc/CMakeLists.txt index 88a549ebe8..c49f8a67fc 100644 --- a/lib/swoc/CMakeLists.txt +++ b/lib/swoc/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.11) project(Lib-SWOC CXX) -set(LIBSWOC_VERSION "1.4.10") +set(LIBSWOC_VERSION "1.5.0") set(CMAKE_CXX_STANDARD 17) cmake_policy(SET CMP0087 NEW) # override "lib64" to be "lib" unless the user explicitly sets it. diff --git a/lib/swoc/Makefile.am b/lib/swoc/Makefile.am index 98d72ca31d..d5d9b9261b 100644 --- a/lib/swoc/Makefile.am +++ b/lib/swoc/Makefile.am @@ -22,7 +22,7 @@ library_includedir=$(includedir)/swoc AM_CPPFLAGS += @SWOC_INCLUDES@ -libtsswoc_la_LDFLAGS = @AM_LDFLAGS@ -no-undefined -release 1.4.10 +libtsswoc_la_LDFLAGS = @AM_LDFLAGS@ -no-undefined -release 1.5.0 libtsswoc_la_SOURCES = \ src/ArenaWriter.cc src/bw_format.cc src/bw_ip_format.cc src/Errata.cc src/MemArena.cc src/RBTree.cc src/swoc_file.cc src/swoc_ip.cc src/TextView.cc src/string_view_util.cc diff --git a/lib/swoc/include/swoc/DiscreteRange.h b/lib/swoc/include/swoc/DiscreteRange.h index a04a3e7d27..ff681f2484 100644 --- a/lib/swoc/include/swoc/DiscreteRange.h +++ b/lib/swoc/include/swoc/DiscreteRange.h @@ -127,7 +127,7 @@ enum class DiscreteRangeEdgeRelation : uint8_t { - have value semantics - have minimum and maximum values either - members @c MIN and @c MAX that define static instances - - support @c std::numeric_limits<T> + - @c std::numeric_limits<T> support. The interval is always an inclusive (closed) contiguous interval, defined by the minimum and maximum values contained in the interval. @@ -147,7 +147,7 @@ public: using EdgeRelation = DiscreteRangeEdgeRelation; ///< Import type for convenience. /** Default constructor. - An invalid (empty) range is constructed. + * An invalid (empty) range is constructed. */ constexpr DiscreteRange() : _min(detail::maximum<T>()), _max(detail::minimum<T>()) {} @@ -174,109 +174,139 @@ public: */ bool empty() const; + /** Update the range. + * + * @param min New minimum value. + * @param max New maximum value. + * @return @a this. + */ self_type &assign(metric_type const &min, metric_type const &max); - /// Set the interval to be a singleton. - self_type &assign(metric_type const &singleton); + /** Update the range. + * + * @param value The new minimum and maximum value. + * @return @a this. + * + * The range will contain the single value @a value. + */ + self_type &assign(metric_type const &value); + /** Update the minimum value. + * + * @param min The new minimum value. + * @return @a this. + * + * @note No checks are done - this can result in an empty range. + */ self_type &assign_min(metric_type const &min); + /** Update the maximum value. + * + * @param max The new maximum value. + * @return @a this. + * + * @note No checks are done - this can result in an empty range. + */ self_type &assign_max(metric_type const &max); /** Decrement the maximum value. * * @return @a this. + * + * @note No checks are done, the caller must ensure it is valid to decremented the current maximum. */ - self_type & - clip_max() { - --_max; - return *this; - } + self_type & clip_max(); - /** Get the minimum value in the interval. - @note The return value is unspecified if the interval is empty. + /** Minimum value. + * @return the minimum value in the range. + * @note The return value is unspecified if the interval is empty. */ metric_type const &min() const; - /** Get the maximum value in the interval. - @note The return value is unspecified if the interval is empty. + /** Maximum value. + * @return The maximum value in the range. + * @note The return value is unspecified if the interval is empty. */ metric_type const &max() const; - /// Test for equality. - bool - operator==(self_type const &that) const { - return _min == that._min && _max == that._max; - } + /// Equality. + bool operator==(self_type const &that) const; - /// Test for inequality. - bool - operator!=(self_type const &that) const { - return _min != that._min | _max != that._max; - } + /// Inequality. + bool operator!=(self_type const &that) const; /** Check if a value is in @a this range. * - * @param m Metric value to check. - * @return @c true if @a m is in the range, @c false if not. + * @param value Metric value to check. + * @return @c true if @a value is in the range, @c false if not. */ - bool - contains(metric_type const &m) const { - return _min <= m && m <= _max; - } + bool contains(metric_type const &value) const; - /** Logical intersection test for two intervals. - @return @c true if there is at least one common value in the - two intervals, @c false otherwise. - */ + /** Logical intersection. + * @return @c true if there is at least one common value in the two intervals, @c false otherwise. + */ bool has_intersection_with(self_type const &that) const; /** Compute the intersection of two intervals - @return The interval consisting of values that are contained by - both intervals. This may be the empty interval if the intervals - are disjoint. - @internal Co-variant + * + * @return The interval consisting of values that are contained by both intervals. The return range + * is empty if the intervals are disjoint. + * + * @internal Co-variant */ self_type intersection(self_type const &that) const; /** Test for adjacency. - @return @c true if the intervals are adjacent. - @note Only disjoint intervals can be adjacent. + * @return @c true if the intervals are adjacent. + * @note Only disjoint intervals can be adjacent. */ bool is_adjacent_to(self_type const &that) const; - /** Test for @a this being adjacent on the left of @a that. + /** Lower adjacency. * * @param that Range to check for adjacency. - * @return @c true if @a this ends exactly the value before @a that begins. + * @return @c true if @a this and @ta that are adjacent and @a this is less than @a that. */ bool is_left_adjacent_to(self_type const &that) const; - //! Test if the union of two intervals is also an interval. + /** Valid union. + * + * @param that Range to compare. + * + * @return @a true if the hull of @a this and @a that contains only elements that are also in + * @a this or @a that. + */ bool has_union(self_type const &that) const; - /** Test if an interval is a superset of or equal to another. - @return @c true if every value in @c that is also in @c this. + /** Superset test. + * + * @return @c true if every value in @c that is also in @c this. */ bool is_superset_of(self_type const &that) const; - /** Test if an interval is a subset or equal to another. - @return @c true if every value in @c this is also in @c that. + /** Subset test. + * + * @return @c true if every value in @c this is also in @c that. */ bool is_subset_of(self_type const &that) const; - /** Test if an interval is a strict superset of another. - @return @c true if @c this is strictly a superset of @a rhs. + /** Strict superset test. + * + * @return @c true if @a this contains every value in @a this and @a this has at least one value not + * in @a that. */ bool is_strict_superset_of(self_type const &that) const; - /** Test if an interval is a strict subset of another. - @return @c true if @c this is strictly a subset of @a that. + /** Strict subset test. + * + * @return @c true if @a that contains every value in @a this and @a that has at least one value not + * in @a this. */ bool is_strict_subset_of(self_type const &that) const; - /** Determine the relationship between @c this and @a that interval. - @return The relationship type. + /** Generic relationship. + * + * @return The relationship between @a this and @a that. */ Relation relationship(self_type const &that) const; @@ -292,18 +322,11 @@ public: * - OVLP: @a that left edge is inside @a this. * - NONE: @a that left edge is left of @a this. */ - EdgeRelation - left_edge_relationship(self_type const &that) const { - if (_max < that._max) { - return ++metric_type(_max) < that._max ? EdgeRelation::GAP : EdgeRelation::ADJ; - } - return _min >= that._min ? EdgeRelation::NONE : EdgeRelation::OVLP; - } + EdgeRelation left_edge_relationship(self_type const &that) const; - /** Compute the convex hull of this interval and another one. - @return The smallest interval that is a superset of @c this - and @a that interval. - @internal Co-variant + /** Convex hull. + * @return The smallest interval that is a superset of @c this and @a that. + * @internal Co-variant */ self_type hull(self_type const &that) const; @@ -313,36 +336,32 @@ public: /** Test for empty, operator form. @return @c true if the interval is empty, @c false otherwise. */ - bool - operator!() const { - return _min > _max; - } + bool operator!() const; /** Test for non-empty. * * @return @c true if there values in the range, @c false if no values in the range. */ - explicit operator bool() const { return _min <= _max; } + explicit operator bool() const; - /// @return @c true if the range is maximal, @c false otherwise. + /** Maximality. + * @return @c true if this range contains every value. + */ bool is_maximal() const; /** Clip interval. - Remove all element in @c this interval not in @a that interval. + * Remove all element in @c this interval not in @a that interval. */ self_type &operator&=(self_type const &that); /** Convex hull. - Extend interval to cover all elements in @c this and @a that. + * Minimally extend @a this to cover all elements in @c this and @a that. + * @return @a this. */ self_type &operator|=(self_type const &that); - self_type & - clear() { - _min = detail::maximum<T>(); - _max = detail::minimum<T>(); - return *this; - } + /// Make the range empty. + self_type & clear(); /** Functor for lexicographic ordering. If, for some reason, an interval needs to be put in a container @@ -368,6 +387,47 @@ public: }; }; +template <typename T> +bool +DiscreteRange<T>::operator==(DiscreteRange::self_type const &that) const { + return _min == that._min && _max == that._max; +} + +template <typename T> +bool +DiscreteRange<T>::operator!=(DiscreteRange::self_type const &that) const { + return _min != that._min | _max != that._max; +} + +template <typename T> +auto +DiscreteRange<T>::clip_max() -> self_type & { + --_max; + return *this; +} + +template <typename T> +bool +DiscreteRange<T>::operator!() const { + return _min > _max; +} + +template <typename T> DiscreteRange<T>::operator bool() const { return _min <= _max; } + +template <typename T> +bool +DiscreteRange<T>::contains(metric_type const &value) const { + return _min <= value && value <= _max; +} + +template <typename T> +auto +DiscreteRange<T>::clear() -> self_type & { + _min = detail::maximum<T>(); + _max = detail::minimum<T>(); + return *this; +} + template <typename T> bool DiscreteRange<T>::lexicographic_order::operator()(DiscreteRange::self_type const &lhs, DiscreteRange::self_type const &rhs) const { @@ -390,8 +450,17 @@ DiscreteRange<T>::hull(DiscreteRange::self_type const &that) const { } template <typename T> -typename DiscreteRange<T>::Relation -DiscreteRange<T>::relationship(self_type const &that) const { +auto +DiscreteRange<T>::left_edge_relationship(self_type const &that) const -> EdgeRelation { + if (_max < that._max) { + return ++metric_type(_max) < that._max ? EdgeRelation::GAP : EdgeRelation::ADJ; + } + return _min >= that._min ? EdgeRelation::NONE : EdgeRelation::OVLP; +} + +template <typename T> +auto +DiscreteRange<T>::relationship(self_type const &that) const -> Relation{ Relation retval = Relation::NONE; if (this->has_intersection(that)) { if (*this == that) @@ -534,7 +603,8 @@ DiscreteRange<T>::is_left_adjacent_to(DiscreteRange::self_type const &that) cons * T has a modulus and not depend on ++t > t always being true. However, we know that if t1 > * t0 then ++t0 > t0. */ - return _max < that._min && ++metric_type(_max) == that._min; + metric_type max(_max); // some built in integer types don't support increment on rvalues. + return _max < that._min && ++max == that._min; } template <typename T> @@ -543,10 +613,6 @@ DiscreteRange<T>::intersection(DiscreteRange::self_type const &that) const { return {std::max(_min, that._min), std::min(_max, that._max)}; } -/** Equality. - Two intervals are equal if their min and max values are equal. - @relates DiscreteRange - */ template <typename T> bool operator==(DiscreteRange<T> const &lhs, DiscreteRange<T> const &rhs) { diff --git a/lib/swoc/include/swoc/IPRange.h b/lib/swoc/include/swoc/IPRange.h index aa93d17e65..b6e68f5ce3 100644 --- a/lib/swoc/include/swoc/IPRange.h +++ b/lib/swoc/include/swoc/IPRange.h @@ -8,22 +8,19 @@ #include <string_view> #include <variant> // for std::monostate +#include <tuple> #include <swoc/DiscreteRange.h> #include <swoc/IPAddr.h> namespace swoc { inline namespace SWOC_VERSION_NS { -using ::std::string_view; +using std::string_view; class IP4Net; class IP6Net; class IPNet; -namespace detail { -extern void *const pseudo_nullptr; -} - /** An inclusive range of IPv4 addresses. */ class IP4Range : public DiscreteRange<IP4Addr> { @@ -33,7 +30,7 @@ class IP4Range : public DiscreteRange<IP4Addr> { public: /// Default constructor, invalid range. - IP4Range() = default; + constexpr IP4Range() = default; /// Construct from an network expressed as @a addr and @a mask. IP4Range(IP4Addr const &addr, IPMask const &mask); @@ -47,8 +44,10 @@ public: * @param text Range text. * @see IP4Range::load * - * This results in a zero address if @a text is not a valid string. If this should be checked, + * This results in an empty range if @a text is not a valid string. If this should be checked, * use @c load. + * + * @see load */ IP4Range(string_view const &text); @@ -186,9 +185,9 @@ public: /** Construct range from @a text. * * @param text Range text. - * @see IP4Range::load + * @see IP6Range::load * - * This results in a zero address if @a text is not a valid string. If this should be checked, + * This results in an empty range if @a text is not a valid string. If this should be checked, * use @c load. */ IP6Range(string_view const &text); @@ -311,6 +310,8 @@ protected: bool is_valid(IPMask const &mask); }; +class IPRangeView; // Forward declare. + /** Range of IP addresses. * Although this can hold IPv4 or IPv6, any specific instance is one or the other, this can never contain * a range of different address families. @@ -335,6 +336,7 @@ public: * @param max Maximum range value. */ IPRange(IP4Addr const &min, IP4Addr const &max); + /** Construct an inclusive range. * * @param min Minimum range value. @@ -346,8 +348,8 @@ public: * * @param addr Address of range. */ - IPRange(IPAddr const& addr) : IPRange(addr, addr) {} + /** Construct a singleton range. * * @param addr Address of range. @@ -366,6 +368,8 @@ public: /// Construct from an IPv6 @a range. IPRange(IP6Range const &range); + /// Construct from view. + IPRange(IPRangeView const& view); /** Construct from a string format. * @@ -375,25 +379,35 @@ public: */ IPRange(string_view const &text); + /** Update with an IPv4 range. + * + * @param min Minimum value. + * @param max Maximum value. + * @return @a this. + */ self_type & assign(IP4Addr const& min, IP4Addr const& max); + + /** Update with an IPv6 range. + * + * @param min Minimum address. + * @param max Maximum address. + * @return @a this + */ self_type & assign(IP6Addr const& min, IP6Addr const& max); + /// Assign from view. + self_type & operator = (IPRangeView const& rv); + /// Equality bool operator==(self_type const &that) const; /// Inequality bool operator!=(self_type const& that) const; /// @return @c true if this is an IPv4 range, @c false if not. - bool - is_ip4() const { - return AF_INET == _family; - } + bool is_ip4() const; /// @return @c true if this is an IPv6 range, @c false if not. - bool - is_ip6() const { - return AF_INET6 == _family; - } + bool is_ip6() const; /** Check if @a this range is the IP address @a family. * @@ -421,6 +435,9 @@ public: /// @return @c true if there are no addresses in the range. bool empty() const; + /// Make the range empty / invalid. + self_type & clear(); + /// @return The IPv4 range. IP4Range const & ip4() const { return _range._ip4; } @@ -438,6 +455,7 @@ public: */ IPMask network_mask() const; + /// Container for remaining range for network generation. class NetSource; /** Generate a list of networks covering @a this range. @@ -473,6 +491,7 @@ protected: IP4Range _ip4; ///< IPv4 range. IP6Range _ip6; ///< IPv6 range. } _range{std::monostate{}}; + /// Family of @a _range. sa_family_t _family{AF_UNSPEC}; }; @@ -490,6 +509,9 @@ public: /// Construct from @a range. explicit NetSource(range_type const &range); + /// Construct from view. + explicit NetSource(IPRangeView const& rv); + /// Copy constructor. NetSource(self_type const &that) = default; @@ -533,9 +555,98 @@ protected: IP4Range::NetSource _ip4; ///< IPv4 addresses. IP6Range::NetSource _ip6; ///< IPv6 addresses. }; + sa_family_t _family = AF_UNSPEC; ///< Mark for union content. }; +/** View of a range. + * + * The point of this is @c IPRange is really a union on top of @c IP4Range and @c IP6Range therefore + * using it requires copying. A view enable using an IPv4 or IPv6 range as a generic range without + * the copy. This is useful in situations where performance is critical. + * + * Used primarily for iterator implementation where the ranges are stored in family specific ranges + * in the container. The iterator can use this to provide access as an @c IPRange without a copying + * every iteration. + * + * @note An earlier implementation used references and updated those to achieve this same effect but + * that appeared to confusion the optimizer and odd effects would happen at higher optimization levels + * (in particular, iterators would appear to not actually increment even thought the increment code + * was invoked). + */ +class IPRangeView { + using self_type = IPRangeView; + friend IPRange; + /// Storage for the view pointer - union because only one can be valid. + using storage_type = union { + std::monostate _nil; ///< No data present. + IP4Range const * _4; ///< IPv4 range. + IP6Range const * _6; ///< IPv6 range. + }; +public: + /// Default constructor - invalid view. + IPRangeView() = default; + + /// Compare to a range. + bool operator == (IPRange const& r) const; + + /// @return @c true if there is no valid view to a range. + bool valid() const; + + // @return @c true if the range is empty (invalid views are empty) + bool empty() const; + + /// Reset to invalid view. + self_type & clear(); + + /// Update the view. + self_type & assign(IP4Range const& r); + + /// Update the view. + self_type & assign(IP6Range const& r); + + /// @return @c true if this is an IPv4 range. + bool is_ip4() const; + + /// @return @c true if this is an IPv6 range. + bool is_ip6() const; + + /// @return Reference to the viewed IPv4 range. + IP4Range const& ip4() const; + + /// @return Reference to the viewd IPv6 range. + IP6Range const& ip6() const; + + /// @{ + /// Forwarding methods. + + /// @return The minimum address in the range. + IPAddr min() const; + + /// @return The maximum value in the range. + IPAddr max() const; + + /// Equality. + bool operator == (self_type const& that) const; + + /// Inequality. + bool operator != (self_type const& that) const; + + /** Generate a list of networks covering @a this range. + * + * @return A network generator. + * + * @see IPRange::networks + */ + IPRange::NetSource networks() const { return IPRange::NetSource(*this); } + + /// @} + +// protected: + storage_type _raw = { std::monostate() }; ///< Storage for the view pointer. + sa_family_t _family = AF_UNSPEC; ///< Range address family. +}; + /// An IPv4 network. class IP4Net { using self_type = IP4Net; ///< Self reference type. @@ -563,8 +674,8 @@ public: */ bool load(swoc::TextView text); - /// @return @c true if the network is valid, @c false if not. - bool is_valid() const; + /// @return @c true if the network contains no addresses (is invalid). + bool empty() const; /// @return Network address - smallest address in the network. IP4Addr min() const; @@ -572,12 +683,6 @@ public: /// @return The largest address in the network. IP4Addr max() const; - /// @return Network address. - [[deprecated]] IP4Addr lower_bound() const; - - /// @return The largest address in the network. - [[deprecated]] IP4Addr upper_bound() const; - /// @return The mask for the network. IPMask const &mask() const; @@ -624,6 +729,15 @@ public: */ IP6Net(IP6Addr addr, IPMask mask); + /** Construct from text. + * + * @param text Network description. + * + * The format must be "addr/mask" where "addr" is a valid address and mask is either a single + * number for the mask width (CIDR) or a mask in address notation. + */ + IP6Net(TextView text) { this->load(text); } + /** Parse network as @a text. * * @param text String describing the network in CIDR format. @@ -631,8 +745,8 @@ public: */ bool load(swoc::TextView text); - /// @return @c true if the network is valid, @c false if not. - bool is_valid() const; + /// @return @c true if the network contains no addresses (is invalid). + bool empty() const; /// @return Network address - smallest address in the network. IP6Addr min() const; @@ -640,12 +754,6 @@ public: /// @return Largest address in the network. IP6Addr max() const; - /// @return THh smallest address in the network. - [[deprecated]] IP6Addr lower_bound() const; - - /// @return The largest address in the network. - [[deprecated]] IP6Addr upper_bound() const; - /// @return The mask for the network. IPMask const &mask() const; @@ -698,6 +806,13 @@ public: */ IPNet(IPAddr const &addr, IPMask const &mask); + /** Construct from text. + * + * @param text Network description. + * + * The format must be "addr/mask" where "addr" is a valid address and mask is either a single + * number for the mask width (CIDR) or a mask in address notation. + */ IPNet(TextView text); /** Parse network as @a text. @@ -707,8 +822,8 @@ public: */ bool load(swoc::TextView text); - /// @return @c true if the network is valid, @c false if not. - bool is_valid() const; + /// @return @c true if the network contains no addresses (is invalid). + bool empty() const; /// @return Network address - smallest address in the network. IPAddr min() const; @@ -716,12 +831,7 @@ public: /// @return Largest address in the network. IPAddr max() const; - /// @return THh smallest address in the network. - [[deprecated]] IPAddr lower_bound() const; - - /// @return The largest address in the network. - [[deprecated]] IPAddr upper_bound() const; - + /// @return The number of bits in the mask. IPMask::raw_type width() const; /// @return The mask for the network. @@ -730,21 +840,20 @@ public: /// @return A range that exactly covers the network. IPRange as_range() const; + /// @return @c true if network is IPv4. bool is_ip4() const { return _addr.is_ip4(); } + /// @return @c true if network is IPv6. bool is_ip6() const { return _addr.is_ip6(); } + /// Address family of network. sa_family_t family() const { return _addr.family(); } - IP4Net - ip4() const { - return IP4Net{_addr.ip4(), _mask}; - } + /// @return The network as explicitly IPv4. + IP4Net ip4() const; - IP6Net - ip6() const { - return IP6Net{_addr.ip6(), _mask}; - } + /// @return The network as explicitly IPv6. + IP6Net ip6() const; /** Assign an @a addr and @a mask to @a this. * @@ -755,11 +864,7 @@ public: self_type &assign(IPAddr const &addr, IPMask const &mask); /// Reset network to invalid state. - self_type & - clear() { - _mask.clear(); - return *this; - } + self_type & clear(); /// Equality. bool operator==(self_type const &that) const; @@ -773,6 +878,135 @@ protected: }; // -------------------------------------------------------------------------- +namespace detail { +/** Value type for @c IPSpace constant iterator. + * + * @tparam PAYLOAD User data type to be stored. + * + * @internal Exported because inner classes in template classes cannot be used in partial + * specialization which is required for tuple support. + * + * @internal The value type must be split in two to provide @c const consistency. The structured + * bidingin types are determined only by the type of the value, therefore if the structured binding + * should vary in whether the payload binding is @c const then there must be two types. I tested using + * @c const in the typle type templates but the binding always used the @c const variant regardless of + * the @const state of the payload instance. + * + * @note The @c assign methods are used to update iterators. + * + * @see IPSpace + */ +template < typename PAYLOAD > struct ip_space_const_value_type { + using self_type = ip_space_const_value_type; + + IPRangeView _rv; ///< View to the current range. + PAYLOAD * _payload = nullptr; ///< Payload for @a _range. + + ip_space_const_value_type() = default; + + /// Reset to default constructed state. + self_type & clear(); + + /** Update the internal state. + * + * @param r Range. + * @param payload Payload. + * @return @a this. + */ + self_type & assign(IP4Range const& r, PAYLOAD & payload); + + /** Update the internal state. + * + * @param r Range. + * @param payload Payload. + * @return @a this. + */ + self_type & assign(IP6Range const& r, PAYLOAD & payload); + + /** Update from another instance. + * + * @param that Other instance. + * @return @a this. + */ + self_type & assign(self_type const& that); + + /// Support assignemnt to @c std::tie. + std::tuple<IPRangeView, PAYLOAD &> tuple() const { return { _rv, *_payload }; } + + /// Equality against an equivalent tuple. + bool operator == (std::tuple<swoc::IPRange, PAYLOAD> const& t) const; + + /// @return The address range. + IPRange range() const { return _rv; } + + /// @return The range view. + IPRangeView range_view() const { return _rv; } + + /// @return A reference to the payload (user content). + PAYLOAD const & payload() const { return *_payload; } +}; + +/** Value type for @c IPSpace. + * + * @tparam PAYLOAD User data type to be stored. + * + * @internal Exported because inner classes in template classes cannot be used in partial + * specialization which is required for tuple support. This should be removed next API incompatible + * change release because tuple access is being deprecated. + * + * @note The @c assign methods are used to update iterators. + * + * @see IPSpace + */ +template < typename PAYLOAD > struct ip_space_value_type : ip_space_const_value_type<PAYLOAD> { + using self_type = ip_space_value_type; + using super_type = ip_space_const_value_type<PAYLOAD>; + + /// @return A reference to the payload (user content). + PAYLOAD & payload() { return *super_type::_payload; } + +}; + +template <typename PAYLOAD> +auto +ip_space_const_value_type<PAYLOAD>::clear() -> self_type & { + _rv.clear(); + _payload = nullptr; + return *this; +} + +template <typename PAYLOAD> +auto +ip_space_const_value_type<PAYLOAD>::assign(IP4Range const &r, PAYLOAD &payload) -> self_type &{ + _rv.assign(r); + _payload = &payload; + return *this; +} + +template <typename PAYLOAD> +auto +ip_space_const_value_type<PAYLOAD>::assign(IP6Range const &r, PAYLOAD &payload) -> self_type & { + _rv.assign(r); + _payload = &payload; + return *this; +} + +template <typename PAYLOAD> +auto +ip_space_const_value_type<PAYLOAD>::assign(ip_space_const_value_type::self_type const &that) -> self_type & { + _rv = that._rv; + _payload = that._payload; + return *this; +} + +template <typename PAYLOAD> +bool +ip_space_const_value_type<PAYLOAD>::operator==(std::tuple<swoc::IPRange, PAYLOAD> const &t) const { + return _rv == std::get<0>(t) && std::get<1>(t) == *_payload; +} + +} // namespace detail + /** Coloring of IP address space. * * @tparam PAYLOAD The color class. @@ -793,7 +1027,8 @@ template <typename PAYLOAD> class IPSpace { public: using payload_t = PAYLOAD; ///< Export payload type. - using value_type = std::tuple<IPRange const, PAYLOAD &>; + /// Iterator value, a range and payload. + using value_type = detail::ip_space_value_type<PAYLOAD>; /// Construct an empty space. IPSpace() = default; @@ -850,8 +1085,7 @@ public: self_type & blend(IP4Range const &range, U const &color, F &&blender); template <typename F, typename U = PAYLOAD> - self_type & - blend(IP6Range const &range, U const &color, F &&blender); + self_type & blend(IP6Range const &range, U const &color, F &&blender); /// @return The number of distinct ranges. size_t count() const; @@ -898,11 +1132,11 @@ public: friend class IPSpace; public: - using value_type = std::tuple<IPRange const, PAYLOAD const &>; /// Import for API compliance. + using value_type = detail::ip_space_const_value_type<PAYLOAD>; /// Import for API compliance. // STL algorithm compliance. using iterator_category = std::bidirectional_iterator_tag; - using pointer = value_type *; - using reference = value_type &; + using pointer = value_type const *; + using reference = value_type const &; using difference_type = int; /// Default constructor. @@ -936,31 +1170,11 @@ public: /// Dereference. /// @return A reference to the referent. - value_type operator*() const; + reference operator*() const; /// Dereference. /// @return A pointer to the referent. - value_type const *operator->() const; - - /** The range for the iterator. - * - * @return Iterator range. - * - * @note If the iterator is not valid the returned range will be empty. - */ - IPRange const& range() const; - - /** The payload for the iterator. - * - * @return The payload. - * - * @note This yields undetermined results for invalid iterators. Always check for validity befure - * using this method. - * - * @note It is not possible to retrieve a modifiable payload because that can break the internal - * invariant that adjcent ranges always have different payloads. - */ - PAYLOAD const& payload() const; + pointer operator->() const; /// Equality bool operator==(self_type const &that) const; @@ -977,7 +1191,7 @@ public: typename IP4Space::iterator _iter_4; ///< IPv4 sub-space iterator. typename IP6Space::iterator _iter_6; ///< IPv6 sub-space iterator. /// Current value. - value_type _value{IPRange{}, *static_cast<PAYLOAD *>(detail::pseudo_nullptr)}; + value_type _value; /** Internal constructor. * @@ -987,6 +1201,14 @@ public: * In practice, at most one iterator should be "internal", the other should be the beginning or end. */ const_iterator(typename IP4Space::iterator const &iter4, typename IP6Space::iterator const &iter6); + + // These are required because the actual range type is @c DiescreteRange<T> and returning as a + // range class creates a temporary which in turns causes some nasty side effects. + + /// @return Properly type cast range from iterator. + IP4Range const& r4() { return static_cast<IP4Range const&>(_iter_4->range()); } + /// @return Properly type cast range from iterator. + IP6Range const& r6() { return static_cast<IP6Range const&>(_iter_6->range()); } }; /** Iterator. @@ -1008,7 +1230,7 @@ public: iterator(const_iterator const& that) : const_iterator(that) {} public: /// Value type of iteration. - using value_type = std::tuple<IPRange const, PAYLOAD &>; + using value_type = detail::ip_space_value_type<PAYLOAD>; using pointer = value_type *; using reference = value_type &; @@ -1034,31 +1256,24 @@ public: /// Post-increment. /// Move to the next element in the list. /// @return The iterator value before the increment. - self_type - operator++(int) { - self_type zret{*this}; - ++*this; - return zret; - } + self_type operator++(int); /// Post-decrement. /// Move to the previous element in the list. /// @return The iterator value before the decrement. - self_type - operator--(int) { - self_type zret{*this}; - --*this; - return zret; - } + self_type operator--(int); /// Dereference. /// @return A reference to the referent. - value_type operator*() const; + reference operator*() const; /// Dereference. /// @return A pointer to the referent. - value_type const *operator->() const; + pointer operator->() const; + protected: + // Retrieve the value and convert from the super (const) type value to the value for iterator. + value_type & value() const; }; /** Find the payload for an @a addr. @@ -1254,12 +1469,11 @@ public: friend class IPRangeSet; public: - using value_type = IPRange const; + using value_type = IPRangeView; // STL algorithm compliance. using iterator_category = std::bidirectional_iterator_tag; - using pointer = value_type *; - using reference = value_type &; - using const_reference = value_type const &; + using pointer = value_type const *; + using reference = value_type const &; using difference_type = int; /// Default constructor. @@ -1293,11 +1507,11 @@ public: /// Dereference. /// @return A reference to the referent. - value_type const& operator*() const; + reference operator*() const; /// Dereference. /// @return A pointer to the referent. - value_type const *operator->() const; + pointer operator->() const; /// Equality bool operator==(self_type const &that) const; @@ -1309,6 +1523,7 @@ public: const_iterator(super_type const& spot) : _iter(spot) {} super_type _iter; ///< Underlying iterator. + IPRange _r; ///< Some less temporary storage for dereferences. }; using iterator = const_iterator; @@ -1376,9 +1591,9 @@ template <typename PAYLOAD> IPSpace<PAYLOAD>::const_iterator::const_iterator(typename IP4Space::iterator const &iter4, typename IP6Space::iterator const &iter6) : _iter_4(iter4), _iter_6(iter6) { if (_iter_4.has_next()) { - new (&_value) value_type{_iter_4->range(), _iter_4->payload()}; + _value.assign(this->r4(), _iter_4->payload()); } else if (_iter_6.has_next()) { - new (&_value) value_type{_iter_6->range(), _iter_6->payload()}; + _value.assign(this->r6(), _iter_6->payload()); } } @@ -1387,7 +1602,7 @@ auto IPSpace<PAYLOAD>::const_iterator::operator=(self_type const &that) -> self_type & { _iter_4 = that._iter_4; _iter_6 = that._iter_6; - new (&_value) value_type{that._value}; + _value.assign(that._value); return *this; } @@ -1399,18 +1614,18 @@ IPSpace<PAYLOAD>::const_iterator::operator++() -> self_type & { ++_iter_4; incr_p = true; if (_iter_4.has_next()) { - new (&_value) value_type{_iter_4->range(), _iter_4->payload()}; + _value.assign(this->r4(), _iter_4->payload()); return *this; } } if (_iter_6.has_next()) { if (incr_p || (++_iter_6).has_next()) { - new (&_value) value_type{_iter_6->range(), _iter_6->payload()}; + _value.assign(this->r6(), _iter_6->payload()); return *this; } } - new (&_value) value_type{IPRange{}, *static_cast<PAYLOAD *>(detail::pseudo_nullptr)}; + _value.clear(); return *this; } @@ -1427,15 +1642,15 @@ auto IPSpace<PAYLOAD>::const_iterator::operator--() -> self_type & { if (_iter_6.has_prev()) { --_iter_6; - new (&_value) value_type{_iter_6->range(), _iter_6->payload()}; + _value.assign(this->r6(), _iter_6->payload()); return *this; } if (_iter_4.has_prev()) { --_iter_4; - new (&_value) value_type{_iter_4->range(), _iter_4->payload()}; + _value.assign(this->r4(), _iter_4->payload()); return *this; } - new (&_value) value_type{IPRange{}, *static_cast<PAYLOAD *>(detail::pseudo_nullptr)}; + _value.clear(); return *this; } @@ -1449,24 +1664,16 @@ IPSpace<PAYLOAD>::const_iterator::operator--(int) -> self_type { template <typename PAYLOAD> auto -IPSpace<PAYLOAD>::const_iterator::operator*() const -> value_type { +IPSpace<PAYLOAD>::const_iterator::operator*() const -> reference { return _value; } template <typename PAYLOAD> auto -IPSpace<PAYLOAD>::const_iterator::operator->() const -> value_type const * { +IPSpace<PAYLOAD>::const_iterator::operator->() const -> pointer { return &_value; } -template <typename PAYLOAD> -IPRange const & -IPSpace<PAYLOAD>::const_iterator::range() const { return std::get<0>(_value); } - -template <typename PAYLOAD> -PAYLOAD const & -IPSpace<PAYLOAD>::const_iterator::payload() const { return std::get<1>(_value); } - /* Bit of subtlety with equality - although it seems that if @a _iter_4 is valid, it doesn't matter * where @a _iter6 is (because it is really the iterator location that's being checked), it's * necessary to do the @a _iter_4 validity on both iterators to avoid the case of a false positive @@ -1496,14 +1703,20 @@ IPSpace<PAYLOAD>::iterator::operator=(self_type const &that) -> self_type & { template <typename PAYLOAD> auto -IPSpace<PAYLOAD>::iterator::operator->() const -> value_type const * { - return static_cast<value_type *>(&super_type::_value); +IPSpace<PAYLOAD>::iterator::value() const -> value_type & { + return reinterpret_cast<value_type&>(const_cast<self_type*>(this)->_value); +} + +template <typename PAYLOAD> +auto +IPSpace<PAYLOAD>::iterator::operator->() const -> pointer { + return &(this->value()); } template <typename PAYLOAD> auto -IPSpace<PAYLOAD>::iterator::operator*() const -> value_type { - return reinterpret_cast<value_type const &>(super_type::_value); +IPSpace<PAYLOAD>::iterator::operator*() const -> reference { + return this->value(); } template <typename PAYLOAD> @@ -1513,6 +1726,14 @@ IPSpace<PAYLOAD>::iterator::operator++() -> self_type & { return *this; } +template <typename PAYLOAD> +auto +IPSpace<PAYLOAD>::iterator::operator++(int) -> self_type { + self_type zret{*this}; + ++*this; + return zret; +} + template <typename PAYLOAD> auto IPSpace<PAYLOAD>::iterator::operator--() -> self_type & { @@ -1520,7 +1741,14 @@ IPSpace<PAYLOAD>::iterator::operator--() -> self_type & { return *this; } -// -------------------------------------------------------------------------- +template <typename PAYLOAD> +auto +IPSpace<PAYLOAD>::iterator::operator--(int) -> self_type { + self_type zret{*this}; + --*this; + return zret; +} + /// ------------------------------------------------------------------------------------ // +++ IPRange +++ @@ -1563,6 +1791,14 @@ inline IPRange::IPRange(string_view const &text) { this->load(text); } +inline IPRange::IPRange(IPRangeView const& view) { + if (AF_INET == view._family) { + *this = *view._raw._4; + } else if (AF_INET6 == _family) { + *this = *view._raw._6; + } +} + inline auto IPRange::assign(IP4Addr const &min, IP4Addr const &max) -> self_type & { _range._ip4.assign(min, max); @@ -1577,6 +1813,22 @@ IPRange::assign(IP6Addr const &min, IP6Addr const &max) -> self_type & { return *this; } +inline auto +IPRange::operator=(const IPRangeView &rv) -> self_type & { + if (AF_INET == rv._family) { + *this = *rv._raw._4; + } else if (AF_INET6 == rv._family) { + *this = *rv._raw._6; + } + return *this; +} + +inline auto +IPRange::clear() -> self_type & { + _family = AF_UNSPEC; + return *this; +} + inline auto IPRange::networks() const -> NetSource { return {NetSource{*this}}; @@ -1592,6 +1844,68 @@ IPRange::operator!=(const self_type &that) const { return ! (*this == that); } +inline bool +IPRange::is_ip4() const { + return AF_INET == _family; +} + +inline bool IPRange::is_ip6() const { + return AF_INET6 == _family; +} + +inline auto +IPRangeView::clear() -> self_type & { + _family = AF_UNSPEC; + return *this; +} + +inline bool +IPRangeView::empty() const { + return AF_INET6 == _family ? _raw._6->empty() + : AF_INET == _family ? _raw._4->empty() + : true; +} + +inline bool IPRangeView::is_ip4() const { return AF_INET == _family; } + +inline bool IPRangeView::is_ip6() const { return AF_INET6 == _family; } + +inline IP4Range const & IPRangeView::ip4() const { return *_raw._4; } + +inline IP6Range const & IPRangeView::ip6() const { return *_raw._6; } + +inline bool +IPRangeView::valid() const { return AF_INET == _family || AF_INET6 == _family; } + +inline auto +IPRangeView::assign(IP4Range const &r) -> self_type & { + _family = r.family(); + _raw._4 = &r; + return *this; +} + +inline auto +IPRangeView::assign(IP6Range const &r) -> self_type & { + _family = r.family(); + _raw._6 = &r; + return *this; +} + +inline bool +IPRangeView::operator!=(IPRangeView::self_type const &that) const { + return ! (*this == that); +} + +inline IPAddr +IPRangeView::min() const { + return AF_INET == _family ? _raw._4->min() : AF_INET6 == _family ? _raw._6->min() : IPAddr::INVALID; +} + +inline IPAddr +IPRangeView::max() const { + return AF_INET == _family ? _raw._4->max() : AF_INET6 == _family ? _raw._6->max() : IPAddr::INVALID; +} + // +++ IPNet +++ inline IP4Net::IP4Net(swoc::IP4Addr addr, swoc::IPMask mask) : _addr(addr & mask), _mask(mask) {} @@ -1602,8 +1916,8 @@ IP4Net::mask() const { } inline bool -IP4Net::is_valid() const { - return _mask.is_valid(); +IP4Net::empty() const { + return ! _mask.is_valid(); } inline IP4Addr @@ -1616,16 +1930,6 @@ IP4Net::max() const { return _addr | _mask; } -inline IP4Addr -IP4Net::lower_bound() const { - return this->min(); -} - -inline IP4Addr -IP4Net::upper_bound() const { - return this->max(); -} - inline IP4Range IP4Net::as_range() const { return {this->min(), this->max()}; @@ -1656,8 +1960,8 @@ IP6Net::mask() const { } inline bool -IP6Net::is_valid() const { - return _mask.is_valid(); +IP6Net::empty() const { + return ! _mask.is_valid(); } inline IP6Addr @@ -1670,9 +1974,6 @@ IP6Net::max() const { return _addr | _mask; } -inline IP6Addr IP6Net::lower_bound() const { return this->min(); } -inline IP6Addr IP6Net::upper_bound() const { return this->max(); } - inline IP6Range IP6Net::as_range() const { return {this->min(), this->max()}; @@ -1702,8 +2003,8 @@ inline IPNet::IPNet(TextView text) { } inline bool -IPNet::is_valid() const { - return _mask.is_valid(); +IPNet::empty() const { + return ! _mask.is_valid(); } inline IPAddr @@ -1716,9 +2017,6 @@ IPNet::max() const { return _addr | _mask; } -inline IPAddr IPNet::lower_bound() const { return this->min(); } -inline IPAddr IPNet::upper_bound() const { return this->max(); } - inline IPMask::raw_type IPNet::width() const { return _mask.width(); @@ -1771,6 +2069,22 @@ operator==(IP6Net const &lhs, IPNet const &rhs) { return rhs.is_ip6() && rhs.ip6() == lhs; } +inline IP4Net +IPNet::ip4() const { + return IP4Net{_addr.ip4(), _mask}; +} + +inline IP6Net +IPNet::ip6() const { + return IP6Net{_addr.ip6(), _mask}; +} + +inline auto +IPNet::clear() -> self_type & { + _mask.clear(); + return *this; +} + // +++ Range -> Network classes +++ inline bool @@ -1883,6 +2197,16 @@ inline IPRange::NetSource::NetSource(IPRange::NetSource::range_type const &range } } +inline IPRange::NetSource::NetSource(IPRangeView const &rv) { + if (rv.is_ip4()) { + new (&_ip4) decltype(_ip4)(rv.ip4()); + _family = AF_INET; + } else if (rv.is_ip6()) { + new (&_ip6) decltype(_ip6)(rv.ip6()); + _family = AF_INET6; + } +} + inline auto IPRange::NetSource::begin() const -> iterator { return *this; @@ -2179,13 +2503,13 @@ IPRangeSet::const_iterator::operator--(int) -> self_type { } inline auto -IPRangeSet::const_iterator::operator*() const -> value_type const& { - return _iter.range(); +IPRangeSet::const_iterator::operator*() const -> reference { + return _iter->_rv; } inline auto -IPRangeSet::const_iterator::operator->() const -> value_type const * { - return &(_iter.range()); +IPRangeSet::const_iterator::operator->() const -> pointer { + return &(_iter->_rv); } inline bool @@ -2284,3 +2608,69 @@ get(swoc::IPNet const &net) { /// @endcond }} // namespace swoc::SWOC_VERSION_NS + +// Tuple support for IPSpace values. +/// @cond NOT_DOCUMENTED + +namespace std { + +template <typename P> class tuple_size<swoc::detail::ip_space_value_type<P>> : public integral_constant<size_t, 2> {}; +template <typename P> class tuple_size<swoc::detail::ip_space_const_value_type<P>> : public integral_constant<size_t, 2> {}; + +template <size_t IDX, typename P> class tuple_element<IDX, swoc::detail::ip_space_value_type<P>> { + static_assert("swoc::IPSpace::value_type tuple index out of range"); +}; +template <size_t IDX, typename P> class tuple_element<IDX, swoc::detail::ip_space_const_value_type<P>> { + static_assert("swoc::IPSpace::value_type tuple index out of range"); +}; + +template <typename P> class tuple_element<0, swoc::detail::ip_space_value_type<P>> { +public: + using type = swoc::IPRangeView; +}; + +template <typename P> class tuple_element<1, swoc::detail::ip_space_value_type<P>> { +public: + using type = P &; +}; + +template <typename P> class tuple_element<0, swoc::detail::ip_space_const_value_type<P>> { +public: + using type = swoc::IPRangeView; +}; + +template <typename P> class tuple_element<1, swoc::detail::ip_space_const_value_type<P>> { +public: + using type = P const &; +}; + +} // namespace std + +namespace swoc { inline namespace SWOC_VERSION_NS { namespace detail { +template <size_t IDX, typename P> +auto +get(ip_space_const_value_type<P> const& p) -> typename std::tuple_element<IDX, ip_space_const_value_type<P>>::type { + if constexpr (IDX == 0) { + return p._rv; + } else if constexpr (IDX == 1) { + return *(p._payload); + } +} + +template <size_t IDX, typename P> +auto +get(ip_space_value_type<P> const& p) -> typename std::tuple_element<IDX, ip_space_value_type<P>>::type { + if constexpr (IDX == 0) { + return p._rv; + } else if constexpr (IDX == 1) { + return *(p._payload); + } +} +}}} // namespace swoc::detail + +// Support unqualified @c get because ADL doesn't seem to work. +using swoc::detail::get; +// Support @c std::get +namespace std { using swoc::detail::get; } + +/// @endcond diff --git a/lib/swoc/include/swoc/Lexicon.h b/lib/swoc/include/swoc/Lexicon.h index c81337f633..166cb6f21a 100644 --- a/lib/swoc/include/swoc/Lexicon.h +++ b/lib/swoc/include/swoc/Lexicon.h @@ -39,7 +39,7 @@ what(std::string_view const &fmt, Args &&...args) { } // Exported because inner classes in template classes cannot be used in partial specialization -// which is required for tuple support. This should be remove next time there is a API changing +// which is required for tuple support. This should be removed next time there is a API changing // release because tuple access is being deprecated. template < typename E > struct lexicon_pair_type { E _value; diff --git a/lib/swoc/include/swoc/MemSpan.h b/lib/swoc/include/swoc/MemSpan.h index 8b71e0cc02..d586493fe0 100644 --- a/lib/swoc/include/swoc/MemSpan.h +++ b/lib/swoc/include/swoc/MemSpan.h @@ -354,12 +354,6 @@ public: /// Destruct all elements in the span. void destroy(); - /** Return a view of the memory. - * - * @return A @c string_view covering the span contents. - */ - [[deprecated]] std::string_view view() const; - template <typename U> friend class MemSpan; }; @@ -616,12 +610,6 @@ public: */ self_type align(size_t alignment, size_t obj_size) const; - /** Return a view of the memory. - * - * @return A @c string_view covering the span contents. - */ - [[deprecated]] std::string_view view() const; - }; template <> class MemSpan<void> : public MemSpan<void const> { @@ -1231,11 +1219,6 @@ MemSpan<T>::destroy() { } } -template <typename T> -std::string_view -MemSpan<T>::view() const { - return {reinterpret_cast<const char *>(_ptr), this->data_size()}; -} // --- void specializations --- template <typename U> constexpr MemSpan<void const>::MemSpan(MemSpan<U> const &that) : _ptr(const_cast<std::remove_const_t<U> *>(that._ptr)), _size(sizeof(U) * that.size()) {} @@ -1533,13 +1516,11 @@ MemSpan<void const>::as_ptr() const { return static_cast<U const *>(_ptr); } -inline std::string_view -MemSpan<void const>::view() const { - return {static_cast<char const *>(_ptr), _size}; -} /// Deduction guides template<typename T, size_t N> MemSpan(std::array<T,N> &) -> MemSpan<T>; template<typename T, size_t N> MemSpan(std::array<T,N> const &) -> MemSpan<T const>; +template<size_t N> MemSpan(char (&)[N]) -> MemSpan<char>; +template<size_t N> MemSpan(char const (&)[N]) -> MemSpan<char const>; template<typename T> MemSpan(std::vector<T> &) -> MemSpan<T>; template<typename T> MemSpan(std::vector<T> const&) -> MemSpan<T const>; MemSpan(std::string_view const&) -> MemSpan<char const>; diff --git a/lib/swoc/include/swoc/bwf_base.h b/lib/swoc/include/swoc/bwf_base.h index f2e69c7bb2..39e998a2fc 100644 --- a/lib/swoc/include/swoc/bwf_base.h +++ b/lib/swoc/include/swoc/bwf_base.h @@ -134,6 +134,9 @@ protected: struct Format { using self_type = Format; ///< Self reference type. + /// Empty format. + Format() = default; + /// Construct from a format string @a fmt. Format(TextView fmt); @@ -196,11 +199,16 @@ struct Format { /// @return @c true if all specifiers are literal. bool is_literal() const; -protected: - /// Default constructor for use by subclasses with alternate formatting. - Format() = default; + using Container = std::vector<Spec>; + Container _items; ///< Items from format string. - std::vector<Spec> _items; ///< Items from format string. + using iterator = Container::iterator; + using const_iterator = Container::const_iterator; + + iterator begin() { return _items.begin(); } + iterator end() { return _items.end(); } + const_iterator begin() const { return _items.begin(); } + const_iterator end() const { return _items.end(); } }; // Name binding - support for having format specifier names. @@ -271,11 +279,14 @@ public: * suitable for the subclass generators. */ template <typename F> class NameMap { -private: - using self_type = NameMap; ///< self reference type. public: /// Signature for generators. using Generator = std::function<F>; +protected: + using Map = std::unordered_map<std::string_view, Generator>; +private: + using self_type = NameMap; ///< self reference type. +public: /// Construct an empty container. NameMap(); @@ -290,12 +301,15 @@ public: */ self_type &assign(std::string_view const &name, Generator const &generator); + bool contains(std::string_view name) { + return _map.end() != _map.find(name); + } + protected: /// Copy @a name in to local storage and return a view of it. std::string_view localize(std::string_view const &name); /// Name to name generator. - using Map = std::unordered_map<std::string_view, Generator>; Map _map; ///< Defined generators. MemArena _arena{1024}; ///< Local name storage. }; diff --git a/lib/swoc/include/swoc/swoc_version.h b/lib/swoc/include/swoc/swoc_version.h index 137945b003..91543d678c 100644 --- a/lib/swoc/include/swoc/swoc_version.h +++ b/lib/swoc/include/swoc/swoc_version.h @@ -23,11 +23,11 @@ #pragma once #if !defined(SWOC_VERSION_NS) -#define SWOC_VERSION_NS _1_4_10 +#define SWOC_VERSION_NS _1_5_0 #endif namespace swoc { inline namespace SWOC_VERSION_NS { static constexpr unsigned MAJOR_VERSION = 1; -static constexpr unsigned MINOR_VERSION = 4; -static constexpr unsigned POINT_VERSION = 10; +static constexpr unsigned MINOR_VERSION = 5; +static constexpr unsigned POINT_VERSION = 0; }} // namespace swoc::SWOC_VERSION_NS diff --git a/lib/swoc/src/swoc_file.cc b/lib/swoc/src/swoc_file.cc index bfb34b0298..44ea3e7426 100644 --- a/lib/swoc/src/swoc_file.cc +++ b/lib/swoc/src/swoc_file.cc @@ -385,6 +385,7 @@ remove_all(const path &p, std::error_code &ec) // Invariant - @a p is a directory. // recursively remove nested files and directories + //coverity[toctou : SUPPRESS] if (nullptr == (dir = opendir(p.c_str()))) { ec = std::error_code(errno, std::system_category()); return zret; @@ -416,6 +417,7 @@ bool remove(path const& p, std::error_code &ec) { } else if (::stat(p.c_str(), &fs) < 0) { ec = std::error_code(errno, std::system_category()); } else if (S_ISREG(fs.st_mode)) { // regular file, try to remove it! + //coverity[toctou : SUPPRESS] if (unlink(p.c_str()) != 0) { ec = std::error_code(errno, std::system_category()); } diff --git a/lib/swoc/src/swoc_ip.cc b/lib/swoc/src/swoc_ip.cc index 29e7f1d051..301adcaf71 100644 --- a/lib/swoc/src/swoc_ip.cc +++ b/lib/swoc/src/swoc_ip.cc @@ -676,11 +676,11 @@ auto IPMask::mask_for_quad(IP6Addr::quad_type q) -> raw_type { raw_type cidr = IP6Addr::QUAD_WIDTH; if (q != 0) { - auto mask = IP6Addr::QUAD_MASK; + IP6Addr::quad_type mask = IP6Addr::QUAD_MASK; do { mask <<= 1; --cidr; - } while ((q | ~mask) == q); + } while ((q & mask) == q); ++cidr; // loop goes exactly 1 too far. } return cidr; @@ -842,18 +842,20 @@ auto IPSrv::assign(const sockaddr *sa) -> self_type & { bool IP4Net::load(TextView text) { - auto idx = text.find('/'); - if (idx != text.npos) { - if (idx + 1 < text.size()) { // must have something past the separator or it's bogus. - IP4Addr addr; - if (addr.load(text.substr(0, idx))) { // load the address - IPMask mask; - text.remove_prefix(idx + 1); // drop address and separator. - if (mask.load(text)) { - this->assign(addr, mask); - return true; + if (auto mask_text = text.split_suffix_at('/') ; !mask_text.empty() ) { + IPMask mask; + bool mask_p = mask.load(mask_text); + if (IP4Addr addr; addr.load(text)) { + if (!mask_p) { + if (IP4Addr m ; m.load(mask_text)) { + mask = IPMask::mask_for(m); + mask_p = (m == mask.as_ip4()); // must be an actual mask like address. } } + if (mask_p) { + this->assign(addr, mask); + return true; + } } } @@ -863,18 +865,20 @@ IP4Net::load(TextView text) { bool IP6Net::load(TextView text) { - auto idx = text.find('/'); - if (idx != text.npos) { - if (idx + 1 < text.size()) { // must have something past the separator or it's bogus. - IP6Addr addr; - if (addr.load(text.substr(0, idx))) { // load the address - IPMask mask; - text.remove_prefix(idx + 1); // drop address and separator. - if (mask.load(text)) { - this->assign(addr, mask); - return true; + if (auto mask_text = text.split_suffix_at('/') ; !mask_text.empty() ) { + IPMask mask; + bool mask_p = mask.load(mask_text); + if (IP6Addr addr; addr.load(text)) { + if (!mask_p) { + if (IP6Addr m ; m.load(mask_text)) { + mask = IPMask::mask_for(m); + mask_p = (m == mask.as_ip6()); // must be an actual mask like address. } } + if (mask_p) { + this->assign(addr, mask); + return true; + } } } @@ -886,11 +890,27 @@ bool IPNet::load(TextView text) { if ( auto mask_text = text.split_suffix_at('/') ; !mask_text.empty() ) { IPMask mask; - if (mask.load(mask_text)) { - if (IP6Addr a6; a6.load(text)) { // load the address + bool mask_p = mask.load(mask_text); + + if (IP6Addr a6 ; a6.load(text)) { // load the address + if (!mask_p) { + if (IP6Addr m ; m.load(mask_text)) { + mask = IPMask::mask_for(m); + mask_p = (m == mask.as_ip6()); // must be an actual mask like address. + } + } + if (mask_p) { this->assign(a6, mask); return true; - } else if (IP4Addr a4; a4.load(text)) { + } + } else if (IP4Addr a4 ; a4.load(text)) { + if (!mask_p) { + if (IP4Addr m ; m.load(mask_text)) { + mask = IPMask::mask_for(m); + mask_p = (m == mask.as_ip4()); // must be an actual mask like address. + } + } + if (mask_p) { this->assign(a4, mask); return true; } @@ -1212,4 +1232,30 @@ IP6Range::NetSource::search_narrower() { } } +bool +IPRangeView::operator==(IPRange const &r) const { + if (_family == r.family()) { + if (AF_INET == _family) { + return *_raw._4 == r.ip4(); + } else if (AF_INET6 == _family) { + return *_raw._6 == r.ip6(); + } + return true; + } + return false; +} + +bool +IPRangeView::operator==(IPRangeView::self_type const &that) const { + if (_family == that._family) { // different families are not equal + if (AF_INET == _family) { + return (_raw._4 == that._raw._4) || (*_raw._4 == *that._raw._4); + } else if (AF_INET6 == _family) { + return (_raw._6 == that._raw._6) || (*_raw._6 == *that._raw._6); + } + return true; + } + return false; +} + }} // namespace swoc::SWOC_VERSION_NS diff --git a/proxy/ControlMatcher.cc b/proxy/ControlMatcher.cc index 6e072105d0..a633679e97 100644 --- a/proxy/ControlMatcher.cc +++ b/proxy/ControlMatcher.cc @@ -651,7 +651,7 @@ template <class Data, class MatchResult> void IpMatcher<Data, MatchResult>::Match(sockaddr const *addr, RequestData *rdata, MatchResult *result) const { - if (auto &&[range, data]{*ip_addrs.find(swoc::IPAddr(addr))}; !range.empty()) { + if (auto [range, data]{*ip_addrs.find(swoc::IPAddr(addr))}; !range.empty()) { ink_assert(data != nullptr); data->UpdateMatch(result, rdata); }