diff --git a/code/include/swoc/Errata.h b/code/include/swoc/Errata.h index 944129d..d765ef7 100644 --- a/code/include/swoc/Errata.h +++ b/code/include/swoc/Errata.h @@ -504,10 +504,7 @@ class Errata { bool is_ok() const; /// @return If there is top level severity. - bool - has_severity() const { - return _data && _data->_severity.has_value(); - } + bool has_severity() const; /// @return Top level severity. Severity severity() const; @@ -1124,9 +1121,12 @@ Errata::assign(code_type code) -> self_type & { return *this; } +inline bool Errata::has_severity() const { + return _data && _data->_severity.has_value(); +} inline auto Errata::severity() const -> Severity { - return _data ? _data->_severity.value() : DEFAULT_SEVERITY; + return this->has_severity() ? _data->_severity.value() : DEFAULT_SEVERITY; } inline auto @@ -1142,7 +1142,7 @@ Errata::length() const { inline bool Errata::is_ok() const { - return this->empty() || _data->_severity < FAILURE_SEVERITY; + return nullptr == _data || this->severity() < FAILURE_SEVERITY; } inline const Errata::Annotation & diff --git a/code/include/swoc/swoc_meta.h b/code/include/swoc/swoc_meta.h index 0d987b2..5c3855e 100644 --- a/code/include/swoc/swoc_meta.h +++ b/code/include/swoc/swoc_meta.h @@ -237,4 +237,62 @@ template struct type_list { template static constexpr bool contains = is_any_of::value; }; +/** Scoped value change. + * + * The purpose of this class is to change the value of a variable in a scope and then change it + * back when the scope is exited. The old value will be moved to a cache variable and then moved + * back when the instance is destructed. This is very useful to temporarily tweak global variables + * which having to know what the current value is. + * + * @code + * { + * let save(var, value); + * // var now has value. + * } + * // var now has original value. + * @endcode + * + * @tparam T Type of variable to scope. + */ +template struct let { + using self_type = let; + + let(self_type const& that) = delete; + self_type & operator = (self_type const&) = delete; + + T &_var; ///< Reference to scoped variable. + T _value; ///< Original value. + + /** Construct a scope. + * + * @param var Variable to scope. + * @param value temporary value to assign. + */ + let(T &var, T const &value); + + /** Construct a scope. + * + * @param var Variable to scope. + * @param value temporary value to assign. + */ + let(T &var, T &&value); + + ~let(); +}; + +template let::let(T &var, T const &value) : _var(var), _value(std::move(var)) +{ + _var = value; +} +template let::let(T &var, T &&value) : _var(var), _value(std::move(var)) +{ + _var = std::move(value); +} + +template let::~let() +{ + _var = std::move(_value); +} + + }}} // namespace swoc::SWOC_VERSION_NS::meta diff --git a/doc/code/TextView.en.rst b/doc/code/TextView.en.rst index 740f85d..c2ede1b 100644 --- a/doc/code/TextView.en.rst +++ b/doc/code/TextView.en.rst @@ -86,6 +86,18 @@ E.g. the code to write a simple hash function [#]_ could be return hash; } +Although alternatively, this can be done in a non-modifying way. + +.. code-block:: cpp + + void hasher(TextView v) { + size_t hash = 0; + for ( auto c : v) { + hash = hash * 13 + c; + } + return hash; + } + Because |TV| inherits from :code:`std::string_view` it can also be used as a container for range :code:`for` loops. @@ -97,10 +109,21 @@ Because |TV| inherits from :code:`std::string_view` it can also be used as a con return hash; } +The first approach enables dropping out of the loop on some condition with the view updated to +no longer contain processed characters, making restart or other processing simple. + The standard functions :code:`strcmp`, :code:`memcmp`, code:`memcpy`, and :code:`strcasecmp` are overloaded for |TV| so that a |TV| can be used as if it were a C-style string. The size is is taken from the |TV| and doesn't need to be passed in explicitly. +.. class:: CharSet + + :libswoc:`Reference documentation `. + +This is a simple class that contains a set of characters. This is intended primarily to make +parsing faster and simpler. Rather than checking a list of delimiters the character can be checked +with a single `std::bitset` lookup. + Basic Operations ================ @@ -130,7 +153,7 @@ Searching --------- Because |TV| is a subclass of :code:`std::string_view` all of its search method work on a |TV|. The -only search methods provided beyond those are :libswoc:`TextView::find_if` and +only search methods provided beyond those in :code:`std::string` are :libswoc:`TextView::find_if` and :libswoc:`TextView::rfind_if` which search the view by a predicate. The predicate takes a single :code:`char` argument and returns a :code:`bool`. The search terminates on the first character for which the predicate returns :code:`true`. @@ -281,12 +304,12 @@ developing |TV| parsing. The first was to minimize the need to allocate memory to hold intermediate results. For this reason, the normal style of use is a streaming / incremental one, where tokens are extracted from a source one by one -and placed in |TV| instances, with the orignal source |TV| being reduced by each extraction until +and placed in |TV| instances, with the original source |TV| being reduced by each extraction until it is empty. The second was to minimize cut and paste coding. Typical C or C++ parsing logic consists mostly of very generic code to handle pointer and size updates. The point of |TV| is to automate all of that -so the resulting code is focused entirely on the parsing logic, not boiler plate string or view manipulation. +yielding code focused entirely on the parsing logic, not boiler plate string or view manipulation. It is a common occurrence to not get such code exactly correct leading to hard to track bugs. Use of |TV| eliminates those problems. @@ -313,7 +336,7 @@ are very cheap to copy. This is essentially the same as having a current pointer and checking for :code:`current >= end` except :code:`TextView` does all the work, leading to simpler and less buggy code. -White space is dropped because of the calls to :code:`ltrim_if` and `rtrim_if`. By calling in the +White space is dropped because of the calls to :code:`ltrim_if` and :code:`rtrim_if`. By calling in the loop condition, the loop exits if the remaining text is only whitespace and no token is processed. Alternatively :code:`trim_if` could be used after extraction. The performance will be *slightly* better because although :code:`trim_if` calls :code:`ltrim_if` and :code:`rtrim_if`, a final diff --git a/doc/index.rst b/doc/index.rst index b6f573e..f63bb30 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -18,9 +18,16 @@ Solid Wall of C++ ***************** The Solid Wall of C++ library is a collection of C++ classes and utilities. This code evolved out of -infrastructure used in `Apache Traffic Server `__. The utilities -had become useful enough there were requests to be able to use them in ATS plugins and other, -unrelated projects. Hence this library. I hope you find it as useful as I have. +infrastructure used in `Apache Traffic Server `__ as I strove to combine +functionality, ease of use, and performance. +The utilities had become useful enough there were requests to be able to use them in ATS plugins and other, +unrelated projects. Hence this library. After much production use, this library has been imported back +in to Traffic Server and can be used there in the core or any plugin. +I hope you find it as useful as I have. + +Most of the library is dedicated to convenience, such as :class:`TextView` which provides Python like +string manipulation on top of :code:`std::string_view`, and performance, such as :class:`IPSpace` which +enables very fast IP address range storage. .. toctree:: :maxdepth: 1 diff --git a/tools/ats-drop.sh b/tools/ats-drop.sh index 839a07f..0048b29 100644 --- a/tools/ats-drop.sh +++ b/tools/ats-drop.sh @@ -23,9 +23,6 @@ else mkdir -p ${TARGET_SRC_DIR} fi -cp code/CMakeLists.txt ${TARGET_BASE_DIR}/swoc -(cd ${ATS}/${BASE_PATH}/swoc ; sed -i -e '/^if (LIBSWOC_INSTALL)/,/^endif/d' CMakeLists.txt ; git add CMakeLists.txt) - cp code/src/*.cc ${TARGET_SRC_DIR} (cd ${ATS}; git add ${BASE_PATH}/swoc/CMakeLists.txt ; git add ${SRC_PATH}/*.cc) @@ -48,82 +45,3 @@ cp code/include/swoc/*.h ${TARGET_INC_DIR} cp code/include/swoc/ext/*.h ${TARGET_INC_DIR}/ext cp code/include/swoc/ext/HashFNV.h ${TARGET_INC_DIR} (cd ${ATS}; git add ${INC_PATH}/*.h ; git add ${INC_PATH}/ext/*.h) - -# Build the source -cat <<'TEXT' > ${ATS}/${BASE_PATH}/swoc/Makefile.am -# swoc Makefile.am -# -# 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. - -lib_LTLIBRARIES = libtsswoc.la - -library_includedir=$(includedir)/swoc - -AM_CPPFLAGS += @SWOC_INCLUDES@ - -libtsswoc_la_LDFLAGS = @AM_LDFLAGS@ -no-undefined -release 1.5.8 -libtsswoc_la_SOURCES = \ - 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 src/string_view_util.cc - -if EXPORT_SWOC_HEADERS -library_include_HEADERS = \ - include/swoc/ArenaWriter.h \ - include/swoc/BufferWriter.h \ - include/swoc/bwf_base.h \ - include/swoc/bwf_ex.h \ - include/swoc/bwf_fwd.h \ - include/swoc/bwf_ip.h \ - include/swoc/bwf_std.h \ - include/swoc/DiscreteRange.h \ - include/swoc/Errata.h \ - include/swoc/IntrusiveDList.h \ - include/swoc/IntrusiveHashMap.h \ - include/swoc/IPAddr.h \ - include/swoc/IPEndpoint.h \ - include/swoc/IPRange.h \ - include/swoc/IPSrv.h \ - include/swoc/Lexicon.h \ - include/swoc/MemArena.h \ - include/swoc/MemSpan.h \ - include/swoc/RBTree.h \ - include/swoc/Scalar.h \ - include/swoc/swoc_file.h \ - include/swoc/swoc_ip.h \ - include/swoc/swoc_meta.h \ - include/swoc/swoc_version.h\ - include/swoc/string_view_util.h \ - include/swoc/TextView.h \ - include/swoc/Vectray.h \ - include/swoc/HashFNV.h -endif - -clean-local: - -clang-tidy-local: $(DIST_SOURCES) - $(CXX_Clang_Tidy) -TEXT -(cd ${ATS} ; git add ${BASE_PATH}/swoc/Makefile.am) - -if ! grep -q swoc ${ATS}/configure.ac ; then - sed -i -e 's!lib/yamlcpp/Makefile!lib/swoc/Makefile\n &!' ${ATS}/configure.ac - (cd ${ATS} ; git add configure.ac) -fi - -if ! grep -q swoc ${ATS}/${BASE_PATH}/Makefile.am ; then - sed -i -e '/SUBDIRS =/s!$! swoc!' ${ATS}/${BASE_PATH}/Makefile.am - (cd ${ATS} ; git add ${BASE_PATH}/Makefile.am) -fi diff --git a/unit_tests/test_Errata.cc b/unit_tests/test_Errata.cc index 8ce82a5..e46bd07 100644 --- a/unit_tests/test_Errata.cc +++ b/unit_tests/test_Errata.cc @@ -414,4 +414,15 @@ TEST_CASE("Errata Autotext", "[libswoc][errata]") { REQUIRE(b.front().text() == "Bravo [2]"); Errata c{ecode(ECode::ALPHA), ERRATA_ERROR, Errata::AUTO}; REQUIRE(c.front().text() == "Error: Alpha [1]"); + + Errata d{ERRATA_ERROR}; + REQUIRE_FALSE(d.is_ok()); + Errata e{ERRATA_INFO}; + REQUIRE(e.is_ok()); + Errata f{ecode(ECode::BRAVO)}; + REQUIRE_FALSE(f.is_ok()); + // Change properties but need to restore them for other tests. + swoc::meta::let g1(Errata::DEFAULT_SEVERITY, ERRATA_WARN); + swoc::meta::let g2(Errata::FAILURE_SEVERITY, ERRATA_ERROR); + REQUIRE(f.is_ok()); } diff --git a/unit_tests/test_meta.cc b/unit_tests/test_meta.cc index 55c9c3e..782bae4 100644 --- a/unit_tests/test_meta.cc +++ b/unit_tests/test_meta.cc @@ -93,3 +93,27 @@ TEST_CASE("Meta vary", "[meta][vary]") { v = "956"_tv; REQUIRE(std::visit(visitor, v) == 956); } + +TEST_CASE("Meta let", "[meta][let]") { + using swoc::meta::let; + + unsigned x = 56; + { + REQUIRE(x == 56); + let guard(x, unsigned(3136)); + REQUIRE(x == 3136); + // auto bogus = guard; // should not compile. + } + REQUIRE(x == 56); + + // Checking move semantics - avoid reallocating the original string. + std::string s{"Evil Dave Rulz With An Iron Keyboard"}; // force allocation. + auto sptr = s.data(); + { + char const * text = "Twas brillig and the slithy toves"; + let guard(s, std::string(text)); + REQUIRE(s == text); + REQUIRE(s.data() != sptr); + } + REQUIRE(s.data() == sptr); +}