You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by bn...@apache.org on 2024/01/30 16:26:37 UTC
(trafficserver-libswoc) 04/04: IP: add compact range output support.
This is an automated email from the ASF dual-hosted git repository.
bneradt pushed a commit to branch dev-1-2-12
in repository https://gitbox.apache.org/repos/asf/trafficserver-libswoc.git
commit e525832f22bd2721bb7321090117d3dadc454592
Author: Alan M. Carroll <am...@apache.org>
AuthorDate: Tue Oct 20 09:04:42 2020 -0500
IP: add compact range output support.
---
code/include/swoc/swoc_ip.h | 38 ++++++++++++++++++++++++++++++++++++++
code/src/bw_ip_format.cc | 26 ++++++++++++++++++++++++++
code/src/swoc_ip.cc | 25 +++++++++++++++++++++++++
doc/code/IPSpace.en.rst | 14 ++++++++++++++
unit_tests/test_ip.cc | 33 ++++++++++++++++++++++++++++++++-
5 files changed, 135 insertions(+), 1 deletion(-)
diff --git a/code/include/swoc/swoc_ip.h b/code/include/swoc/swoc_ip.h
index de9627d..44152c0 100644
--- a/code/include/swoc/swoc_ip.h
+++ b/code/include/swoc/swoc_ip.h
@@ -831,6 +831,14 @@ public:
*/
bool load(string_view text);
+ /** Compute the mask for @a this as a network.
+ *
+ * @return If @a this is a network, the mask for that network. Otherwise an invalid mask.
+ *
+ * @see IPMask::is_valid
+ */
+ IPMask network_mask() const;
+
class NetSource;
/** Generate a list of networks covering @a this range.
@@ -874,6 +882,9 @@ public:
iterator begin() const; ///< First network.
iterator end() const; ///< Past last network.
+ /// Return @c true if there are valid networks, @c false if not.
+ bool empty() const;
+
/// @return The current network.
IP4Net operator*() const;
@@ -955,6 +966,14 @@ public:
*/
bool load(string_view text);
+ /** Compute the mask for @a this as a network.
+ *
+ * @return If @a this is a network, the mask for that network. Otherwise an invalid mask.
+ *
+ * @see IPMask::is_valid
+ */
+ IPMask network_mask() const;
+
class NetSource;
/** Generate a list of networks covering @a this range.
@@ -998,6 +1017,9 @@ public:
iterator begin() const; ///< First network.
iterator end() const; ///< Past last network.
+ /// Return @c true if there are valid networks, @c false if not.
+ bool empty() const;
+
/// @return The current network.
IP6Net operator*() const;
@@ -1106,6 +1128,14 @@ public:
IP6Range const& ip6() const { return _range._ip6; }
+ /** Compute the mask for @a this as a network.
+ *
+ * @return If @a this is a network, the mask for that network. Otherwise an invalid mask.
+ *
+ * @see IPMask::is_valid
+ */
+ IPMask network_mask() const;
+
class NetSource;
/** Generate a list of networks covering @a this range.
@@ -2627,6 +2657,10 @@ inline IP4Range::NetSource::iterator IP4Range::NetSource::end() const {
return self_type{range_type{}};
}
+inline bool IP4Range::NetSource::empty() const {
+ return _range.empty();
+}
+
inline IPMask IP4Range::NetSource::mask() const { return IPMask{_cidr}; }
inline auto IP4Range::NetSource::operator->() -> self_type * { return this; }
@@ -2650,6 +2684,10 @@ inline auto IP6Range::NetSource::end() const -> iterator {
return self_type{range_type{}};
}
+inline bool IP6Range::NetSource::empty() const {
+ return _range.empty();
+}
+
inline IP6Net IP6Range::NetSource::operator*() const {
return IP6Net{_range.min(), _mask};
}
diff --git a/code/src/bw_ip_format.cc b/code/src/bw_ip_format.cc
index f45c46e..2efa30d 100644
--- a/code/src/bw_ip_format.cc
+++ b/code/src/bw_ip_format.cc
@@ -262,6 +262,19 @@ bwformat(BufferWriter& w, Spec const& spec, IP4Range const& range) {
if (range.empty()) {
w.write("*-*"_tv);
} else {
+ // Compact means output as singleton or CIDR if that's possible.
+ if (spec._ext.find('c') != spec._ext.npos) {
+ if (range.is_singleton()) {
+ return bwformat(w, spec, range.min());
+ }
+ auto mask { range.network_mask() };
+ if (mask.is_valid()) {
+ bwformat(w, spec, range.min());
+ w.write('/');
+ bwformat(w, bwf::Spec::DEFAULT, mask);
+ return w;
+ }
+ }
bwformat(w, spec, range.min());
w.write('-');
bwformat(w, spec, range.max());
@@ -274,6 +287,19 @@ bwformat(BufferWriter& w, Spec const& spec, IP6Range const& range) {
if (range.empty()) {
w.write("*-*"_tv);
} else {
+ // Compact means output as singleton or CIDR if that's possible.
+ if (spec._ext.find('c') != spec._ext.npos) {
+ if (range.is_singleton()) {
+ return bwformat(w, spec, range.min());
+ }
+ auto mask { range.network_mask() };
+ if (mask.is_valid()) {
+ bwformat(w, spec, range.min());
+ w.write('/');
+ bwformat(w, bwf::Spec::DEFAULT, mask);
+ return w;
+ }
+ }
bwformat(w, spec, range.min());
w.write('-');
bwformat(w, spec, range.max());
diff --git a/code/src/swoc_ip.cc b/code/src/swoc_ip.cc
index 10f1d3c..c971088 100644
--- a/code/src/swoc_ip.cc
+++ b/code/src/swoc_ip.cc
@@ -771,6 +771,14 @@ bool IP4Range::load(string_view text) {
return false;
}
+IPMask IP4Range::network_mask() const {
+ NetSource nets { *this };
+ if (! nets.empty() && (*nets).as_range() == *this) {
+ return nets->mask();
+ }
+ return {}; // default constructed (invalid) mask.
+}
+
IP4Range::NetSource::NetSource(IP4Range::NetSource::range_type const& range) : _range(range) {
if (!_range.empty()) {
this->search_wider();
@@ -926,6 +934,23 @@ bool IPRange::empty() const {
return true;
}
+IPMask IPRange::network_mask() const {
+ switch (_family) {
+ case AF_INET: return _range._ip4.network_mask();
+ case AF_INET6: return _range._ip6.network_mask();
+ default: break;
+ }
+ return {};
+}
+
+IPMask IP6Range::network_mask() const {
+ NetSource nets { *this };
+ if (! nets.empty() && (*nets).as_range() == *this) {
+ return nets->mask();
+ }
+ return {}; // default constructed (invalid) mask.
+}
+
IP6Range::NetSource::NetSource(IP6Range::NetSource::range_type const& range) : _range(range) {
if (!_range.empty()) {
this->search_wider();
diff --git a/doc/code/IPSpace.en.rst b/doc/code/IPSpace.en.rst
index a2dfed9..bd4db81 100644
--- a/doc/code/IPSpace.en.rst
+++ b/doc/code/IPSpace.en.rst
@@ -83,6 +83,20 @@ properly formatted, otherwise the range will be default constructed to an invali
also the :libswoc:`swoc::IPRange::load` method which returns a :code:`bool` to indicate if the
parsing was successful.
+This class has formatting support in "bwf_ip.h". In addition to all of the formatting supported for
+:code:`sockaddr`, the additional extension code 'c' can be used to indicate compact range formatting.
+Compact means a singleton range will be written as just the single address, and if the range is
+also a network it will be printed in CIDR format.
+
+======================= =======================
+Range Compact
+======================= =======================
+10.1.0.0-10.1.0.127 10.1.0.0/25
+10.2.0.1-10.2.0.127 10.2.0.1-10.2.0.127
+10.3.0.0-10.3.0.126 10.3.0.0-10.3.0.126
+10.4.1.1-10.4.1.1 10.4.1.1
+======================= =======================
+
.. _ip-space:
IPSpace
diff --git a/unit_tests/test_ip.cc b/unit_tests/test_ip.cc
index aed4618..5e381b0 100644
--- a/unit_tests/test_ip.cc
+++ b/unit_tests/test_ip.cc
@@ -321,6 +321,37 @@ TEST_CASE("IP Formatting", "[libswoc][ip][bwformat]") {
REQUIRE(w.view() == "0000:0000:0000:0000:0000:0000:0000:0001");
w.clear().print("{:: =a}", ep);
REQUIRE(w.view() == " 0: 0: 0: 0: 0: 0: 0: 1");
+
+ std::string_view r_1{"10.1.0.0-10.1.0.127"};
+ std::string_view r_2{"10.2.0.1-10.2.0.127"}; // not a network - bad start
+ std::string_view r_3{"10.3.0.0-10.3.0.126"}; // not a network - bad end
+ std::string_view r_4{"10.4.1.1-10.4.1.1"}; // singleton
+
+ IPRange r;
+
+ r.load(r_1);
+ w.clear().print("{}", r);
+ REQUIRE(w.view() == r_1);
+ w.clear().print("{::c}", r);
+ REQUIRE(w.view() == "10.1.0.0/25");
+
+ r.load(r_2);
+ w.clear().print("{}", r);
+ REQUIRE(w.view() == r_2);
+ w.clear().print("{::c}", r);
+ REQUIRE(w.view() == r_2);
+
+ r.load(r_3);
+ w.clear().print("{}", r);
+ REQUIRE(w.view() == r_3);
+ w.clear().print("{::c}", r);
+ REQUIRE(w.view() == r_3);
+
+ r.load(r_4);
+ w.clear().print("{}", r);
+ REQUIRE(w.view() == r_4);
+ w.clear().print("{::c}", r);
+ REQUIRE(w.view() == "10.4.1.1");
}
TEST_CASE("IP ranges and networks", "[libswoc][ip][net][range]") {
@@ -565,7 +596,7 @@ TEST_CASE("IP ranges and networks", "[libswoc][ip][net][range]") {
++r5_net;
}
- // Try it again, using @c IPRange.
+ // Try it again, using @c IPNet.
r5_net = r_5_nets.begin();
for ( auto const&[addr, mask] : IPRange{r_5}.networks()) {
REQUIRE(r5_net != r_5_nets.end());