diff --git a/iocore/eventsystem/IOBuffer.cc b/iocore/eventsystem/IOBuffer.cc index e23d1f858e9..d95c836c258 100644 --- a/iocore/eventsystem/IOBuffer.cc +++ b/iocore/eventsystem/IOBuffer.cc @@ -79,22 +79,23 @@ init_buffer_allocators(int iobuffer_advice) auto make_buffer_size_parser() { - return [l = swoc::Lexicon{ - {{0, {"128"}}, - {1, {"256"}}, - {2, {"512"}}, - {3, {"1k", "1024"}}, - {4, {"2k", "2048"}}, - {5, {"4k", "4096"}}, - {6, {"8k", "8192"}}, - {7, {"16k"}}, - {8, {"32k"}}, - {9, {"64k"}}, - {10, {"128k"}}, - {11, {"256k"}}, - {12, {"512k"}}, - {13, {"1M", "1024k"}}, - {14, {"2M", "2048k"}}}, + using L = swoc::Lexicon; + return [l = L{ + L::with_multi{{0, {"128"}}, + {1, {"256"}}, + {2, {"512"}}, + {3, {"1k", "1024"}}, + {4, {"2k", "2048"}}, + {5, {"4k", "4096"}}, + {6, {"8k", "8192"}}, + {7, {"16k"}}, + {8, {"32k"}}, + {9, {"64k"}}, + {10, {"128k"}}, + {11, {"256k"}}, + {12, {"512k"}}, + {13, {"1M", "1024k"}}, + {14, {"2M", "2048k"}}}, -1 }](swoc::TextView esize) -> std::optional { int result = l[esize]; diff --git a/lib/swoc/CMakeLists.txt b/lib/swoc/CMakeLists.txt index 249e4ebfea8..88a549ebe8f 100644 --- a/lib/swoc/CMakeLists.txt +++ b/lib/swoc/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.11) project(Lib-SWOC CXX) -set(LIBSWOC_VERSION "1.4.9") +set(LIBSWOC_VERSION "1.4.10") set(CMAKE_CXX_STANDARD 17) cmake_policy(SET CMP0087 NEW) # override "lib64" to be "lib" unless the user explicitly sets it. diff --git a/lib/swoc/Makefile.am b/lib/swoc/Makefile.am index 0969831c1d7..98d72ca31db 100644 --- a/lib/swoc/Makefile.am +++ b/lib/swoc/Makefile.am @@ -22,7 +22,7 @@ library_includedir=$(includedir)/swoc AM_CPPFLAGS += @SWOC_INCLUDES@ -libtsswoc_la_LDFLAGS = @AM_LDFLAGS@ -no-undefined -release 1.4.9 +libtsswoc_la_LDFLAGS = @AM_LDFLAGS@ -no-undefined -release 1.4.10 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 diff --git a/lib/swoc/include/swoc/BufferWriter.h b/lib/swoc/include/swoc/BufferWriter.h index 69ddfe5ab16..5ea465f5eaa 100644 --- a/lib/swoc/include/swoc/BufferWriter.h +++ b/lib/swoc/include/swoc/BufferWriter.h @@ -18,17 +18,9 @@ #include "swoc/swoc_version.h" #include "swoc/TextView.h" #include "swoc/MemSpan.h" +#include "swoc/bwf_fwd.h" namespace swoc { inline namespace SWOC_VERSION_NS { -namespace bwf { -struct Spec; - -class Format; - -class NameBinding; - -class ArgPack; -} // namespace bwf /** Wrapper for operations on a buffer. * diff --git a/lib/swoc/include/swoc/IPAddr.h b/lib/swoc/include/swoc/IPAddr.h index ac359c4c98d..bc005b18748 100644 --- a/lib/swoc/include/swoc/IPAddr.h +++ b/lib/swoc/include/swoc/IPAddr.h @@ -437,6 +437,9 @@ class IP6Addr { /// Number of bits per word. static constexpr size_t WORD_WIDTH = std::numeric_limits::digits * WORD_SIZE; + /// Mask the size of a word. + static constexpr word_type WORD_MASK = ~word_type(0); + /// Number of words used for basic address storage. static constexpr size_t N_STORE = SIZE / WORD_SIZE; @@ -1085,22 +1088,27 @@ IP6Addr::copy_to(sockaddr *sa) const { inline IP6Addr & IP6Addr::operator&=(IPMask const &mask) { - if (mask._cidr < WORD_WIDTH) { - _addr._store[MSW] &= (~word_type{0} << (WORD_WIDTH - mask._cidr)); + if (0 == mask._cidr) { + _addr._store[LSW] = _addr._store[MSW] = 0; + } else if (mask._cidr < WORD_WIDTH) { + _addr._store[MSW] &= (WORD_MASK << (WORD_WIDTH - mask._cidr)); _addr._store[LSW] = 0; } else if (mask._cidr < WIDTH) { - _addr._store[LSW] &= (~word_type{0} << (2 * WORD_WIDTH - mask._cidr)); + _addr._store[LSW] &= (WORD_MASK << (2 * WORD_WIDTH - mask._cidr)); } return *this; } inline IP6Addr & IP6Addr::operator|=(IPMask const &mask) { - if (mask._cidr < WORD_WIDTH) { - _addr._store[MSW] |= (~word_type{0} >> mask._cidr); - _addr._store[LSW] = ~word_type{0}; + if (0 == mask._cidr) { // do nothing in this case. + } else if (0 < mask._cidr && mask._cidr < WORD_WIDTH) { + _addr._store[MSW] |= (WORD_MASK >> mask._cidr); + _addr._store[LSW] = WORD_MASK; } else if (mask._cidr < WIDTH) { - _addr._store[LSW] |= (~word_type{0} >> (mask._cidr - WORD_WIDTH)); + _addr._store[LSW] |= (WORD_MASK >> (mask._cidr - WORD_WIDTH)); + } else { + _addr._store[LSW] = _addr._store[MSW] = WORD_MASK; } return *this; } @@ -1138,7 +1146,9 @@ inline IP4Addr IPMask::as_ip4() const { static constexpr auto MASK = ~in_addr_t{0}; in_addr_t addr = MASK; - if (_cidr < IP4Addr::WIDTH) { + if (0 == _cidr) { + addr = in_addr_t(0); + } else if (_cidr < IP4Addr::WIDTH) { addr <<= IP4Addr::WIDTH - _cidr; } return IP4Addr{addr}; diff --git a/lib/swoc/include/swoc/IPRange.h b/lib/swoc/include/swoc/IPRange.h index 8102c2c7f88..aa93d17e651 100644 --- a/lib/swoc/include/swoc/IPRange.h +++ b/lib/swoc/include/swoc/IPRange.h @@ -566,11 +566,17 @@ class IP4Net { /// @return @c true if the network is valid, @c false if not. bool is_valid() const; - /// @return THh smallest address in the network. - IP4Addr lower_bound() const; + /// @return Network address - smallest address in the network. + IP4Addr min() const; + + /// @return The largest address in the network. + IP4Addr max() const; + + /// @return Network address. + [[deprecated]] IP4Addr lower_bound() const; /// @return The largest address in the network. - IP4Addr upper_bound() const; + [[deprecated]] IP4Addr upper_bound() const; /// @return The mask for the network. IPMask const &mask() const; @@ -628,11 +634,17 @@ class IP6Net { /// @return @c true if the network is valid, @c false if not. bool is_valid() const; + /// @return Network address - smallest address in the network. + IP6Addr min() const; + + /// @return Largest address in the network. + IP6Addr max() const; + /// @return THh smallest address in the network. - IP6Addr lower_bound() const; + [[deprecated]] IP6Addr lower_bound() const; /// @return The largest address in the network. - IP6Addr upper_bound() const; + [[deprecated]] IP6Addr upper_bound() const; /// @return The mask for the network. IPMask const &mask() const; @@ -698,11 +710,17 @@ class IPNet { /// @return @c true if the network is valid, @c false if not. bool is_valid() const; + /// @return Network address - smallest address in the network. + IPAddr min() const; + + /// @return Largest address in the network. + IPAddr max() const; + /// @return THh smallest address in the network. - IPAddr lower_bound() const; + [[deprecated]] IPAddr lower_bound() const; /// @return The largest address in the network. - IPAddr upper_bound() const; + [[deprecated]] IPAddr upper_bound() const; IPMask::raw_type width() const; @@ -1589,18 +1607,28 @@ IP4Net::is_valid() const { } inline IP4Addr -IP4Net::lower_bound() const { +IP4Net::min() const { return _addr; } inline IP4Addr -IP4Net::upper_bound() const { +IP4Net::max() const { return _addr | _mask; } +inline IP4Addr +IP4Net::lower_bound() const { + return this->min(); +} + +inline IP4Addr +IP4Net::upper_bound() const { + return this->max(); +} + inline IP4Range IP4Net::as_range() const { - return {this->lower_bound(), this->upper_bound()}; + return {this->min(), this->max()}; } inline bool @@ -1633,18 +1661,21 @@ IP6Net::is_valid() const { } inline IP6Addr -IP6Net::lower_bound() const { +IP6Net::min() const { return _addr; } inline IP6Addr -IP6Net::upper_bound() const { +IP6Net::max() const { return _addr | _mask; } +inline IP6Addr IP6Net::lower_bound() const { return this->min(); } +inline IP6Addr IP6Net::upper_bound() const { return this->max(); } + inline IP6Range IP6Net::as_range() const { - return {this->lower_bound(), this->upper_bound()}; + return {this->min(), this->max()}; } inline bool @@ -1676,15 +1707,18 @@ IPNet::is_valid() const { } inline IPAddr -IPNet::lower_bound() const { +IPNet::min() const { return _addr; } inline IPAddr -IPNet::upper_bound() const { +IPNet::max() const { return _addr | _mask; } +inline IPAddr IPNet::lower_bound() const { return this->min(); } +inline IPAddr IPNet::upper_bound() const { return this->max(); } + inline IPMask::raw_type IPNet::width() const { return _mask.width(); @@ -1697,7 +1731,7 @@ IPNet::mask() const { inline IPRange IPNet::as_range() const { - return {this->lower_bound(), this->upper_bound()}; + return {this->min(), this->max()}; } inline IPNet::self_type & @@ -2222,7 +2256,7 @@ template typename std::tuple_element::type get(swoc::IP4Net const &net) { if constexpr (IDX == 0) { - return net.lower_bound(); + return net.min(); } else if constexpr (IDX == 1) { return net.mask(); } @@ -2232,7 +2266,7 @@ template typename std::tuple_element::type get(swoc::IP6Net const &net) { if constexpr (IDX == 0) { - return net.lower_bound(); + return net.min(); } else if constexpr (IDX == 1) { return net.mask(); } @@ -2242,7 +2276,7 @@ template typename std::tuple_element::type get(swoc::IPNet const &net) { if constexpr (IDX == 0) { - return net.lower_bound(); + return net.min(); } else if constexpr (IDX == 1) { return net.mask(); } diff --git a/lib/swoc/include/swoc/Lexicon.h b/lib/swoc/include/swoc/Lexicon.h index 8db0f37b4d8..c81337f633d 100644 --- a/lib/swoc/include/swoc/Lexicon.h +++ b/lib/swoc/include/swoc/Lexicon.h @@ -33,10 +33,24 @@ namespace detail { */ template std::string -what(std::string_view const &fmt, Args &&... args) { +what(std::string_view const &fmt, Args &&...args) { std::string zret; return swoc::bwprint_v(zret, fmt, std::forward_as_tuple(args...)); } + +// Exported because inner classes in template classes cannot be used in partial specialization +// which is required for tuple support. This should be remove next time there is a API changing +// release because tuple access is being deprecated. +template < typename E > struct lexicon_pair_type { + E _value; + TextView _name; + + /// Constructor. + /// @internal Required to make the @c Lexicon constructors work as intended by forbidding + /// construction of this type with only a value. + lexicon_pair_type(E value, TextView name) : _value(value), _name(name) {} +}; + } // namespace detail /// Policy template use to specify the hash function for the integral type of @c Lexicon. @@ -79,8 +93,9 @@ template class Lexicon { public: /// An association of an enumeration value and a name. /// @ note Used for initializer lists that have just a primary value. - using Pair = std::tuple; + using Pair = detail::lexicon_pair_type; + // Deprecated - use the member names now. /// Index in @c Pair for the enumeration value. static constexpr auto VALUE_IDX = 0; /// Index in @c Pair for name. @@ -95,7 +110,7 @@ template class Lexicon { * without a destructor being called. Unfortunately this can't be done any better without * imposing memory management costs on normal use. */ - using UnknownValueHandler = std::function; + using UnknownValueHandler = std::function; /** A function to be called if a name is not found, to provide a default value. * @param name The name @@ -103,23 +118,27 @@ template class Lexicon { * * The @a name is provided and a value in the enumeration type is expected. */ - using UnknownNameHandler = std::function; + using UnknownNameHandler = std::function; /** A default handler. * * This handles providing a default value or name for a missing name or value. */ - using DefaultHandler = std::variant; + using Default = std::variant; /// Element of an initializer list that contains secondary names. + /// @note This is used only in the constructor and contains transient data. struct Definition { - const E &value; ///< Value for definition. - const std::initializer_list &names; ///< Primary then secondary names. + const E &value; ///< Value for definition. + std::initializer_list const &names; ///< Primary then secondary names. }; /// Construct empty instance. Lexicon(); + using with = std::initializer_list const &; + using with_multi = std::initializer_list const &; + /** Construct with names, possible secondary values, and optional default handlers. * * @param items A list of initializers, each of which is a name and a list of values. @@ -135,8 +154,7 @@ template class Lexicon { * * @see set_default. */ - explicit Lexicon(const std::initializer_list &items, DefaultHandler handler_1 = DefaultHandler{}, - DefaultHandler handler_2 = DefaultHandler{}); + explicit Lexicon(with_multi items, Default handler_1 = Default{}, Default handler_2 = Default{}); /** Construct with names / value pairs, and optional default handlers. * @@ -151,8 +169,7 @@ template class Lexicon { * * @see set_default. */ - explicit Lexicon(const std::initializer_list &items, DefaultHandler handler_1 = DefaultHandler{}, - DefaultHandler handler_2 = DefaultHandler{}); + explicit Lexicon(with items, Default handler_1 = Default{}, Default handler_2 = Default{}); /** Construct with only default values / handlers. * @@ -164,7 +181,7 @@ template class Lexicon { * * @see set_default. */ - explicit Lexicon(DefaultHandler handler_1, DefaultHandler handler_2 = DefaultHandler{}); + explicit Lexicon(Default handler_1, Default handler_2 = Default{}); Lexicon(self_type &&that) = default; @@ -173,24 +190,24 @@ template class Lexicon { * @param value Value to look up. * @return The name for @a value. */ - std::string_view operator[](E const& value) const; + TextView operator[](E const &value) const; /** Get the value for a @a name. * * @param name Name to look up. * @return The value for the @a name. */ - E operator[](std::string_view const &name) const; + E operator[](TextView const &name) const; /// Define the @a names for a @a value. /// The first name is the primary name. All @a names must be convertible to @c std::string_view. /// lexicon.define(Value, primary, [secondary, ... ]); - template self_type &define(E value, Args &&... names); + template self_type &define(E value, Args &&...names); // These are really for consistency with constructors, they're not expected to be commonly used. /// Define a value and names. /// lexicon.define(Value, { primary, [secondary, ...] }); - self_type &define(E value, const std::initializer_list &names); + self_type &define(E value, const std::initializer_list &names); /** Define a name, value pair. * @@ -230,7 +247,7 @@ template class Lexicon { * - A @c DefaultValueHandler. This is a functor that takes a name as a @c string_view and returns * an enumeration value as the value for any name that is not found. */ - self_type &set_default(DefaultHandler const &handler); + self_type &set_default(Default const &handler); /// Get the number of values with definitions. size_t count() const; @@ -239,11 +256,12 @@ template class Lexicon { /// Common features of container iterators. class base_iterator { using self_type = base_iterator; + public: - using value_type = const Pair; ///< Iteration value. - using pointer = value_type *; ///< Pointer to iteration value. - using reference = value_type &; ///< Reference to iteration value. - using difference_type = ptrdiff_t; ///< Type of difference between iterators. + using value_type = const Pair; ///< Iteration value. + using pointer = value_type *; ///< Pointer to iteration value. + using reference = value_type &; ///< Reference to iteration value. + using difference_type = ptrdiff_t; ///< Type of difference between iterators. using iterator_category = std::bidirectional_iterator_tag; ///< Concepts for iterator. /// Default constructor (invalid iterator) base_iterator() = default; @@ -257,24 +275,23 @@ template class Lexicon { bool operator!=(self_type const &that) const; protected: - base_iterator(Item const * item) : _item(item) {} + explicit base_iterator(Item const *item) : _item(item) {} - const Item *_item{nullptr}; ///< Current location in the container. + const Item *_item{nullptr}; ///< Current location in the container. }; public: - /** Iterator over pairs of values and primary name pairs. * The value type is a @c Pair with the value and name. */ class value_iterator : public base_iterator { using super_type = base_iterator; - using self_type = value_iterator; + using self_type = value_iterator; public: - using value_type = typename super_type::value_type; - using pointer = typename super_type::pointer; - using reference = typename super_type::reference; + using value_type = typename super_type::value_type; + using pointer = typename super_type::pointer; + using reference = typename super_type::reference; /// Default constructor. value_iterator() = default; @@ -301,15 +318,16 @@ template class Lexicon { self_type operator--(int); protected: - value_iterator(const Item *item) : super_type(item) {}; ///< Internal constructor. + value_iterator(const Item *item) : super_type(item){}; ///< Internal constructor. friend Lexicon; }; class name_iterator : public base_iterator { private: - using self_type = name_iterator; + using self_type = name_iterator; using super_type = base_iterator; + public: /// Default constructor. name_iterator() = default; @@ -336,7 +354,7 @@ template class Lexicon { self_type operator--(int); protected: - name_iterator(const Item *item) : super_type(item) {}; ///< Internal constructor. + name_iterator(const Item *item) : super_type(item){}; ///< Internal constructor. friend Lexicon; }; @@ -354,17 +372,29 @@ template class Lexicon { const_iterator end() const; /// Iteration over names - every value/name pair. - name_iterator begin_names() const { return { _by_name.begin() }; } + name_iterator + begin_names() const { + return {_by_name.begin()}; + } /// Iteration over names - every value/name pair. - name_iterator end_names() const { return { _by_name.end() }; } + name_iterator + end_names() const { + return {_by_name.end()}; + } /// @cond INTERNAL // Helper struct to return to enable container iteration for names. struct ByNameHelper { - self_type const & _lexicon; - ByNameHelper(self_type const & self) : _lexicon(self) {} - name_iterator begin() const { return _lexicon.begin_names(); } - name_iterator end() const { return _lexicon.end_names(); } + self_type const &_lexicon; + ByNameHelper(self_type const &self) : _lexicon(self) {} + name_iterator + begin() const { + return _lexicon.begin_names(); + } + name_iterator + end() const { + return _lexicon.end_names(); + } }; /// @endcond @@ -379,7 +409,10 @@ template class Lexicon { * @endcode * @return Temporary. */ - ByNameHelper by_names() const { return { *this }; } + ByNameHelper + by_names() const { + return {*this}; + } protected: /// Handle providing a default name. @@ -399,7 +432,7 @@ template class Lexicon { /// Visitor - literal string. std::string_view - operator()(std::string_view const &name) const { + operator()(TextView const &name) const { return name; } @@ -442,7 +475,7 @@ template class Lexicon { * @param name The name. * */ - Item(E value, std::string_view name); + Item(E value, TextView name); Pair _payload; ///< Enumeration and name. @@ -474,7 +507,7 @@ template class Lexicon { }; /// Copy @a name in to local storage. - std::string_view localize(std::string_view const &name); + TextView localize(TextView const &name); /// Storage for names. MemArena _arena{1024}; @@ -492,7 +525,7 @@ template class Lexicon { // ---- // Item -template Lexicon::Item::Item(E value, std::string_view name) : _payload(value, name) {} +template Lexicon::Item::Item(E value, TextView name) : _payload{value, name} {} /// @cond INTERNAL_DETAIL template @@ -522,13 +555,13 @@ Lexicon::Item::ValueLinkage::prev_ptr(Item *item) -> Item *& { template std::string_view Lexicon::Item::NameLinkage::key_of(Item *item) { - return std::get(item->_payload); + return item->_payload._name; } template E Lexicon::Item::ValueLinkage::key_of(Item *item) { - return std::get(item->_payload); + return item->_payload._value; } template @@ -561,8 +594,7 @@ Lexicon::Item::ValueLinkage::equal(E lhs, E rhs) { template Lexicon::Lexicon() {} -template -Lexicon::Lexicon(const std::initializer_list &items, DefaultHandler handler_1, DefaultHandler handler_2) { +template Lexicon::Lexicon(with_multi items, Default handler_1, Default handler_2) { for (auto const &item : items) { this->define(item.value, item.names); } @@ -572,8 +604,7 @@ Lexicon::Lexicon(const std::initializer_list &items, DefaultHandl } } -template -Lexicon::Lexicon(const std::initializer_list &items, DefaultHandler handler_1, DefaultHandler handler_2) { +template Lexicon::Lexicon(with items, Default handler_1, Default handler_2) { for (auto const &item : items) { this->define(item); } @@ -583,49 +614,49 @@ Lexicon::Lexicon(const std::initializer_list &items, DefaultHandler han } } -template Lexicon::Lexicon(DefaultHandler handler_1, DefaultHandler handler_2) { +template Lexicon::Lexicon(Default handler_1, Default handler_2) { for (auto &&h : {handler_1, handler_2}) { this->set_default(h); } } template -std::string_view -Lexicon::localize(std::string_view const &name) { +TextView +Lexicon::localize(TextView const &name) { auto span = _arena.alloc_span(name.size()); memcpy(span, name); - return { span.data(), span.size() }; + return {span.data(), span.size()}; } template -std::string_view -Lexicon::operator[](E const& value) const { - if ( auto spot = _by_value.find(value) ; spot != _by_value.end()) { - return std::get(spot->_payload); +TextView +Lexicon::operator[](E const &value) const { + if (auto spot = _by_value.find(value); spot != _by_value.end()) { + return spot->_payload._name; } return std::visit(NameDefaultVisitor{value}, _name_default); } template E -Lexicon::operator[](std::string_view const &name) const { - if ( auto spot = _by_name.find(name) ; spot != _by_name.end()) { - return std::get(spot->_payload); +Lexicon::operator[](TextView const &name) const { + if (auto spot = _by_name.find(name); spot != _by_name.end()) { + return spot->_payload._value; } return std::visit(ValueDefaultVisitor{name}, _value_default); } template auto -Lexicon::define(E value, const std::initializer_list &names) -> self_type & { +Lexicon::define(E value, const std::initializer_list &names) -> self_type & { if (names.size() < 1) { throw std::invalid_argument("A defined value must have at least a primary name"); } - for (auto const& name : names) { + for (auto const &name : names) { if (_by_name.find(name) != _by_name.end()) { throw std::invalid_argument(detail::what("Duplicate name '{}' in Lexicon", name)); } - auto i = new Item(value, this->localize(name)); + auto i = _arena.make(value, this->localize(name)); _by_name.insert(i); // Only put primary names in the value table. if (_by_value.find(value) == _by_value.end()) { @@ -638,7 +669,7 @@ Lexicon::define(E value, const std::initializer_list &names template template auto -Lexicon::define(E value, Args &&... names) -> self_type & { +Lexicon::define(E value, Args &&...names) -> self_type & { static_assert(sizeof...(Args) > 0, "A defined value must have at least a primary name"); return this->define(value, {std::forward(names)...}); } @@ -646,7 +677,7 @@ Lexicon::define(E value, Args &&... names) -> self_type & { template auto Lexicon::define(const Pair &pair) -> self_type & { - return this->define(std::get(pair), {std::get(pair)}); + return this->define(pair._value, pair._name); } template @@ -657,7 +688,7 @@ Lexicon::define(const Definition &init) -> self_type & { template auto -Lexicon::set_default(DefaultHandler const &handler) -> self_type & { +Lexicon::set_default(Default const &handler) -> self_type & { switch (handler.index()) { case 0: break; @@ -813,4 +844,33 @@ bwformat(BufferWriter &w, bwf::Spec const &spec, Lexicon const &lex) { return w; } -}} // namespace swoc::SWOC_VERSION_NS +}} // namespace swoc + +namespace std { + +template class tuple_element> { + static_assert("swoc::Lexicon::Pair tuple index out of range"); +}; + +template class tuple_element<0, swoc::detail::lexicon_pair_type> { +public: + using type = E; +}; + +template class tuple_element<1, swoc::detail::lexicon_pair_type> { +public: + using type = swoc::TextView; +}; + +template +auto +get(swoc::detail::lexicon_pair_type const &p) -> typename std::tuple_element>::type { + if constexpr (IDX == 0) { + return p._value; + } else if constexpr (IDX == 1) { + return p._name; + } +} + +} // namespace std + diff --git a/lib/swoc/include/swoc/MemArena.h b/lib/swoc/include/swoc/MemArena.h index 13423937286..eabd0f17dfd 100644 --- a/lib/swoc/include/swoc/MemArena.h +++ b/lib/swoc/include/swoc/MemArena.h @@ -7,6 +7,10 @@ #pragma once +#include "swoc/MemSpan.h" +#include "swoc/Scalar.h" +#include "swoc/IntrusiveDList.h" + #include #include #include @@ -15,10 +19,6 @@ #include #endif -#include "swoc/MemSpan.h" -#include "swoc/Scalar.h" -#include "swoc/IntrusiveDList.h" - namespace swoc { inline namespace SWOC_VERSION_NS { /** A memory arena. @@ -38,9 +38,9 @@ class MemArena public: static constexpr size_t DEFAULT_ALIGNMENT{1}; ///< Default memory alignment. - /// Functor for destructing a self contained arena. - /// @see MemArena::unique_ptr - static inline auto destroyer = std::destroy_at; + /// Convenient alias for use with @c unique_ptr. + /// @internal Can't be @c inline because the instantiation requires a complete type. + static void (*destroyer)(self_type *); /// Correct type for a unique pointer to an instance. /// Initialization is diff --git a/lib/swoc/include/swoc/MemSpan.h b/lib/swoc/include/swoc/MemSpan.h index 6c5711f2356..8b71e0cc027 100644 --- a/lib/swoc/include/swoc/MemSpan.h +++ b/lib/swoc/include/swoc/MemSpan.h @@ -9,6 +9,9 @@ #pragma once +#include "swoc/swoc_version.h" +#include "swoc/Scalar.h" + #include #include #include @@ -21,9 +24,6 @@ #include #include -#include "swoc/swoc_version.h" -#include "swoc/Scalar.h" - namespace swoc { inline namespace SWOC_VERSION_NS { /** A span of contiguous piece of memory. diff --git a/lib/swoc/include/swoc/TextView.h b/lib/swoc/include/swoc/TextView.h index ce8240255a2..baad8f233d0 100644 --- a/lib/swoc/include/swoc/TextView.h +++ b/lib/swoc/include/swoc/TextView.h @@ -19,6 +19,7 @@ #include #include #include +#include #include "swoc/swoc_version.h" #include "swoc/string_view_util.h" @@ -78,8 +79,23 @@ class TextView : public std::string_view { * @param last End of half open range. * * The character at @a first will be in the view, but the character at @a last will not. + * + * @note @c explicit to avoid interpreting a string initializer list as a view. + * + * @internal For the love of Turing, WHY DID YOU DO THIS? + * + * Well, estemed reader, because the C++ standard doesn't have a better way to support overloads + * that handle character pointers and literal strings differently. If the parameters were simply + * (char const *, char const *) then a cosntruct like { "really", "broken" } can + * be interpreted as a @c TextView because the elements implicitly convert to char const + * *. This makes no sense and creates some @b very annoying ambiguities for lists of strings + * if there are exactly two in the list. See @c Lexicon for an example. + * + * The template itself does the check to make sure it's a character @b pointer and not an array. Arrays + * are handled by a different constructor so this only disables constructing from two char arrays + * which IMHO makes no sense and should be forbidden. */ - constexpr TextView(char const *first, char const *last) noexcept; + template < typename T > explicit TextView(T first, std::enable_if_t && std::is_pointer_v && std::is_convertible_v, T> last) noexcept : super_type(first, last - first) {} /** Construct from any character container following STL standards. * @@ -847,14 +863,25 @@ class TextView : public std::string_view { } }; - /** Get a pointer to past the last byte. + /** A pointer to the first byte. + * + * @return Address of the first byte of the view. + * + * @internal This fixes an error in @c std::string_view where this method is declared to return + * a template parameter instead of the correct @c value_type. The effect is @c string_view::data + * is not considered by the compiler to return char const * which makes meta-programming + * painful. + */ + constexpr value_type const *data() const noexcept; + + /** A pointer to past the last byte. * - * @return The first byte past the end of the view. + * @return Address of the first byte past the end of the view. * * This is effectively @c std::string_view::end() except it explicit returns a pointer and not * (potentially) an iterator class, to match up with @c data(). */ - constexpr char const *data_end() const noexcept; + constexpr value_type const *data_end() const noexcept; /// Specialized stream operator implementation. /// @note Use the standard stream operator unless there is a specific need for this, which is unlikely. @@ -974,7 +1001,6 @@ double svtod(swoc::TextView text, swoc::TextView *parsed = nullptr); // Doxygen doesn't match these up well due to various type and template issues. inline constexpr TextView::TextView(const char *ptr, size_t n) noexcept : super_type(ptr, n == npos ? (ptr ? ::strlen(ptr) : 0) : n) {} -inline constexpr TextView::TextView(char const *first, char const *last) noexcept : super_type(first, last - first) {} inline constexpr TextView::TextView(std::nullptr_t) noexcept : super_type(nullptr, 0) {} inline TextView::TextView(std::string const &str) noexcept : super_type(str) {} inline constexpr TextView::TextView(super_type const &that) noexcept : super_type(that) {} @@ -1471,8 +1497,13 @@ TextView::trim_if(F const &pred) { return this->ltrim_if(pred).rtrim_if(pred); } -constexpr inline char const * -TextView::data_end() const noexcept { +constexpr inline auto +TextView::data() const noexcept -> value_type const* { + return super_type::data(); +} + +constexpr inline auto +TextView::data_end() const noexcept -> value_type const* { return this->data() + this->size(); } diff --git a/lib/swoc/include/swoc/bwf_base.h b/lib/swoc/include/swoc/bwf_base.h index de9e40966d7..f2e69c7bb22 100644 --- a/lib/swoc/include/swoc/bwf_base.h +++ b/lib/swoc/include/swoc/bwf_base.h @@ -129,13 +129,19 @@ struct Spec { * When used by the print formatting logic, there is an abstraction layer, "extraction", which * performs the equivalent of the @c parse method. This allows the formatting to treat * pre-compiled or immediately parsed format strings the same. It also enables formatted print - * support any parser that can deliver literals and @c Spec instances. + * support for any parser that can deliver literals and @c Spec instances. */ -class Format { -public: +struct Format { + using self_type = Format; ///< Self reference type. + /// Construct from a format string @a fmt. Format(TextView fmt); + /// Move constructor. + Format(self_type && that) = default; + /// No copy + Format(self_type const&) = delete; + /// Extraction support for TextView. struct TextViewExtractor { TextView _fmt; ///< Format string. @@ -170,7 +176,7 @@ class Format { /// Extraction support for pre-parsed format strings. struct FormatExtractor { - const std::vector &_fmt; ///< Parsed format string. + MemSpan _fmt; ///< Parsed format string. int _idx = 0; ///< Element index. /// @return @c true if more format string, @c false if none. explicit operator bool() const; @@ -187,6 +193,9 @@ class Format { /// Wrap the format instance in an extractor. FormatExtractor bind() const; + /// @return @c true if all specifiers are literal. + bool is_literal() const; + protected: /// Default constructor for use by subclasses with alternate formatting. Format() = default; diff --git a/lib/swoc/include/swoc/bwf_fwd.h b/lib/swoc/include/swoc/bwf_fwd.h index 59fe601dfca..c07bcf115ad 100644 --- a/lib/swoc/include/swoc/bwf_fwd.h +++ b/lib/swoc/include/swoc/bwf_fwd.h @@ -7,15 +7,24 @@ #pragma once +#include #include "swoc/swoc_version.h" namespace swoc { inline namespace SWOC_VERSION_NS { class BufferWriter; class FixedBufferWriter; -template class LocalBufferWriter; +template class LocalBufferWriter; + +template +std::string & bwprint_v(std::string &s, TextView fmt, std::tuple const &args); + +template +std::string & bwprint(std::string &s, TextView fmt, Args &&... args); namespace bwf { struct Spec; -class Format; +struct Format; +class NameBinding; +class ArgPack; } // namespace bwf }} // namespace swoc diff --git a/lib/swoc/include/swoc/bwf_std.h b/lib/swoc/include/swoc/bwf_std.h index a7fb2bda514..7003e975bea 100644 --- a/lib/swoc/include/swoc/bwf_std.h +++ b/lib/swoc/include/swoc/bwf_std.h @@ -14,23 +14,39 @@ #include "swoc/swoc_version.h" #include "swoc/bwf_base.h" -namespace std { +namespace swoc { inline namespace SWOC_VERSION_NS { + /// Format atomics by stripping the atomic and formatting the underlying type. template -swoc::BufferWriter & -bwformat(swoc::BufferWriter &w, swoc::bwf::Spec const &spec, atomic const &v) { +BufferWriter & +bwformat(BufferWriter &w, bwf::Spec const &spec, std::atomic const &v) { return ::swoc::bwformat(w, spec, v.load()); } -swoc::BufferWriter &bwformat(swoc::BufferWriter &w, swoc::bwf::Spec const &spec, error_code const &ec); +BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, std::error_code const &ec); template -swoc::BufferWriter & -bwformat(swoc::BufferWriter &w, swoc::bwf::Spec const & /* spec */, bitset const &bits) { +BufferWriter & +bwformat(BufferWriter &w, bwf::Spec const & /* spec */, std::bitset const &bits) { for (unsigned idx = 0; idx < N; ++idx) { w.write(bits[idx] ? '1' : '0'); } return w; } -} // end namespace std +template +BufferWriter & +bwformat(BufferWriter &w, bwf::Spec const &spec, std::chrono::duration const &d) +{ + return bwformat(w, spec, d.count()); +} + +template +BufferWriter & +bwformat(BufferWriter &w, bwf::Spec const &spec, std::chrono::time_point const &t) +{ + return bwformat(w, spec, t.time_since_epoch()); +} + + +}} // end namespace swoc diff --git a/lib/swoc/include/swoc/swoc_version.h b/lib/swoc/include/swoc/swoc_version.h index ad671b97827..137945b0036 100644 --- a/lib/swoc/include/swoc/swoc_version.h +++ b/lib/swoc/include/swoc/swoc_version.h @@ -23,11 +23,11 @@ #pragma once #if !defined(SWOC_VERSION_NS) -#define SWOC_VERSION_NS _1_4_9 +#define SWOC_VERSION_NS _1_4_10 #endif namespace swoc { inline namespace SWOC_VERSION_NS { static constexpr unsigned MAJOR_VERSION = 1; static constexpr unsigned MINOR_VERSION = 4; -static constexpr unsigned POINT_VERSION = 9; +static constexpr unsigned POINT_VERSION = 10; }} // namespace swoc::SWOC_VERSION_NS diff --git a/lib/swoc/src/MemArena.cc b/lib/swoc/src/MemArena.cc index 3bfd63685b3..033f209b393 100644 --- a/lib/swoc/src/MemArena.cc +++ b/lib/swoc/src/MemArena.cc @@ -5,11 +5,13 @@ MemArena memory allocator. Chunks of memory are allocated, frozen into generations and thawed away when unused. */ -#include #include "swoc/MemArena.h" +#include namespace swoc { inline namespace SWOC_VERSION_NS { +void (*MemArena::destroyer)(MemArena*) = std::destroy_at; + inline bool MemArena::Block::satisfies(size_t n, size_t align) const { auto r = this->remaining(); @@ -173,9 +175,10 @@ MemArena::require(size_t n, size_t align) { void MemArena::destroy_active() { + auto sb = _static_block; // C++20 nonsense - capture of @a this is incompatible with C++17. _active .apply([=](Block *b) { - if (b != _static_block) + if (b != sb) delete b; }) .clear(); @@ -183,9 +186,10 @@ MemArena::destroy_active() { void MemArena::destroy_frozen() { + auto sb = _static_block; // C++20 nonsense - capture of @a this is incompatible with C++17. _frozen .apply([=](Block *b) { - if (b != _static_block) + if (b != sb) delete b; }) .clear(); diff --git a/lib/swoc/src/bw_format.cc b/lib/swoc/src/bw_format.cc index 0aa735986f8..c2d1c653b90 100644 --- a/lib/swoc/src/bw_format.cc +++ b/lib/swoc/src/bw_format.cc @@ -1,6 +1,6 @@ // SPDX-License-Identifier: Apache-2.0 -// SPDX-License-Identifier: Apache-2.0 // Copyright Apache Software Foundation 2019 + /** @file Formatted output for BufferWriter. @@ -27,7 +27,9 @@ swoc::bwf::ExternalNames swoc::bwf::Global_Names; using swoc::svto_radix; namespace swoc { inline namespace SWOC_VERSION_NS { + namespace bwf { + const Spec Spec::DEFAULT; const Spec::Property Spec::_prop; @@ -625,7 +627,18 @@ Format::Format(TextView fmt) { } } +bool +Format::is_literal() const { + for (auto const& spec : _items) { + if (Spec::LITERAL_TYPE != spec._type) { + return false; + } + } + return true; +} + NameBinding::~NameBinding() {} + } // namespace bwf BufferWriter & @@ -947,16 +960,8 @@ bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::Pattern const &pattern) { return w; } -}} // namespace swoc::SWOC_VERSION_NS - -namespace std { -ostream & -operator<<(ostream &s, swoc::FixedBufferWriter &w) { - return s << w.view(); -} - swoc::BufferWriter & -bwformat(swoc::BufferWriter &w, swoc::bwf::Spec const &spec, error_code const &ec) { +bwformat(swoc::BufferWriter &w, swoc::bwf::Spec const &spec, std::error_code const &ec) { static const auto GENERIC_CATEGORY = &std::generic_category(); static const auto SYSTEM_CATEGORY = &std::system_category(); @@ -979,4 +984,12 @@ bwformat(swoc::BufferWriter &w, swoc::bwf::Spec const &spec, error_code const &e return w; } +}} // namespace swoc::SWOC_VERSION_NS + +namespace std { +ostream & +operator<<(ostream &s, swoc::FixedBufferWriter &w) { + return s << w.view(); +} + } // namespace std diff --git a/lib/swoc/src/bw_ip_format.cc b/lib/swoc/src/bw_ip_format.cc index 101fe0d4241..ca7de50715e 100644 --- a/lib/swoc/src/bw_ip_format.cc +++ b/lib/swoc/src/bw_ip_format.cc @@ -347,7 +347,7 @@ bwformat(BufferWriter &w, Spec const &spec, IPRange const &range) { BufferWriter & bwformat(BufferWriter &w, Spec const &spec, IP4Net const &net) { - bwformat(w, spec, net.lower_bound()); + bwformat(w, spec, net.min()); w.write('/'); bwformat(w, Spec{}, net.mask().width()); return w; @@ -355,7 +355,7 @@ bwformat(BufferWriter &w, Spec const &spec, IP4Net const &net) { BufferWriter & bwformat(BufferWriter &w, Spec const &spec, IP6Net const &net) { - bwformat(w, spec, net.lower_bound()); + bwformat(w, spec, net.min()); w.write('/'); bwformat(w, Spec{}, net.mask().width()); return w; diff --git a/lib/swoc/src/swoc_ip.cc b/lib/swoc/src/swoc_ip.cc index b8c3237dff3..29e7f1d051c 100644 --- a/lib/swoc/src/swoc_ip.cc +++ b/lib/swoc/src/swoc_ip.cc @@ -715,7 +715,9 @@ IPMask::mask_for(IP6Addr const &addr) { IP6Addr IPMask::as_ip6() const { static constexpr auto MASK = ~IP6Addr::word_type{0}; - if (_cidr <= IP6Addr::WORD_WIDTH) { + if (_cidr == 0) { + return { 0, 0 }; + } else if (_cidr <= IP6Addr::WORD_WIDTH) { return {MASK << (IP6Addr::WORD_WIDTH - _cidr), 0}; } else if (_cidr < 2 * IP6Addr::WORD_WIDTH) { return {MASK, MASK << (2 * IP6Addr::WORD_WIDTH - _cidr)};