example/ex_netcompact.cc (111 lines of code) (raw):
// SPDX-License-Identifier: Apache-2.0
// Copyright 2014 Network Geographics
/** @file
Example tool to compact networks.
Input is a file with addresses. Each line should be an address, an address range,
or a network in CIDR format.
4441:34F8:1E40:1EF:0:0:A0:0/108
192.168.12.1
10.0.23.0-10.0.14.255
The output is the same set of addresses in as few networks as possible.
*/
#include <unordered_set>
#include <iostream>
#include <fstream>
#include "swoc/TextView.h"
#include "swoc/swoc_ip.h"
#include "swoc/bwf_ip.h"
#include "swoc/bwf_std.h"
#include "swoc/bwf_std.h"
#include "swoc/swoc_file.h"
using namespace std::literals;
using namespace swoc::literals;
using swoc::TextView;
using swoc::IPRange;
/// Type for temporary buffer writer output.
using W = swoc::LocalBufferWriter<512>;
/// IPSpace for mapping address. Treating it as a set so use a no-data payload.
using Space = swoc::IPSpace<std::monostate>;
void post_processing_performance_test(Space &space);
/// Process the @a content of a file in to @a space.
unsigned
process(Space &space, TextView content) {
int line_no = 0; /// Track for error reporting.
unsigned n_ranges = 0;
// For each line in @a content
while (content) {
TextView line = content.take_prefix_at('\n').trim_if(&isspace);
++line_no;
// Allow empty lines and '#' comments without error.
if (line.empty() || '#' == *line) {
continue;
}
// Get the range, make sure it's a valid range.
IPRange range{line};
if (range.empty()) {
std::cerr << W().print("Invalid range '{}' on line {}\n", line, line_no);
continue;
}
++n_ranges;
space.mark(range, std::monostate{});
}
return n_ranges;
}
int
main(int argc, char *argv[]) {
Space space;
if (argc < 2) {
std::cerr << W().print("Input file name required.\n");
exit(1);
}
auto t0 = std::chrono::system_clock::now(); // timing
// Load the file.
swoc::file::path path{argv[1]};
std::error_code ec;
std::string content = swoc::file::load(path, ec);
if (ec) {
std::cerr << W().print(R"(Failed to open file "{}" - {}\n)", path, ec);
exit(1);
}
// Paint the IPSpace.
auto n_ranges = process(space, content);
// Dump the results.
unsigned n_nets = 0;
for (auto &&[range, payload] : space) {
for (auto &&net : range.networks()) {
++n_nets;
std::cout << W().print("{}\n", net);
}
}
auto delta = std::chrono::system_clock::now() - t0;
std::cerr << W().print("{} ranges in, {} ranges condensed, {} networks out in {} ms\n", n_ranges, space.count(), n_nets,
std::chrono::duration_cast<std::chrono::milliseconds>(delta).count());
post_processing_performance_test(space);
return 0;
}
void
post_processing_performance_test(Space &space) {
using swoc::IP4Addr;
using swoc::IP6Addr;
std::vector<IP4Addr> a4;
std::vector<IP6Addr> a6;
for (auto &&[r, p] : space) {
if (r.is_ip4()) {
IP4Addr a = r.min().ip4();
a4.push_back(a);
a4.push_back(--IP4Addr(a));
a4.push_back(++IP4Addr(a));
a = r.max().ip4();
a4.push_back(a);
a4.push_back(--IP4Addr(a));
a4.push_back(++IP4Addr(a));
} else if (r.is_ip6()) {
IP6Addr a = r.min().ip6();
a6.push_back(a);
a6.push_back(--IP6Addr(a));
a6.push_back(++IP6Addr(a));
a = r.max().ip6();
a6.push_back(a);
a6.push_back(--IP6Addr(a));
a6.push_back(++IP6Addr(a));
}
}
if (!a4.empty()) {
auto t0 = std::chrono::system_clock::now();
for (auto const &addr : a4) {
[[maybe_unused]] auto spot = space.find(addr);
}
auto delta = std::chrono::system_clock::now() - t0;
std::cout << W().print("IPv4 time - {} addresses, {} ns total, {} ns per lookup\n", a4.size(), delta.count(),
delta.count() / a4.size());
}
if (!a6.empty()) {
auto t0 = std::chrono::system_clock::now();
for (auto const &addr : a6) {
[[maybe_unused]] auto spot = space.find(addr);
}
auto delta = std::chrono::system_clock::now() - t0;
std::cout << W().print("IPv6 time - {} addresses, {} ns total, {} ns per lookup\n", a6.size(), delta.count(),
delta.count() / a6.size());
}
}