This is an automated email from the ASF dual-hosted git repository. bneradt pushed a commit to branch dev-1-0-12 in repository https://gitbox.apache.org/repos/asf/trafficserver-libswoc.git
commit 024b1608c9e78e84b9a73ebee0401e8152105988 Author: Alan M. Carroll <[email protected]> AuthorDate: Sun Feb 23 20:08:47 2020 -0600 Some documentation and example updates. --- doc/code/IPSpace.en.rst | 49 ++++++++++++------------- swoc++/CMakeLists.txt | 2 +- swoc++/include/swoc/bwf_std.h | 10 ++++++ unit_tests/CMakeLists.txt | 1 + unit_tests/ex_ipspace_properties.cc | 71 +++++++++++++++++++++++++++++++++++-- unit_tests/test_ip.cc | 64 +++++++++++++++++++-------------- 6 files changed, 142 insertions(+), 55 deletions(-) diff --git a/doc/code/IPSpace.en.rst b/doc/code/IPSpace.en.rst index fa2ef35..e5918cd 100644 --- a/doc/code/IPSpace.en.rst +++ b/doc/code/IPSpace.en.rst @@ -1,15 +1,4 @@ -.. Licensed to the Apache Software Foundation (ASF) under one or more contributor license - agreements. See the NOTICE file distributed with this work for additional information regarding - copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 - (the "License"); you may not use this file except in compliance with the License. You may obtain - a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software distributed under the License - is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - or implied. See the License for the specific language governing permissions and limitations - under the License. +.. SPDX-License-Identifier: Apache-2.0 .. include:: ../common-defs.rst @@ -119,30 +108,38 @@ blend Blend +++++ +Blending is different than marking or filling, as the latter two apply the payload passed to the +method. That is, if an address is marked by either method, it is marked with precisely the payload +passed to the method. :code:`blend` is different because it can cause an address to be marked by +a payload that was not explicitly passed in to any coloring method. Instead of replacing an +existing payload, it enables computing the resulting payload from the existing payload and a value +passed to the method. + The :libswoc:`swoc::IPSpace::blend` method requires a range and a "blender", which is a functor that blends a :arg:`color` into a :code:`PAYLOAD` instances. The signature is :: bool blender(PAYLOAD & payload, U const& color) -The type :code:`U` is that same as the template argument :code:`U` to the method, which must be -compatible with the second argument to the :code:`blend` method. The argument passed to -:code:`blender` is the second argument to :code:`blend`. +The type :code:`U` is that same as the template argument :code:`U` to the :code:`blend` method, +which must be compatible with the second argument to the :code:`blend` method. The argument passed +to :code:`blender` is the second argument to :code:`blend`. The method is modeled on C++ `compound assignment operators <https://en.cppreference.com/w/cpp/language/operator_assignment#Builtin_compound_assignment>`__. If -the blend operation is thought of as the "@" operator, then the blend functor performs :code:`lhs @= -rhs`. That is, :arg:`lhs` is modified to be the combination of :arg:`lhs` and :arg`rhs`. :arg:`lhs` -is always the previous payload already in the space, and :arg:`rhs` is the :arg:`color` argument -to the :code:`blend` method. The internal logic handles copying the payload instances as needed. - -The return value indicates whether the combined result in :arg:`lhs` is a valid payload or not. If -it is the method should return :code:`true`. In general most implementations will :code:`return -true;` in all cases. If the method returns :code:`false` then the address(es) for the combined -payload are removed from the container. This allows payloads to be "unblended", for one payload to -cancel out another, or to do selective erasing of ranges. +the blend operation is thought of as the "@" operator, then the blend functor performs +:code:`lhs @=rhs`. That is, :arg:`lhs` is modified to be the combination of :arg:`lhs` and :arg`rhs`. +:arg:`lhs` is always the previous payload already in the space, and :arg:`rhs` is the :arg:`color` +argument to the :code:`blend` method. The internal logic handles copying the payload instances as +needed. + +The return value of the blender indicates whether the combined result in :arg:`lhs` is a valid +payload or not. If it is the method should return :code:`true`. In general most implementations will +:code:`return true;` in all cases. If the method returns :code:`false` then the address(es) for the +combined payload are removed from the container. This allows payloads to be "unblended", for one +payload to cancel out another, or to do selective erasing of ranges. As an example, consider the case where the payload is a bitmask. It might be reasonable to keep -empty bitmasks in the container, but it would also be reasonble to decide the empty bitmask and any +empty bitmasks in the container, but it would also be reasonable to decide the empty bitmask and any address mapped to it should removed entirely from the container. In such a case, a blender that clears bits in the payloads should return :code:`false` when the result is the empty bitmask. diff --git a/swoc++/CMakeLists.txt b/swoc++/CMakeLists.txt index a5f52fc..067ba2d 100644 --- a/swoc++/CMakeLists.txt +++ b/swoc++/CMakeLists.txt @@ -45,7 +45,7 @@ set(CC_FILES ) add_library(swoc++ STATIC ${CC_FILES}) -add_compile_options(-Wall -Wextra -Werror -Wno-ignored-qualifiers -Wno-unused-parameter -Wno-format-truncation -Wno-cast-function-type -Wno-stringop-overflow -Wno-invalid-offsetof) +add_compile_options(-Wall -Wextra -Werror -Wno-ignored-qualifiers -Wno-unused-parameter -Wno-format-truncation -Wno-stringop-overflow -Wno-invalid-offsetof) # 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. diff --git a/swoc++/include/swoc/bwf_std.h b/swoc++/include/swoc/bwf_std.h index d6b092a..2b200f7 100644 --- a/swoc++/include/swoc/bwf_std.h +++ b/swoc++/include/swoc/bwf_std.h @@ -25,10 +25,12 @@ #include <atomic> #include <chrono> +#include <bitset> #include "swoc/bwf_base.h" namespace std { +/// Format atomics by stripping the atomic and formatting the underlying type. template <typename T> swoc::BufferWriter & bwformat(swoc::BufferWriter &w, swoc::bwf::Spec const &spec, atomic<T> const &v) { @@ -37,4 +39,12 @@ bwformat(swoc::BufferWriter &w, swoc::bwf::Spec const &spec, atomic<T> const &v) swoc::BufferWriter &bwformat(swoc::BufferWriter &w, swoc::bwf::Spec const &spec, error_code const &ec); +template < size_t N > +swoc::BufferWriter &bwformat(swoc::BufferWriter &w, swoc::bwf::Spec const& spec, bitset<N> const& bits) { + for ( unsigned idx = 0 ; idx < N ; ++idx) { + w.write(bits[idx] ? '1' : '0'); + } + return w; +} + } // end namespace std diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt index 5bddd95..fd17d9f 100644 --- a/unit_tests/CMakeLists.txt +++ b/unit_tests/CMakeLists.txt @@ -28,3 +28,4 @@ add_executable(test_libswoc target_link_libraries(test_libswoc PUBLIC swoc++) set_target_properties(test_libswoc PROPERTIES CLANG_FORMAT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) +add_definitions(-DVERBOSE_EXAMPLE_OUTPUT=1) diff --git a/unit_tests/ex_ipspace_properties.cc b/unit_tests/ex_ipspace_properties.cc index d00cafb..8ee8e9c 100644 --- a/unit_tests/ex_ipspace_properties.cc +++ b/unit_tests/ex_ipspace_properties.cc @@ -14,8 +14,7 @@ #include <swoc/TextView.h> #include <swoc/swoc_ip.h> #include <swoc/bwf_ip.h> -#include <swoc/Scalar.h> -#include <swoc/swoc_file.h> +#include <swoc/bwf_std.h> using namespace std::literals; using namespace swoc::literals; @@ -36,6 +35,74 @@ using swoc::MemSpan; using swoc::MemArena; using W = swoc::LocalBufferWriter<256>; +namespace { +bool Verbose_p = +#if VERBOSE_EXAMPLE_OUTPUT + true +#else + false +#endif + ; +} + +TEST_CASE("IPSpace bitset blending", "[libswoc][ipspace][bitset][blending]") { + // Color each address with a set of bits. + using PAYLOAD = std::bitset<32>; + // Declare the IPSpace. + using Space = swoc::IPSpace<PAYLOAD>; + + // Dump the ranges to stdout. + auto dump = [](Space&space) -> void { + if (Verbose_p) { + std::cout << W().print("{} ranges\n", space.count()); + for (auto &&[r, payload] : space) { + std::cout << W().print("{:12}-{:12} : {}\n", r.min(), r.max(), payload); + } + } + }; + + // Blend functor which computes a union of the bitsets. + auto blender = [](PAYLOAD& lhs, PAYLOAD const& rhs) -> bool { + lhs |= rhs; + return lhs != 0; + }; + + // test ranges. + std::array<std::tuple<TextView, std::initializer_list<unsigned>>, 9> ranges = { + { + { "100.0.0.0-100.0.0.255", { 0 } } + , { "100.0.1.0-100.0.1.255", { 1 } } + , { "100.0.2.0-100.0.2.255", { 2 } } + , { "100.0.3.0-100.0.3.255", { 3 } } + , { "100.0.4.0-100.0.4.255", { 4 } } + , { "100.0.5.0-100.0.5.255", { 5 } } + , { "100.0.6.0-100.0.6.255", { 6 } } + , { "100.0.0.0-100.0.0.255", { 31 } } + , { "100.0.1.0-100.0.1.255", { 30 } } + }}; + + // The IPSpace instance. + Space space; + + // For each test range, compute the bitset from the list of bit indices. + for (auto &&[text, bit_list] : ranges) { + PAYLOAD bits; // zero bitset. + for (auto bit : bit_list) { + bits[bit] = true; + } + space.blend(IPRange{text}, bits, blender); + } + + dump(space); + + auto resetter = [](PAYLOAD& lhs, PAYLOAD const& rhs) -> bool { + auto mask = rhs; + lhs &= mask.flip(); + return lhs != 0; + }; + space.blend(IPRange{"100.0.2.128-100.0.3.127"}, PAYLOAD{"1111"}, resetter); + dump(space); +} // --- diff --git a/unit_tests/test_ip.cc b/unit_tests/test_ip.cc index e3b5d74..4cec9f1 100644 --- a/unit_tests/test_ip.cc +++ b/unit_tests/test_ip.cc @@ -400,29 +400,17 @@ TEST_CASE("IPSpace bitset", "[libswoc][ipspace][bitset]") { TEST_CASE("IPSpace docJJ", "[libswoc][ipspace][docJJ]") { using PAYLOAD = std::bitset<32>; using Space = swoc::IPSpace<PAYLOAD>; - - auto dump = [](Space&space) -> void { - swoc::LocalBufferWriter<1024> w; - std::cout << "Dumping " << space.count() << " ranges" << std::endl; - for (auto &&[r, payload] : space) { - w.clear().print("{}-{} :", r.min(), r.max()); - std::cout << w << payload << std::endl; - } - }; - auto reverse_dump = [](Space&space) -> void { - swoc::LocalBufferWriter<1024> w; - std::cout << "Dumping " << space.count() << " ranges" << std::endl; - for (auto spot = space.end(); spot != space.begin();) { - auto &&[r, payload]{*--spot}; - w.clear().print("{} :", r); - std::cout << w << payload << std::endl; - } - }; - auto blender = [](PAYLOAD& lhs, PAYLOAD const& rhs) -> bool { lhs |= rhs; return true; }; + auto make_bits = [](std::initializer_list<unsigned> idx) -> PAYLOAD { + PAYLOAD bits; + for (auto bit : idx) { + bits[bit] = true; + } + return bits; + }; std::array<std::tuple<TextView, std::initializer_list<unsigned>>, 9> ranges = { { @@ -437,17 +425,41 @@ TEST_CASE("IPSpace docJJ", "[libswoc][ipspace][docJJ]") { , { "100.0.1.0-100.0.1.255", { 30 } } }}; + std::array<std::initializer_list<unsigned>, 7> results = {{ + { 0, 31 } + , { 1, 30 } + , { 2 } + , { 3 } + , { 4 } + , { 5 } + , { 6 } + }}; + Space space; for (auto &&[text, bit_list] : ranges) { - PAYLOAD bits; - for (auto bit : bit_list) { - bits[bit] = true; - } - space.blend(IPRange{text}, bits, blender); + space.blend(IPRange{text}, make_bits(bit_list), blender); + } + + // Check iteration - verify forward and reverse iteration yield the correct number of ranges + // and the range payloads match what is expected. + REQUIRE(space.count() == results.size()); + unsigned idx; + + idx = 0; + for ( auto const& [ range, bits ] : space) { + REQUIRE(idx < results.size()); + CHECK(bits == make_bits(results[idx])); + ++idx; + } + + idx = results.size(); + for ( auto spot = space.end() ; spot != space.begin() ; ) { + auto const& [ range, bits ] { *--spot }; + REQUIRE(idx > 0); + --idx; + CHECK(bits == make_bits(results[idx])); } - dump(space); - reverse_dump(space); } #if 0
