This is an automated email from the ASF dual-hosted git repository.

bneradt pushed a commit to branch ipsrv
in repository https://gitbox.apache.org/repos/asf/trafficserver-libswoc.git

commit 3b6ed80a2597ad0df6a8bfbd191453fa4cd878f6
Author: Alan M. Carroll <a...@apache.org>
AuthorDate: Sat Nov 12 17:55:36 2022 -0600

    IP fixes for refreshing ATS usage.
---
 code/include/swoc/DiscreteRange.h |  53 +++++---
 code/include/swoc/IPAddr.h        | 175 ++++++++++++++++--------
 code/include/swoc/IPRange.h       | 280 +++++++++++++++++++++++---------------
 code/include/swoc/IPSrv.h         |  92 ++++++++++++-
 code/include/swoc/TextView.h      |   2 +-
 code/include/swoc/bwf_fwd.h       |   4 +-
 code/src/bw_ip_format.cc          |   2 +-
 code/src/swoc_ip.cc               | 143 +++++++++++++++----
 unit_tests/test_Errata.cc         |   2 -
 unit_tests/test_ip.cc             | 130 ++++++++++++++++--
 10 files changed, 645 insertions(+), 238 deletions(-)

diff --git a/code/include/swoc/DiscreteRange.h 
b/code/include/swoc/DiscreteRange.h
index fa7cf7c..ee306a9 100644
--- a/code/include/swoc/DiscreteRange.h
+++ b/code/include/swoc/DiscreteRange.h
@@ -812,34 +812,27 @@ public:
   /** Find the payload at @a metric.
    *
    * @param metric The metric for which to search.
-   * @return The payload for @a metric if found, @c nullptr if not found.
+   * @return An iterator for the item or the @c end iterator if not.
    */
   iterator find(METRIC const &metric);
 
+  /** Find the payload at @a metric.
+   *
+   * @param metric The metric for which to search.
+   * @return An iterator for the item or the @c end iterator if not.
+   */
+  const_iterator find(METRIC const &metric) const;
+
   /// @return The number of distinct ranges.
   size_t count() const;
 
-  iterator
-  begin() {
-    return _list.begin();
-  }
-
-  iterator
-  end() {
-    return _list.end();
-  }
+  iterator begin() { return _list.begin(); }
+  iterator end() { return _list.end(); }
+  const_iterator begin() const { return _list.begin(); }
+  const_iterator end() const { return _list.end(); }
 
   /// Remove all ranges.
-  void
-  clear() {
-    for (auto &node : _list) {
-      std::destroy_at(&node.payload());
-    }
-    _list.clear();
-    _root = nullptr;
-    _arena.clear();
-    _fa.clear();
-  }
+  void clear();
 
 protected:
   /** Find the lower bound range for @a target.
@@ -962,6 +955,13 @@ DiscreteSpace<METRIC, PAYLOAD>::find(METRIC const &metric) 
-> iterator {
   return this->end();
 }
 
+template <typename METRIC, typename PAYLOAD>
+auto
+DiscreteSpace<METRIC, PAYLOAD>::find(METRIC const &metric) const -> 
const_iterator
+{
+  return const_cast<self_type *>(this)->find(metric);
+}
+
 template <typename METRIC, typename PAYLOAD>
 auto
 DiscreteSpace<METRIC, PAYLOAD>::lower_bound(METRIC const &target) -> Node * {
@@ -1519,4 +1519,17 @@ DiscreteSpace<METRIC, 
PAYLOAD>::blend(DiscreteSpace::range_type const &range, U
   return *this;
 }
 
+template <typename METRIC, typename PAYLOAD>
+void
+DiscreteSpace<METRIC, PAYLOAD>::clear()
+{
+  for (auto &node : _list) {
+    std::destroy_at(&node.payload());
+  }
+  _list.clear();
+  _root = nullptr;
+  _arena.clear();
+  _fa.clear();
+}
+
 }} // namespace swoc::SWOC_VERSION_NS
diff --git a/code/include/swoc/IPAddr.h b/code/include/swoc/IPAddr.h
index 3a567d1..cacc455 100644
--- a/code/include/swoc/IPAddr.h
+++ b/code/include/swoc/IPAddr.h
@@ -53,9 +53,6 @@ public:
   /// If the @a text is invalid the result is an invalid instance.
   IP4Addr(string_view const &text);
 
-  /// Construct from generic address @a addr.
-  explicit IP4Addr(IPAddr const &addr);
-
   /// Self assignment.
   self_type & operator=(self_type const& that) = default;
 
@@ -74,12 +71,11 @@ public:
   /** Byte access.
    *
    * @param idx Byte index.
-   * @return The byte at @a idx in the address.
+   * @return The byte at @a idx in the address (network order).
+   *
+   * For convenience, this returns in "text order" of the octets.
    */
-  uint8_t
-  operator[](unsigned idx) const {
-    return reinterpret_cast<bytes const &>(_addr)[idx];
-  }
+  uint8_t operator[](unsigned idx) const;
 
   /// Apply @a mask to address, leaving the network portion.
   self_type &operator&=(IPMask const &mask);
@@ -121,6 +117,12 @@ public:
   /// @return @c true if this is a loopback address, @c false if not.
   bool is_loopback() const;
 
+  /// @return @c true if the address is in the link local network.
+  bool is_link_local() const;
+
+  /// @return @c true if the address is private.
+  bool is_private() const;
+
   /** Left shift.
    *
    * @param n Number of bits to shift left.
@@ -215,11 +217,15 @@ public:
   explicit IP6Addr(sockaddr_in6 const *addr) { *this = addr; }
 
   /// Construct from text representation.
-  /// If the @a text is invalid the result is an invalid instance.
+  /// If the @a text is invalid the result is any address.
+  /// @see load
   IP6Addr(string_view const &text);
 
-  /// Construct from generic @a addr.
-  explicit IP6Addr(IPAddr const &addr);
+  /** Construct mapped IPv4 address.
+   *
+   * @param addr IPv4 address
+   */
+  explicit IP6Addr(IP4Addr addr);
 
   /// Self assignment.
   self_type & operator=(self_type const& that) = default;
@@ -268,6 +274,13 @@ public:
   /// Set to the address in @a addr.
   self_type &operator=(sockaddr_in6 const *addr);
 
+  /** Access a byte in the address.
+   *
+   * @param idx Byte index.
+   * @return The "text order" byte.
+   */
+  constexpr uint8_t operator [] (int idx) const;
+
   /// Write to @c sockaddr using network order and @a host_order_port.
   sockaddr *copy_to(sockaddr *sa, in_port_t port = 0) const;
 
@@ -301,8 +314,14 @@ public:
   /// @return @c true if this is a multicast address, @c false if not.
   bool is_multicast() const;
 
+  /// @return @c true if this is a link local address, @c false if not.
+  bool is_link_local() const;
+
+  /// @return @c true if the address is private.
+  bool is_private() const;
+
   ///  @return @c true if this is an IPv4 addressed mapped to IPv6, @c false 
