diff --git a/CMakeLists.txt b/CMakeLists.txt index 518ba09..0f6a02f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,14 +4,14 @@ project("Solid Wall Of C++ Library") set(INSTALL_DIR ${CMAKE_HOME_DIRECTORY}) # Fortunately this has no external dependencies so the set up can be simple. -add_subdirectory(swoc++) +add_subdirectory(code) 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. set(_CLANG_DIRS "") -get_target_property(_TMP swoc++ CLANG_FORMAT_DIRS) +get_target_property(_TMP libswoc CLANG_FORMAT_DIRS) list(APPEND _CLANG_DIRS ${_TMP}) get_target_property(_TMP test_libswoc CLANG_FORMAT_DIRS) list(APPEND _CLANG_DIRS ${_TMP}) diff --git a/Sconstruct b/Sconstruct index 1293d0d..9842a6c 100644 --- a/Sconstruct +++ b/Sconstruct @@ -1,4 +1,4 @@ from parts import * -Part("swoc++/swoc++.part") +Part("code/libswoc.part") Part("unit_tests/unit_tests.part") diff --git a/swoc++/CMakeLists.txt b/code/CMakeLists.txt similarity index 68% rename from swoc++/CMakeLists.txt rename to code/CMakeLists.txt index 6c9982f..cd9ffcb 100644 --- a/swoc++/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.12) -project(lib-swoc++ CXX) -set(LIBSWOC_VERSION "1.2.0") +project(Lib-SWOC CXX) +set(LIBSWOC_VERSION "1.2.1") set(CMAKE_CXX_STANDARD 17) include(GNUInstallDirs) @@ -44,38 +44,37 @@ set(CC_FILES src/TextView.cc ) -add_library(swoc++ STATIC ${CC_FILES}) -#add_compile_options(-Wall -Wextra -Werror -Wno-unused-parameter -Wno-format-truncation -Wno-stringop-overflow -Wno-invalid-offsetof) -target_compile_options(swoc++ PRIVATE -Wall -Wextra -Werror -Wnon-virtual-dtor -Wno-unused-parameter -Wno-stringop-overflow) +add_library(libswoc STATIC ${CC_FILES}) +target_compile_options(libswoc PRIVATE -Wall -Wextra -Werror -Wnon-virtual-dtor -Wpedantic) # Not quite sure how this works, but I think it generates one of two paths depending on the context. # That is, the generator functions return non-empty strings only in the corresponding context. -target_include_directories(swoc++ +target_include_directories(libswoc PUBLIC $ $ ) # These install target variables are created by GNUInstallDirs. -install(TARGETS swoc++ - EXPORT swoc++-config +install(TARGETS libswoc + EXPORT libswoc-config ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} ) install(DIRECTORY include/swoc DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(EXPORT swoc++-config - NAMESPACE swoc++:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/swoc++ +install(EXPORT libswoc-config + NAMESPACE libswoc:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libswoc ) -set(PKG_CONFIG_FILE ${CMAKE_BINARY_DIR}/libswoc++.pc) -configure_file("libswoc++.pc.cmake" ${PKG_CONFIG_FILE} @ONLY) +set(PKG_CONFIG_FILE ${CMAKE_BINARY_DIR}/libswoc.pc) +configure_file("libswoc.pc.cmake" ${PKG_CONFIG_FILE} @ONLY) install(FILES ${PKG_CONFIG_FILE} DESTINATION ${LIB_INSTALL_DIR}/pkgconfig) # Alledgedly this makes the targets "importable from the build directory" but I see no evidence of that. # AFAICT the file isn't created at all even with this enabled. -#export(TARGETS swoc++ FILE libswoc++-config.cmake) +#export(TARGETS libswoc FILE libswoc-config.cmake) set(CLANG_DIRS ) -set_target_properties(swoc++ PROPERTIES CLANG_FORMAT_DIRS "${PROJECT_SOURCE_DIR}/src;${PROJECT_SOURCE_DIR}/include") +set_target_properties(libswoc PROPERTIES CLANG_FORMAT_DIRS "${PROJECT_SOURCE_DIR}/src;${PROJECT_SOURCE_DIR}/include") diff --git a/swoc++/include/swoc/ArenaWriter.h b/code/include/swoc/ArenaWriter.h similarity index 100% rename from swoc++/include/swoc/ArenaWriter.h rename to code/include/swoc/ArenaWriter.h diff --git a/swoc++/include/swoc/BufferWriter.h b/code/include/swoc/BufferWriter.h similarity index 100% rename from swoc++/include/swoc/BufferWriter.h rename to code/include/swoc/BufferWriter.h diff --git a/swoc++/include/swoc/DiscreteRange.h b/code/include/swoc/DiscreteRange.h similarity index 96% rename from swoc++/include/swoc/DiscreteRange.h rename to code/include/swoc/DiscreteRange.h index 80da09c..56e8e68 100644 --- a/swoc++/include/swoc/DiscreteRange.h +++ b/code/include/swoc/DiscreteRange.h @@ -163,6 +163,16 @@ template class DiscreteRange { */ metric_type const& max() const; + /// Test for equality. + bool operator == (self_type const& that) const { + return _min == that._min && _max == that._max; + } + + /// Test for inequality. + bool operator != (self_type const& that) const { + return _min != that._min | _max != that._max; + } + /** Check if a value is in @a this range. * * @param m Metric value to check. @@ -971,6 +981,37 @@ DiscreteSpace::insert_after(DiscreteSpace::Node *spot, Discrete _root = static_cast(node->rebalance_after_insert()); } +template +DiscreteSpace& +DiscreteSpace::erase(DiscreteSpace::range_type const& range) { + Node *n = this->lower_bound(range.min()); // current node. + while (n) { + auto nn = next(n); // cache in case @a n disappears. + if (n->min() > range.max()) { // cleared the target range, done. + break; + } + + if (n->max() >= range.min()) { // some overlap + if (n->max() <= range.max()) { // pure left overlap, clip. + if (n->min() >= range.min()) { // covered, remove. + this->remove(n); + } else { // stub on the left, clip to that. + n->assign_max(--metric_type{range.min()}); + } + } else if (n->min() >= range.min()) { // pure left overlap, clip. + n->assign_min(++metric_type{range.max()}); + } else { // @a n covers @a range, must split. + auto y = _fa.make(range_type{n->min(), --metric_type{range.min()}}, n->payload()); + n->assign_min(++metric_type{range.max()}); + this->insert_before(n, y); + break; + } + } + n = nn; + } + return *this; +} + template DiscreteSpace& DiscreteSpace::mark(DiscreteSpace::range_type const& range @@ -979,16 +1020,15 @@ DiscreteSpace::mark(DiscreteSpace::range_type const& range Node *x = nullptr; // New node, gets set if we re-use an existing one. Node *y = nullptr; // Temporary for removing and advancing. - METRIC max_plus_1 = range.max(); - ++max_plus_1; // careful to use this only in places there's no chance of overflow. + // Use carefully, only in situations where it is known there is no overflow. + auto max_plus_1 = ++metric_type{range.max()}; /* We have lots of special cases here primarily to minimize memory allocation by re-using an existing node as often as possible. */ if (n) { // Watch for wrap. - METRIC min_minus_1 = range.min(); - --min_minus_1; + auto min_minus_1 = --metric_type{range.min()}; if (n->min() == range.min()) { // Could be another span further left which is adjacent. // Coalesce if the data is the same. min_minus_1 is OK because @@ -1006,7 +1046,7 @@ DiscreteSpace::mark(DiscreteSpace::range_type const& range return *this; // request is covered by existing span with the same data } else { // request span is covered by existing span. - x = new Node{range, payload}; // + x = _fa.make(range, payload); // n->assign_min(max_plus_1); // clip existing. this->insert_before(n, x); return *this; @@ -1187,7 +1227,7 @@ DiscreteSpace::fill(DiscreteSpace::range_type const& range } } else { // no carry node. if (max < n->_min) { // entirely before next span. - this->insert_before(n, new Node(min, max, payload)); + this->insert_before(n, _fa.make(min, max, payload)); return *this; } else { if (min < n->_min) { // leading section, need node. diff --git a/swoc++/include/swoc/Errata.h b/code/include/swoc/Errata.h similarity index 100% rename from swoc++/include/swoc/Errata.h rename to code/include/swoc/Errata.h diff --git a/swoc++/include/swoc/IntrusiveDList.h b/code/include/swoc/IntrusiveDList.h similarity index 99% rename from swoc++/include/swoc/IntrusiveDList.h rename to code/include/swoc/IntrusiveDList.h index eca4cf8..3b7edd6 100644 --- a/swoc++/include/swoc/IntrusiveDList.h +++ b/code/include/swoc/IntrusiveDList.h @@ -418,7 +418,7 @@ ptr_ref_cast(P *& p) { T **_t; } u{&p}; return *(u._t); -}; +} /** Utility class to provide intrusive links. * @@ -738,7 +738,7 @@ template auto IntrusiveDList::erase(const iterator& loc) -> iterator { return this->iterator_for(this->erase(loc._v)); -}; +} template auto @@ -763,7 +763,7 @@ IntrusiveDList::erase(const iterator& first, const iterator& limit) -> iterat }; return {limit._v, this}; -}; +} template auto @@ -781,43 +781,43 @@ template size_t IntrusiveDList::count() const { return _count; -}; +} template auto IntrusiveDList::begin() const -> const_iterator { return const_iterator{this, _head}; -}; +} template auto IntrusiveDList::begin() -> iterator { return iterator{this, _head}; -}; +} template auto IntrusiveDList::end() const -> const_iterator { return const_iterator{this, nullptr}; -}; +} template auto IntrusiveDList::end() -> iterator { return iterator{this, nullptr}; -}; +} template auto IntrusiveDList::iterator_for(value_type *v) -> iterator { return iterator{this, v}; -}; +} template auto IntrusiveDList::iterator_for(const value_type *v) const -> const_iterator { return const_iterator{this, v}; -}; +} template auto @@ -849,7 +849,7 @@ IntrusiveDList::clear() -> self_type& { _head = _tail = nullptr; _count = 0; return *this; -}; +} namespace detail { // Make @c apply more convenient by allowing the function to take a reference type or pointer type @@ -884,6 +884,6 @@ template auto IntrusiveDList::apply(F&& f) -> self_type& { return detail::Intrusive_DList_Apply(*this, f); -}; +} }} // namespace swoc diff --git a/swoc++/include/swoc/IntrusiveHashMap.h b/code/include/swoc/IntrusiveHashMap.h similarity index 100% rename from swoc++/include/swoc/IntrusiveHashMap.h rename to code/include/swoc/IntrusiveHashMap.h diff --git a/swoc++/include/swoc/Lexicon.h b/code/include/swoc/Lexicon.h similarity index 100% rename from swoc++/include/swoc/Lexicon.h rename to code/include/swoc/Lexicon.h diff --git a/swoc++/include/swoc/MemArena.h b/code/include/swoc/MemArena.h similarity index 100% rename from swoc++/include/swoc/MemArena.h rename to code/include/swoc/MemArena.h diff --git a/swoc++/include/swoc/MemSpan.h b/code/include/swoc/MemSpan.h similarity index 98% rename from swoc++/include/swoc/MemSpan.h rename to code/include/swoc/MemSpan.h index c94c2d2..4bc63d2 100644 --- a/swoc++/include/swoc/MemSpan.h +++ b/code/include/swoc/MemSpan.h @@ -920,24 +920,24 @@ MemSpan::view() const { /// @cond NO_DOXYGEN // STL tuple support - this allows the @c MemSpan to be used as a tuple of a pointer // and size. -namespace std -{ -template class tuple_element> { +namespace std { +template class tuple_element> { static_assert("swoc::MemSpan tuple index out of range"); }; -template class tuple_element<0, swoc::MemSpan> { +template class tuple_element<0, swoc::MemSpan> { public: using type = R *; }; -template class tuple_element<1, swoc::MemSpan> { +template class tuple_element<1, swoc::MemSpan> { public: using type = size_t; }; -template class tuple_size> : public std::integral_constant {}; +template class tuple_size> : public std::integral_constant { +}; -}; // namespace std +} // namespace std /// @endcond diff --git a/swoc++/include/swoc/RBTree.h b/code/include/swoc/RBTree.h similarity index 100% rename from swoc++/include/swoc/RBTree.h rename to code/include/swoc/RBTree.h diff --git a/swoc++/include/swoc/Scalar.h b/code/include/swoc/Scalar.h similarity index 100% rename from swoc++/include/swoc/Scalar.h rename to code/include/swoc/Scalar.h diff --git a/swoc++/include/swoc/TextView.h b/code/include/swoc/TextView.h similarity index 99% rename from swoc++/include/swoc/TextView.h rename to code/include/swoc/TextView.h index 34a04f5..3e80541 100644 --- a/swoc++/include/swoc/TextView.h +++ b/code/include/swoc/TextView.h @@ -170,7 +170,7 @@ class TextView : public std::string_view { * * @note @c c_str must be a null terminated string. The null byte is not included in the view. */ - self_type& assign(char const* c_str);; + self_type& assign(char const* c_str); /// Explicitly set the start @a ptr and size @a n of the view. self_type &assign(char const *ptr, size_t n); @@ -819,7 +819,7 @@ svto_radix(swoc::TextView &src) { ++src; } return zret; -}; +} template uintmax_t diff --git a/swoc++/include/swoc/bwf_base.h b/code/include/swoc/bwf_base.h similarity index 98% rename from swoc++/include/swoc/bwf_base.h rename to code/include/swoc/bwf_base.h index 046c2ce..131a5ef 100644 --- a/swoc++/include/swoc/bwf_base.h +++ b/code/include/swoc/bwf_base.h @@ -683,7 +683,7 @@ Tuple_Nth(T const& t, size_t idx) { /// provide one. template auto -arg_capture(F&& f, BufferWriter&, Spec const&, std::any&&, swoc::meta::CaseTag<0>) -> void { +arg_capture(F&&, BufferWriter&, Spec const&, std::any&&, swoc::meta::CaseTag<0>) -> void { throw std::runtime_error("Capture specification used in format extractor that does not support capture"); } @@ -718,7 +718,7 @@ extractor_spec_type(bool (EXTRACTOR::*m)(VIEW, SPEC)) -> SPEC { * * @internal After much consideration, I decided this was the correct choice, to enable type * erasure of the arguments to the base formatting logic. This costs a virtual function dispatch - * but prevents the formatting logic from being dpulicated for every permutation of arguments. + * but prevents the formatting logic from being duplicated for every permutation of arguments. * Overall, for any reasonably sized project, I think this is the better option. * * This also supports passing arguments in other than a tuple, which is necessary in order to @@ -740,12 +740,13 @@ class ArgPack { /** Generate formatted output for an argument. * - * @param idx Argument index. * @param w Output. * @param spec Formatting specifier. + * @param idx Argument index. + * * @return @a w */ - virtual BufferWriter& print(unsigned idx, BufferWriter& w, Spec const& spec) const = 0; + virtual BufferWriter& print(BufferWriter& w, Spec const& spec, unsigned idx) const = 0; /// Number of arguments in the pack. virtual unsigned count() const = 0; @@ -764,12 +765,16 @@ template class ArgTuple : public ArgPack { ArgTuple(std::tuple const& tuple) : _tuple(tuple) {} protected: + /// Numnber of arguments in the tuple. unsigned count() const override; - BufferWriter& print(unsigned idx, BufferWriter& w, Spec const& spec) const override; + /// Generate formatted output on @a w for argument at @a idx. + BufferWriter& print(BufferWriter& w, Spec const& spec, unsigned idx) const override; + /// Capture the @a idx argument for later use. std::any capture(unsigned idx) const override; + /// The source arguments. std::tuple const& _tuple; }; @@ -781,7 +786,7 @@ ArgTuple::count() const { template BufferWriter& -ArgTuple::print(unsigned idx, BufferWriter& w, Spec const& spec) const { +ArgTuple::print(BufferWriter& w, Spec const& spec, unsigned idx) const { static const auto _fa{ bwf::Get_Arg_Formatter_Array>(std::index_sequence_for{})}; return _fa[idx](w, spec, _tuple); @@ -793,7 +798,7 @@ ArgTuple::capture(unsigned idx) const { return {Tuple_Nth(_tuple, idx)}; } -}; // namespace bwf +} // namespace bwf template BufferWriter& @@ -836,7 +841,7 @@ BufferWriter::print_nfv(Binding&& names, Extractor&& ex, bwf::ArgPack const& arg if (spec._type == bwf::Spec::CAPTURE_TYPE) { bwf::arg_capture(ex, lw, spec, args.capture(spec._idx), swoc::meta::CaseArg); } else { - args.print(spec._idx, lw, spec); + args.print(lw, spec, spec._idx); } } else { bwf::Err_Bad_Arg_Index(lw, spec._idx, N); @@ -942,7 +947,7 @@ bwformat(BufferWriter& w, bwf::Spec const& spec, MemSpan const& span) { s._prec = sizeof(T); } return bwformat(w, s, span.template rebind()); -}; +} template BufferWriter& @@ -980,7 +985,7 @@ bwformat(BufferWriter& w, bwf::Spec const& spec, TextView tv) { template BufferWriter& -bwformat(BufferWriter& w, bwf::Spec const& spec, TransformView&& view) { +bwformat(BufferWriter& w, bwf::Spec const&, TransformView&& view) { while (view) w.write(char(*(view++))); return w; diff --git a/swoc++/include/swoc/bwf_ex.h b/code/include/swoc/bwf_ex.h similarity index 99% rename from swoc++/include/swoc/bwf_ex.h rename to code/include/swoc/bwf_ex.h index b843b35..d3ed09f 100644 --- a/swoc++/include/swoc/bwf_ex.h +++ b/code/include/swoc/bwf_ex.h @@ -82,7 +82,7 @@ FirstOf(Args&& ... args) { return s; } return std::string_view{}; -}; +} /** Wrapper for a sub-text, where the @a args are output according to @a fmt. * diff --git a/swoc++/include/swoc/bwf_fwd.h b/code/include/swoc/bwf_fwd.h similarity index 100% rename from swoc++/include/swoc/bwf_fwd.h rename to code/include/swoc/bwf_fwd.h diff --git a/swoc++/include/swoc/bwf_ip.h b/code/include/swoc/bwf_ip.h similarity index 86% rename from swoc++/include/swoc/bwf_ip.h rename to code/include/swoc/bwf_ip.h index b2a56b6..d17c365 100644 --- a/swoc++/include/swoc/bwf_ip.h +++ b/code/include/swoc/bwf_ip.h @@ -30,11 +30,19 @@ BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, IP6Addr const& ad BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, IPAddr const& addr); -BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, IP4Range const& Range); +BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, IP4Range const& range); -BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, IP6Range const& Range); +BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, IP6Range const& range); -BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, IPRange const& Range); +BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, IPRange const& range); + +BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, IPNet const& net); + +BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, IP4Net const& net); + +BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, IP6Net const& net); + +BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, IPMask const& mask); inline BufferWriter& bwformat(BufferWriter& w, bwf::Spec const& spec, IPEndpoint const& addr) { diff --git a/swoc++/include/swoc/bwf_std.h b/code/include/swoc/bwf_std.h similarity index 100% rename from swoc++/include/swoc/bwf_std.h rename to code/include/swoc/bwf_std.h diff --git a/swoc++/include/swoc/ext/HashFNV.h b/code/include/swoc/ext/HashFNV.h similarity index 100% rename from swoc++/include/swoc/ext/HashFNV.h rename to code/include/swoc/ext/HashFNV.h diff --git a/swoc++/include/swoc/swoc_file.h b/code/include/swoc/swoc_file.h similarity index 91% rename from swoc++/include/swoc/swoc_file.h rename to code/include/swoc/swoc_file.h index 8621c9f..86be6e3 100644 --- a/swoc++/include/swoc/swoc_file.h +++ b/code/include/swoc/swoc_file.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "swoc/swoc_version.h" #include "swoc/TextView.h" @@ -98,18 +99,15 @@ class file_status { struct ::stat _stat; ///< File information. friend self_type status(const path& file, std::error_code& ec) noexcept; - friend int file_type(const self_type&); - friend off_t file_size(const self_type&); - friend bool is_regular_file(const file_status&); - friend bool is_dir(const file_status&); - friend bool is_char_device(const file_status&); - friend bool is_block_device(const file_status&); + friend std::chrono::system_clock::time_point modify_time(file_status const& fs); + friend std::chrono::system_clock::time_point access_time(file_status const& fs); + friend std::chrono::system_clock::time_point status_time(file_status const& fs); }; /** Get the status of the file at @a p. @@ -144,6 +142,10 @@ off_t file_size(const file_status& fs); /// Check if file is readable. bool is_readable(const path& s); +std::chrono::system_clock::time_point modify_time(file_status const& fs); +std::chrono::system_clock::time_point access_time(file_status const& fs); +std::chrono::system_clock::time_point status_time(file_status const& fs); + /** Load the file at @a p into a @c std::string. * * @param p Path to file diff --git a/swoc++/include/swoc/swoc_ip.h b/code/include/swoc/swoc_ip.h similarity index 99% rename from swoc++/include/swoc/swoc_ip.h rename to code/include/swoc/swoc_ip.h index 203a32b..43c385b 100644 --- a/swoc++/include/swoc/swoc_ip.h +++ b/code/include/swoc/swoc_ip.h @@ -1034,6 +1034,20 @@ class IPRange { */ IPRange(string_view const& text); + /// Equality + bool operator == (self_type const& that) const { + if (_family != that._family) { + return false; + } + if (this->is_ip4()) { + return _range._ip4 == that._range._ip4; + } + if (this->is_ip6()) { + return _range._ip6 == that._range._ip6; + } + return true; + } + /// @return @c true if this is an IPv4 range, @c false if not. bool is_ip4() const { return AF_INET == _family; } @@ -1400,6 +1414,7 @@ template class IPSpace { public: using payload_t = PAYLOAD; ///< Export payload type. + using value_type = std::tuple; /// Construct an empty space. IPSpace() = default; @@ -1424,6 +1439,13 @@ template class IPSpace { */ self_type& fill(IPRange const& range, PAYLOAD const& payload); + /** Erase addresses in @a range. + * + * @param range Address range. + * @return @a this + */ + self_type& erase(IPRange const& range); + /** Blend @a color in to the @a range. * * @tparam F Blending functor type (deduced). @@ -2676,6 +2698,16 @@ auto IPSpace::fill(IPRange const& range, PAYLOAD const& payload) -> sel return *this; } +template +auto IPSpace::erase(IPRange const& range) -> self_type& { + if (range.is(AF_INET)) { + _ip4.erase(range.ip4()); + } else if (range.is(AF_INET6)) { + _ip6.erase(range.ip6()); + } + return *this; +} + template template auto IPSpace::blend(IPRange const& range, U const& color, F&& blender) -> self_type& { diff --git a/swoc++/include/swoc/swoc_meta.h b/code/include/swoc/swoc_meta.h similarity index 94% rename from swoc++/include/swoc/swoc_meta.h rename to code/include/swoc/swoc_meta.h index 32b1056..7da1765 100644 --- a/swoc++/include/swoc/swoc_meta.h +++ b/code/include/swoc/swoc_meta.h @@ -54,9 +54,12 @@ namespace meta { * method to verify it will compile. It is annoying to type it twice but there's not a better * option. * - * Note @c decltype does not accept explicit types - to have the type of "int" an @c int must be - * constructed. This is easy for builtin types except @c void. @c CaseVoidFunc is provided for that - * situation, e.g. decltype(CaseVoidFunc()) provides @c void via @c decltype. + * Note @c decltype does not accept explicit types - to have the type of "int" a function returning + * @c int must be provided. This is easy for simple builtin types such as @c int - use the + * constructor @c int(). For @c void and non-simple types (such as @c int* ) this is a bit more + * challenging. A general utility is provided for this - @c TypeFunc. For the @c void case this + * would be decltype(TypeFunc()). For @c int* it would be + * decltype(TypeFunc()). */ /// Case hierarchy. diff --git a/swoc++/include/swoc/swoc_version.h b/code/include/swoc/swoc_version.h similarity index 92% rename from swoc++/include/swoc/swoc_version.h rename to code/include/swoc/swoc_version.h index 11751e2..8640696 100644 --- a/swoc++/include/swoc/swoc_version.h +++ b/code/include/swoc/swoc_version.h @@ -22,11 +22,11 @@ #pragma once #if !defined(SWOC_VERSION_NS) -# define SWOC_VERSION_NS _1_2_0 +# define SWOC_VERSION_NS _1_2_1 #endif namespace swoc { inline namespace SWOC_VERSION_NS { static constexpr unsigned MAJOR_VERSION = 1; static constexpr unsigned MINOR_VERSION = 2; -static constexpr unsigned POINT_VERSION = 0; +static constexpr unsigned POINT_VERSION = 1; }} // namespace SWOC_VERSION_NS diff --git a/code/libswoc.part b/code/libswoc.part new file mode 100644 index 0000000..b4576a2 --- /dev/null +++ b/code/libswoc.part @@ -0,0 +1,21 @@ +Import("*") +PartName("libswoc") +PartVersion("1.2.1") + +src_files = [ + "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", +] + +env.Part("libswoc.static.part", package_group="libswoc", src_files=src_files) +env.Part("libswoc.shared.part", package_group="libswoc", src_files=src_files) + +inc_files = Pattern(src_dir="include", includes=["swoc/*.h"]) +env.InstallInclude(inc_files) diff --git a/swoc++/libswoc++.pc.cmake b/code/libswoc.pc.cmake similarity index 100% rename from swoc++/libswoc++.pc.cmake rename to code/libswoc.pc.cmake diff --git a/swoc++/libswoc++-static.pc.in b/code/libswoc.pc.in similarity index 86% rename from swoc++/libswoc++-static.pc.in rename to code/libswoc.pc.in index 9a78d45..d613bd2 100644 --- a/swoc++/libswoc++-static.pc.in +++ b/code/libswoc.pc.in @@ -7,5 +7,5 @@ Name: LibSWOC++ Description: A collection of solid C++ utilities and classes. Version: pkg_version Requires: -Libs: -L${libdir} -lswoc++ +Libs: -L${libdir} -lswoc.pkg_version Cflags: -I${includedir} diff --git a/swoc++/swoc++-shared.part b/code/libswoc.shared.part similarity index 66% rename from swoc++/swoc++-shared.part rename to code/libswoc.shared.part index 7d52392..340bcaa 100644 --- a/swoc++/swoc++-shared.part +++ b/code/libswoc.shared.part @@ -1,7 +1,7 @@ Import("*") PartName("shared") -DependsOn([ Component("libswoc.headers") ]) +DependsOn([ Component("libswoc") ]) src_files = env.get("src_files") @@ -11,11 +11,11 @@ env.AppendUnique( ) # build the library -out = env.SharedLibrary("libswoc++", src_files) +out = env.SharedLibrary("${PART_ROOT_NAME}.${PART_VERSION}.so", src_files, SHLIBPREFIX="") env.InstallLib(out) # Export the package config. -pc_file = env.Substfile("libswoc++.pc", "libswoc++.pc.in" +pc_file = env.Substfile("libswoc.pc", "libswoc.pc.in" , SUBST_DICT = { "pkg_prefix": env.Dir("$INSTALL_ROOT").abspath , "pkg_version": "$PART_VERSION" diff --git a/swoc++/swoc++-static.part b/code/libswoc.static.part similarity index 65% rename from swoc++/swoc++-static.part rename to code/libswoc.static.part index 02b1759..39ac2f6 100644 --- a/swoc++/swoc++-static.part +++ b/code/libswoc.static.part @@ -1,7 +1,7 @@ Import("*") PartName("static") -DependsOn([ Component("libswoc.headers") ]) +DependsOn([ Component("libswoc") ]) src_files = env.get("src_files") @@ -11,11 +11,11 @@ env.AppendUnique( ) # build the library -out = env.StaticLibrary("libswoc++", src_files) +out = env.StaticLibrary("${PART_ROOT_NAME}.${PART_VERSION}.a", src_files, LIBPREFIX="") env.InstallLib(out) # Export the package config. -pc_file = env.Substfile("libswoc++-static.pc", "libswoc++-static.pc.in" +pc_file = env.Substfile("libswoc.static.pc", "libswoc.static.pc.in" , SUBST_DICT = { "pkg_prefix": env.Dir("$INSTALL_ROOT").abspath , "pkg_version": "$PART_VERSION" diff --git a/swoc++/libswoc++.pc.in b/code/libswoc.static.pc.in similarity index 86% rename from swoc++/libswoc++.pc.in rename to code/libswoc.static.pc.in index 9a78d45..d613bd2 100644 --- a/swoc++/libswoc++.pc.in +++ b/code/libswoc.static.pc.in @@ -7,5 +7,5 @@ Name: LibSWOC++ Description: A collection of solid C++ utilities and classes. Version: pkg_version Requires: -Libs: -L${libdir} -lswoc++ +Libs: -L${libdir} -lswoc.pkg_version Cflags: -I${includedir} diff --git a/swoc++/src/ArenaWriter.cc b/code/src/ArenaWriter.cc similarity index 100% rename from swoc++/src/ArenaWriter.cc rename to code/src/ArenaWriter.cc diff --git a/swoc++/src/Errata.cc b/code/src/Errata.cc similarity index 98% rename from swoc++/src/Errata.cc rename to code/src/Errata.cc index 2d8b9fe..2e84b50 100644 --- a/swoc++/src/Errata.cc +++ b/code/src/Errata.cc @@ -173,7 +173,7 @@ bwformat(BufferWriter& bw, bwf::Spec const& spec, Errata::Severity level) { } BufferWriter& -bwformat(BufferWriter& bw, bwf::Spec const& spec, Errata const& errata) { +bwformat(BufferWriter& bw, bwf::Spec const&, Errata const& errata) { for (auto& m : errata) { bw.print("{}[{}] {}\n", swoc::bwf::Pattern{int(m.level()), " "}, m.severity(), m.text()); } diff --git a/swoc++/src/MemArena.cc b/code/src/MemArena.cc similarity index 99% rename from swoc++/src/MemArena.cc rename to code/src/MemArena.cc index dd23fca..f5a3e77 100644 --- a/swoc++/src/MemArena.cc +++ b/code/src/MemArena.cc @@ -30,7 +30,7 @@ MemArena::MemArena(swoc::MemArena::self_type&& that) MemArena * MemArena::construct_self_contained(size_t n) { - MemArena tmp; + MemArena tmp{n}; return tmp.make(std::move(tmp)); } diff --git a/swoc++/src/RBTree.cc b/code/src/RBTree.cc similarity index 100% rename from swoc++/src/RBTree.cc rename to code/src/RBTree.cc diff --git a/swoc++/src/TextView.cc b/code/src/TextView.cc similarity index 100% rename from swoc++/src/TextView.cc rename to code/src/TextView.cc diff --git a/swoc++/src/bw_format.cc b/code/src/bw_format.cc similarity index 100% rename from swoc++/src/bw_format.cc rename to code/src/bw_format.cc diff --git a/swoc++/src/bw_ip_format.cc b/code/src/bw_ip_format.cc similarity index 86% rename from swoc++/src/bw_ip_format.cc rename to code/src/bw_ip_format.cc index afddfcf..f45c46e 100644 --- a/swoc++/src/bw_ip_format.cc +++ b/code/src/bw_ip_format.cc @@ -259,16 +259,26 @@ bwformat(BufferWriter& w, Spec const& spec, IPAddr const& addr) { BufferWriter& bwformat(BufferWriter& w, Spec const& spec, IP4Range const& range) { - return range.empty() - ? w.write("*-*"_tv) - : w.print("{}-{}", range.min(), range.max()); + if (range.empty()) { + w.write("*-*"_tv); + } else { + bwformat(w, spec, range.min()); + w.write('-'); + bwformat(w, spec, range.max()); + } + return w; } BufferWriter& bwformat(BufferWriter& w, Spec const& spec, IP6Range const& range) { - return range.empty() - ? w.write("*-*"_tv) - : w.print("{}-{}", range.min(), range.max()); + if (range.empty()) { + w.write("*-*"_tv); + } else { + bwformat(w, spec, range.min()); + w.write('-'); + bwformat(w, spec, range.max()); + } + return w; } BufferWriter& @@ -280,5 +290,35 @@ bwformat(BufferWriter& w, Spec const& spec, IPRange const& range) { : w.write("*-*"_tv); } +BufferWriter& +bwformat(BufferWriter& w, Spec const& spec, IP4Net const& net) { + bwformat(w, spec, net.lower_bound()); + w.write('/'); + bwformat(w, Spec{}, net.mask().width()); + return w; +} + +BufferWriter& +bwformat(BufferWriter& w, Spec const& spec, IP6Net const& net) { + bwformat(w, spec, net.lower_bound()); + w.write('/'); + bwformat(w, Spec{}, net.mask().width()); + return w; +} + +BufferWriter& +bwformat(BufferWriter& w, Spec const& spec, IPNet const& net) { + if (net.is_ip6()) { + return bwformat(w, spec, net.ip6()); + } else if (net.is_ip4()) { + return bwformat(w, spec, net.ip4()); + } + return w.write("*invalid*"); +} + +BufferWriter& +bwformat(BufferWriter& w, Spec const& spec, IPMask const& mask) { + return bwformat(w, spec, mask.width()); +} }} // namespace swoc diff --git a/swoc++/src/swoc_file.cc b/code/src/swoc_file.cc similarity index 80% rename from swoc++/src/swoc_file.cc rename to code/src/swoc_file.cc index fb88d07..860d8b4 100644 --- a/swoc++/src/swoc_file.cc +++ b/code/src/swoc_file.cc @@ -19,7 +19,7 @@ path::parent_path() const { TextView parent{_path}; parent.split_suffix_at(SEPARATOR); return parent ? parent : "/"_tv; -}; +} path& path::operator/=(std::string_view that) { @@ -80,6 +80,25 @@ is_dir(const file_status& fs) { return file_type(fs) == S_IFDIR; } +namespace { +inline std::chrono::system_clock::time_point chrono_cast(timespec const& ts) { + using namespace std::chrono; + return system_clock::time_point{duration_cast(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec})}; +} +} // namesapce + +std::chrono::system_clock::time_point modify_time(file_status const& fs) { + return chrono_cast(fs._stat.st_mtim); +} + +std::chrono::system_clock::time_point access_time(file_status const& fs) { + return chrono_cast(fs._stat.st_atim); +} + +std::chrono::system_clock::time_point status_time(file_status const& fs) { + return chrono_cast(fs._stat.st_ctim); +} + bool is_readable(const path& p) { return 0 == access(p.c_str(), R_OK); diff --git a/swoc++/src/swoc_ip.cc b/code/src/swoc_ip.cc similarity index 99% rename from swoc++/src/swoc_ip.cc rename to code/src/swoc_ip.cc index 1d8f88c..e708ff5 100644 --- a/swoc++/src/swoc_ip.cc +++ b/code/src/swoc_ip.cc @@ -275,6 +275,7 @@ auto IP4Addr::operator=(sockaddr_in const *sa) -> self_type& { sockaddr_in *IP4Addr::fill(sockaddr_in *sa, in_port_t port) const { sa->sin_addr.s_addr = this->network_order(); + sa->sin_port = port; return sa; } diff --git a/doc/Doxyfile b/doc/Doxyfile index ac00ccd..ae6dc71 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = "LibSWOC++" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "1.2.0" +PROJECT_NUMBER = "1.2.1" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/doc/code/IPSpace.en.rst b/doc/code/IPSpace.en.rst index a8890c8..dc20813 100644 --- a/doc/code/IPSpace.en.rst +++ b/doc/code/IPSpace.en.rst @@ -170,7 +170,7 @@ Blending Bitsets Some details are omitted for brevity and because they aren't directly relevant. The full implementation, which is run as a unit test to verify its correctness, - `is available here `__. + `is available here `__. You can compile and step through the code to see how it works in more detail, or experiment with changing some of the example data. diff --git a/doc/code/TextView.en.rst b/doc/code/TextView.en.rst index fd46f5c..2769f0b 100644 --- a/doc/code/TextView.en.rst +++ b/doc/code/TextView.en.rst @@ -275,7 +275,7 @@ separated by commas. .. sidebar:: Verification - `Test code for example `__. + `Test code for example `__. If :arg:`value` was :literal:`bob ,dave, sam` then :arg:`token` would be successively :literal:`bob`, :literal:`dave`, :literal:`sam`. After :literal:`sam` was extracted :arg:`value` @@ -297,7 +297,7 @@ for values that are boolean. .. sidebar:: Verification - `Test code for example `__. + `Test code for example `__. The basic list processing is the same as the previous example, with each element being treated as a "list" with ``=`` as the separator. Note if there is no ``=`` character then all of the list @@ -348,7 +348,7 @@ do not, so a flag to strip quotes from the resulting elements is needed. The fin .. sidebar:: Verification - `Test code for example `__. + `Test code for example `__. This takes a :code:`TextView&` which is the source view which will be updated as tokens are removed (therefore the caller must do the empty view check). The other arguments are the separator character diff --git a/doc/conf.py b/doc/conf.py index 805d4a4..8268c16 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -80,7 +80,7 @@ copyright = u'{}, amc@apache.org'.format(date.today().year) # The full version, including alpha/beta/rc tags. -release = "1.2.0" +release = "1.2.1" # The short X.Y version. version = '.'.join(release.split('.', 2)[:2]) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 07394ab..4150aa5 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -2,10 +2,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++) +add_executable(ex_netdb ex_netdb.cc) +target_link_libraries(ex_netdb PUBLIC libswoc) 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) +target_compile_options(ex_netdb PRIVATE -Wall -Wextra -Werror) + +add_executable(ex_netcompact ex_netcompact.cc) +target_link_libraries(ex_netcompact PUBLIC libswoc) +target_compile_options(ex_netcompact PRIVATE -Wall -Wextra -Werror) diff --git a/example/ex_netcompact.cc b/example/ex_netcompact.cc new file mode 100644 index 0000000..a5002c2 --- /dev/null +++ b/example/ex_netcompact.cc @@ -0,0 +1,102 @@ +// 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/23 + + The output is the same set of addresses in as few networks as possible. +*/ + +#include +#include + +#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::IPAddr; +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; + +/// 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 + 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. + 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."); + exit(1); + } + + auto t0 = std::chrono::system_clock::now(); + 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); + } + auto n_ranges = process(space, content); + + // Dump the resulting space. + 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(delta).count()); + + return 0; +} diff --git a/swoc++/swoc++-headers.part b/swoc++/swoc++-headers.part deleted file mode 100644 index 4c61a89..0000000 --- a/swoc++/swoc++-headers.part +++ /dev/null @@ -1,6 +0,0 @@ -Import("*") -PartName("headers") - -# export the include directory -inc_files = Pattern(src_dir="include", includes=["swoc/*.h"]) -env.InstallInclude(inc_files) diff --git a/swoc++/swoc++.part b/swoc++/swoc++.part deleted file mode 100644 index d9270a4..0000000 --- a/swoc++/swoc++.part +++ /dev/null @@ -1,19 +0,0 @@ -Import("*") -PartName("libswoc") -PartVersion("1.2.0") - -src_files = [ - "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", -] - -env.Part("swoc++-static.part", package_group="libswoc", src_files=src_files) -env.Part("swoc++-shared.part", package_group="libswoc", src_files=src_files) -env.Part("swoc++-headers.part", package_group="libswoc") diff --git a/tools/update-version.sh b/tools/update-version.sh index d54f1fd..408ce70 100644 --- a/tools/update-version.sh +++ b/tools/update-version.sh @@ -6,7 +6,7 @@ if [ -z "$3" ] ; then fi # Header -sed -i swoc++/include/swoc/swoc_version.h --expr "s/SWOC_namespace SWOC_NAMESPACE_(?:\d+)_(?:\d+)_(?:\d+)/SWOC_namespace SWOC_NAMESPACE_$1_$2_$3/" +sed -i -E swoc++/include/swoc/swoc_version.h --expr "s/SWOC_VERSION_NS _[0-9]+_[0-9]+_[0-9]+/SWOC_VERSION_NS _$1_$2_$3/"wqq sed -i swoc++/include/swoc/swoc_version.h --expr "s/\(MAJOR_VERSION *= *\).*\$/\\1$1;/" sed -i swoc++/include/swoc/swoc_version.h --expr "s/\(MINOR_VERSION *= *\).*\$/\\1$2;/" sed -i swoc++/include/swoc/swoc_version.h --expr "s/\(POINT_VERSION *= *\).*\$/\\1$3;/" diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt index 88f949a..e71c71e 100644 --- a/unit_tests/CMakeLists.txt +++ b/unit_tests/CMakeLists.txt @@ -26,7 +26,7 @@ add_executable(test_libswoc ex_TextView.cc ) -target_link_libraries(test_libswoc PUBLIC swoc++) +target_link_libraries(test_libswoc PUBLIC libswoc) set_target_properties(test_libswoc PROPERTIES CLANG_FORMAT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_options(test_libswoc PRIVATE -Wall -Wextra -Werror -Wno-unused-parameter -Wno-format-truncation -Wno-stringop-overflow -Wno-invalid-offsetof) #add_definitions(-DVERBOSE_EXAMPLE_OUTPUT=1) diff --git a/unit_tests/test_ip.cc b/unit_tests/test_ip.cc index ff8a5c3..d703d29 100644 --- a/unit_tests/test_ip.cc +++ b/unit_tests/test_ip.cc @@ -538,8 +538,8 @@ TEST_CASE("IP ranges and networks", "[libswoc][ip][net][range]") { } TEST_CASE("IP Space Int", "[libswoc][ip][ipspace]") { - using int_space = swoc::IPSpace; - int_space space; + using uint_space = swoc::IPSpace; + uint_space space; REQUIRE(space.count() == 0); @@ -627,12 +627,43 @@ TEST_CASE("IP Space Int", "[libswoc][ip][ipspace]") { } CHECK(7 == space.count()); + // Make sure all of these addresses yield the same result. 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"}})); + // same for negative result + CHECK(space.end() == space.find(IP4Addr{"10.0.4.16"})); + CHECK(space.end() == space.find(IPAddr{"10.0.4.16"})); + CHECK(space.end() == space.find(IPAddr{IPEndpoint{"10.0.4.16:80"}})); - auto b2 = [] (unsigned &lhs, unsigned const& rhs) { lhs = rhs; return true; }; + std::array, 3> r_clear = { + { + {"2.2.2.2-2.2.2.40", 0} + , {"2.2.2.50-2.2.2.60", 1} + , {"2.2.2.70-2.2.2.100", 2} + }}; + space.clear(); + for (auto &&[text, value] : r_clear) { + IPRange range{text}; + space.mark(IPRange{text}, value); + } + CHECK(space.count() == 3); + space.erase(IPRange{"2.2.2.35-2.2.2.75"}); + CHECK(space.count() == 2); + { + spot = space.begin(); + auto [ r0, p0 ] = *spot; + auto [ r2, p2 ] = *++spot; + CHECK(r0 == IPRange{"2.2.2.2-2.2.2.34"}); + CHECK(p0 == 0); + CHECK(r2 == IPRange{"2.2.2.76-2.2.2.100"}); + CHECK(p2 == 2); + } + // This is about testing repeated colorings of the same addresses, which happens quite a + // bit in normal network datasets. In fact, the test dataset is based on such a dataset + // and its use. + auto b2 = [] (unsigned &lhs, unsigned const& rhs) { lhs = rhs; return true; }; std::array, 31> r2 = { { {"2001:4998:58:400::1/128", 1} // 1 @@ -669,6 +700,7 @@ TEST_CASE("IP Space Int", "[libswoc][ip][ipspace]") { }}; space.clear(); + // Start with basic blending. for (auto &&[text, value] : r2) { IPRange range{text}; space.blend(IPRange{text}, value, b2); @@ -676,6 +708,7 @@ TEST_CASE("IP Space Int", "[libswoc][ip][ipspace]") { REQUIRE(space.end() != space.find(range.max())); } CHECK(6 == space.count()); + // Do the exact same networks again, should not change the range count. for (auto &&[text, value] : r2) { IPRange range{text}; space.blend(IPRange{text}, value, b2); @@ -683,12 +716,13 @@ TEST_CASE("IP Space Int", "[libswoc][ip][ipspace]") { REQUIRE(space.end() != space.find(range.max())); } CHECK(6 == space.count()); + // Verify that earlier ranges are still valid after the double blend. for (auto &&[text, value] : r2) { IPRange range{text}; REQUIRE(space.end() != space.find(range.min())); REQUIRE(space.end() != space.find(range.max())); } - // Drop a non-intersecting range between existing ranges 1 and 2, make sure both sides coalesce. + // Color the non-intersecting range between ranges 1 and 2, verify coalesce. space.blend(IPRange{"2001:4998:58:400::C/126"_tv}, 1, b2); CHECK(5 == space.count()); // Verify all the data is in the ranges.