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

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

commit 82b5d7f47316abc748bd052a614debec2eb69140
Author: Alan M. Carroll <a...@apache.org>
AuthorDate: Fri Mar 27 14:49:01 2020 -0500

    Fix bugs in IPSPace blending where ranges were not coalesced properly.
---
 CMakeLists.txt                      |   1 +
 example/CMakeLists.txt              |  11 ++
 example/ex_netdb.cc                 | 295 ++++++++++++++++++++++++++++++++++++
 swoc++/include/swoc/DiscreteRange.h |  62 +++++---
 unit_tests/test_ip.cc               | 204 +++++++------------------
 5 files changed, 402 insertions(+), 171 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7c6e0c4..518ba09 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,6 +6,7 @@ set(INSTALL_DIR ${CMAKE_HOME_DIRECTORY})
 # Fortunately this has no external dependencies so the set up can be simple.
 add_subdirectory(swoc++)
 add_subdirectory(unit_tests)
+add_subdirectory(example)
 add_subdirectory(doc EXCLUDE_FROM_ALL)
 
 # Find all of the directories subject to clang formatting and make a target to 
do the format.
diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt
new file mode 100644
index 0000000..07394ab
--- /dev/null
+++ b/example/CMakeLists.txt
@@ -0,0 +1,11 @@
+cmake_minimum_required(VERSION 3.12)
+project(libswoc_examples CXX)
+set(CMAKE_CXX_STANDARD 17)
+
+add_executable(ex_netdb
+    ex_netdb.cc
+    )
+
+target_link_libraries(ex_netdb PUBLIC swoc++)
+set_target_properties(ex_netdb PROPERTIES CLANG_FORMAT_DIRS 
${CMAKE_CURRENT_SOURCE_DIR})
+target_compile_options(ex_netdb PRIVATE -Wall -Wextra -Werror 
-Wno-unused-parameter -Wno-format-truncation -Wno-stringop-overflow 
-Wno-invalid-offsetof)
diff --git a/example/ex_netdb.cc b/example/ex_netdb.cc
new file mode 100644
index 0000000..fc50158
--- /dev/null
+++ b/example/ex_netdb.cc
@@ -0,0 +1,295 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright 2014 Network Geographics
+
+/** @file
+
+    Example tool to process network DB files.
+
+    This generates an executable that processes a list of network files in a 
specific format.
+    Tokens are (variable) space separated. Example lines look like
+
+    4441:34F8:1E40:1EF:0:0:A0:0/108  partner:ngeo  raz     ?_  prod,dmz        
 "shared space"
+    4441:34F8:1E40:1EF:0:0:B0:0/108  yahoo  raz     ?_  prod,dmz         
"local routing"
+
+    There is
+    - an IP address network
+    - type/owner field. If "yahoo", it's type "yahoo" and owner "yahoo". 
Otherwise it's a
+      partner network, with "partner:" followed by the partner name.
+    - pod / colo code
+    - Useless column marker
+    - Network property flags
+    - Comment in double quotes
+
+    The goal is to transform this in a standard CSV file, dropping the useless 
column, and
+    tweaking the flags to use ';' instead of ',' as a separator (to avoid 
confusing the CSV).
+    Adjacent networks with identical properties should also be merged to 
reduce the data set size.
+    The comments may or not be kept - currently the implement discards them to 
get better
+    network coalescence. One of the goals of the over all work is to make it 
easy to build
+    variants of this code to output CSV files tuned to specific uses.
+*/
+
+#include <unordered_set>
+#include <fstream>
+
+#include <swoc/TextView.h>
+#include <swoc/swoc_ip.h>
+#include <swoc/bwf_ip.h>
+#include <swoc/bwf_std.h>
+#include <swoc/bwf_ex.h>
+#include <swoc/swoc_file.h>
+#include <swoc/Lexicon.h>
+
+using namespace std::literals;
+using namespace swoc::literals;
+
+using swoc::TextView;
+using swoc::IPEndpoint;
+
+using swoc::IP4Addr;
+using swoc::IP4Range;
+
+using swoc::IP6Addr;
+using swoc::IP6Range;
+
+using swoc::IPAddr;
+using swoc::IPRange;
+
+using swoc::IPMask;
+
+/// Type for temporary buffer writer output.
+using W = swoc::LocalBufferWriter<512>;
+
+/// Network properties.
+enum class Flag {
+  INVALID = -1, ///< Value for initialization, invalid.
+  INTERNAL = 0, ///< Internal network.
+  PROD, ///< Production network.
+  DMZ, ///< DMZ
+  SECURE, ///< Secure network
+  NONE ///< No flags - useful because the input uses "-" sometimes to mark no 
properties.
+};
+
+namespace std {
+/// Make the enum size look like a tuple size.
+template <> struct tuple_size<Flag> : public std::integral_constant<size_t, 
static_cast<size_t>(Flag::NONE)> {};
+} // namespace std
+
+/// Bit set for network property flags.
+using FlagSet = std::bitset<std::tuple_size<Flag>::value>;
+
+/// Pod type.
+enum class PodType {
+  INVALID, ///< Initialization value.
+  YAHOO, ///< Yahoo! pod.
+  PARTNER ///< Partner pod.
+};
+
+/// Mapping of names and property flags.
+swoc::Lexicon<Flag> FlagNames {{
+                                   {Flag::NONE, {"-", "NONE"}}
+                                   , {Flag::INTERNAL, { "internal" }}
+                                   , {Flag::PROD, {"prod"}}
+                                   , {Flag::DMZ, {"dmz"}}
+                                   , {Flag::SECURE, {"secure"}}
+                               }};
+
+/// Mapping of names and pod types.
+swoc::Lexicon<PodType> PodTypeNames {{
+                                   {PodType::YAHOO, "yahoo"}
+                                   , {PodType::PARTNER, "partner"}
+                               }};
+
+// Create BW formatters for the types so they can be used for output.
+namespace swoc {
+
+BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, PodType pt) {
+  return w.write(PodTypeNames[pt]);
+}
+
+BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, FlagSet const& 
flags) {
+  bool first_p = true; // Track to get separators correct.
+  // Loop through the indices and write the flag name if it's set.
+  for ( unsigned idx = 0 ; idx < std::tuple_size<Flag>::value ; ++idx) {
+    if (flags[idx]) {
+      if (!first_p) {
+        w.write(';');
+      }
+      w.write(FlagNames[static_cast<Flag>(idx)]);
+      first_p = false;
+    }
+  }
+  return w;
+}
+
+} // namespace swoc
+
+// These are used to keep pointers for the same string identical so the 
payloads
+// can be directly compared.
+std::unordered_set<TextView, std::hash<std::string_view>> PodNames;
+std::unordered_set<TextView, std::hash<std::string_view>> OwnerNames;
+std::unordered_set<TextView, std::hash<std::string_view>> Descriptions;
+
+/// The "color" for the IPSpace.
+struct Payload {
+  PodType _type = PodType::INVALID; ///< Type of ownership.
+  TextView _owner; ///< Corporate owner.
+  TextView _pod; ///< Pod / colocation.
+  TextView _descr; ///< Description / comment
+  FlagSet _flags; ///< Flags.
+
+  /// @return @c true if @a this is equal to @a that.
+  bool operator == (Payload const& that) {
+    return _type == that._type &&
+    _owner == that._owner &&
+    _pod == that._pod &&
+    _flags == that._flags &&
+    _descr == that._descr;
+  }
+
+  /// @return @c true if @a this is not equal to @a that.
+  bool operator != (Payload const& that) {
+    return ! (*this == that);
+  }
+};
+
+
+/// IPSpace for mapping address to @c Payload
+using Space = swoc::IPSpace<Payload>;
+
+/// Place to store strings parsed from the input files.
+swoc::MemArena Storage;
+
+/// Convert a parsed string into a stored string to make the pointer 
persistent.
+TextView store(TextView const& text) {
+  auto span = Storage.alloc(text.size()).rebind<char>();
+  memcpy(span, text);
+  return span.view();
+}
+
+/// Process the @a content of a file in to @a space.
+void process(Space& space, TextView content) {
+  int line_no = 0; /// Track for error reporting.
+
+  // For each line in @a content
+  for  (TextView line ; ! (line = content.take_prefix_at('\n')).empty() ; ) {
+    ++line_no;
+    line.trim_if(&isspace);
+    // Allow empty lines and '#' comments without error.
+    if (line.empty() || '#' == *line) {
+      continue;
+    }
+
+    // Get the range, make sure it's a valid range.
+    auto range_token = line.take_prefix_if(&isspace);
+    IPRange range{range_token};
+    if (range.empty()) {
+      std::cerr << W().print("Invalid range '{}' on line {}\n", range_token, 
line_no);
+      continue;
+    }
+
+    // Get the owner / type.
+    auto owner_token = line.ltrim_if(&isspace).take_prefix_if(&isspace);
+    PodType pod_type = PodType::YAHOO; // default to this if no owner 
specifier found.
+    if (auto type_token = owner_token.split_prefix_at(':') ; ! 
type_token.empty()) {
+      pod_type = PodTypeNames[type_token];
+      if (PodType::INVALID == pod_type) {
+        std::cerr << W().print("Invalid type '{}' on line {}\n", type_token, 
line_no);
+        continue;
+      }
+    }
+
+    // normalize owner
+    if ( auto spot = OwnerNames.find(owner_token) ; spot == OwnerNames.end()) {
+      owner_token = store(owner_token);
+      OwnerNames.insert(owner_token);
+    } else {
+      owner_token = *spot;
+    }
+
+    // Get the pod code.
+    auto pod_token = line.ltrim_if(&isspace).take_prefix_if(&isspace);
+
+    // normalize
+    if ( auto spot = PodNames.find(pod_token) ; spot == PodNames.end()) {
+      pod_token = store(pod_token);
+      PodNames.insert(pod_token);
+    } else {
+      pod_token = *spot;
+    }
+
+    line.ltrim_if(&isspace).take_prefix_if(&isspace); // skip bogus column.
+
+    // Work on the flags.
+    auto flag_token = line.ltrim_if(&isspace).take_prefix_if(&isspace);
+    FlagSet flags;
+    // Loop over the token, picking out comma separated keys.
+    for ( TextView key ; ! (key = flag_token.take_prefix_at(',')).empty() ; ) {
+      auto idx = FlagNames[key]; // look up the key.
+      if (Flag::INVALID == idx) {
+        std::cerr << W().print("Invalid flag '{}' on line {}\n", key, line_no);
+        continue;
+      }
+      if (idx != Flag::NONE) { // "NONE" means the input was marked explicitly 
as no flags.
+        flags[int(idx)] = true;
+      }
+    }
+
+    #if 0
+    // The description is what's left, trim the spaces and then the quotes.
+    auto descr_token = line.trim_if(&isspace).trim('"');
+    // normalize
+    if ( auto spot = Descriptions.find(descr_token) ; spot == 
Descriptions.end()) {
+      descr_token = store(descr_token);
+      Descriptions.insert(descr_token);
+    } else {
+      descr_token = *spot;
+    }
+    #endif
+
+    // Everything went OK, create the payload and put it in the space.
+    Payload payload{pod_type, owner_token, pod_token, {}, flags};
+    space.blend(range, payload, [&](Payload & lhs, Payload const& rhs) {
+      // It's an error if there's overlap that's not consistent.
+      if (lhs._type != PodType::INVALID && lhs != rhs) {
+        std::cerr << W().print("Range collision while blending {} on line 
{}\n", range, line_no);
+      }
+      lhs = rhs;
+      return true;
+    });
+  }
+}
+
+int main(int argc, char *argv[]) {
+  Space space;
+
+  // Set the defaults so bogus input doesn't throw.
+  PodTypeNames.set_default(PodType::INVALID);
+  FlagNames.set_default(Flag::INVALID);
+
+  // Open the output file.
+  std::ofstream output;
+  output.open("vz_netdb.csv", std::ofstream::out | std::ofstream::trunc);
+  if (! output.is_open()) {
+    std::cerr << W().print("Unable to open output file: {}", 
swoc::bwf::Errno{errno});
+  }
+
+  // Process the files in the command line.
+  for ( int idx = 1 ; idx < argc ; ++idx ) {
+    swoc::file::path path(argv[idx]);
+    std::error_code ec;
+    std::string content = swoc::file::load(path, ec);
+    if (! ec) {
+      std::cout << W().print("Processing {}, {} bytes\n", path, 
content.size());
+      process(space, content);
+    }
+  }
+
+  // Dump the resulting space.
+  std::cout << W().print("{} ranges\n", space.count());
+  for ( auto && [ r, p] : space) {
+    // Note - if description is to be used, it needs to be added here.
+    output << W().print("{},{},{},{},{}\n", r, p._type, p._owner, p._pod, 
p._flags);
+  }
+
+  return 0;
+}
diff --git a/swoc++/include/swoc/DiscreteRange.h 
b/swoc++/include/swoc/DiscreteRange.h
index 967460b..d6f933e 100644
--- a/swoc++/include/swoc/DiscreteRange.h
+++ b/swoc++/include/swoc/DiscreteRange.h
@@ -242,9 +242,7 @@ public:
    */
   EdgeRelation left_edge_relationship(self_type const& that) const {
     if (_max < that._max) {
-      auto tmp{_max};
-      ++tmp;
-      return tmp < that._max ? EdgeRelation::GAP : EdgeRelation::ADJ;
+      return ++metric_type(_max) < that._max ? EdgeRelation::GAP : 
EdgeRelation::ADJ;
     }
     return _min >= that._min ? EdgeRelation::NONE : EdgeRelation::OVLP;
   }
@@ -1215,7 +1213,7 @@ DiscreteSpace<METRIC, 
PAYLOAD>::blend(DiscreteSpace::range_type const&range, U c
                                       , F &&blender) -> self_type & {
   // Do a base check for the color to use on unmapped values. If self blending 
on @a color
   // is @c false, then do not color currently unmapped values.
-  auto plain_color = PAYLOAD();
+  auto plain_color = PAYLOAD{};
   bool plain_color_p = blender(plain_color, color);
 
   auto node_cleaner = [&] (Node * ptr) -> void { _fa.destroy(ptr); };
@@ -1238,28 +1236,52 @@ DiscreteSpace<METRIC, 
PAYLOAD>::blend(DiscreteSpace::range_type const&range, U c
 
   // Process @a n, covering the values from the previous range to @a n.max
   while (n) {
-    // Always look back at prev, so if there's no overlap at all, skip it.
+    // If there's no overlap, skip because this will be checked next loop, or 
it's the last node
+    // and will be checked post loop. Loop logic is simpler if n->max() >= 
remaining.min()
     if (n->max() < remaining.min()) {
       n = next(n);
       continue;
     }
-
     // Invariant - n->max() >= remaining.min();
+
     Node* pred = prev(n);
 
     // Check for left extension. If found, clip that node to be adjacent and 
put in a
     // temporary that covers the overlap with the original payload.
     if (n->min() < remaining.min()) {
-      auto stub = _fa.make(remaining.min(), n->max(), n->payload());
-      auto x { remaining.min() };
-      --x;
-      n->assign_max(x);
-      this->insert_after(n, stub);
-      pred = n;
-      n = stub;
+      // @a fill is inserted iff n->max() < remaining.max(), in which case the 
max is correct.
+      // This is needed in other cases only for the color blending result.
+      unique_node fill { _fa.make(remaining.min(), n->max(), n->payload()), 
node_cleaner };
+      bool fill_p = blender(fill->payload(), color); // fill or clear?
+
+      if (fill_p) {
+        bool same_color_p = fill->payload() == n->payload();
+        if (same_color_p && n->max() >= remaining.max()) {
+          return *this; // incoming range is completely covered by @a n in the 
same color, done.
+        }
+        remaining.assign_min(++METRIC(n->max())); // going to fill up 
n->max(), clip target.
+        if (! same_color_p) {
+          n->assign_max(--METRIC(remaining.min())); // clip @a n down.
+          this->insert_after(n, fill.get()); // add intersection node in 
different color.
+          n = fill.release();
+        }
+      } else { // clear, don't fill.
+        auto max = n->max(); // cache this before reassigning.
+        if (max > remaining.max()) { // overhang on the right, must split.
+          fill.release();
+          this->insert_before(n, _fa.make(n->min(), --METRIC(remaining.min()), 
n->payload()));
+          n->assign_min(++METRIC(remaining.max()));
+          return *this;
+        }
+        n->assign_max(--METRIC(remaining.min())); // clip @a n down.
+        if (max == remaining.max()) {
+          return *this;
+        }
+        remaining.assign_min(++METRIC(max));
+      }
+      continue;
     }
 
-    auto pred_edge = pred ? remaining.left_edge_relationship(pred->range()) : 
DiscreteRangeEdgeRelation::NONE;
     // invariant - pred->max() < remaining.min()
 
     // Calculate and cache key relationships between @a n and @a remaining.
@@ -1269,12 +1291,14 @@ DiscreteSpace<METRIC, 
PAYLOAD>::blend(DiscreteSpace::range_type const&range, U c
     // @a n strictly right overlaps with @a remaining.
     bool right_overlap_p = remaining.contains(n->min());
     // @a n is adjacent on the right to @a remaining.
-    bool right_adj_p = remaining.is_left_adjacent_to(n->range());
+    bool right_adj_p = !right_overlap_p && 
remaining.is_left_adjacent_to(n->range());
     // @a n has the same color as would be used for unmapped values.
     bool n_plain_colored_p = plain_color_p && (n->payload() == plain_color);
-    // @a rped has the same color as would be used for unmapped values.
+    // @a pred has the same color as would be used for unmapped values.
     bool pred_plain_colored_p =
-        (DiscreteRangeEdgeRelation::NONE != pred_edge && 
DiscreteRangeEdgeRelation::GAP != pred_edge) &&
+        pred &&
+        pred->max() < remaining.min() &&
+        ++metric_type(pred->max()) == remaining.min() &&
         pred->payload() == plain_color;
 
     // Check for no right overlap - that means @a n is past the target range.
@@ -1336,8 +1360,8 @@ DiscreteSpace<METRIC, 
PAYLOAD>::blend(DiscreteSpace::range_type const&range, U c
         } else {
           n->assign_min(range_max_plus_1);
           this->insert_before(n, fill.release());
-          return *this;
         }
+        return *this; // @a n extends past @a remaining, -> all done.
       } else {
         // Collapse in to previous range if it's adjacent and the color 
matches.
         if (nullptr != (pred = prev(n)) && 
pred->range().is_left_adjacent_to(fill->range()) && pred->payload() == 
fill->payload()) {
@@ -1361,7 +1385,7 @@ DiscreteSpace<METRIC, 
PAYLOAD>::blend(DiscreteSpace::range_type const&range, U c
 
   // Arriving here means there are no more ranges past @a range (those cases 
return from the loop).
   // Therefore the final fill node is always last in the tree.
-  if (plain_color_p && remaining.min() <= range.max()) {
+  if (plain_color_p && ! remaining.empty()) {
     // Check if the last node can be extended to cover because it's left 
adjacent.
     // Can decrement @a range_min because if there's a range to the left, @a 
range_min is not minimal.
     n = _list.tail();
diff --git a/unit_tests/test_ip.cc b/unit_tests/test_ip.cc
index 4639c16..f1c8d05 100644
--- a/unit_tests/test_ip.cc
+++ b/unit_tests/test_ip.cc
@@ -631,6 +631,58 @@ TEST_CASE("IP Space Int", "[libswoc][ip][ipspace]") {
   CHECK(space.end() != space.find(IP4Addr{"100.0.4.16"}));
   CHECK(space.end() != space.find(IPAddr{"100.0.4.16"}));
   CHECK(space.end() != space.find(IPAddr{IPEndpoint{"100.0.4.16:80"}}));
+
+  auto b2 = [] (unsigned &lhs, unsigned const& rhs) { lhs = rhs; return true; 
};
+
+  std::array<std::tuple<TextView, unsigned>, 31> r2 = {
+      {
+          {"2001:4998:58:400::1/128", 1} // 1
+          , {"2001:4998:58:400::2/128", 1}
+          , {"2001:4998:58:400::3/128", 1}
+          , {"2001:4998:58:400::4/128", 1}
+          , {"2001:4998:58:400::5/128", 1}
+          , {"2001:4998:58:400::6/128", 1}
+          , {"2001:4998:58:400::7/128", 1}
+          , {"2001:4998:58:400::8/128", 1}
+          , {"2001:4998:58:400::9/128", 1}
+          , {"2001:4998:58:400::A/127", 1}
+          , {"2001:4998:58:400::10/127", 1} // 2
+          , {"2001:4998:58:400::12/127", 1}
+          , {"2001:4998:58:400::14/127", 1}
+          , {"2001:4998:58:400::16/127", 1}
+          , {"2001:4998:58:400::18/127", 1}
+          , {"2001:4998:58:400::1a/127", 1}
+          , {"2001:4998:58:400::1c/127", 1}
+          , {"2001:4998:58:400::1e/127", 1}
+          , {"2001:4998:58:400::20/127", 1}
+          , {"2001:4998:58:400::22/127", 1}
+          , {"2001:4998:58:400::24/127", 1}
+          , {"2001:4998:58:400::26/127", 1}
+          , {"2001:4998:58:400::2a/127", 1} // 3
+          , {"2001:4998:58:400::2c/127", 1}
+          , {"2001:4998:58:400::2e/127", 1}
+          , {"2001:4998:58:400::30/127", 1}
+          , {"2001:4998:58:400::140/127", 1} // 4
+          , {"2001:4998:58:400::142/127", 1}
+          , {"2001:4998:58:400::146/127", 1} // 5
+          , {"2001:4998:58:400::148/127", 1}
+          , {"2001:4998:58:400::150/127", 1} // 6
+      }};
+
+  space.clear();
+  for (auto &&[text, value] : r2) {
+    IPRange range{text};
+    space.blend(IPRange{text}, value, b2);
+  }
+  CHECK(6 == space.count());
+  for (auto &&[text, value] : r2) {
+    IPRange range{text};
+    space.blend(IPRange{text}, value, b2);
+  }
+  CHECK(6 == space.count());
+  // Drop a non-intersecting range between existing ranges 1 and 2, make sure 
both sides coalesce.
+  space.blend(IPRange{"2001:4998:58:400::C/126"_tv}, 1, b2);
+  CHECK(5 == space.count());
 }
 
 TEST_CASE("IPSpace bitset", "[libswoc][ipspace][bitset]") {
@@ -736,155 +788,3 @@ TEST_CASE("IPSpace docJJ", "[libswoc][ipspace][docJJ]") {
     ++idx;
   }
 }
-
-#if 0
-TEST_CASE("IP Space YNETDB", "[libswoc][ipspace][ynetdb]") {
-  std::set<std::string_view> Locations;
-  std::set<std::string_view> Owners;
-  std::set<std::string_view> Descriptions;
-  swoc::MemArena arena;
-  auto Localize = [&](TextView const&view) -> TextView {
-    auto span = arena.alloc(view.size() + 1).rebind<char>();
-    memcpy(span, view);
-    span[view.size()] = '\0';
-    return span.view();
-  };
-
-  struct Payload {
-    std::string_view _colo;
-    std::string_view _owner;
-    std::string_view _descr;
-    unsigned int _internal_p : 1;
-    unsigned int _prod_p : 1;
-    unsigned int _corp_p : 1;
-    unsigned int _flakey_p : 1;
-    unsigned int _secure_p : 1;
-
-    Payload() {
-      _internal_p = _prod_p = _corp_p = _flakey_p = _secure_p = false;
-    }
-
-    bool operator==(Payload const&that) {
-      return
-          this->_colo == that._colo &&
-          this->_owner == that._owner &&
-          this->_descr == that._descr &&
-          this->_corp_p == that._corp_p &&
-          this->_internal_p == that._internal_p &&
-          this->_prod_p == that._prod_p &&
-          this->_flakey_p == that._flakey_p &&
-          this->_secure_p == that._secure_p;
-    }
-  };
-
-  using Space = swoc::IPSpace<Payload>;
-
-  Space space;
-
-  std::error_code ec;
-  swoc::file::path csv{"/tmp/ynetdb.csv"};
-
-  // Force these so I can easily change the set of tests.
-  auto start            = std::chrono::high_resolution_clock::now();
-  auto delta = std::chrono::high_resolution_clock::now() - start;
-
-  auto file_content{swoc::file::load(csv, ec)};
-  TextView content{file_content};
-
-  delta = std::chrono::high_resolution_clock::now() - start;
-  std::cout << "File load " << delta.count() << "ns or " << 
std::chrono::duration_cast<std::chrono::milliseconds>(delta).count()
-            << "ms" << std::endl;
-
-  start = std::chrono::high_resolution_clock::now();
-  while (content) {
-    Payload payload;
-
-    auto line = content.take_prefix_at('\n');
-    if (line.trim_if(&isspace).empty() || *line == '#') {
-      continue;
-    }
-
-    auto range_token{line.take_prefix_at(',')};
-    auto internal_token{line.take_prefix_at(',')};
-    auto owner_token{line.take_prefix_at(',')};
-    auto colo_token{line.take_prefix_at(',')};
-    auto flags_token{line.take_prefix_at(',')};
-    auto descr_token{line.take_prefix_at(',')};
-
-    if (auto spot{Owners.find(owner_token)}; spot != Owners.end()) {
-      payload._owner = *spot;
-    } else {
-      payload._owner = Localize(owner_token);
-      Owners.insert(payload._owner);
-    }
-
-    if (auto spot{Descriptions.find(owner_token)}; spot != Descriptions.end()) 
{
-      payload._descr = *spot;
-    } else {
-      payload._descr = Localize(owner_token);
-      Descriptions.insert(payload._descr);
-    }
-
-    if (auto spot{Locations.find(owner_token)}; spot != Locations.end()) {
-      payload._colo = *spot;
-    } else {
-      payload._colo = Localize(owner_token);
-      Locations.insert(payload._colo);
-    }
-
-    static constexpr TextView INTERNAL_TAG{"yahoo"};
-    static constexpr TextView FLAKEY_TAG{"flakey"};
-    static constexpr TextView PROD_TAG{"prod"};
-    static constexpr TextView CORP_TAG{"corp"};
-    static constexpr TextView SECURE_TAG { "secure" };
-
-    if (0 == strcasecmp(internal_token, INTERNAL_TAG)) {
-      payload._internal_p = true;
-    }
-
-    if (flags_token != "-"_sv) {
-      while (flags_token) {
-        auto key = flags_token.take_prefix_at(';').trim_if(&isspace);
-        if (0 == strcasecmp(key, FLAKEY_TAG)) {
-          payload._flakey_p = true;
-        } else if (0 == strcasecmp(key, PROD_TAG)) {
-          payload._prod_p = true;
-        } else if (0 == strcasecmp(key, CORP_TAG)) {
-          payload._corp_p = true;
-        } else if (0 == strcasecmp(key, SECURE_TAG)) {
-          payload._secure_p = true;
-        } else {
-          throw std::domain_error("Bad flag tag");
-        }
-      }
-    }
-
-    auto BF = [](Payload&lhs, Payload const&rhs) -> bool {
-      if (! lhs._colo.empty()) {
-        std::cout << "Overlap " << lhs._descr << " with " << rhs._descr << 
std::endl;
-      }
-      return true;
-    };
-    swoc::LocalBufferWriter<1024> bw;
-    swoc::IPRange range;
-    if (range.load(range_token)) {
-      space.blend(range, payload, BF);
-    } else {
-      std::cout << "Failed to parse range " << range_token << std::endl;
-    }
-  }
-  delta = std::chrono::high_resolution_clock::now() - start;
-  std::cout << "Space load " << delta.count() << "ns or " << 
std::chrono::duration_cast<std::chrono::milliseconds>(delta).count()
-            << "ms" << std::endl;
-
-  static constexpr size_t N_LOOPS = 1000000;
-  start = std::chrono::high_resolution_clock::now();
-  for ( size_t idx = 0 ; idx < N_LOOPS ; ++idx ) {
-    space.find(IP4Addr(static_cast<in_addr_t>(idx * 2500)));
-  }
-  delta = std::chrono::high_resolution_clock::now() - start;
-  std::cout << "Space IP4 lookup " << delta.count() << "ns or " << 
std::chrono::duration_cast<std::chrono::milliseconds>(delta).count()
-            << "ms" << std::endl;
-
-}
-#endif

Reply via email to