if not.
-  bool is_mapped_ipv4() const;
+  bool is_mapped_ip4() const;
 
   /** Reset to default constructed state.
    *
@@ -397,6 +416,10 @@ protected:
   /// This converts from the position in the text format to the quads in the 
binary format.
   static constexpr std::array<unsigned, N_QUADS> QUAD_IDX = {3, 2, 1, 0, 7, 6, 
5, 4};
 
+  /// Index of bytes in @a _addr._raw
+  /// This converts MSB (0) to LSB (15) indicies to the bytes in the binary 
format.
+  static constexpr std::array<unsigned, SIZE> RAW_IDX = { 7, 6, 5, 4, 3, 2, 1, 
0, 15, 14, 13, 12, 11, 10, 9, 8 };
+
   /// Convert between network and host order.
   /// The conversion is symmetric.
   /// @param dst Output where reordered value is placed.
@@ -505,7 +528,7 @@ public:
 
   /// Test for same address family.
   /// @c return @c true if @a that is the same address family as @a this.
-  bool isCompatibleWith(self_type const &that);
+  bool is_same_family(self_type const &that);
 
   /// Get the address family.
   /// @return The address family.
@@ -517,35 +540,29 @@ public:
   /// Test for IPv6.
   bool is_ip6() const;
 
+  /// @return As IPv4 address - results are undefined if it is not actually 
IPv4.
   IP4Addr const &ip4() const;
 
+  /// @return As IPv6 address - results are undefined if it is not actually 
IPv6.
   IP6Addr const &ip6() const;
 
-  explicit operator IP4Addr const &() const { return _addr._ip4; }
-
-  explicit
-  operator IP4Addr &() {
-    return _addr._ip4;
-  }
-
-  explicit operator IP6Addr const &() const { return _addr._ip6; }
-
-  explicit
-  operator IP6Addr &() {
-    return _addr._ip6;
-  }
-
   /// Test for validity.
   bool is_valid() const;
 
   /// Make invalid.
   self_type &invalidate();
 
+  /// Test for loopback
+  bool is_loopback() const;
+
   /// Test for multicast
   bool is_multicast() const;
 
-  /// Test for loopback
-  bool is_loopback() const;
+  /// @return @c true if this is a link local address, @c false if not.
+  bool is_link_local() const;
+
+  /// @return @c true if this is a private address, @c false if not.
+  bool is_private() const;
 
   ///< Pre-constructed invalid instance.
   static self_type const INVALID;
@@ -684,8 +701,6 @@ inline IP4Addr::IP4Addr(string_view const &text) {
   }
 }
 
-inline IP4Addr::IP4Addr(IPAddr const &addr) : _addr(addr._family == AF_INET ? 
addr._addr._ip4._addr : INADDR_ANY) {}
-
 inline constexpr sa_family_t
 IP4Addr::family() const {
   return AF_value;
@@ -798,7 +813,7 @@ IP4Addr::is_any() const {
 
 inline bool
 IP4Addr::is_loopback() const {
-  return (*this)[3] == IN_LOOPBACKNET;
+  return (*this)[0] == IN_LOOPBACKNET;
 }
 
 inline bool
@@ -806,6 +821,24 @@ IP4Addr::is_multicast() const {
   return IN_MULTICAST(_addr);
 }
 
+inline bool
+IP4Addr::is_link_local() const {
+  return (_addr & 0xFFFF0000) == 0xA9FE0000; // 169.254.0.0/16
+}
+
+inline bool IP4Addr::is_private() const {
+  return (((_addr & 0xFF000000) == 0x0A000000) ||        // 10.0.0.0/8
+          ((_addr & 0xFFC00000) == 0x64400000) ||        // 100.64.0.0/10
+          ((_addr & 0xFFF00000) == 0xAC100000) || // 172.16.0.0/12
+          ((_addr & 0xFFFF0000) == 0xC0A80000)           // 192.168.0.0/16
+  );
+}
+
+inline uint8_t
+IP4Addr::operator[](unsigned int idx) const {
+  return reinterpret_cast<bytes const &>(_addr)[3 - idx];
+}
+
 inline int
 IP4Addr::cmp(IP4Addr::self_type const &that) const {
   return _addr < that._addr ? -1 : _addr > that._addr ? 1 : 0;
@@ -833,26 +866,41 @@ inline IP6Addr::IP6Addr(string_view const &text) {
   }
 }
 
-inline IP6Addr::IP6Addr(IPAddr const &addr) : _addr{addr._addr._ip6._addr} {}
+inline IP6Addr::IP6Addr(IP4Addr addr) {
+  _addr._store[MSW] = 0;
+  _addr._quad[QUAD_IDX[4]] = 0;
+  _addr._quad[QUAD_IDX[5]] = 0xffff;
+  _addr._quad[QUAD_IDX[6]] = addr.host_order() >> QUAD_WIDTH;
+  _addr._quad[QUAD_IDX[7]] = addr.host_order();
+}
 
 inline bool
 IP6Addr::is_loopback() const {
-  return _addr._store[0] == 0 && _addr._store[1] == 1;
+  return _addr._store[MSW] == 0 && _addr._store[LSW] == 1;
 }
 
 inline bool
 IP6Addr::is_multicast() const {
-  return _addr._raw[7] == 0xFF;
+  return _addr._raw[RAW_IDX[0]] == 0xFF;
 }
 
 inline bool
 IP6Addr::is_any() const {
-  return _addr._store[0] == 0 && _addr._store[1] == 0;
+  return _addr._store[MSW] == 0 && _addr._store[LSW] == 0;
+}
+
+inline bool
+IP6Addr::is_mapped_ip4() const {
+  return 0 == _addr._store[MSW] && (_addr._quad[QUAD_IDX[4]] == 0 && 
_addr._quad[QUAD_IDX[5]] == 0xFFFF);
 }
 
 inline bool
-IP6Addr::is_mapped_ipv4() const {
-  return 0 == _addr._store[0] && (_addr._quad[7] == 0 && _addr._quad[6] == 
0xFFFF);
+IP6Addr::is_link_local() const {
+  return _addr._raw[RAW_IDX[0]] == 0xFE && (_addr._raw[RAW_IDX[1]] & 0xC0) == 
0x80; // fe80::/10
+}
+
+inline bool IP6Addr::is_private() const {
+  return (_addr._raw[RAW_IDX[0]]& 0xFE) == 0xFC; // fc00::/7
 }
 
 inline in6_addr &
@@ -869,7 +917,7 @@ IP6Addr::network_order() const {
 
 inline auto
 IP6Addr::clear() -> self_type & {
-  _addr._store[0] = _addr._store[1] = 0;
+  _addr._store[MSW] = _addr._store[LSW] = 0;
   return *this;
 }
 
@@ -891,16 +939,16 @@ IP6Addr::operator=(sockaddr_in6 const *addr) -> self_type 
& {
 
 inline IP6Addr &
 IP6Addr::operator++() {
-  if (++(_addr._store[1]) == 0) {
-    ++(_addr._store[0]);
+  if (++(_addr._store[LSW]) == 0) {
+    ++(_addr._store[MSW]);
   }
   return *this;
 }
 
 inline IP6Addr &
 IP6Addr::operator--() {
-  if (--(_addr._store[1]) == ~static_cast<uint64_t>(0)) {
-    --(_addr._store[0]);
+  if (--(_addr._store[LSW]) == ~static_cast<uint64_t>(0)) {
+    --(_addr._store[MSW]);
   }
   return *this;
 }
@@ -915,20 +963,20 @@ IP6Addr::reorder(unsigned char dst[WORD_SIZE], unsigned 
char const src[WORD_SIZE
 /// @return @c true if @a lhs is equal to @a rhs.
 inline bool
 operator==(IP6Addr const &lhs, IP6Addr const &rhs) {
-  return lhs._addr._store[0] == rhs._addr._store[0] && lhs._addr._store[1] == 
rhs._addr._store[1];
+  return lhs._addr._store[IP6Addr::MSW] == rhs._addr._store[IP6Addr::MSW] && 
lhs._addr._store[IP6Addr::LSW] == rhs._addr._store[IP6Addr::LSW];
 }
 
 /// @return @c true if @a lhs is not equal to @a rhs.
 inline bool
 operator!=(IP6Addr const &lhs, IP6Addr const &rhs) {
-  return lhs._addr._store[0] != rhs._addr._store[0] || lhs._addr._store[1] != 
rhs._addr._store[1];
+  return lhs._addr._store[IP6Addr::MSW] != rhs._addr._store[IP6Addr::MSW] || 
lhs._addr._store[IP6Addr::LSW] != rhs._addr._store[IP6Addr::LSW];
 }
 
 /// @return @c true if @a lhs is less than @a rhs.
 inline bool
 operator<(IP6Addr const &lhs, IP6Addr const &rhs) {
-  return lhs._addr._store[0] < rhs._addr._store[0] ||
-  (lhs._addr._store[0] == rhs._addr._store[0] && lhs._addr._store[1] < 
rhs._addr._store[1]);
+  return lhs._addr._store[IP6Addr::MSW] < rhs._addr._store[IP6Addr::MSW] ||
+  (lhs._addr._store[IP6Addr::MSW] == rhs._addr._store[IP6Addr::MSW] && 
lhs._addr._store[IP6Addr::LSW] < rhs._addr._store[IP6Addr::LSW]);
 }
 
 /// @return @c true if @a lhs is greater than @a rhs.
@@ -940,8 +988,8 @@ operator>(IP6Addr const &lhs, IP6Addr const &rhs) {
 /// @return @c true if @a lhs is less than or equal to @a rhs.
 inline bool
 operator<=(IP6Addr const &lhs, IP6Addr const &rhs) {
-  return lhs._addr._store[0] < rhs._addr._store[0] ||
-  (lhs._addr._store[0] == rhs._addr._store[0] && lhs._addr._store[1] <= 
rhs._addr._store[1]);
+  return lhs._addr._store[IP6Addr::MSW] < rhs._addr._store[IP6Addr::MSW] ||
+  (lhs._addr._store[IP6Addr::MSW] == rhs._addr._store[IP6Addr::MSW] && 
lhs._addr._store[IP6Addr::LSW] <= rhs._addr._store[IP6Addr::LSW]);
 }
 
 /// @return @c true if @a lhs is greater than or equal to @a rhs.
@@ -1051,6 +1099,11 @@ operator|(IP6Addr const &addr, IPMask const &mask) {
   return IP6Addr{addr} |= mask;
 }
 
+constexpr uint8_t
+IP6Addr::operator[](int idx) const {
+  return _addr._raw[RAW_IDX[idx]];
+}
+
 inline IPAddr
 operator&(IPAddr const &addr, IPMask const &mask) {
   return IPAddr{addr} &= mask;
@@ -1111,7 +1164,7 @@ IPAddr::is_ip6() const {
 }
 
 inline bool
-IPAddr::isCompatibleWith(self_type const &that) {
+IPAddr::is_same_family(self_type const &that) {
   return this->is_valid() && _family == that._family;
 }
 
@@ -1120,6 +1173,20 @@ IPAddr::is_loopback() const {
   return (AF_INET == _family && _addr._ip4.is_loopback()) || (AF_INET6 == 
_family && _addr._ip6.is_loopback());
 }
 
+inline bool
+IPAddr::is_link_local() const {
+  return this->is_ip4() ? this->ip4().is_link_local()
+         : this->is_ip6() ? this->ip6().is_link_local()
+                          : false;
+}
+
+inline bool
+IPAddr::is_private() const {
+  return this->is_ip4() ? this->ip4().is_private()
+         : this->is_ip6() ? this->ip6().is_private()
+                          : false;
+}
+
 inline IPAddr &
 IPAddr::assign(in_addr_t addr) {
   _family    = AF_INET;
@@ -1247,22 +1314,22 @@ operator!=(IPAddr const &lhs, IP4Addr const &rhs) {
 
 inline bool
 operator==(IP4Addr const &lhs, IPAddr const &rhs) {
-  return rhs.is_ip4() && lhs == static_cast<IP4Addr const &>(rhs);
+  return rhs.is_ip4() && lhs == rhs.ip4();
 }
 
 inline bool
 operator!=(IP4Addr const &lhs, IPAddr const &rhs) {
-  return !rhs.is_ip4() || lhs != static_cast<IP4Addr const &>(rhs);
+  return !rhs.is_ip4() || lhs != rhs.ip4();
 }
 
 inline bool
 operator==(IPAddr const &lhs, IP6Addr const &rhs) {
-  return lhs.is_ip6() && static_cast<IP6Addr const &>(lhs) == rhs;
+  return lhs.is_ip6() && lhs.ip6() == rhs;
 }
 
 inline bool
 operator!=(IPAddr const &lhs, IP6Addr const &rhs) {
-  return !lhs.is_ip6() || static_cast<IP6Addr const &>(lhs) != rhs;
+  return !lhs.is_ip6() || lhs.ip6() != rhs;
 }
 
 inline bool
diff --git a/code/include/swoc/IPRange.h b/code/include/swoc/IPRange.h
index 6e88705..919cb48 100644
--- a/code/include/swoc/IPRange.h
+++ b/code/include/swoc/IPRange.h
@@ -797,7 +797,7 @@ public:
   void clear();
 
   /** Constant iterator.
-   * THe value type is a tuple of the IP address range and the @a PAYLOAD. 
Both are constant.
+   * The value type is a tuple of the IP address range and the @a PAYLOAD. 
Both are constant.
    *
    * @internal The non-const iterator is a subclass of this, in order to share 
implementation. This
    * also makes it easy to convert from iterator to const iterator, which is 
desirable.
@@ -871,7 +871,7 @@ public:
 
   protected:
     // These are stored non-const to make implementing @c iterator easier. The 
containing class provides the
-    // required @c const protection. This is basic a tuple of iterators - for 
forward iteration if
+    // required @c const protection. Internally a tuple of iterators is stored 
for forward iteration. If
     // the primary (ipv4) iterator is at the end, then use the secondary 
(ipv6) iterator. The reverse
     // is done for reverse iteration. This depends on the extra support @c 
IntrusiveDList iterators
     // provide.
@@ -885,7 +885,7 @@ public:
      * @param iter4 Starting place for IPv4 subspace.
      * @param iter6 Starting place for IPv6 subspace.
      *
-     * In practice, both iterators should be either the beginning or ending 
iterator for the subspace.
+     * 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);
   };
@@ -902,62 +902,63 @@ public:
 
     friend class IPSpace;
 
-  public:
+  protected:
+    using super_type::super_type; /// Inherit supertype constructors.
+    /// Protected constructor to convert const to non-const.
+    /// @note This makes for much less code duplication in iterator relevant 
methods.
+    iterator(const_iterator const& that) : const_iterator(that) {}
   public:
     /// Value type of iteration.
     using value_type = std::tuple<IPRange const, PAYLOAD &>;
-  using pointer    = value_type *;
-  using reference  = value_type &;
+    using pointer    = value_type *;
+    using reference  = value_type &;
 
-  /// Default constructor.
-  iterator() = default;
+    /// Default constructor.
+    iterator() = default;
 
-  /// Copy constructor.
-  iterator(self_type const &that);
+    /// Copy constructor.
+    iterator(self_type const &that);
 
-  /// Assignment.
-  self_type &operator=(self_type const &that);
+    /// Assignment.
+    self_type &operator=(self_type const &that);
 
-  /// Pre-increment.
-  /// Move to the next element in the list.
-  /// @return The iterator.
-  self_type &operator++();
+    /// Pre-increment.
+    /// Move to the next element in the list.
+    /// @return The iterator.
+    self_type &operator++();
 
-  /// Pre-decrement.
-  /// Move to the previous element in the list.
-  /// @return The iterator.
-  self_type &operator--();
-
-  /// 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;
-  }
+    /// Pre-decrement.
+    /// Move to the previous element in the list.
+    /// @return The iterator.
+    self_type &operator--();
 
-  /// 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;
-  }
+    /// 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;
+    }
 
-  /// Dereference.
-  /// @return A reference to the referent.
-  value_type operator*() const;
+    /// 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;
+    }
 
-  /// Dereference.
-  /// @return A pointer to the referent.
-  value_type const *operator->() const;
+    /// Dereference.
+    /// @return A reference to the referent.
+    value_type operator*() const;
 
-  protected:
-    using super_type::super_type; /// Inherit supertype constructors.
+    /// Dereference.
+    /// @return A pointer to the referent.
+    value_type const *operator->() const;
   };
 
   /** Find the payload for an @a addr.
@@ -965,74 +966,80 @@ public:
    * @param addr Address to find.
    * @return Iterator for the range containing @a addr.
    */
