diff --git a/lib/swoc/include/swoc/Errata.h b/lib/swoc/include/swoc/Errata.h index 5cbf46db1bb..6ee0be5c256 100644 --- a/lib/swoc/include/swoc/Errata.h +++ b/lib/swoc/include/swoc/Errata.h @@ -337,26 +337,38 @@ class Errata { * @param text Text of the message. * @return *this * - * The error code is set to the default. * @a text is localized to @a this and does not need to be persistent. * The severity is updated to @a severity if the latter is more severe. */ self_type ¬e(Severity severity, std::string_view text); + /** Add an @c Annotation to the top with @a text and local @a severity. + * @param severity The local severity. + * @return *this + * + * The annotation uses the format @c AUTOTEXT_SEVERITY with the argument @a severity. + * @see AUTOTEXT_SEVERITY + */ + self_type ¬e(Severity severity); + /** Add an @c Annotation to the top based on error code @a code. * @param code Error code. * @return *this * - * The annotation text is constructed as the short, long, and numeric value of @a code. + * The annotation uses the format @c AUTOTEXT_CODE with the argument @a ec. + * @see AUTOTEXT_CODE + * + * @note @a ec is used only for formatting, the @c Errata error code is unchanged. */ - self_type ¬e(code_type const &code); + self_type ¬e(code_type const &ec); /** Append an @c Annotation to the top based on error code @a code with @a severity. * @param severity Local severity. * @param code Error code. * @return *this * - * The annotation text is constructed as the short, long, and numeric value of @a code. + * The annotation uses the format @c AUTOTEXT_SEVERITY_CODE with the argument @a severity. + * @see AUTOTEXT_SEVERITY_CODE */ self_type ¬e(code_type const &code, Severity severity); @@ -1173,6 +1185,21 @@ Errata::note(Severity severity, std::string_view text) { return this->note_s(severity, text); } +inline Errata & +Errata::note(Severity severity) { + return this->note(severity, AUTOTEXT_SEVERITY, severity); +} + +inline Errata & +Errata::note(code_type const &code) { + return this->note(AUTOTEXT_CODE, code); +} + +inline Errata & +Errata::note(code_type const &ec, Severity severity) { + return this->note(severity, AUTOTEXT_SEVERITY_CODE, severity, ec); +} + template Errata & Errata::note_sv(std::optional severity, std::string_view fmt, std::tuple const &args) { diff --git a/lib/swoc/include/swoc/IPAddr.h b/lib/swoc/include/swoc/IPAddr.h index 50d14afe074..49641299f78 100644 --- a/lib/swoc/include/swoc/IPAddr.h +++ b/lib/swoc/include/swoc/IPAddr.h @@ -6,14 +6,12 @@ #pragma once -#include -#include - #include #include "swoc/swoc_version.h" #include "swoc/swoc_meta.h" #include "swoc/MemSpan.h" +#include "swoc/swoc_ip_util.h" namespace swoc { inline namespace SWOC_VERSION_NS { @@ -836,6 +834,7 @@ IP4Addr::copy_to(sockaddr *sa) const { } /// Equality. + inline bool operator==(IP4Addr const &lhs, IP4Addr const &rhs) { return lhs._addr == rhs._addr; @@ -900,16 +899,12 @@ IP4Addr::is_multicast() const { inline bool IP4Addr::is_link_local() const { - return (_addr & 0xFFFF0000) == 0xA9FE0000; // 169.254.0.0/16 + return ip::is_link_local_host_order(_addr); } inline bool IP4Addr::is_private() const { - return (((_addr & 0xFF000000) == 0x0A000000) || // 10.0.0.0/8 - ((_addr & 0xFFC00000) == 0x64400000) || // 100.64.0.0/10 - ((_addr & 0xFFF00000) == 0xAC100000) || // 172.16.0.0/12 - ((_addr & 0xFFFF0000) == 0xC0A80000) // 192.168.0.0/16 - ); + return ip::is_private_host_order(_addr); } inline uint8_t @@ -1388,14 +1383,12 @@ inline IPAddr::operator IP6Addr() const { inline bool IPAddr::operator==(self_type const &that) const { - switch (_family) { - case AF_INET: - return that._family == AF_INET && _addr._ip4 == that._addr._ip4; - case AF_INET6: - return that._family == AF_INET6 && _addr._ip6 == that._addr._ip6; - default: - return !that.is_valid(); - } + return _family == that.family() && + ( + ( this->is_ip4() && _addr._ip4 == that._addr._ip4 ) || + ( this->is_ip6() && _addr._ip6 == that._addr._ip6 ) || + _family == AF_UNSPEC + ); } inline bool diff --git a/lib/swoc/include/swoc/IPEndpoint.h b/lib/swoc/include/swoc/IPEndpoint.h index edfa707e430..0cfde9ca331 100644 --- a/lib/swoc/include/swoc/IPEndpoint.h +++ b/lib/swoc/include/swoc/IPEndpoint.h @@ -6,15 +6,13 @@ #pragma once -#include -#include - #include #include "swoc/swoc_version.h" #include "swoc/MemSpan.h" #include "swoc/TextView.h" #include "swoc/string_view_util.h" +#include "swoc/swoc_ip_util.h" namespace swoc { inline namespace SWOC_VERSION_NS { @@ -198,6 +196,24 @@ union IPEndpoint { /// @return @c true if this is a loopback address, @c false if not. bool is_loopback() const; + /// @return @c true if the address is in the link local network. + bool is_link_local() const; + + /// @return @c true if the address is in the link local network. + static bool is_link_local(sockaddr const * sa); + + /// @return @c true if the address is private. + bool is_private() const; + + /// @return @c true if the address is private. + static bool is_private(sockaddr const * sa); + + /// @return @c true if the address is multicast. + bool is_multicast() const; + + /// @return @c true if the address is multicast. + static bool is_multicast(sockaddr const * sa); + /** Port in network order. * * @return The port or 0 if not a valid IP address. @@ -217,8 +233,9 @@ union IPEndpoint { static bool is_valid(sockaddr const *sa); /// Direct access to port. - /// @return Refernec to the port in the socket address. + /// @return Reference to the port in the socket address. /// @note If @a sa is not a valid IP address an assertion is thrown. + /// @note The raw port is in network order. /// @a is_valid static in_port_t &port(sockaddr *sa); @@ -237,10 +254,16 @@ union IPEndpoint { static in_port_t host_order_port(sockaddr const *sa); /// Automatic conversion to @c sockaddr. - operator sockaddr *() { return &sa; } + operator sockaddr *(); /// Automatic conversion to @c sockaddr. - operator sockaddr const *() const { return &sa; } + operator sockaddr const *() const; + + /// Size of the sockaddr variant based on the family. + size_t sa_size() const; + + /// Size of the sockaddr based on the family. + static size_t sa_size(sockaddr const* sa); /** The address as a byte sequence. * @@ -401,4 +424,40 @@ IPEndpoint::raw_addr() const { return {}; } +inline bool IPEndpoint::is_link_local() const { + return swoc::ip::is_link_local(&sa); +} + +inline bool IPEndpoint::is_link_local(sockaddr const * sa) { + return swoc::ip::is_link_local(sa); +} + +inline bool IPEndpoint::is_private() const { + return swoc::ip::is_private(&sa); +} + +inline bool IPEndpoint::is_private(sockaddr const * sa) { + return swoc::ip::is_private(sa); +} + +inline bool IPEndpoint::is_multicast() const { + return swoc::ip::is_multicast(&sa); +} + +inline bool IPEndpoint::is_multicast(sockaddr const * sa) { + return swoc::ip::is_multicast(sa); +} + +inline IPEndpoint::operator sockaddr *() { return &sa; } + +inline IPEndpoint::operator sockaddr const *() const { return &sa; } + +inline size_t IPEndpoint::sa_size() const { + return sa_size(&sa); +} + +inline size_t IPEndpoint::sa_size(sockaddr const* sa) { + return AF_INET == sa->sa_family ? sizeof(sockaddr_in) : AF_INET6 == sa->sa_family ? sizeof(sockaddr_in6) : sizeof(sockaddr); +} + }} // namespace swoc::SWOC_VERSION_NS diff --git a/lib/swoc/include/swoc/IPRange.h b/lib/swoc/include/swoc/IPRange.h index 8d079460f16..547afdb694a 100644 --- a/lib/swoc/include/swoc/IPRange.h +++ b/lib/swoc/include/swoc/IPRange.h @@ -432,6 +432,27 @@ class IPRange { */ bool load(std::string_view const &text); + /** Test if an address is in the range. + * + * @param addr Address to test. + * @return @c true if in @a this range, @c false if not. + */ + bool contains(IPAddr const& addr) const; + + /** Test if an address is in the range. + * + * @param addr Address to test. + * @return @c true if in @a this range, @c false if not. + */ + bool contains(IP6Addr const& addr) const; + + /** Test if an address is in the range. + * + * @param addr Address to test. + * @return @c true if in @a this range, @c false if not. + */ + bool contains(IP4Addr const& addr) const; + /// @return The minimum address in the range. IPAddr min() const; @@ -445,22 +466,13 @@ class IPRange { self_type &clear(); /// @return The IPv4 range. - IP4Range const & - ip4() const { - return _range._ip4; - } + IP4Range const & ip4() const; /// @return The IPv6 range. - IP6Range const & - ip6() const { - return _range._ip6; - } + IP6Range const & ip6() const; /// @return The range family. - sa_family_t - family() const { - return _family; - } + sa_family_t family() const; /** Compute the mask for @a this as a network. * @@ -642,6 +654,27 @@ class IPRangeView { */ bool is(sa_family_t family) const; + /** Test if an address is in the range. + * + * @param addr Address to test. + * @return @c true if in @a this range, @c false if not. + */ + bool contains(IPAddr const& addr) const; + + /** Test if an address is in the range. + * + * @param addr Address to test. + * @return @c true if in @a this range, @c false if not. + */ + bool contains(IP6Addr const& addr) const; + + /** Test if an address is in the range. + * + * @param addr Address to test. + * @return @c true if in @a this range, @c false if not. + */ + bool contains(IP4Addr const& addr) const; + /// @return Reference to the viewed IPv4 range. IP4Range const &ip4() const; @@ -1933,6 +1966,17 @@ IPRange::is_ip6() const { return AF_INET6 == _family; } +inline sa_family_t IPRange::family() const { + return _family; +} +inline IP4Range const& IPRange::ip4() const { + return _range._ip4; +} + +inline IP6Range const& IPRange::ip6() const { + return _range._ip6; +} + inline auto IPRangeView::clear() -> self_type & { _family = AF_UNSPEC; @@ -1959,6 +2003,26 @@ IPRangeView::is(sa_family_t f) const { return f == _family; } +inline bool IPRange::contains(IPAddr const & addr) const { + if (addr.family() != _family) { + return false; + } + if (this->ip4()) { + return _range._ip4.contains(addr.ip4()); + } else if (this->is_ip6()) { + return _range._ip6.contains(addr.ip6()); + } + return false; +} + +inline bool IPRange::contains(IP6Addr const & addr) const { + return this->is_ip6() && _range._ip6.contains(addr); +} + +inline bool IPRange::contains(IP4Addr const & addr) const { + return this->is_ip4() && _range._ip4.contains(addr); +} + inline IP4Range const & IPRangeView::ip4() const { return *_raw._4; @@ -2003,6 +2067,24 @@ IPRangeView::max() const { return AF_INET == _family ? _raw._4->max() : AF_INET6 == _family ? _raw._6->max() : IPAddr::INVALID; } +inline bool IPRangeView::contains(IPAddr const& addr) const { + if (_family != addr.family()) { + return false; + } + return (_family == addr.family() ) && + ( ( this->is_ip4() && _raw._4->contains(addr.ip4()) ) || + ( this->is_ip6() && _raw._6->contains(addr.ip6()) ) + ); +} + +inline bool IPRangeView::contains(IP6Addr const& addr) const { + return this->is_ip6() && _raw._6->contains(addr); +} + +inline bool IPRangeView::contains(IP4Addr const& addr) const { + return this->is_ip4() && _raw._4->contains(addr); +} + // +++ IPNet +++ inline IP4Net::IP4Net(swoc::IP4Addr addr, swoc::IPMask mask) : _addr(addr & mask), _mask(mask) {} diff --git a/lib/swoc/include/swoc/IPSrv.h b/lib/swoc/include/swoc/IPSrv.h index de2f5e8f145..d4f245b9e75 100644 --- a/lib/swoc/include/swoc/IPSrv.h +++ b/lib/swoc/include/swoc/IPSrv.h @@ -85,29 +85,29 @@ class IP4Srv { */ bool load(swoc::TextView text); - /** Change the address. + /** Assign an IPv4 address. * * @param addr Address to assign. * @return @a this */ self_type &assign(IP4Addr const &addr); - /** Change the host_order_port. + /** Assign a port. * - * @param port Port to assign. + * @param port Port to assign (host order) * @return @a this. */ self_type &assign(in_port_t port); - /** Change the address and port. + /** Assign an address and port. * * @param addr Address to assign. - * @param port Port to assign. + * @param port Port to assign (host order). * @return @a this */ self_type &assign(IP4Addr const &addr, in_port_t port); - /** Change the address and port. + /** Assign an address and port from an IPv4 socket address. * * @param s A socket address. * @return @a this @@ -116,7 +116,7 @@ class IP4Srv { protected: IP4Addr _addr; ///< Address. - in_port_t _port = 0; ///< Port. + in_port_t _port = 0; ///< Port [host order]. }; /// An IPv6 address and host_order_port, modeled on an SRV type for DNS. @@ -193,17 +193,17 @@ class IP6Srv { */ self_type &assign(IP6Addr const &addr); - /** Change the host_order_port. + /** Assign a port. * - * @param port Port to assign. + * @param port Port [host order]. * @return @a this. */ self_type &assign(in_port_t port); - /** Change the address and host_order_port. + /** Assign an address and port. * - * @param addr Address to assign. - * @param port Port to assign. + * @param addr Address. + * @param port Port [host order]. * @return @a this */ self_type &assign(IP6Addr const &addr, in_port_t port); @@ -217,7 +217,7 @@ class IP6Srv { protected: IP6Addr _addr; ///< Address. - in_port_t _port = 0; ///< Port. + in_port_t _port = 0; ///< Port [host order] }; /// An IP address and host_order_port, modeled on an SRV type for DNS. @@ -228,11 +228,17 @@ class IPSrv { public: IPSrv() = default; ///< Default constructor. explicit IPSrv(IP4Addr addr, in_port_t port = 0) : _srv(IP4Srv{addr, port}), _family(addr.family()) {} + /// Construct for IPv6 address and port. explicit IPSrv(IP6Addr addr, in_port_t port = 0) : _srv(IP6Srv{addr, port}), _family(addr.family()) {} + /// Construct from generic address and port. explicit IPSrv(IPAddr addr, in_port_t port = 0); + /// Construct from socket address. explicit IPSrv(sockaddr const *sa); + /// Construct IPv4 service from socket address. explicit IPSrv(sockaddr_in const *s); + /// Construct IPv6 service from socket address. explicit IPSrv(sockaddr_in6 const *s); + /// Construct from Endpoint. explicit IPSrv(IPEndpoint const &ep); /** Construct from a string. @@ -259,21 +265,17 @@ class IPSrv { /// @return The protocol of the current value. constexpr sa_family_t family() const; + /// @return @c true if this is a valid service, @c false if not. + bool is_valid() const; /// @return @c true if the data is IPv4, @c false if not. bool is_ip4() const; /// @return @c true if hte data is IPv6, @c false if not. bool is_ip6() const; /// @return The IPv4 data. - IP4Srv const & - ip4() const { - return _srv._ip4; - } + IP4Srv const & ip4() const; /// @return The IPv6 data. - IP6Srv const & - ip6() const { - return _srv._ip6; - } + IP6Srv const & ip6() const; /** Change the address. * @@ -289,88 +291,91 @@ class IPSrv { */ self_type &assign(IP6Addr const &addr); - /** Change the address. + /** Assign an address. * - * @param addr Address to assign. + * @param addr Address. * @return @a this * - * If @a addr isn't valid then no assignment is made. + * If @a addr isn't valid then no assignment is made, otherwise the family is changed to that of + * @a addr. */ self_type &assign(IPAddr const &addr); - /** Change the host_order_port. + /** Assign port. * - * @param port Port in host order. + * @param port Port [host order]. * @return @a this. */ self_type &assign(in_port_t port); - /** Change the address and port. + /** Assign an IPv4 address and port. * - * @param addr Address to assign. - * @param port Port to assign. + * @param addr Address. + * @param port Port [host order]. * @return @a this */ self_type &assign(IP4Addr const &addr, in_port_t port); - /** Change the address and port. + /** Assign an IPv6 address and port. * - * @param addr Address to assign. - * @param port Port to assign. + * @param addr Address. + * @param port Port [host order]. * @return @a this */ self_type &assign(IP6Addr const &addr, in_port_t port); - /** Change the address and port. + /** Assogm address amd [prt/ * * @param sa Socket address. * @return @a this + * + * The assignment is ignored if @a sa is not a valid IP family, otherwise the family is changed + * to that of @a sa. */ self_type &assign(sockaddr const *sa); - /** Change the address and port. + /** Assign an IPv4 address and port. * * @param s Socket address. * @return @a this */ self_type &assign(sockaddr_in const *s); - /** Change the address and port. + /** Assign an IPv6 address and port. * * @param s Socket address. * @return @a this */ self_type &assign(sockaddr_in6 const *s); - /** Change the address and host_order_port. + /** Assign an address and port. * - * @param addr Address to assign. - * @param port Port to assign. + * @param addr Address. + * @param port Port [host order]. * @return @a this * - * If @a addr isn't valid then no assignment is made. + * If @a addr isn't valid then no assignment is made, otherwise the family is changed to match + * @a addr. */ self_type &assign(IPAddr const &addr, in_port_t port); + /// Copy assignment. self_type &operator=(self_type const &that) = default; + /// Assign from IPv4. self_type &operator=(IP4Srv const &that); + /// Assign from IPv6. self_type &operator=(IP6Srv const &that); - self_type & - operator=(sockaddr const *sa) { - return this->assign(sa); - } - self_type & - operator=(sockaddr_in const *s) { - return this->assign(s); - } - self_type & - operator=(sockaddr_in6 const *s) { - return this->assign(s); - } + /// Assign from generic socket address. + self_type & operator=(sockaddr const *sa); + /// Assign from IPv4 socket address. + self_type & operator=(sockaddr_in const *s); + /// Assign from IPv6 socket address. + self_type & operator=(sockaddr_in6 const *s); protected: /// Family specialized data. union data { + std::monostate _nil; ///< Nil / invalid state. IP4Srv _ip4; ///< IPv4 address (host) IP6Srv _ip6; ///< IPv6 address (host) @@ -555,19 +560,42 @@ inline IPAddr IPSrv::addr() const { return _srv.addr(_family); } + inline constexpr sa_family_t IPSrv::family() const { return _family; } + +inline bool IPSrv::is_valid() const { return AF_INET == _family || AF_INET6 == _family; } + inline bool IPSrv::is_ip4() const { return _family == AF_INET; } + inline bool IPSrv::is_ip6() const { return _family == AF_INET6; } +inline IP4Srv const& IPSrv::ip4() const { + return _srv._ip4; +} + +inline IP6Srv const& IPSrv::ip6() const { + return _srv._ip6; +} + +inline constexpr in_port_t +IPSrv::host_order_port() const { + return _srv.port(_family); +} + +inline in_port_t +IPSrv::network_order_port() const { + return ntohs(_srv.port(_family)); +} + inline auto IPSrv::assign(IP6Addr const &addr) -> self_type & { _srv._ip6.assign(addr, this->host_order_port()); @@ -656,14 +684,16 @@ IPSrv::assign(sockaddr_in6 const *s) -> self_type & { return *this; } -inline constexpr in_port_t -IPSrv::host_order_port() const { - return _srv.port(_family); +inline IPSrv::self_type & IPSrv::operator=(sockaddr const *sa) { + return this->assign(sa); } -inline in_port_t -IPSrv::network_order_port() const { - return ntohs(_srv.port(_family)); +inline IPSrv::self_type & IPSrv::operator=(sockaddr_in const *s) { + return this->assign(s); +} + +inline IPSrv::self_type & IPSrv::operator=(sockaddr_in6 const *s) { + return this->assign(s); } inline IPAddr @@ -675,6 +705,7 @@ constexpr inline in_port_t IPSrv::data::port(sa_family_t f) const { return (f == AF_INET) ? _ip4.host_order_port() : (f == AF_INET6) ? _ip6.host_order_port() : 0; } + // --- Independent comparisons. inline bool diff --git a/lib/swoc/include/swoc/MemArena.h b/lib/swoc/include/swoc/MemArena.h index b699e607b68..e0b25fb2e11 100644 --- a/lib/swoc/include/swoc/MemArena.h +++ b/lib/swoc/include/swoc/MemArena.h @@ -7,10 +7,6 @@ #pragma once -#include "swoc/MemSpan.h" -#include "swoc/Scalar.h" -#include "swoc/IntrusiveDList.h" - #include #include #include @@ -19,7 +15,13 @@ #include #endif +#include "swoc/MemSpan.h" +#include "swoc/Scalar.h" +#include "swoc/IntrusiveDList.h" +#include "swoc/TextView.h" + namespace swoc { inline namespace SWOC_VERSION_NS { + /** A memory arena. The intended use is for allocating many small chunks of memory - few, large allocations are best @@ -65,6 +67,12 @@ class MemArena /// Get the start of the data in this block. const char *data() const; + /// @return The first byte past allocated storage. + char *allocated_data_end(); + + /// @return The first byte past allocated storage. + const char *allocated_data_end() const; + /// Amount of unallocated storage. size_t remaining() const; @@ -281,6 +289,40 @@ class MemArena */ template T *make(Args &&...args); + /** Copy the contents of a string view into the arena. + * + * @param s Original string. + * @return A view of the copy of @a s. + */ + MemSpan localize(MemSpan s); + + /** Copy the contents of a string view into the arena. + * + * @param s Original string. + * @return A view of the copy of @a s. + */ + MemSpan localize(char const * s); + + /** Copy the contents of a string view into the arena as a C string. + * + * @param s Original string. + * @return A view of the copy of @a s. + * + * A terminating nul character is added to the copy which is not included in the returned view. + * This enables using the string view as a C string. + */ + MemSpan localize_c(MemSpan s); + + /** Copy the contents of a string view into the arena as a C string. + * + * @param s Original string. + * @return A view of the copy of @a s. + * + * A terminating nul character is added to the copy which is not included in the returned view. + * This enables using the string view as a C string. + */ + MemSpan localize_c(char const * s); + /** Freeze reserved memory. All internal memory blocks are frozen and will not be involved in future allocations. @@ -315,6 +357,25 @@ class MemArena */ MemArena &clear(size_t hint = 0); + /** Best effort allocation discard. + * + * The allocation is discard (become unallocated memory) if and only if it is at the end of + * a recent allocation block. If nothing has been allocated, this always works. Otherwise if later + * allocation exists, this method silently fails. This can work with multiple allocations in a + * stack - if all later allocations are discarded when this method is invoked it will succeed. + * + * @note This is purely a performance enhancement to enable computing with internally allocated + * but unused memory. Compare with @c remnant + * + * @note No destruction or other clean up is done, the span is simply marked as unallocated. + * + * @param hint Size hint for the next internal allocation. + * @return @a this. + * + * @see remnant + */ + MemArena &discard(MemSpan span); + /** Discard all allocations. * * All active internal memory blocks are reset to be empty, discarding any allocations. These blocks @@ -421,11 +482,10 @@ class MemArena static constexpr size_t DEFAULT_BLOCK_SIZE = Page::SCALE - Paragraph{round_up(ALLOC_HEADER_SIZE + sizeof(Block))}; size_t _active_allocated = 0; ///< Total allocations in the active generation. - size_t _active_reserved = 0; ///< Total current reserved memory. + size_t _active_reserved = 0; ///< Total reserved memory (allocated from OS). /// Total allocations in the previous generation. This is only non-zero while the arena is frozen. size_t _frozen_allocated = 0; - /// Total frozen reserved memory. - size_t _frozen_reserved = 0; + size_t _frozen_reserved = 0; ///< Total frozen reserved memory. /// Minimum free space needed in the next allocated block. /// This is not zero iff @c reserve was called. @@ -531,6 +591,16 @@ MemArena::Block::data() const { return reinterpret_cast(this + 1); } +inline char * +MemArena::Block::allocated_data_end() { + return this->data() + allocated; +} + +inline const char * +MemArena::Block::allocated_data_end() const { + return this->data() + allocated; +} + inline bool MemArena::Block::contains(const void *ptr) const { const char *base = this->data(); @@ -602,6 +672,27 @@ MemArena::make(Args &&...args) { return new (this->alloc(sizeof(T), alignof(T)).data()) T(std::forward(args)...); } +inline MemSpan MemArena::localize(MemSpan s) { + auto span = this->alloc(s.size()).rebind(); + memcpy(span.data(), s.data(), span.size()); + return { span.data(), span.size() }; +} + +inline MemSpan MemArena::localize(char const * s) { + return this->localize(MemSpan(s, strlen(s))); +} + +inline MemSpan MemArena::localize_c(MemSpan s) { + auto span = this->alloc(s.size() + 1).rebind(); + memcpy(span.data(), s.data(), span.size()); + span[s.size()] = '\0'; + return { span.data(), span.size() }; +} + +inline MemSpan MemArena::localize_c(char const * s) { + return this->localize_c(MemSpan(s, strlen(s))); +} + template MemSpan MemArena::remnant_span(size_t n) { diff --git a/lib/swoc/include/swoc/TextView.h b/lib/swoc/include/swoc/TextView.h index 05b224a0768..01ddb659cd1 100644 --- a/lib/swoc/include/swoc/TextView.h +++ b/lib/swoc/include/swoc/TextView.h @@ -1057,15 +1057,15 @@ uintmax_t svtou(TextView src, TextView *parsed = nullptr, int base = 0); template uintmax_t svto_radix(TextView &src) { - static_assert(0 < RADIX && RADIX <= 36, "Radix must be in the range 1..36"); + static_assert(1 <= RADIX && RADIX <= 36, "Radix must be in the range 2..36"); static constexpr auto MAX = std::numeric_limits::max(); static constexpr auto OVERFLOW_LIMIT = MAX / RADIX; uintmax_t zret = 0; - int8_t v; + uintmax_t v; while (src.size() && (0 <= (v = swoc::svtoi_convert[uint8_t(*src)])) && v < RADIX) { // Tweaked for performance - need to check range after @a RADIX multiply. ++src; // Update view iff the character is parsed. - if (zret <= OVERFLOW_LIMIT && uintmax_t(v) <= MAX - (zret *= RADIX)) { + if (zret <= OVERFLOW_LIMIT && v <= (MAX - (zret *= RADIX)) ) { zret += v; } else { zret = MAX; // clamp to max - once set will always hit this case for subsequent input. diff --git a/lib/swoc/include/swoc/swoc_ip_util.h b/lib/swoc/include/swoc/swoc_ip_util.h new file mode 100644 index 00000000000..78016bf0b89 --- /dev/null +++ b/lib/swoc/include/swoc/swoc_ip_util.h @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Network Geographics 2014 +/** @file + Shared utilities for IP address classes. + */ + +#pragma once +#include + +#include "swoc/swoc_version.h" + +// These have to be global namespace, unfortunately. +inline bool +operator==(in6_addr const& lhs, in6_addr const& rhs) { + return 0 == memcmp(&lhs, &rhs, sizeof(in6_addr)); +} + +inline bool +operator!=(in6_addr const& lhs, in6_addr const& rhs) { + return 0 != memcmp(&lhs, &rhs, sizeof(in6_addr)); +} + +namespace swoc { inline namespace SWOC_VERSION_NS { + +/// Internal IP address utilities. +namespace ip { + +inline bool is_loopback_host_order(in_addr_t addr) { + return (addr & 0xFF000000) == 0x7F000000; +} + +inline bool is_link_local_host_order(in_addr_t addr) { + return (addr & 0xFFFF0000) == 0xA9FE0000; // 169.254.0.0/16 +} + +inline bool is_multicast_host_order(in_addr_t addr) { + return IN_MULTICAST(addr); +} + +inline bool is_private_host_order(in_addr_t addr) { + return (((addr & 0xFF000000) == 0x0A000000) || // 10.0.0.0/8 + ((addr & 0xFFC00000) == 0x64400000) || // 100.64.0.0/10 + ((addr & 0xFFF00000) == 0xAC100000) || // 172.16.0.0/12 + ((addr & 0xFFFF0000) == 0xC0A80000) // 192.168.0.0/16 + ); +} + +// There really is no "host order" for IPv6, so only the network order utilities are defined. +// @c IP6Addr uses an idiosyncratic ordering for performance, not really useful to expose to +// clients. + +inline bool is_loopback_network_order(in6_addr const& addr) { + return addr == in6addr_loopback; +} + +inline bool is_multicast_network_order(in6_addr const& addr) { + return addr.s6_addr[0] == 0xFF; +} + +inline bool is_link_local_network_order(in6_addr const& addr) { + return addr.s6_addr[0] == 0xFE && (addr.s6_addr[1] & 0xC0) == 0x80; // fe80::/10 +} + +inline bool is_private_network_order(in6_addr const& addr) { + return (addr.s6_addr[0] & 0xFE) == 0xFC; // fc00::/7 +} + +#if BYTE_ORDER == LITTLE_ENDIAN + +inline bool is_loopback_network_order(in_addr_t addr) { + return (addr & 0xFF) == 0x7F; +} + +inline bool is_private_network_order(in_addr_t addr) { + return (((addr & 0xFF) == 0x0A) || // 10.0.0.0/8 + ((addr & 0xC0FF) == 0x4064) || // 100.64.0.0/10 + ((addr & 0xF0FF) == 0x10AC) || // 172.16.0.0/12 + ((addr & 0xFFFF) == 0xA8C0) // 192.168.0.0/16 + ); +} + +inline bool is_link_local_network_order(in_addr_t addr) { + return (addr & 0xFFFF) == 0xFEA9; // 169.254.0.0/16 +} + +inline bool is_multicast_network_order(in_addr_t addr) { + return (addr & 0xF0) == 0xE0; +} + +#else + +inline bool is_loopback_network_order(in_addr_t addr) { + return is_loopback_host_order(addr); +} + +inline bool is_link_local_network_order(inaddr_t addr) { + return is_link_local_host_order(addr); +} + +inline bool is_private_network_order(in_addr_t addr) { + return is_link_local_host_order(addr); +} + +inline bool is_multicast_network_order(in_addr_t addr) { + return is_multicast_host_order(addr); +} + +#endif + +/** Check if the address in a socket address is a loopback address.. + * @return @c true if so, @c false if not. + */ +inline bool is_loopback(sockaddr const * sa) { + return ( sa->sa_family == AF_INET && is_loopback_network_order(reinterpret_cast(sa)->sin_addr.s_addr)) || + ( sa->sa_family == AF_INET6 && is_loopback_network_order(reinterpret_cast(sa)->sin6_addr)) + ; +} + +/** Check if the address in a socket address is multicast. + * @return @c true if so, @c false if not. + */ +inline bool is_multicast(sockaddr const * sa) { + return ( sa->sa_family == AF_INET && is_multicast_network_order(reinterpret_cast(sa)->sin_addr.s_addr)) || + ( sa->sa_family == AF_INET6 && is_multicast_network_order(reinterpret_cast(sa)->sin6_addr)) + ; +} + +/** Check if the IP address in a socket address is link local. + * @return @c true if link local, @c false if not. + */ +inline bool is_link_local(sockaddr const* sa) { + return ( sa->sa_family == AF_INET && is_link_local_network_order(reinterpret_cast(sa)->sin_addr.s_addr)) || + ( sa->sa_family == AF_INET6 && is_link_local_network_order(reinterpret_cast(sa)->sin6_addr)) + ; +} + +/** Check if the IP address in a socket address is private (non-routable) + * @return @c true if private, @c false if not. + */ +inline bool is_private(sockaddr const* sa) { + return ( sa->sa_family == AF_INET && is_private_network_order(reinterpret_cast(sa)->sin_addr.s_addr)) || + ( sa->sa_family == AF_INET6 && is_private_network_order(reinterpret_cast(sa)->sin6_addr)) + ; +} + +} // hnamespace ip + +}} // namespace swoc::SWOC_VERSION_NS diff --git a/lib/swoc/include/swoc/swoc_version.h b/lib/swoc/include/swoc/swoc_version.h index e975e2ea2e0..c51c2a4481e 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_5_8 +#define SWOC_VERSION_NS _1_5_9 #endif namespace swoc { inline namespace SWOC_VERSION_NS { static constexpr unsigned MAJOR_VERSION = 1; static constexpr unsigned MINOR_VERSION = 5; -static constexpr unsigned POINT_VERSION = 8; +static constexpr unsigned POINT_VERSION = 9; }} // namespace swoc::SWOC_VERSION_NS diff --git a/lib/swoc/src/Errata.cc b/lib/swoc/src/Errata.cc index 3b78c89307f..4ffd875f23f 100644 --- a/lib/swoc/src/Errata.cc +++ b/lib/swoc/src/Errata.cc @@ -61,16 +61,6 @@ Errata::sink() { return *this; } -Errata & -Errata::note(code_type const &code) { - return this->note("{}"_sv, code); -} - -Errata & -Errata::note(code_type const &code, Severity severity) { - return this->note(severity, "{}"_sv, code); -} - Errata::Data * Errata::data() { if (!_data) { @@ -80,6 +70,11 @@ Errata::data() { return _data; } +MemSpan +Errata::alloc(size_t n) { + return this->data()->_arena.alloc(n).rebind(); +} + Errata & Errata::note_s(std::optional severity, std::string_view text) { if (severity.has_value()) { @@ -101,11 +96,6 @@ Errata::note_localized(std::string_view const &text, std::optional sev return *this; } -MemSpan -Errata::alloc(size_t n) { - return this->data()->_arena.alloc(n).rebind(); -} - Errata & Errata::note(const self_type &that) { if (that._data) { diff --git a/lib/swoc/src/MemArena.cc b/lib/swoc/src/MemArena.cc index 54bdbdd4ec2..1d4d708ca3b 100644 --- a/lib/swoc/src/MemArena.cc +++ b/lib/swoc/src/MemArena.cc @@ -209,9 +209,43 @@ MemArena::clear(size_t hint) { return *this; } +MemArena & +MemArena::discard(MemSpan span) { + // This is intended to iterate over empty blocks until @a span is found. + for ( auto & block : _active) { + if (block.contains(span.data())) { // it's in this block, final iteration. + if (block.allocated_data_end() == span.data_end()) { + block.allocated -= span.size(); + _active_allocated -= span.size(); + } + break; + } else if (block.allocated > 0) { + // If the block wasn't empty the only other place + // @a span could be is in the most recent filled block, which is last in the list. + // Invariant - the first block does not contain @a span. + // Therefore, if the last block contains @a span, it is not the first block. + auto lfb = _active.tail(); // list is not empty, must exist. + if (lfb->contains(span.data()) && lfb->allocated_data_end() == span.data_end()) { + lfb->allocated -= span.size(); + _active_allocated -= span.size(); + if (!lfb->is_full()) { + _active.erase(lfb); + _active.prepend(lfb); + } + } + break; // loop always ends after hitting a non-empty block. + } + } + return *this; +} + MemArena & MemArena::discard(size_t hint) { - _reserve_hint = hint ? hint : _frozen_allocated + _active_allocated; + // Because existing blocks remain, clear the reserve hint so then when a new block is allocated + // it uses the allocation size then, not what it is now. Now is handled by the existing blocks, + // unless the caller explicitly provides a hint, + _reserve_hint = hint ? hint : 0; + for (auto &block : _active) { block.discard(); } diff --git a/lib/swoc/src/TextView.cc b/lib/swoc/src/TextView.cc index 4c7710d3f6b..fbfb3431066 100644 --- a/lib/swoc/src/TextView.cc +++ b/lib/swoc/src/TextView.cc @@ -43,6 +43,8 @@ const int8_t svtoi_convert[256] = { intmax_t svtoi(TextView src, TextView *out, int base) { + static constexpr uintmax_t ABS_MAX = std::numeric_limits::max(); + static constexpr uintmax_t ABS_MIN = uintmax_t(std::numeric_limits::min()); intmax_t zret = 0; if (src.ltrim_if(&isspace)) { @@ -55,13 +57,15 @@ svtoi(TextView src, TextView *out, int base) { } else if ('+' == *src) { ++src; } - zret = intmax_t(svtou(src, &parsed, base)); + auto n = svtou(src, &parsed, base); if (!parsed.empty()) { if (out) { out->assign(start, parsed.data_end()); } if (neg) { - zret = -zret; + zret = -intmax_t(std::min(n, ABS_MIN)); + } else { + zret = std::min(n, ABS_MAX); } } } @@ -75,12 +79,9 @@ svtou(TextView src, TextView *out, int base) { if (out) { out->clear(); } - if (!(0 <= base && base <= 36)) { - return 0; - } + if (src.ltrim_if(&isspace).size()) { - auto origin = src.data(); - int8_t v = 0; + auto origin = src.data(); // cache to handle prefix skipping. // If base is 0, it wasn't specified - check for standard base prefixes if (0 == base) { base = 10; @@ -103,6 +104,9 @@ svtou(TextView src, TextView *out, int base) { } } } + if (!(1 <= base && base <= 36)) { + return 0; + } // For performance in common cases, use the templated conversion. switch (base) { @@ -118,17 +122,20 @@ svtou(TextView src, TextView *out, int base) { case 16: zret = svto_radix<16>(src); break; - default: + default: { + static constexpr auto MAX = std::numeric_limits::max(); + const auto OVERFLOW_LIMIT = MAX / base; + intmax_t v = 0; while (src.size() && (0 <= (v = svtoi_convert[static_cast(*src)])) && v < base) { - auto n = zret * base + v; - if (n < zret) { - zret = std::numeric_limits::max(); - break; // overflow, stop parsing. - } - zret = n; ++src; + if (zret <= OVERFLOW_LIMIT && uintmax_t(v) <= (MAX - (zret *= base))) { + zret += v; + } else { + zret = MAX; + } } break; + } } if (out) { diff --git a/lib/swoc/src/swoc_ip.cc b/lib/swoc/src/swoc_ip.cc index 0ddc9bb7e43..64039221587 100644 --- a/lib/swoc/src/swoc_ip.cc +++ b/lib/swoc/src/swoc_ip.cc @@ -41,6 +41,11 @@ IP4Addr const IP4Addr::MAX{INADDR_BROADCAST}; IP6Addr const IP6Addr::MIN{0, 0}; IP6Addr const IP6Addr::MAX{std::numeric_limits::max(), std::numeric_limits::max()}; +IPEndpoint::IPEndpoint(string_view const &text) { + this->invalidate(); + this->parse(text); +} + bool IPEndpoint::assign(sockaddr *dst, sockaddr const *src) { size_t n = 0; @@ -642,9 +647,16 @@ IPAddr::is_multicast() const { return (AF_INET == _family && _addr._ip4.is_multicast()) || (AF_INET6 == _family && _addr._ip6.is_multicast()); } -IPEndpoint::IPEndpoint(string_view const &text) { - this->invalidate(); - this->parse(text); +bool operator == (IPAddr const& lhs, sockaddr const * sa) { + using sa4 = sockaddr_in const *; + using sa6 = sockaddr_in6 const *; + + if (lhs.family() != sa->sa_family) { + return false; + } + return lhs.family() == AF_UNSPEC || + ( lhs.is_ip4() && (lhs.ip4().network_order() == sa4(sa)->sin_addr.s_addr) ) || + ( lhs.is_ip6() && (lhs.ip6().network_order() == sa6(sa)->sin6_addr) ); } bool