-  iterator
-  find(IPAddr const &addr) {
-    if (addr.is_ip4()) {
-      return this->find(addr.ip4());
-    } else if (addr.is_ip6()) {
-      return this->find(addr.ip6());
-    }
-    return this->end();
-  }
+  iterator find(IPAddr const &addr);
 
   /** Find the payload for an @a addr.
    *
    * @param addr Address to find.
-   * @return The payload if any, @c nullptr if the address is not in the space.
+   * @return Iterator for the range containing @a addr.
    */
-  iterator
-  find(IP4Addr const &addr) {
-    auto spot = _ip4.find(addr);
-    return spot == _ip4.end() ? this->end() : iterator{_ip4.find(addr), 
_ip6.begin()};
-  }
+  const_iterator find(IPAddr const &addr) const;
 
   /** Find the payload for an @a addr.
    *
    * @param addr Address to find.
-   * @return The payload if any, @c nullptr if the address is not in the space.
+   * @return An iterator which is valid if @a addr was found, @c end if not.
    */
-  iterator
-  find(IP6Addr const &addr) {
-    return {_ip4.end(), _ip6.find(addr)};
-  }
+  iterator find(IP4Addr const &addr);
 
-  /// @return A constant iterator to the first element.
-  const_iterator begin() const;
+  /** Find the payload for an @a addr.
+   *
+   * @param addr Address to find.
+   * @return An iterator which is valid if @a addr was found, @c end if not.
+   */
+  const_iterator find(IP4Addr const &addr) const;
 
-  /// @return A constent iterator past the last element.
-  const_iterator end() const;
+  /** Find the payload for an @a addr.
+   *
+   * @param addr Address to find.
+   * @return An iterator which is valid if @a addr was found, @c end if not.
+   */
+  iterator find(IP6Addr const &addr);
 
+  /** Find the payload for an @a addr.
+   *
+   * @param addr Address to find.
+   * @return An iterator which is valid if @a addr was found, @c end if not.
+   */
+  const_iterator find(IP6Addr const &addr) const;
+
+  /// @return An iterator to the first element.
   iterator begin();
 
+  /// @return A constant iterator to the first element.
+  const_iterator begin() const;
+
+  /// @return An iterator past the last element.
   iterator end();
 
-  /// Iterator to the first IPv4 address.
+  /// @return A constant iterator past the last element.
+  const_iterator end() const;
+
+  /// @return Iterator to the first IPv4 address.
+  iterator begin_ip4();
+  /// @return Iterator to the first IPv4 address.
   const_iterator begin_ip4() const;
-  /// Iterator past the last IPv4 address.
+
+  /// @return Iterator past the last IPv4 address.
+  iterator end_ip4();
+  /// @return Iterator past the last IPv4 address.
   const_iterator end_ip4() const;
 
+  /// @return Iterator at the first IPv6 address.
+  iterator begin_ip6();
+  /// @return Iterator at the first IPv6 address.
   const_iterator begin_ip6() const;
+  /// @return Iterator past the last IPv6 address.
+  iterator end_ip6();
+  /// @return Iterator past the last IPv6 address.
   const_iterator end_ip6() const;
 
-  const_iterator
-  begin(sa_family_t family) const {
-    if (AF_INET == family) {
-      return this->begin_ip4();
-    } else if (AF_INET6 == family) {
-      return this->begin_ip6();
-    }
-    return this->end();
-  }
+  /// @return Iterator to the first address of @a family.
+  const_iterator begin(sa_family_t family) const;
 
+  /// @return Iterator past the last address of @a family.
   const_iterator
-  end(sa_family_t family) const {
-    if (AF_INET == family) {
-      return this->end_ip4();
-    } else if (AF_INET6 == family) {
-      return this->end_ip6();
-    }
-    return this->end();
-  }
+  end(sa_family_t family) const;
 
 protected:
   IP4Space _ip4; ///< Sub-space containing IPv4 ranges.
@@ -1648,55 +1655,102 @@ IPSpace<PAYLOAD>::clear() {
 }
 
 template <typename PAYLOAD>
-auto
-IPSpace<PAYLOAD>::begin() const -> const_iterator {
-  auto nc_this = const_cast<self_type *>(this);
-  return const_iterator(nc_this->_ip4.begin(), nc_this->_ip6.begin());
+auto IPSpace<PAYLOAD>::begin() -> iterator { return iterator{_ip4.begin(), 
_ip6.begin()}; }
+
+template <typename PAYLOAD>
+auto IPSpace<PAYLOAD>::begin() const -> const_iterator { return 
const_cast<self_type *>(this)->begin(); }
+
+template <typename PAYLOAD>
+auto IPSpace<PAYLOAD>::end() -> iterator { return iterator{_ip4.end(), 
_ip6.end()}; }
+
+template <typename PAYLOAD>
+auto IPSpace<PAYLOAD>::end() const -> const_iterator { return 
const_cast<self_type *>(this)->end(); }
+
+template <typename PAYLOAD>
+auto IPSpace<PAYLOAD>::begin_ip4() -> iterator { return this->begin(); }
+
+template <typename PAYLOAD>
+auto IPSpace<PAYLOAD>::begin_ip4() const -> const_iterator { return 
this->begin(); }
+
+template <typename PAYLOAD>
+auto IPSpace<PAYLOAD>::end_ip4() -> iterator { return { _ip4.end(), 
_ip6.begin() }; }
+
+template <typename PAYLOAD>
+auto IPSpace<PAYLOAD>::end_ip4() const -> const_iterator { return 
const_cast<self_type*>(this)->end_ip4(); }
+
+template <typename PAYLOAD>
+auto IPSpace<PAYLOAD>::begin_ip6() -> iterator {
+  return { _ip4.end(), _ip6.begin() };
 }
 
 template <typename PAYLOAD>
-auto
-IPSpace<PAYLOAD>::end() const -> const_iterator {
-  auto nc_this = const_cast<self_type *>(this);
-  return const_iterator(nc_this->_ip4.end(), nc_this->_ip6.end());
+auto IPSpace<PAYLOAD>::begin_ip6() const -> const_iterator {
+  return const_cast<self_type*>(this)->begin_ip6();
 }
 
+template <typename PAYLOAD>
+auto IPSpace<PAYLOAD>::end_ip6() -> iterator { return this->end(); }
+
+template <typename PAYLOAD>
+auto IPSpace<PAYLOAD>::end_ip6() const -> const_iterator { return this->end(); 
}
+
 template <typename PAYLOAD>
 auto
-IPSpace<PAYLOAD>::begin_ip4() const -> const_iterator {
-  return this->begin();
+IPSpace<PAYLOAD>::find(IPAddr const &addr) -> iterator {
+  if (addr.is_ip4()) {
+    return this->find(addr.ip4());
+  } else if (addr.is_ip6()) {
+    return this->find(addr.ip6());
+  }
+  return this->end();
 }
 
 template <typename PAYLOAD>
-auto
-IPSpace<PAYLOAD>::end_ip4() const -> const_iterator {
-  auto nc_this = const_cast<self_type *>(this);
-  return { nc_this->_ip4.end(), nc_this->_ip6.begin() };
+auto IPSpace<PAYLOAD>::find(IPAddr const &addr) const -> const_iterator {
+  return const_cast<self_type *>(this)->find(addr);
 }
 
 template <typename PAYLOAD>
 auto
-IPSpace<PAYLOAD>::begin_ip6() const -> const_iterator {
-  auto nc_this = const_cast<self_type *>(this);
-  return { nc_this->_ip4.end(), nc_this->_ip6.begin() };
+IPSpace<PAYLOAD>::find(IP4Addr const &addr) -> iterator {
+  if ( auto spot = _ip4.find(addr) ; spot != _ip4.end()) {
+    return { spot, _ip6.begin() };
+  }
+  return this->end();
 }
 
 template <typename PAYLOAD>
 auto
-IPSpace<PAYLOAD>::end_ip6() const -> const_iterator {
-  return this->end();
+IPSpace<PAYLOAD>::find(IP4Addr const &addr) const -> const_iterator {
+  return const_cast<self_type *>(this)->find(addr);
 }
 
+template <typename PAYLOAD>
+auto IPSpace<PAYLOAD>::find(IP6Addr const &addr) -> iterator { return 
{_ip4.end(), _ip6.find(addr)}; }
+
+template <typename PAYLOAD>
+auto IPSpace<PAYLOAD>::find(IP6Addr const &addr) const -> const_iterator { 
return {_ip4.end(), _ip6.find(addr)}; }
+
 template <typename PAYLOAD>
 auto
-IPSpace<PAYLOAD>::begin() -> iterator {
-  return iterator{_ip4.begin(), _ip6.begin()};
+IPSpace<PAYLOAD>::begin(sa_family_t family) const -> const_iterator {
+  if (AF_INET == family) {
+    return this->begin_ip4();
+  } else if (AF_INET6 == family) {
+    return this->begin_ip6();
+  }
+  return this->end();
 }
 
 template <typename PAYLOAD>
 auto
-IPSpace<PAYLOAD>::end() -> iterator {
-  return iterator{_ip4.end(), _ip6.end()};
+IPSpace<PAYLOAD>::end(sa_family_t family) const -> const_iterator {
+  if (AF_INET == family) {
+    return this->end_ip4();
+  } else if (AF_INET6 == family) {
+    return this->end_ip6();
+  }
+  return this->end();
 }
 
 template <typename PAYLOAD>
diff --git a/code/include/swoc/IPSrv.h b/code/include/swoc/IPSrv.h
index 42ce573..e0e4d48 100644
--- a/code/include/swoc/IPSrv.h
+++ b/code/include/swoc/IPSrv.h
@@ -45,6 +45,14 @@ public:
    */
   IP4Srv(sockaddr_in const * s) : _addr(s), _port(ntohs(s->sin_port)) {}
 
+  /** Construct from a string.
+   *
+   * @param text Input text.
+   *
+   * If the port is not present it is set to zero.
+   */
+  explicit IP4Srv(swoc::TextView text);
+
   /// Implicit conversion to an address.
   constexpr operator IP4Addr const& () const;
 
@@ -70,6 +78,13 @@ public:
   bool operator > (self_type const& that) const;
   bool operator >= (self_type const& that) const;
 
+  /** Load from a string.
+   *
+   * @param text Input string.
+   * @return @c true if @a text in a valid format, @c false if not.
+   */
+  bool load(swoc::TextView text);
+
   /** Change the address.
    *
    * @param addr Address to assign.
@@ -133,6 +148,21 @@ public:
    */
   explicit IP6Srv(sockaddr_in6 const * s);
 
+  /** Construct from a string.
+   *
+   * @param text Input text.
+   *
+   * If the port is not present it is set to zero.
+   */
+  explicit IP6Srv(swoc::TextView text);
+
+  /** Load from a string.
+   *
+   * @param text Input string.
+   * @return @c true if @a text in a valid format, @c false if not.
+   */
+  bool load(swoc::TextView text);
+
   /// Implicit conversion to address.
   constexpr operator IP6Addr const& () const;
 
@@ -205,19 +235,34 @@ public:
   explicit IPSrv(sockaddr_in6 const * s);
   explicit IPSrv(IPEndpoint const& ep);
 
+  /** Construct from a string.
+   *
+   * @param text Input text.
+   *
+   * If the port is not present it is set to zero.
+   */
+  explicit IPSrv(swoc::TextView text);
+
+  /** Load from a string.
+   *
+   * @param text Input string.
+   * @return @c true if @a text in a valid format, @c false if not.
+   */
+  bool load(swoc::TextView text);
+
   /// @return The address.
-  IPAddr addr() const { return _srv.addr(_family); }
+  IPAddr addr() const;
   /// @return The host_order_port in host order..
   constexpr in_port_t host_order_port() const;
   /// @return The host_order_port in network order.
   in_port_t network_order_port() const;
   /// @return The protocol of the current value.
-  constexpr sa_family_t family() const { return _family; }
+  constexpr sa_family_t family() const;
 
   /// @return @c true if the data is IPv4, @c false if not.
-  bool is_ip4() const { return _family == AF_INET; }
+  bool is_ip4() const;
   /// @return @c true if hte data is IPv6, @c false if not.
-  bool is_ip6() const { return _family == AF_INET6; }
+  bool is_ip6() const;
 
   /// @return The IPv4 data.
   IP4Srv const& ip4() const { return _srv._ip4; }
@@ -402,6 +447,11 @@ inline IPSrv::IPSrv(const sockaddr_in *s) : _srv(s), 
_family(AF_INET) {}
 inline IPSrv::IPSrv(const sockaddr_in6 *s) : _srv(s), _family(AF_INET6) {}
 inline IPSrv::IPSrv(const sockaddr *sa) { this->assign(sa); }
 
+inline IPAddr IPSrv::addr() const { return _srv.addr(_family); }
+inline constexpr sa_family_t IPSrv::family() const { return _family; }
+inline bool IPSrv::is_ip4() const { return _family == AF_INET; }
+inline bool IPSrv::is_ip6() const { return _family == AF_INET6; }
+
 inline auto
 IPSrv::assign(IP6Addr const &addr) -> self_type & {
   _srv._ip6.assign(addr, this->host_order_port());
@@ -593,4 +643,38 @@ inline bool operator >= (IP6Srv const& lhs, IPSrv const& 
rhs) {
   return rhs.is_ip6() && lhs >= rhs.ip6();
 }
 
+// --- Cross address equality
+
+inline bool operator == (IPSrv const& lhs, IP4Addr const& rhs) {
+  return lhs.is_ip4() && lhs.ip4() == rhs;
+}
+
+inline bool operator == (IP4Addr const& lhs, IPSrv const& rhs) {
+  return rhs.is_ip4() && lhs == rhs.ip4();
+}
+
+inline bool operator != (IPSrv const& lhs, IP4Addr const& rhs) {
+  return ! lhs.is_ip4() || lhs.ip4() != rhs;
+}
+
+inline bool operator != (IP4Addr const& lhs, IPSrv const& rhs) {
+  return ! rhs.is_ip4() || lhs != rhs.ip4();
+}
+
+inline bool operator == (IPSrv const& lhs, IP6Addr const& rhs) {
+  return lhs.is_ip6() && lhs.ip6() == rhs;
+}
+
+inline bool operator == (IP6Addr const& lhs, IPSrv const& rhs) {
+  return rhs.is_ip6() && lhs == rhs.ip6();
+}
+
+inline bool operator != (IPSrv const& lhs, IP6Addr const& rhs) {
+  return ! lhs.is_ip6() || lhs.ip6() != rhs;
+}
+
+inline bool operator != (IP6Addr const& lhs, IPSrv const& rhs) {
+  return ! rhs.is_ip6() || lhs != rhs.ip6();
+}
+
 }} // namespace swoc::SWOC_VERSION_NS
diff --git a/code/include/swoc/TextView.h b/code/include/swoc/TextView.h
index 0903a32..e153741 100644
--- a/code/include/swoc/TextView.h
+++ b/code/include/swoc/TextView.h
@@ -148,7 +148,7 @@ public:
   /// Assign from C-string @a s.
   self_type &operator=(char *&s);
   /// Assign from C-string @a s.
-  self_type &operator=(char const *&s);
+  self_type &operator=(char const * & s);
 
   /// Assign from a @c std::string.
   self_type &operator=(const std::string &s);
diff --git a/code/include/swoc/bwf_fwd.h b/code/include/swoc/bwf_fwd.h
index f7a9054..59fe601 100644
--- a/code/include/swoc/bwf_fwd.h
+++ b/code/include/swoc/bwf_fwd.h
@@ -9,7 +9,7 @@
 
 #include "swoc/swoc_version.h"
 
-namespace SWOC_NAMESPACE {
+namespace swoc { inline namespace SWOC_VERSION_NS {
 class BufferWriter;
 class FixedBufferWriter;
 template <size_t N> class LocalBufferWriter;
@@ -18,4 +18,4 @@ namespace bwf {
 struct Spec;
 class Format;
 } // namespace bwf
-} // namespace SWOC_NAMESPACE
+}} // namespace swoc
diff --git a/code/src/bw_ip_format.cc b/code/src/bw_ip_format.cc
index 1c96b53..ecef729 100644
--- a/code/src/bw_ip_format.cc
+++ b/code/src/bw_ip_format.cc
@@ -238,7 +238,7 @@ bwformat(BufferWriter &w, Spec const &spec, IPAddr const 
&addr) {
 
   if (addr_p) {
     if (addr.is_ip4()) {
-      swoc::bwformat(w, spec, static_cast<IP4Addr const &>(addr));
+      swoc::bwformat(w, spec, addr.ip4());
     } else if (addr.is_ip6()) {
       swoc::bwformat(w, spec, addr.ip6().network_order());
     } else {
diff --git a/code/src/swoc_ip.cc b/code/src/swoc_ip.cc
index bb4c394..9434221 100644
--- a/code/src/swoc_ip.cc
+++ b/code/src/swoc_ip.cc
@@ -258,26 +258,42 @@ IP4Addr::load(std::string_view const &text) {
   TextView src{text};
   int n = SIZE; /// # of octets
 
-  if (src.empty() || ('[' == *src && ((++src).empty() || src.back() != ']'))) {
+  _addr = INADDR_ANY; // clear to zero.
+
+  // empty or trailing dot or empty brackets or unmatched brackets.
+  if (src.empty() || src.back() == '.' || ('[' == *src && ((++src).empty() || 
src.back() != ']'))) {
     return false;
   }
 
-  auto octet = reinterpret_cast<uint8_t *>(&_addr);
-  while (n > 0 && !src.empty()) {
-    TextView token{src.take_prefix_at('.')};
-    auto x = svto_radix<10>(token);
-    if (token.empty() && x <= std::numeric_limits<uint8_t>::max()) {
-      octet[--n] = x;
-    } else {
+  in_addr_t max = std::numeric_limits<in_addr_t>::max();
+  while (n > 0) {
+    TextView parsed;
+    auto token = src.take_prefix_at('.');
+    auto v = svtou(token, &parsed);
+    if (parsed.size() != token.size()) {
+      break;
+    }
+    if (src.empty()) {
+      if (v <= max) {
+        _addr += v;
+        n = 0; // signal complete.
+      }
       break;
+    } else if (v <= std::numeric_limits<uint8_t>::max()){
+      _addr += v << ( --n * 8);
+    } else {
+      break; // invalid.
     }
+    max >>= 8; // reduce by one octet.
   }
 
-  if (n == 0 && src.empty()) {
-    return true;
+  // If there's text left, or not all the octets were filled, fail.
+  if (! src.empty() || n != 0) {
+    _addr = INADDR_ANY;
+    return false;
   }
-  _addr = INADDR_ANY;
-  return false;
+
+  return true;
 }
 
 IP4Addr::IP4Addr(sockaddr_in const *sa) : _addr(reorder(sa->sin_addr.s_addr)) 
{}
@@ -595,9 +611,9 @@ IPMask::load(string_view const &text) {
 IPMask
 IPMask::mask_for(IPAddr const &addr) {
   if (addr.is_ip4()) {
-    return self_type::mask_for(static_cast<IP4Addr const &>(addr));
+    return self_type::mask_for(addr.ip4());
   } else if (addr.is_ip6()) {
-    return self_type::mask_for(static_cast<IP6Addr const &>(addr));
+    return self_type::mask_for(addr.ip6());
   }
   return {};
 }
@@ -655,6 +671,85 @@ IPMask::as_ip6() const {
 
 // --- SRV ---
 
+IP4Srv::IP4Srv(swoc::TextView text) {
+  this->load(text);
+}
+
+bool
+IP4Srv::load(swoc::TextView text) {
+  TextView addr_text, port_text, rest;
+  if (IPEndpoint::tokenize(text, &addr_text, &port_text, &rest)) {
+    if (rest.empty()) {
+      uintmax_t n = 0;
+      if (!port_text.empty()) {
+        n = swoc::svtou(port_text, &rest);
+        if (rest.size() != port_text.size() || n > 
std::numeric_limits<in_port_t>::max()) {
+          return false; // bad port.
+        }
+      }
+      IP4Addr addr;
+      if (addr.load(addr_text)) {
+        this->assign(addr, n);
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+IP6Srv::IP6Srv(swoc::TextView text) {
+  this->load(text);
+}
+
+bool
+IP6Srv::load(swoc::TextView text) {
+  TextView addr_text, port_text, rest;
+  if (IPEndpoint::tokenize(text, &addr_text, &port_text, &rest)) {
+    if (rest.empty()) {
+      uintmax_t n = 0;
+      if (!port_text.empty()) {
+        n = swoc::svtou(port_text, &rest);
+        if (rest.size() != port_text.size() || n > 
std::numeric_limits<in_port_t>::max()) {
+          return false; // bad port.
+        }
+      }
+      IP6Addr addr;
+      if (addr.load(addr_text)) {
+        this->assign(addr, n);
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+IPSrv::IPSrv(swoc::TextView text) {
+  this->load(text);
+}
+
+bool
+IPSrv::load(swoc::TextView text) {
+  TextView addr_text, port_text, rest;
+  if (IPEndpoint::tokenize(text, &addr_text, &port_text, &rest)) {
+    if (rest.empty()) {
+      uintmax_t n = 0;
+      if (!port_text.empty()) {
+        n = swoc::svtou(port_text, &rest);
+        if (rest.size() != port_text.size() || n > 
std::numeric_limits<in_port_t>::max()) {
+          return false; // bad port.
+        }
+      }
+      IPAddr addr;
+      if (addr.load(addr_text)) {
+        this->assign(addr, n);
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+
 IPSrv::IPSrv(IPAddr addr, in_port_t port) {
   _family = addr.family();
   if (addr.is_ip4()) {
@@ -932,20 +1027,16 @@ IPRange::IPRange(IPAddr const &min, IPAddr const &max) {
 
 bool
 IPRange::load(std::string_view const &text) {
-  static const string_view CHARS{".:"};
-  if ( auto idx = text.find_first_of(CHARS) ; idx != text.npos ) {
-    if (text[idx] == '.') {
-      if (_range._ip4.load(text)) {
-        _family = AF_INET;
-        return true;
-      }
-    } else {
-      if (_range._ip6.load(text)) {
-        _family = AF_INET6;
-        return true;
-      }
+  if ( auto idx = text.find_first_of(':') ; idx != text.npos ) {
+    if (_range._ip6.load(text)) {
+      _family = AF_INET6;
+      return true;
     }
+  } else if (_range._ip4.load(text)) {
+    _family = AF_INET;
+    return true;
   }
+
   return false;
 }
 
diff --git a/unit_tests/test_Errata.cc b/unit_tests/test_Errata.cc
index 3ef3b04..8e04d64 100644
--- a/unit_tests/test_Errata.cc
+++ b/unit_tests/test_Errata.cc
@@ -40,8 +40,6 @@ void test_Errata_init() {
   });
 }
 
-
-
 Errata
 Noteworthy(std::string_view text)
 {
diff --git a/unit_tests/test_ip.cc b/unit_tests/test_ip.cc
index ae53d67..128c4ba 100644
--- a/unit_tests/test_ip.cc
+++ b/unit_tests/test_ip.cc
@@ -66,7 +66,7 @@ TEST_CASE("Basic IP", "[libswoc][ip]") {
       , {{"[ffee::24c3:3349:3cee:0143]"}         , 
{"ffee::24c3:3349:3cee:0143"}           , {nullptr}, {nullptr}}
       , {{"[ffee::24c3:3349:3cee:0143]:80"}      , 
{"ffee::24c3:3349:3cee:0143"}           , {"80"}   , {nullptr}}
       , {{"[ffee::24c3:3349:3cee:0143]:8080x"}   , 
{"ffee::24c3:3349:3cee:0143"}           , {"8080"} , {"x"}}
-      ,};
+      };
 
   for (auto const&s : names) {
     std::string_view host, port, rest;
@@ -79,26 +79,71 @@ TEST_CASE("Basic IP", "[libswoc][ip]") {
 
   IP4Addr alpha{"172.96.12.134"};
   CHECK(alpha == IP4Addr{"172.96.12.134"});
-  CHECK(alpha == IP4Addr{IPAddr{"172.96.12.134"}});
   CHECK(alpha == IPAddr{IPEndpoint{"172.96.12.134:80"}});
   CHECK(alpha == IPAddr{IPEndpoint{"172.96.12.134"}});
+  REQUIRE(alpha[1] == 96);
+  REQUIRE(alpha[2] == 12);
+  REQUIRE(alpha[3] == 134);
+
+  // Alternate forms - inet_aton compabitility. Note in truncated forms, the 
last value is for
+  // all remaining octets, those are not zero filled as in IPv6.
+  CHECK(alpha.load("172.96.12"));
+  REQUIRE(alpha[0] == 172);
+  REQUIRE(alpha[2] == 0);
+  REQUIRE(alpha[3] == 12);
+  CHECK_FALSE(alpha.load("172.96.71117"));
+  CHECK(alpha.load("172.96.3136"));
+  REQUIRE(alpha[0] == 172);
+  REQUIRE(alpha[2] == 0xC);
+  REQUIRE(alpha[3] == 0x40);
+  CHECK(alpha.load("172.12586118"));
+  REQUIRE(alpha[0] == 172);
+  REQUIRE(alpha[1] == 192);
+  REQUIRE(alpha[2] == 12);
+  REQUIRE(alpha[3] == 134);
+  CHECK(alpha.load("172.0xD00D56"));
+  REQUIRE(alpha[0] == 172);
+  REQUIRE(alpha[1] == 0xD0);
+  REQUIRE(alpha[2] == 0x0D);
+  REQUIRE(alpha[3] == 0x56);
+  CHECK_FALSE(alpha.load("192.172.3."));
+
+  CHECK(IP6Addr().load("ffee:1f2d:c587:24c3:9128:3349:3cee:143"));
 
   IP4Addr lo{"127.0.0.1"};
-  REQUIRE(lo.is_loopback() == true);
+  CHECK(lo.is_loopback());
   REQUIRE(lo.is_any() == false);
+  REQUIRE(lo.is_multicast() == false);
+  REQUIRE(lo.is_link_local() == false);
+  REQUIRE(lo[0] == 0x7F);
+
   IP4Addr any{"0.0.0.0"};
   REQUIRE(any.is_loopback() == false);
   REQUIRE(any.is_any() == true);
+  REQUIRE(lo.is_link_local() == false);
+  REQUIRE(any == IP4Addr("0"));
+
   IP6Addr lo6{"::1"};
   REQUIRE(lo6.is_loopback() == true);
   REQUIRE(lo6.is_any() == false);
   REQUIRE(lo6.is_multicast() == false);
+  REQUIRE(lo.is_link_local() == false);
+
   IP6Addr any6{"::"};
   REQUIRE(any6.is_loopback() == false);
   REQUIRE(any6.is_any() == true);
-  IP6Addr multi6{"FF02::1"};
+  REQUIRE(lo.is_link_local() == false);
+
+  IP6Addr multi6{"FF02::19"};
   REQUIRE(multi6.is_loopback() == false);
   REQUIRE(multi6.is_multicast() == true);
+  REQUIRE(lo.is_link_local() == false);
+  REQUIRE(IPAddr(multi6).is_multicast() == true);
+
+  IP6Addr ll{"FE80::56"};
+  REQUIRE(ll.is_link_local() == true);
+  REQUIRE(ll.is_multicast() == false);
+  REQUIRE(IPAddr(ll).is_link_local() == true);
 
   // Do a bit of IPv6 testing.
   IP6Addr a6_null;
@@ -124,6 +169,13 @@ TEST_CASE("Basic IP", "[libswoc][ip]") {
   REQUIRE(0 == a6_2.cmp(a6_2));
   REQUIRE(1 == a6_1.cmp(a6_2));
 
+  REQUIRE(a6_1[0] == 0xFE);
+  REQUIRE(a6_1[1] == 0x80);
+  REQUIRE(a6_2[3] == 0xB5);
+  REQUIRE(a6_3[11] == 0xAE);
+  REQUIRE(a6_3[14] == 0x1C);
+  REQUIRE(a6_2[15] == 0x34);
+
   // Little bit of IP4 address arithmetic / comparison testing.
   IP4Addr a4_null;
   IP4Addr a4_1{"172.28.56.33"};
@@ -135,6 +187,8 @@ TEST_CASE("Basic IP", "[libswoc][ip]") {
   REQUIRE(a4_loopback == ip4_loopback);
   REQUIRE(a4_loopback.is_loopback() == true);
   REQUIRE(ip4_loopback.is_loopback() == true);
+  CHECK(a4_2.is_private());
+  CHECK_FALSE(a4_3.is_private());
 
   REQUIRE(a4_1 != a4_null);
   REQUIRE(a4_1 != a4_2);
@@ -195,7 +249,10 @@ TEST_CASE("Basic IP", "[libswoc][ip]") {
     }
   }
 
+  IPRange r;
   IP4Range r4;
+  IP6Range r6;
+
   REQUIRE(r4.load("10.242.129.0-10.242.129.127") == true);
   REQUIRE(r4.min() == IP4Addr("10.242.129.0"));
   REQUIRE(r4.max() == IP4Addr("10.242.129.127"));
@@ -207,6 +264,25 @@ TEST_CASE("Basic IP", "[libswoc][ip]") {
   REQUIRE(r4.max() == IP4Addr("2.2.2.2"));
   REQUIRE(r4.load("2.2.2.2.2") == false);
   REQUIRE(r4.load("2.2.2.2-fe80:20c::29ff:feae:5587::1c33") == false);
+  CHECK(r4.load("0xC0A83801"));
+  REQUIRE(r4 == IP4Addr("192.168.56.1"));
+
+  // A few special cases.
+  static constexpr TextView all_4_txt { "0/0" };
+  static constexpr TextView all_6_txt { "::/0" };
+
+  CHECK(r4.load(all_4_txt));
+  CHECK(r.load(all_4_txt));
+  REQUIRE(r.ip4() == r4);
+  REQUIRE(r4.min() == IP4Addr::MIN);
+  REQUIRE(r4.max() == IP4Addr::MAX);
+  CHECK(r.load(all_6_txt));
+  CHECK(r6.load(all_6_txt));
+  REQUIRE(r.ip6() == r6);
+  REQUIRE(r6.min() == IP6Addr::MIN);
+  REQUIRE(r6.max() == IP6Addr::MAX);
+  CHECK_FALSE(r6.load("2.2.2.2-fe80:20c::29ff:feae:5587::1c33"));
+  CHECK_FALSE(r.load("2.2.2.2-fe80:20c::29ff:feae:5587::1c33"));
 
   IPEndpoint ep;
   ep.set_to_any(AF_INET);
@@ -225,11 +301,12 @@ TEST_CASE("Basic IP", "[libswoc][ip]") {
   REQUIRE(a4.is_loopback() == true);
   REQUIRE(a4.is_any() == false);
 
-  CHECK_FALSE(IP6Addr("1337:0:0:ded:BEEF:0:0:0").is_mapped_ipv4());
-  CHECK_FALSE(IP6Addr("1337:0:0:ded:BEEF::").is_mapped_ipv4());
-  CHECK(IP6Addr("::FFFF:C0A8:381F").is_mapped_ipv4());
-  CHECK_FALSE(IP6Addr("FFFF:C0A8:381F::").is_mapped_ipv4());
-  CHECK_FALSE(IP6Addr("::C0A8:381F").is_mapped_ipv4());
+  CHECK_FALSE(IP6Addr("1337:0:0:ded:BEEF:0:0:0").is_mapped_ip4());
+  CHECK_FALSE(IP6Addr("1337:0:0:ded:BEEF::").is_mapped_ip4());
+  CHECK(IP6Addr("::FFFF:C0A8:381F").is_mapped_ip4());
+  CHECK_FALSE(IP6Addr("FFFF:C0A8:381F::").is_mapped_ip4());
+  CHECK_FALSE(IP6Addr("::C0A8:381F").is_mapped_ip4());
+  CHECK(IP6Addr(a4_2).is_mapped_ip4());
 };
 
 TEST_CASE("IP Formatting", "[libswoc][ip][bwformat]") {
@@ -408,8 +485,8 @@ TEST_CASE("IP ranges and networks", 
"[libswoc][ip][net][range]") {
   CHECK(r_5.min() == (r_5.min() | mask));
   CHECK(r_5.min() != (r_5.min() & mask));
 
-  swoc::IP6Addr a_1{"2001:1f2d:c587:24c4::"};
-  CHECK(a_1 == (a_1 & swoc::IPMask{62}));
+  swoc::IP6Addr aa_1{"2001:1f2d:c587:24c4::"};
+  CHECK(aa_1 == (aa_1 & swoc::IPMask{62}));
 
   std::array<swoc::IP4Net, 7> r_4_nets =
       {{
@@ -627,17 +704,17 @@ TEST_CASE("IP ranges and networks", 
"[libswoc][ip][net][range]") {
        }};
 
   auto r5_net = r_5_nets.begin();
-  for (auto const&[addr, mask] : r_5.networks()) {
+  for (auto const&[a, m] : r_5.networks()) {
     REQUIRE(r5_net != r_5_nets.end());
-    CHECK(*r5_net == swoc::IP6Net{addr, mask});
+    CHECK(*r5_net == swoc::IP6Net{a, m});
     ++r5_net;
   }
 
   // Try it again, using @c IPNet.
   r5_net = r_5_nets.begin();
-  for ( auto const&[addr, mask] : IPRange{r_5}.networks()) {
+  for ( auto const&[a, m] : IPRange{r_5}.networks()) {
     REQUIRE(r5_net != r_5_nets.end());
-    CHECK(*r5_net == swoc::IPNet{addr, mask});
+    CHECK(*r5_net == swoc::IPNet{a, m});
     ++r5_net;
   }
 }
@@ -1765,4 +1842,27 @@ TEST_CASE("IPSrv", "[libswoc][IPSrv]") {
   IP4Addr tmp3{s4};
   REQUIRE(s4 == tmp3);
   REQUIRE(s4.addr() == tmp3); // double check equality.
+
+  IP4Srv s4_1 { "10.9.8.7:56" };
+  REQUIRE(s4_1.host_order_port() == 56);
+  REQUIRE(s4_1 == a2);
+  CHECK(s4_1.load("10.2:56"));
+  CHECK_FALSE(s4_1.load("10.1.2.3.567899"));
+  CHECK_FALSE(s4_1.load("10.1.2.3.56f"));
+  CHECK(s4_1.load("10.1.2.3"));
+  REQUIRE(s4_1.host_order_port() == 0);
+
+  CHECK(s6.load("[ffee:1f2d:c587:24c3:9128:3349:3cee:143]:956"));
+  REQUIRE(s6 == aa1);
+  REQUIRE(s6.host_order_port() == 956);
+  CHECK(s6.load("ffee:1f2d:c587:24c3:9128:3349:3cee:143"));
+  REQUIRE(s6 == aa1);
+  REQUIRE(s6.host_order_port() == 0);
+
+  CHECK(s.load("[ffee:1f2d:c587:24c3:9128:3349:3cee:143]:956"));
+  REQUIRE(s == aa1);
+  REQUIRE(s.host_order_port() == 956);
+  CHECK(s.load("ffee:1f2d:c587:24c3:9128:3349:3cee:143"));
+  REQUIRE(s == aa1);
+  REQUIRE(s.host_order_port() == 0);
 }


Reply via email to