diff --git a/CMakeLists.txt b/CMakeLists.txt index da8e02f..af8524b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ set(IPADDRESS_IPV6_SCOPE_MAX_LENGTH "16" CACHE STRING "Maximum scope-id length f project(ipaddress HOMEPAGE_URL "https://github.com/VladimirShaleev/ipaddress" DESCRIPTION "A library for working and manipulating IPv4/IPv6 addresses and networks" - VERSION 1.0.1 + VERSION 1.1.0 LANGUAGES CXX) include(CMakePackageConfigHelpers) @@ -66,7 +66,7 @@ install( install( FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" - DESTINATION "${CMAKE_INSTALL_DATADIR}/pkgconfig" + DESTINATION "${CMAKE_INSTALL_DATADIR}/pkgconfig" ) install(DIRECTORY include/ DESTINATION include) diff --git a/doc/benchmark.md b/doc/benchmark.md index d2a0a24..642acfa 100644 --- a/doc/benchmark.md +++ b/doc/benchmark.md @@ -37,26 +37,26 @@ CPU Caches:
    L1 Instruction 32 KiB (x8)
    L2 Unified 1280 KiB (x8)
    L3 Unified 18432 KiB (x1)
-Load Average: 3.12, 5.12, 6.21 +Load Average: 2.22, 2.16, 1.02 | input string | ipaddress | socket API | boost asio | |:----------------------------------------------- | ---------:| ----------:| ----------:| -| `127.0.0.1` | 12.6 ns | 16.5 ns | 17.8 ns | -| `192.168.0.1` | 14.5 ns | 19.6 ns | 20.8 ns | -| `0.0.0.0` | 9.29 ns | 12.9 ns | 15.4 ns | -| `100.64.0.0` | 11.6 ns | 16.9 ns | 20.7 ns | -| `192.168.1.1` | 12.5 ns | 17.5 ns | 22.6 ns | -| `127.239.0.1` | 12.3 ns | 17.9 ns | 21.6 ns | -| `127.128.128.255` | 17.9 ns | 23.1 ns | 25.6 ns | -| `224.1.1.1` | 11.0 ns | 16.1 ns | 17.8 ns | -| `2001:db8::1` | 57.7 ns | 24.6 ns | 36.4 ns | -| `0001:0002:0003:0004:0005:0006:0007:0008%12345` | 165 ns | - | 73.3 ns | -| `::ffff:172.32.0.0` | 97.7 ns | 40.7 ns | 50.5 ns | -| `2002:ac1d:2d64::1` | 74.4 ns | 38.4 ns | 43.9 ns | -| `2001:0000:4136:e378:8000:63bf:3fff:fdd2` | 130 ns | 53.0 ns | 69.2 ns | -| `2000::4136:e378:8000:63bf:3fff:fdd2` | 119 ns | 58.3 ns | 68.1 ns | -| `2001:db8:0:0:1:0:0:1` | 89.7 ns | 27.7 ns | 38.2 ns | -| `fe80::1ff:fe23:4567:890a%31` | 98.4 ns | - | 1423 ns | +| `127.0.0.1` | 8.76 ns | 11.8 ns | 14.2 ns | +| `192.168.0.1` | 9.81 ns | 13.4 ns | 16.5 ns | +| `0.0.0.0` | 7.80 ns | 9.84 ns | 12.8 ns | +| `100.64.0.0` | 9.32 ns | 13.1 ns | 15.2 ns | +| `192.168.1.1` | 9.82 ns | 13.8 ns | 16.2 ns | +| `127.239.0.1` | 9.81 ns | 13.5 ns | 16.0 ns | +| `127.128.128.255` | 13.1 ns | 18.2 ns | 26.4 ns | +| `224.1.1.1` | 8.84 ns | 11.6 ns | 14.2 ns | +| `2001:db8::1` | 39.3 ns | 20.6 ns | 27.0 ns | +| `0001:0002:0003:0004:0005:0006:0007:0008%12345` | 103 ns | - | 60.4 ns | +| `::ffff:172.32.0.0` | 62.5 ns | 29.8 ns | 38.5 ns | +| `2002:ac1d:2d64::1` | 50.8 ns | 28.3 ns | 36.4 ns | +| `2001:0000:4136:e378:8000:63bf:3fff:fdd2` | 102 ns | 43.5 ns | 53.9 ns | +| `2000::4136:e378:8000:63bf:3fff:fdd2` | 93.0 ns | 45.7 ns | 55.0 ns | +| `2001:db8:0:0:1:0:0:1` | 68.3 ns | 22.0 ns | 29.4 ns | +| `fe80::1ff:fe23:4567:890a%31` | 69.5 ns | - | 1115 ns | @@ -72,26 +72,26 @@ CPU Caches:
    L1 Instruction 32 KiB
    L2 Unified 512 KiB (x4)
    L3 Unified 6144 KiB
-Load Average: 17.22, 9.41, 6.84 +Load Average: 2.70, 4.59, 7.36 | input string | ipaddress | socket API | boost asio | |:----------------------------------------------- | ---------:| ----------:| ----------:| -| `127.0.0.1` | 14.4 ns | 49.2 ns | 57.8 ns | -| `192.168.0.1` | 16.8 ns | 56.3 ns | 64.9 ns | -| `0.0.0.0` | 12.0 ns | 38.7 ns | 48.4 ns | -| `100.64.0.0` | 15.6 ns | 51.8 ns | 60.2 ns | -| `192.168.1.1` | 16.7 ns | 55.9 ns | 63.3 ns | -| `127.239.0.1` | 16.9 ns | 55.8 ns | 63.9 ns | -| `127.128.128.255` | 22.3 ns | 70.7 ns | 80.9 ns | -| `224.1.1.1` | 14.4 ns | 47.8 ns | 57.2 ns | -| `2001:db8::1` | 71.6 ns | 80.2 ns | 95.0 ns | -| `0001:0002:0003:0004:0005:0006:0007:0008%12345` | 194 ns | 311 ns | 292 ns | -| `::ffff:172.32.0.0` | 115 ns | 132 ns | 146 ns | -| `2002:ac1d:2d64::1` | 92.3 ns | 116 ns | 129 ns | -| `2001:0000:4136:e378:8000:63bf:3fff:fdd2` | 187 ns | 265 ns | 267 ns | -| `2000::4136:e378:8000:63bf:3fff:fdd2` | 171 ns | 218 ns | 244 ns | -| `2001:db8:0:0:1:0:0:1` | 115 ns | 142 ns | 159 ns | -| `fe80::1ff:fe23:4567:890a%31` | 131 ns | 44973 ns | 47695 ns | +| `127.0.0.1` | 11.7 ns | 38.4 ns | 44.4 ns | +| `192.168.0.1` | 13.9 ns | 45.3 ns | 50.6 ns | +| `0.0.0.0` | 10.0 ns | 30.8 ns | 35.9 ns | +| `100.64.0.0` | 12.6 ns | 40.9 ns | 45.7 ns | +| `192.168.1.1` | 13.7 ns | 47.1 ns | 48.9 ns | +| `127.239.0.1` | 13.4 ns | 43.7 ns | 50.0 ns | +| `127.128.128.255` | 18.0 ns | 56.4 ns | 61.5 ns | +| `224.1.1.1` | 11.5 ns | 38.8 ns | 43.6 ns | +| `2001:db8::1` | 55.4 ns | 69.0 ns | 77.4 ns | +| `0001:0002:0003:0004:0005:0006:0007:0008%12345` | 200 ns | 250 ns | 241 ns | +| `::ffff:172.32.0.0` | 91.2 ns | 107 ns | 125 ns | +| `2002:ac1d:2d64::1` | 77.6 ns | 101 ns | 111 ns | +| `2001:0000:4136:e378:8000:63bf:3fff:fdd2` | 174 ns | 229 ns | 227 ns | +| `2000::4136:e378:8000:63bf:3fff:fdd2` | 158 ns | 184 ns | 192 ns | +| `2001:db8:0:0:1:0:0:1` | 115 ns | 121 ns | 131 ns | +| `fe80::1ff:fe23:4567:890a%31` | 118 ns | 40403 ns | 41033 ns | @@ -110,22 +110,22 @@ CPU Caches:
| input string | ipaddress | socket API | boost asio | |:----------------------------------------------- | ---------:| ----------:| ----------:| -| `127.0.0.1` | 9.44 ns | 31.8 ns | 219 ns | -| `192.168.0.1` | 11.7 ns | 34.4 ns | 227 ns | -| `0.0.0.0` | 8.07 ns | 26.0 ns | 214 ns | -| `100.64.0.0` | 10.9 ns | 32.0 ns | 226 ns | -| `192.168.1.1` | 12.1 ns | 33.8 ns | 225 ns | -| `127.239.0.1` | 12.5 ns | 34.9 ns | 227 ns | -| `127.128.128.255` | 16.8 ns | 40.5 ns | 235 ns | -| `224.1.1.1` | 10.2 ns | 27.9 ns | 225 ns | -| `2001:db8::1` | 41.8 ns | 91.1 ns | 262 ns | -| `0001:0002:0003:0004:0005:0006:0007:0008%12345` | 125 ns | - | 424 ns | -| `::ffff:172.32.0.0` | 69.4 ns | 135 ns | 300 ns | -| `2002:ac1d:2d64::1` | 57.4 ns | 131 ns | 307 ns | -| `2001:0000:4136:e378:8000:63bf:3fff:fdd2` | 116 ns | 273 ns | 435 ns | -| `2000::4136:e378:8000:63bf:3fff:fdd2` | 101 ns | 214 ns | 338 ns | -| `2001:db8:0:0:1:0:0:1` | 67.7 ns | 169 ns | 321 ns | -| `fe80::1ff:fe23:4567:890a%31` | 71.6 ns | - | 349 ns | +| `127.0.0.1` | 8.57 ns | 29.7 ns | 214 ns | +| `192.168.0.1` | 9.89 ns | 33.9 ns | 217 ns | +| `0.0.0.0` | 8.00 ns | 28.9 ns | 207 ns | +| `100.64.0.0` | 9.57 ns | 32.2 ns | 217 ns | +| `192.168.1.1` | 10.2 ns | 32.3 ns | 221 ns | +| `127.239.0.1` | 10.2 ns | 33.6 ns | 221 ns | +| `127.128.128.255` | 14.6 ns | 38.9 ns | 229 ns | +| `224.1.1.1` | 9.02 ns | 26.9 ns | 215 ns | +| `2001:db8::1` | 38.3 ns | 94.1 ns | 271 ns | +| `0001:0002:0003:0004:0005:0006:0007:0008%12345` | 105 ns | - | 436 ns | +| `::ffff:172.32.0.0` | 61.6 ns | 144 ns | 313 ns | +| `2002:ac1d:2d64::1` | 51.0 ns | 137 ns | 314 ns | +| `2001:0000:4136:e378:8000:63bf:3fff:fdd2` | 104 ns | 275 ns | 448 ns | +| `2000::4136:e378:8000:63bf:3fff:fdd2` | 93.6 ns | 249 ns | 421 ns | +| `2001:db8:0:0:1:0:0:1` | 66.6 ns | 192 ns | 350 ns | +| `fe80::1ff:fe23:4567:890a%31` | 69.6 ns | - | 367 ns | diff --git a/doc/tutorial.md b/doc/tutorial.md index b0738fb..d794413 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -8,7 +8,7 @@ For working with IP addresses, the library provides three classes: - **ipv4_address** — A class for working with IPv4 addresses, where the instance size is always 4 bytes. - **ipv6_address** — A class for working with IPv6 addresses, where the instance size includes both the space allocated for the IPv6 address itself and the scope id (zone index). As a result, the instance size will be 16 bytes plus the maximum length of the scope id (plus aligned bytes if any). Read about scope ids below. -- **ip_address** — Combines the `ipv4_address` and `ipv6_address` classes via a union. This ensures version-independent IP address manipulation. It has implicit constructors for converting from `ipv4_address` and `ipv6_address`. The instance size is the same as that of `ipv6_address`. +- **ip_address** — Combines the `ipv4_address` and `ipv6_address` classes via a union. This ensures version-independent IP address manipulation. It has implicit constructors for converting from `ipv4_address` and `ipv6_address`. The instance size is the same as that of `ipv6_address` plus IP address version (and data alignment). @parblock @note Regardless of which class you use, the internal address is stored as an array of bytes, not as an unsigned integer. This design choice ensures that the address is stored in network byte order (big-endian) regardless of the platform. This is convenient because ultimately, this address can be used in sockets or similar libraries that require passing addresses in network byte order. diff --git a/include/ipaddress/base-v4.hpp b/include/ipaddress/base-v4.hpp index 8ddf373..1ba7e46 100644 --- a/include/ipaddress/base-v4.hpp +++ b/include/ipaddress/base-v4.hpp @@ -58,7 +58,7 @@ class base_v4 { protected: template - IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base ip_from_string(Iter begin, Iter end, error_code& code, int& index) IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base ip_from_string(Iter begin, Iter end, error_code& code, uint32_t& index) IPADDRESS_NOEXCEPT { if (begin == end) { code = error_code::empty_address; return {}; @@ -71,9 +71,15 @@ class base_v4 { index = 0; code = error_code::no_error; + Iter it = begin; + uint32_t error_symbol = 0; - for (auto it = begin; it != end; ++it) { - auto c = char(*it); + while (it < end) { + auto c = internal::next_char_or_error(it, end, code, error_symbol); + if (code != error_code::no_error) { + index = error_symbol; + return {}; + } if (index >= 4) { code = error_code::expected_4_octets; return {}; @@ -169,13 +175,21 @@ class base_v4 { } template - IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::tuple, size_t> parse_netmask(Iter begin, Iter end, error_code& code, int& index) IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::tuple, size_t> parse_netmask(Iter begin, Iter end, error_code& code, uint32_t& code_value) IPADDRESS_NOEXCEPT { + code = error_code::no_error; + code_value = 0; + Iter it = begin; + size_t prefixlen = 0; auto is_value = true; auto has_prefixlen = false; - for (auto it = begin; it != end; ++it) { + + while (it < end) { has_prefixlen = true; - const auto c = char(*it); + const auto c = internal::next_char_or_error(it, end, code, code_value); + if (code != error_code::no_error) { + return std::make_tuple(ip_address_base(), 0); + } if (c >= '0' && c <= '9') { prefixlen = prefixlen * 10 + (c - '0'); } else { @@ -189,7 +203,7 @@ class base_v4 { return std::make_tuple(ip_address_base(), 0); } } else { - auto ip = ip_to_uint32(ip_from_string(begin, end, code, index).bytes()); + auto ip = ip_to_uint32(ip_from_string(begin, end, code, code_value).bytes()); if (code != error_code::no_error) { code = error_code::invalid_netmask; return std::make_tuple(ip_address_base(), 0); diff --git a/include/ipaddress/base-v6.hpp b/include/ipaddress/base-v6.hpp index 45f2835..0c06fe4 100644 --- a/include/ipaddress/base-v6.hpp +++ b/include/ipaddress/base-v6.hpp @@ -60,38 +60,32 @@ class base_v6 { static constexpr size_t _max_parts = 8; template - IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base ip_from_string(Iter begin, Iter end, error_code& code, int& parts_count) IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base ip_from_string(Iter begin, Iter end, error_code& code, uint32_t& value) IPADDRESS_NOEXCEPT { if (begin == end) { code = error_code::empty_address; return {}; } - auto ip_and_scope = split_scope_id(begin, end, code); - end = ip_and_scope.first; + auto ip_and_scope = split_scope_id(begin, end, code, value); if (code != error_code::no_error) { return {}; } - const auto parts = split_parts(begin, end, parts_count, code); + const auto parts = split_parts(begin, ip_and_scope.end_ip, value, code); if (code != error_code::no_error) { return {}; } - const auto result = get_parts_bound(parts, parts_count, code); + const auto result = get_parts_bound(parts, value, code); if (code != error_code::no_error) { return {}; } - auto ip = ip_address_base(parse_parts(parts, parts_count, std::get<0>(result), std::get<1>(result), std::get<2>(result), code)); - - char scope_id[IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1] = {}; - for (size_t i = 0; i < ip_and_scope.second.size(); ++i) { - scope_id[i] = ip_and_scope.second[i]; - } - ip.set_scope_id(scope_id); + auto ip = ip_address_base(parse_parts(parts, value, std::get<0>(result), std::get<1>(result), std::get<2>(result), code)); + ip.set_scope_id(ip_and_scope.scope_id); return ip; } @@ -224,13 +218,16 @@ class base_v6 { } template - IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::tuple, size_t> parse_netmask(Iter begin, Iter end, error_code& code, int& index) IPADDRESS_NOEXCEPT { - index = 0; + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::tuple, size_t> parse_netmask(Iter begin, Iter end, error_code& code, uint32_t& code_value) IPADDRESS_NOEXCEPT { size_t prefixlen = 0; + auto it = begin; auto has_prefixlen = false; - for (auto it = begin; it != end; ++it) { + while (it < end) { has_prefixlen = true; - const auto c = char(*it); + const auto c = internal::next_char_or_error(it, end, code, code_value); + if (code != error_code::no_error) { + return std::make_tuple(ip_address_base(), 0); + } if (c >= '0' && c <= '9') { prefixlen = prefixlen * 10 + (c - '0'); } else { @@ -272,38 +269,47 @@ class base_v6 { private: template - IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::pair> split_scope_id(Iter begin, Iter end, error_code& error) IPADDRESS_NOEXCEPT { - char scope_id[IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1] = {}; + struct ip_and_scope { + Iter end_ip; + char scope_id[IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1]; + }; + + template + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_and_scope split_scope_id(Iter begin, Iter end, error_code& error, uint32_t& error_value) IPADDRESS_NOEXCEPT { auto index = 0; - Iter end_ip = begin; + auto it = begin; auto scope = false; - for (auto it = begin; it != end; ++it) { - const auto c = char(*it); + ip_and_scope result{}; + while (it < end) { + const auto c = internal::next_char_or_error(it, end, error, error_value); + if (error != error_code::no_error) { + return result; + } if (!scope && c != '%') { - end_ip = it + 1; + result.end_ip = it; } else if (scope) { if (index > IPADDRESS_IPV6_SCOPE_MAX_LENGTH - 1) { error = error_code::scope_id_is_too_long; - return std::make_pair(end_ip, make_fixed_string(scope_id)); + return result; } - if (c == '%' || c == '/') { + if (c == '%' || c == '/' || uint32_t(c) > 127) { error = error_code::invalid_scope_id; - return std::make_pair(end_ip, make_fixed_string(scope_id)); + return result; } - scope_id[index++] = c; + result.scope_id[index++] = c; } else { scope = true; } } - if (scope && scope_id[0] == '\0') { + if (scope && result.scope_id[0] == '\0') { error = error_code::invalid_scope_id; - return std::make_pair(end_ip, make_fixed_string(scope_id)); + return result; } - return std::make_pair(end_ip, make_fixed_string(scope_id)); + return result; } template - IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::array, _max_parts + 1> split_parts(Iter begin, Iter end, int& parts_count, error_code& error) IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::array, _max_parts + 1> split_parts(Iter begin, Iter end, uint32_t& parts_count, error_code& error) IPADDRESS_NOEXCEPT { IPADDRESS_CONSTEXPR std::array, _max_parts + 1> empty_parts = { make_fixed_string("\0\0\0\0"), make_fixed_string("\0\0\0\0"), @@ -323,8 +329,15 @@ class base_v6 { char prev_c = '\0'; bool has_double_colon = false; - for (auto it = begin; it != end; ++it) { - auto c = char(*it); + Iter it = begin; + uint32_t error_symbol = 0; + + while (it < end) { + auto c = internal::next_char_or_error(it, end, error, error_symbol); + if (error != error_code::no_error) { + parts_count = error_symbol; + return empty_parts; + } if (!has_double_colon && c == ':' && prev_c == ':') { has_double_colon = true; } diff --git a/include/ipaddress/byte-array.hpp b/include/ipaddress/byte-array.hpp index a3e65a0..4bae440 100644 --- a/include/ipaddress/byte-array.hpp +++ b/include/ipaddress/byte-array.hpp @@ -29,10 +29,10 @@ namespace IPADDRESS_NAMESPACE { * @remark The purpose of the byte_array class is to provide functionality similar to std::array in . * environments where std::array cannot be used to its full extent during compile-time operations. */ -template +template struct byte_array { using value_type = uint8_t; /**< The type of elements contained in the byte_array. */ - using size_type = std::size_t; /**< The type representing sizes and counts. */ + using size_type = size_t; /**< The type representing sizes and counts. */ using difference_type = std::ptrdiff_t; /**< The type representing the difference between two pointers. */ using pointer = value_type*; /**< A pointer to an element in the byte_array. */ using const_pointer = const value_type*; /**< A pointer to a constant element in the byte_array. */ @@ -277,7 +277,7 @@ template <> class byte_array<0> { public: using value_type = uint8_t; - using size_type = std::size_t; + using size_type = size_t; using difference_type = std::ptrdiff_t; using pointer = value_type*; using const_pointer = const value_type*; @@ -390,9 +390,9 @@ class byte_array<0> { * @retval true the two byte_array objects are equal * @retval false the two byte_array objects are not equal */ -template +template IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator==(const byte_array& lhs, const byte_array& rhs) IPADDRESS_NOEXCEPT { - for (std::size_t i = 0; i < N; ++i) { + for (size_t i = 0; i < N; ++i) { if (lhs[i] != rhs[i]) { return false; } @@ -412,7 +412,7 @@ IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator==(c * @retval true the two byte_array objects are not equal * @retval false the two byte_array objects are equal */ -template +template IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator!=(const byte_array& lhs, const byte_array& rhs) IPADDRESS_NOEXCEPT { return !(lhs == rhs); } @@ -434,9 +434,9 @@ IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator!=(c * @retval std::strong_ordering::greater if lhs is lexicographically greater than rhs * @retval std::strong_ordering::equivalent if lhs is lexicographically equal to rhs */ - template + template IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::strong_ordering operator<=>(const byte_array& lhs, const byte_array& rhs) IPADDRESS_NOEXCEPT { - for (std::size_t i = 0; i < N; ++i) { + for (size_t i = 0; i < N; ++i) { if (const auto result = lhs[i] <=> rhs[i]; result != std::strong_ordering::equivalent) { return result; } @@ -458,9 +458,9 @@ IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator!=(c * @param[in] rhs A reference to the right-hand side byte_array object. * @return `true` if lhs is lexicographically less than rhs, `false` otherwise. */ - template + template IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator<(const byte_array& lhs, const byte_array& rhs) IPADDRESS_NOEXCEPT { - for (std::size_t i = 0; i < N; ++i) { + for (size_t i = 0; i < N; ++i) { if (lhs._data[i] < rhs._data[i]) { return true; } else if (lhs._data[i] != rhs._data[i]) { @@ -478,7 +478,7 @@ IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator!=(c * @param[in] rhs A reference to the right-hand side byte_array object. * @return `true` if lhs is lexicographically greater than rhs, `false` otherwise. */ - template + template IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator>(const byte_array& lhs, const byte_array& rhs) IPADDRESS_NOEXCEPT { return rhs < lhs; } @@ -491,7 +491,7 @@ IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator!=(c * @param[in] rhs A reference to the right-hand side byte_array object. * @return `true` if lhs is lexicographically less than or equal to rhs, `false` otherwise. */ - template + template IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator<=(const byte_array& lhs, const byte_array& rhs) IPADDRESS_NOEXCEPT { return !(rhs < lhs); } @@ -504,7 +504,7 @@ IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator!=(c * @param[in] rhs A reference to the right-hand side byte_array object. * @return `true` if lhs is lexicographically greater than or equal to rhs, `false` otherwise. */ - template + template IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator>=(const byte_array& lhs, const byte_array& rhs) IPADDRESS_NOEXCEPT { return !(lhs < rhs); } diff --git a/include/ipaddress/config.hpp b/include/ipaddress/config.hpp index a9187a4..5cf5f61 100644 --- a/include/ipaddress/config.hpp +++ b/include/ipaddress/config.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/include/ipaddress/endian.hpp b/include/ipaddress/endian.hpp index 10cda95..f1121d4 100644 --- a/include/ipaddress/endian.hpp +++ b/include/ipaddress/endian.hpp @@ -17,8 +17,8 @@ * and handling data with different endianness. * * The determination of endianness is done with a priority order: - * 1. User-defined `IPADDRESS_ENDIAN`. - * 2. C++20's `std::endian`. + * 1. C++20's `std::endian`. + * 2. User-defined `IPADDRESS_ENDIAN`. * 3. Platform/compiler-provided macros. * * If the endianness cannot be determined, a compilation error is raised @@ -93,7 +93,7 @@ # define IPADDRESS_ENDIAN IPADDRESS_LITTLE_ENDIAN # elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) # define IPADDRESS_ENDIAN IPADDRESS_LITTLE_ENDIAN -# else +# elif !defined(IPADDRESS_HAS_STD_ENDIAN) # error Unknown endianness detected. Needs to define IPADDRESS_ENDIAN # endif #endif diff --git a/include/ipaddress/errors.hpp b/include/ipaddress/errors.hpp index af1c6fa..e8e8b25 100644 --- a/include/ipaddress/errors.hpp +++ b/include/ipaddress/errors.hpp @@ -86,7 +86,11 @@ enum class error_code { new_prefix_must_be_shorter, /**< The new prefix length must be shorter for the operation being performed. */ new_prefix_must_be_longer, /**< The new prefix length must be longer for the operation being performed. */ cannot_set_prefixlen_diff_and_new_prefix, /**< Both prefix length difference and new prefix cannot be set simultaneously. */ - not_contained_network /**< The network is not a subnet of the other network as expected. */ + not_contained_network, /**< The network is not a subnet of the other network as expected. */ + + // input string errors + unexpected_symbol, /**< The input string contains an unexpected character. */ + wrong_encoding_sequence /**< Incorrect byte sequence in Unicode encoding. */ }; /** @@ -149,25 +153,40 @@ class error : public std::runtime_error { return _code; } + struct symbol { + uint32_t value; + }; + private: template static std::string concatenate(const Args&... args) { std::ostringstream ss; - print(ss, args...); + concat(ss, args...); return ss.str(); } template - static void print(std::ostringstream& out, const FirstArg& arg, const Args&... args) { - out << arg << ' '; - print(out, args...); + static void concat(std::ostringstream& out, const FirstArg& arg, const Args&... args) { + print(out, arg) << ' '; + concat(out, args...); } template - static void print(std::ostringstream& out, const FirstArg& arg) { + static void concat(std::ostringstream& out, const FirstArg& arg) { + print(out, arg); + } + + template + static std::ostringstream& print(std::ostringstream& out, const T& arg) { out << arg; + return out; } + static std::ostringstream& print(std::ostringstream& out, const symbol& arg); + + template + static std::ostringstream& print(std::ostringstream& out, const T (&str)[N]); + error_code _code; }; @@ -263,11 +282,11 @@ class logic_error : public error { * Raises an error with a specific error code and additional context. * * This function constructs an error message based on the provided error code, - * index, and address, then throws a parse_error or a logic_error exception with the constructed message. + * value, and address, then throws a parse_error or a logic_error exception with the constructed message. * * @tparam T The character type of the address string. * @param[in] code The error code indicating the type of error encountered. - * @param[in] index The index at which the error occurred, if applicable. + * @param[in] value The value at which the error occurred, if applicable. * @param[in] address A pointer to the beginning of the address string. * @param[in] length The length of the address string. * @throw parse_error Thrown with a message corresponding to the error code. @@ -278,15 +297,20 @@ template #ifndef IPADDRESS_NO_EXCEPTIONS [[noreturn]] #endif -IPADDRESS_CONSTEXPR inline void raise_error(error_code code, int index, const T* address, size_t length) { +IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void raise_error(error_code code, uint32_t value, const T* address, size_t length) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { #ifndef IPADDRESS_NO_EXCEPTIONS - char str[101] = {}; + T str[104] = {}; size_t max_len = length; if (length > 100) { max_len = 100; } for (size_t i = 0; i < max_len; ++i) { - str[i] = char(address[i]); + str[i] = address[i]; + } + if (length > 100) { + str[100] = '.'; + str[101] = '.'; + str[102] = '.'; } switch (code) { case error_code::empty_address: @@ -304,25 +328,25 @@ IPADDRESS_CONSTEXPR inline void raise_error(error_code code, int index, const T* case error_code::string_is_too_long: throw parse_error(code, "input string is too long", str); case error_code::empty_octet: - throw parse_error(code, "empty octet", index, "in address", str); + throw parse_error(code, "empty octet", value, "in address", str); case error_code::expected_4_octets: throw parse_error(code, "expected 4 octets in", str); case error_code::leading_0_are_not_permitted: - throw parse_error(code, "leading zeros are not permitted in octet", index, "of address", str); + throw parse_error(code, "leading zeros are not permitted in octet", value, "of address", str); case error_code::octet_more_3_characters: - throw parse_error(code, "in octet", index, "of address", str, "more 3 characters"); + throw parse_error(code, "in octet", value, "of address", str, "more 3 characters"); case error_code::octet_has_invalid_symbol: - throw parse_error(code, "in octet", index, "of address", str, "has invalid symbol"); + throw parse_error(code, "in octet", value, "of address", str, "has invalid symbol"); case error_code::octet_exceeded_255: - throw parse_error(code, "octet", index, "of address", str, "exceeded 255"); + throw parse_error(code, "octet", value, "of address", str, "exceeded 255"); case error_code::least_3_parts: throw parse_error(code, "least 3 parts in address", str); case error_code::most_8_colons_permitted: throw parse_error(code, "most 8 colons permitted in address", str); case error_code::part_is_more_4_chars: - throw parse_error(code, "in part", index, "of address", str, "more 4 characters"); + throw parse_error(code, "in part", value, "of address", str, "more 4 characters"); case error_code::part_has_invalid_symbol: - throw parse_error(code, "in part", index, "of address", str, "has invalid symbols"); + throw parse_error(code, "in part", value, "of address", str, "has invalid symbols"); case error_code::most_one_double_colon_permitted: throw parse_error(code, "at most one '::' permitted in address", str); case error_code::leading_colon_only_permitted_as_part_of_double_colon: @@ -349,6 +373,10 @@ IPADDRESS_CONSTEXPR inline void raise_error(error_code code, int index, const T* throw logic_error(code, "cannot set prefixlen_diff and new_prefix"); case error_code::not_contained_network: throw logic_error(code, "network is not a subnet of other"); + case error_code::unexpected_symbol: + throw parse_error(code, "unexpected next unicode symbol", error::symbol { value }, "in string", str); + case error_code::wrong_encoding_sequence: + throw parse_error(code, "incorrect sequence of bytes in unicode encoding for string", str); default: throw error(code, "unknown error"); } diff --git a/include/ipaddress/fixed-string.hpp b/include/ipaddress/fixed-string.hpp index e7f8640..a5c09bf 100644 --- a/include/ipaddress/fixed-string.hpp +++ b/include/ipaddress/fixed-string.hpp @@ -13,27 +13,10 @@ #define IPADDRESS_FIXED_STRING_HPP #include "config.hpp" +#include "unicode.hpp" namespace IPADDRESS_NAMESPACE { -namespace internal { - -template -IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void is_char_type() IPADDRESS_NOEXCEPT { - static_assert(std::is_same::value - || std::is_same::value - || std::is_same::value - || std::is_same::value - || std::is_same::value - || std::is_same::value - #if __cpp_char8_t >= 201811L - || std::is_same::value - #endif // __cpp_char8_t - , "Only character type supported"); -} - -} // namespace IPADDRESS_NAMESPACE::internal - /** * Fixed size string class. * @@ -44,6 +27,7 @@ IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void is_char_type() IPADDRESS_NOEXCEP */ template struct fixed_string { + using value_type = char; /**< Type of character in a string. */ using const_pointer = const char*; /**< Type of constant pointer to the string data. */ using const_reference = const char&; /**< Type of constant reference to a character in the string. */ using const_iterator = const_pointer; /**< Type of constant iterator for traversing the string. */ @@ -66,26 +50,46 @@ struct fixed_string { * Constructs a fixed_string from a character array. * * This constructor template initializes a fixed_string with the contents of a given character array. - * Characters from encodings other than ASCII may be truncated. * * @tparam T The character type of the input array. * @param[in] data The character array to initialize the fixed_string with. + * @throw parse_error Thrown if contains unexpected characters for addresses */ template - IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string(const T (&data)[N + 1]) IPADDRESS_NOEXCEPT { - internal::is_char_type(); - auto ended = false; + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string(const T (&data)[N + 1]) IPADDRESS_NOEXCEPT(noexcept(internal::char_reader::has_throw())) { + const auto begin = &data[0]; + const auto end = &data[N]; + auto it = begin; for (size_t i = 0; i < N; ++i) { - if (IPADDRESS_IS_CONST_EVALUATED(data) && data[i] > 127) { - const size_t err = data[i] / (data[i] - data[i]); // NOLINT(misc-redundant-expression): invalid symbol for ip address + _data[i] = internal::next_char(it, begin, end); + if (_data[i] == '\0') { + break; } - _data[i] = char(data[i]); - if (data[i] == '\0') { - ended = true; - } - if (!ended) { - ++length; + ++length; + } + } + + /** + * Constructs a fixed_string from a character array. + * + * This constructor template initializes a fixed_string with the contents of a given character array. + * + * @tparam T The character type of the input array. + * @param[in] data The character array to initialize the fixed_string with. + * @param[out] code A reference to an `error_code` object that will be set if an error occurs during parsing. + */ + template + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string(const T (&data)[N + 1], error_code& code) IPADDRESS_NOEXCEPT { + const auto begin = &data[0]; + const auto end = &data[N]; + auto it = begin; + uint32_t error_symbol = 0; + for (size_t i = 0; i < N; ++i) { + _data[i] = internal::next_char_or_error(it, end, code, error_symbol); + if (_data[i] == '\0' || code != error_code::no_error) { + break; } + ++length; } } @@ -382,6 +386,7 @@ struct fixed_string { template <> struct fixed_string<0> { + using value_type = char; using const_pointer = const char*; using const_reference = const char&; using const_iterator = const_pointer; @@ -405,9 +410,29 @@ struct fixed_string<0> { IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string(const char32_t*) IPADDRESS_NOEXCEPT { } + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string(const char*, error_code& code) IPADDRESS_NOEXCEPT { + code = error_code::no_error; + } + + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string(const wchar_t*, error_code& code) IPADDRESS_NOEXCEPT { + code = error_code::no_error; + } + + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string(const char16_t*, error_code& code) IPADDRESS_NOEXCEPT { + code = error_code::no_error; + } + + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string(const char32_t*, error_code& code) IPADDRESS_NOEXCEPT { + code = error_code::no_error; + } + #if __cpp_char8_t >= 201811L IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string(const char8_t*) IPADDRESS_NOEXCEPT { } + + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string(const char8_t*, error_code& code) IPADDRESS_NOEXCEPT { + code = error_code::no_error; + } #endif // __cpp_char8_t IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE const_iterator begin() const IPADDRESS_NOEXCEPT { @@ -618,75 +643,32 @@ IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator!=(c * * Constructs a fixed_string object from a character array, deducing the size automatically. * + * @tparam T The character type of the input array. * @tparam N The size of the character array plus one for the null terminator. * @param[in] data The character array to initialize the fixed_string with. * @return A fixed_string object of size N-1. */ -template -IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string make_fixed_string(const char(&data)[N]) IPADDRESS_NOEXCEPT { +template +IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string make_fixed_string(const T(&data)[N]) IPADDRESS_NOEXCEPT(noexcept(fixed_string(data))) { return fixed_string(data); } /** - * Creates a fixed-length string from a wide character array. - * - * Constructs a fixed_string object from a wide character array, deducing the size automatically. - * - * @tparam N The size of the wide character array plus one for the null terminator. - * @param[in] data The wide character array to initialize the fixed_string with. - * @return A fixed_string object of size N-1. - */ -template -IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string make_fixed_string(const wchar_t(&data)[N]) IPADDRESS_NOEXCEPT { - return fixed_string(data); -} - -/** - * Creates a fixed-length string from a UTF-16 character array. - * - * Constructs a fixed_string object from a UTF-16 character array, deducing the size automatically. - * - * @tparam N The size of the UTF-16 character array plus one for the null terminator. - * @param[in] data The UTF-16 character array to initialize the fixed_string with. - * @return A fixed_string object of size N-1. - */ -template -IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string make_fixed_string(const char16_t(&data)[N]) IPADDRESS_NOEXCEPT { - return fixed_string(data); -} - -/** - * Creates a fixed-length string from a UTF-32 character array. - * - * Constructs a fixed_string object from a UTF-32 character array, deducing the size automatically. - * - * @tparam N The size of the UTF-32 character array plus one for the null terminator. - * @param[in] data The UTF-32 character array to initialize the fixed_string with. - * @return A fixed_string object of size N-1. - */ -template -IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string make_fixed_string(const char32_t(&data)[N]) IPADDRESS_NOEXCEPT { - return fixed_string(data); -} - -#if __cpp_char8_t >= 201811L - -/** - * Creates a fixed-length string from a UTF-8 character array. + * Creates a fixed-length string from a character array. * - * Constructs a fixed_string object from a UTF-8 character array, deducing the size automatically. + * Constructs a fixed_string object from a character array, deducing the size automatically. * - * @tparam N The size of the UTF-8 character array plus one for the null terminator. - * @param[in] data The UTF-8 character array to initialize the fixed_string with. + * @tparam T The character type of the input array. + * @tparam N The size of the character array plus one for the null terminator. + * @param[in] data The character array to initialize the fixed_string with. + * @param[out] code A reference to an `error_code` object that will be set if an error occurs during parsing. * @return A fixed_string object of size N-1. */ -template -IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string make_fixed_string(const char8_t(&data)[N]) IPADDRESS_NOEXCEPT { - return fixed_string(data); +template +IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE fixed_string make_fixed_string(const T(&data)[N], error_code& code) IPADDRESS_NOEXCEPT { + return fixed_string(data, code); } -#endif // __cpp_char8_t - #if IPADDRESS_CPP_VERSION >= 17 #if __cpp_char8_t >= 201811L diff --git a/include/ipaddress/hash.hpp b/include/ipaddress/hash.hpp index c2dfd5b..f92dc3e 100644 --- a/include/ipaddress/hash.hpp +++ b/include/ipaddress/hash.hpp @@ -23,46 +23,46 @@ namespace IPADDRESS_NAMESPACE { namespace internal { -template +template struct hash_combine; template <> struct hash_combine<4> { - IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::size_t operator()(std::uint32_t value) const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t operator()(uint32_t value) const IPADDRESS_NOEXCEPT { value ^= value >> 16; value *= 0x21f0aaad; value ^= value >> 15; value *= 0x735a2d97; value ^= value >> 15; - return std::size_t(value); + return size_t(value); } }; template <> struct hash_combine<8> { - IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::size_t operator()(std::uint64_t value) const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t operator()(uint64_t value) const IPADDRESS_NOEXCEPT { value ^= value >> 32; value *= 0xe9846af9b1a615d; value ^= value >> 32; value *= 0xe9846af9b1a615d; value ^= value >> 28; - return std::size_t(value); + return size_t(value); } }; -IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::size_t hash_sum(std::size_t seed, std::size_t value) IPADDRESS_NOEXCEPT { - const hash_combine hash{}; +IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t hash_sum(size_t seed, size_t value) IPADDRESS_NOEXCEPT { + const hash_combine hash{}; return hash(seed + 0x9e3779b9 + value); } template -IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::size_t calc_hash(std::size_t seed, Arg arg) IPADDRESS_NOEXCEPT { +IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t calc_hash(size_t seed, Arg arg) IPADDRESS_NOEXCEPT { return hash_sum(seed, arg); } template -IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::size_t calc_hash(std::size_t seed, Arg arg, Args... args) IPADDRESS_NOEXCEPT { - seed = hash_sum(seed, std::size_t(arg)); +IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t calc_hash(size_t seed, Arg arg, Args... args) IPADDRESS_NOEXCEPT { + seed = hash_sum(seed, size_t(arg)); return calc_hash(seed, args...); } diff --git a/include/ipaddress/ip-address-base.hpp b/include/ipaddress/ip-address-base.hpp index 3f99d33..74ecce5 100644 --- a/include/ipaddress/ip-address-base.hpp +++ b/include/ipaddress/ip-address-base.hpp @@ -123,10 +123,10 @@ class ip_address_base : public Base { IPADDRESS_NODISCARD static IPADDRESS_CONSTEVAL IPADDRESS_FORCE_INLINE ip_address_base parse() IPADDRESS_NOEXCEPT { constexpr auto str = FixedString; auto code = error_code::no_error; - auto index = 0; - auto result = Base::ip_from_string(str.begin(), str.end(), code, index); + uint32_t value = 0; + auto result = Base::ip_from_string(str.begin(), str.end(), code, value); if (code != error_code::no_error) { - raise_error(code, index, str.data(), str.size()); + raise_error(code, value, str.data(), str.size()); } return ip_address_base(result); } @@ -401,7 +401,6 @@ class ip_address_base : public Base { */ template IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base parse(const T(&address)[N]) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { - internal::is_char_type(); auto str = make_fixed_string(address); return parse_string(str); } @@ -420,9 +419,8 @@ class ip_address_base : public Base { */ template static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base parse(const T(&address)[N], error_code& code) IPADDRESS_NOEXCEPT { - internal::is_char_type(); - auto str = make_fixed_string(address); - return parse_string(str, code); + auto str = make_fixed_string(address, code); + return code == error_code::no_error ? parse_string(str, code) : ip_address_base{}; } /** @@ -524,6 +522,66 @@ class ip_address_base : public Base { return std::string(res, len); } + /** + * Converts the IP address to a string representation. + * + * The function converts the binary representation of the IP address to a string. + * The format of the output string can be adjusted by passing the desired format as an argument. + * The default format is 'compressed'. + * + * @param[in] fmt The format to use for the string representation. Defaults to `format::compressed`. + * @return A `std::wstring` representing the IP address in the specified format. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::wstring to_wstring(format fmt = format::compressed) const { + return internal::string_converter::convert(to_string(fmt)); + } + + /** + * Converts the IP address to a string representation. + * + * The function converts the binary representation of the IP address to a string. + * The format of the output string can be adjusted by passing the desired format as an argument. + * The default format is 'compressed'. + * + * @param[in] fmt The format to use for the string representation. Defaults to `format::compressed`. + * @return A `std::u16string` representing the IP address in the specified format. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u16string to_u16string(format fmt = format::compressed) const { + return internal::string_converter::convert(to_string(fmt)); + } + + /** + * Converts the IP address to a string representation. + * + * The function converts the binary representation of the IP address to a string. + * The format of the output string can be adjusted by passing the desired format as an argument. + * The default format is 'compressed'. + * + * @param[in] fmt The format to use for the string representation. Defaults to `format::compressed`. + * @return A `std::u32string` representing the IP address in the specified format. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u32string to_u32string(format fmt = format::compressed) const { + return internal::string_converter::convert(to_string(fmt)); + } + +#if __cpp_char8_t >= 201811L + + /** + * Converts the IP address to a string representation. + * + * The function converts the binary representation of the IP address to a string. + * The format of the output string can be adjusted by passing the desired format as an argument. + * The default format is 'compressed'. + * + * @param[in] fmt The format to use for the string representation. Defaults to `format::compressed`. + * @return A `std::u8string` representing the IP address in the specified format. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u8string to_u8string(format fmt = format::compressed) const { + return internal::string_converter::convert(to_string(fmt)); + } + +#endif // __cpp_char8_t + /** * Swaps the contents of this IP address with another IP address. * @@ -587,10 +645,12 @@ class ip_address_base : public Base { * * This operator allows the IP address to be converted to a string. * - * @return A `std::string` representation of the IP address. + * @tparam T The character type of the string. + * @return A string representation of the IP address. */ - IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE explicit operator std::string() const { - return to_string(); + template + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE explicit operator std::basic_string, std::allocator>() const { + return internal::string_converter::convert(to_string()); } /** @@ -694,10 +754,10 @@ class ip_address_base : public Base { template IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base parse_string(const Str& address) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { auto code = error_code::no_error; - auto index = 0; - auto result = Base::ip_from_string(address.begin(), address.end(), code, index); + uint32_t value = 0; + auto result = Base::ip_from_string(address.data(), address.data() + address.size(), code, value); if (code != error_code::no_error) { - raise_error(code, index, address.data(), address.size()); + raise_error(code, value, address.data(), address.size()); } return result; } @@ -705,8 +765,8 @@ class ip_address_base : public Base { template static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base parse_string(const Str& address, error_code& code) IPADDRESS_NOEXCEPT { code = error_code::no_error; - auto index = 0; - return Base::ip_from_string(address.begin(), address.end(), code, index); + uint32_t value = 0; + return Base::ip_from_string(address.data(), address.data() + address.size(), code, value); } }; @@ -715,7 +775,7 @@ class ip_address_base : public Base { namespace internal { template -IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base parse_ip_from_literal(const TChar* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { +IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base parse_ip_from_literal(const TChar* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { if (size > MaxLen) { raise_error(error_code::string_is_too_long, 0, address, size); } @@ -737,17 +797,20 @@ IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE int stream_index() { return i; } -IPADDRESS_FORCE_INLINE std::ostream& full(std::ostream& stream) { +template +IPADDRESS_FORCE_INLINE std::basic_ostream>& full(std::basic_ostream>& stream) { stream.iword(stream_index()) = long(format::full) + 1; return stream; } -IPADDRESS_FORCE_INLINE std::ostream& compact(std::ostream& stream) { +template +IPADDRESS_FORCE_INLINE std::basic_ostream>& compact(std::basic_ostream>& stream) { stream.iword(stream_index()) = long(format::compact) + 1; return stream; } -IPADDRESS_FORCE_INLINE std::ostream& compressed(std::ostream& stream) { +template +IPADDRESS_FORCE_INLINE std::basic_ostream>& compressed(std::basic_ostream>& stream) { stream.iword(stream_index()) = long(format::compressed) + 1; return stream; } @@ -762,7 +825,7 @@ namespace std { template struct hash> { - IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::size_t operator()(const IPADDRESS_NAMESPACE::ip_address_base& ip) const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t operator()(const IPADDRESS_NAMESPACE::ip_address_base& ip) const IPADDRESS_NOEXCEPT { return ip.hash(); } }; @@ -778,7 +841,12 @@ IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::string to_string(const IPADDRESS } template -IPADDRESS_FORCE_INLINE std::ostream& operator<<(std::ostream& stream, const IPADDRESS_NAMESPACE::ip_address_base& ip) { +IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::wstring to_wstring(const IPADDRESS_NAMESPACE::ip_address_base& ip) { + return ip.to_wstring(); +} + +template +IPADDRESS_FORCE_INLINE std::basic_ostream>& operator<<(std::basic_ostream>& stream, const IPADDRESS_NAMESPACE::ip_address_base& ip) { auto& iword = stream.iword(IPADDRESS_NAMESPACE::stream_index()); auto fmt = iword ? (IPADDRESS_NAMESPACE::format) (iword - 1) @@ -791,12 +859,12 @@ IPADDRESS_FORCE_INLINE std::ostream& operator<<(std::ostream& stream, const IPAD return std::toupper(c); }); } - return stream << str; + return stream << IPADDRESS_NAMESPACE::internal::string_converter::convert(str); } -template -IPADDRESS_FORCE_INLINE std::istream& operator>>(std::istream& stream, IPADDRESS_NAMESPACE::ip_address_base& ip) { - std::string str; +template +IPADDRESS_FORCE_INLINE std::basic_istream>& operator>>(std::basic_istream>& stream, IPADDRESS_NAMESPACE::ip_address_base& ip) { + std::basic_string, std::allocator> str; stream >> str; IPADDRESS_NAMESPACE::error_code err = IPADDRESS_NAMESPACE::error_code::no_error; ip = IPADDRESS_NAMESPACE::ip_address_base::parse(str, err); diff --git a/include/ipaddress/ip-address-iterator.hpp b/include/ipaddress/ip-address-iterator.hpp index f895a49..a1a5e81 100644 --- a/include/ipaddress/ip-address-iterator.hpp +++ b/include/ipaddress/ip-address-iterator.hpp @@ -38,7 +38,7 @@ class ip_reverse_iterator { using iterator_concept = std::random_access_iterator_tag; /**< The iterator concept, defined if concepts are available. */ #endif using iterator_category = typename std::iterator_traits::iterator_category; /**< The category of the iterator. */ - using iterator_type = Iterator; /**< The underlying iterator type. */ + using iterator_type = Iterator; /**< The underlying iterator type. */ using value_type = typename std::iterator_traits::value_type; /**< The type of the values iterated over. */ using difference_type = typename std::iterator_traits::difference_type; /**< The type representing the difference between two iterators. */ using pointer = typename std::iterator_traits::pointer; /**< The pointer type of the iterated values. */ @@ -426,7 +426,7 @@ class ip_address_iterator> { #endif using iterator_category = std::random_access_iterator_tag; /**< The category of the iterator. */ using value_type = ip_address_base; /**< The type of the values iterated over. */ - using difference_type = std::int64_t; /**< The type representing the difference between two iterators. */ + using difference_type = int64_t; /**< The type representing the difference between two iterators. */ using pointer = const value_type*; /**< The pointer type of the iterated values. */ using reference = const value_type&; /**< The reference type of the iterated values. */ @@ -868,7 +868,7 @@ template class hosts_sequence> { public: using value_type = ip_address_base; /**< The type of the IP addresses in the sequence. */ - using size_type = std::size_t; /**< The type used for representing the size of the sequence. */ + using size_type = size_t; /**< The type used for representing the size of the sequence. */ using difference_type = typename value_type::uint_type; /**< The type used for representing differences between iterators. */ using pointer = value_type*; /**< The pointer type for the value_type. */ using const_pointer = const value_type*; /**< The const pointer type for the value_type. */ diff --git a/include/ipaddress/ip-any-address.hpp b/include/ipaddress/ip-any-address.hpp index b0867db..5c15637 100644 --- a/include/ipaddress/ip-any-address.hpp +++ b/include/ipaddress/ip-any-address.hpp @@ -27,9 +27,6 @@ namespace IPADDRESS_NAMESPACE { namespace internal { -// Parsing has been removed from the ip_address class due to a bug -// in the Clang compiler in version 14 and below -// https://bugs.llvm.org/show_bug.cgi?id=18781 template struct ip_any_parser { template @@ -452,6 +449,58 @@ class ip_address { return _version == ip_version::V4 ? _ipv.ipv4.to_string(fmt) : _ipv.ipv6.to_string(fmt); } + /** + * Converts the IP address to a string. + * + * This function returns a string representation of the IP address. The format can be specified by the \a fmt parameter. + * + * @param[in] fmt The format to use for the string representation, defaults to compressed format. + * @return A `std::wstring` representing the IP address in the specified format. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::wstring to_wstring(format fmt = format::compressed) const { + return _version == ip_version::V4 ? _ipv.ipv4.to_wstring(fmt) : _ipv.ipv6.to_wstring(fmt); + } + + /** + * Converts the IP address to a string. + * + * This function returns a string representation of the IP address. The format can be specified by the \a fmt parameter. + * + * @param[in] fmt The format to use for the string representation, defaults to compressed format. + * @return A `std::u16string` representing the IP address in the specified format. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u16string to_u16string(format fmt = format::compressed) const { + return _version == ip_version::V4 ? _ipv.ipv4.to_u16string(fmt) : _ipv.ipv6.to_u16string(fmt); + } + + /** + * Converts the IP address to a string. + * + * This function returns a string representation of the IP address. The format can be specified by the \a fmt parameter. + * + * @param[in] fmt The format to use for the string representation, defaults to compressed format. + * @return A `std::u32string` representing the IP address in the specified format. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u32string to_u32string(format fmt = format::compressed) const { + return _version == ip_version::V4 ? _ipv.ipv4.to_u32string(fmt) : _ipv.ipv6.to_u32string(fmt); + } + +#if __cpp_char8_t >= 201811L + + /** + * Converts the IP address to a string. + * + * This function returns a string representation of the IP address. The format can be specified by the \a fmt parameter. + * + * @param[in] fmt The format to use for the string representation, defaults to compressed format. + * @return A `std::u8string` representing the IP address in the specified format. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u8string to_u8string(format fmt = format::compressed) const { + return _version == ip_version::V4 ? _ipv.ipv4.to_u8string(fmt) : _ipv.ipv6.to_u8string(fmt); + } + +#endif // __cpp_char8_t + /** * Generates a reverse DNS lookup pointer for the IP address. * @@ -1094,7 +1143,6 @@ class ip_address { */ template IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address parse(const T(&address)[N]) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { - internal::is_char_type(); auto code = error_code::no_error; auto result = internal::ip_any_parser::parse(address, code); if (code != error_code::no_error) { @@ -1117,7 +1165,6 @@ class ip_address { */ template static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address parse(const T(&address)[N], error_code& code) IPADDRESS_NOEXCEPT { - internal::is_char_type(); return internal::ip_any_parser::parse(address, code); } @@ -1133,8 +1180,7 @@ class ip_address { * @remark If scope is disabled in settings (`IPADDRESS_IPV6_SCOPE_MAX_LENGTH == 0`) then this call will have no effect. */ template - IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void set_scope_id(const T(&scope_id)[N]) IPADDRESS_NOEXCEPT { - internal::is_char_type(); + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void set_scope_id(const T(&scope_id)[N]) IPADDRESS_NOEXCEPT(noexcept(ipv6_address().set_scope_id(scope_id))) { if (_version == ip_version::V6) { _ipv.ipv6.set_scope_id(scope_id); } @@ -1187,10 +1233,12 @@ class ip_address { * * This operator allows the IP address to be converted to a string. * + * @tparam T The character type of the string. * @return A `std::string` representation of the IP address. */ - IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE explicit operator std::string() const { - return _version == ip_version::V4 ? _ipv.ipv4.to_string() : _ipv.ipv6.to_string(); + template + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE explicit operator std::basic_string, std::allocator>() const { + return internal::string_converter::convert(to_string()); } /** @@ -1297,35 +1345,6 @@ class ip_address { #endif // !IPADDRESS_HAS_SPACESHIP_OPERATOR private: - // not used due to clang bug in version 14 and below - // https://bugs.llvm.org/show_bug.cgi?id=18781 - // - // template - // IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address parse_string(const Str& address) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { - // auto code = error_code::no_error; - // const auto ipv4 = ipv4_address::parse(address, code); - // if (code == error_code::no_error) { - // return ip_address(ipv4); - // } - // return ip_address(ipv6_address::parse(address)); - // } - // - // template - // static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address parse_string(const Str& address, error_code& code) IPADDRESS_NOEXCEPT { - // code = error_code::no_error; - // const auto ipv4 = ipv4_address::parse(address, code); - // if (code == error_code::no_error) { - // return ip_address(ipv4); - // } - // - // const auto ipv6 = ipv6_address::parse(address, code); - // if (code == error_code::no_error) { - // return ip_address(ipv6); - // } - // - // return ip_address(); - // } - union ip_any_address { IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_any_address() IPADDRESS_NOEXCEPT : ipv4() { } @@ -1370,7 +1389,7 @@ class ip_address { * @param[in] size The size of the character array. * @return An ip_address object parsed from the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address operator""_ip(const char* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address operator""_ip(const char* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { if (size > ipv6_address::base_max_string_len) { raise_error(error_code::string_is_too_long, 0, address, size); } @@ -1388,7 +1407,7 @@ class ip_address { * @param[in] size The size of the character array. * @return An ip_address object parsed from the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address operator""_ip(const wchar_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address operator""_ip(const wchar_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { if (size > ipv6_address::base_max_string_len) { raise_error(error_code::string_is_too_long, 0, address, size); } @@ -1406,7 +1425,7 @@ class ip_address { * @param[in] size The size of the character array. * @return An ip_address object parsed from the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address operator""_ip(const char16_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address operator""_ip(const char16_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { if (size > ipv6_address::base_max_string_len) { raise_error(error_code::string_is_too_long, 0, address, size); } @@ -1424,7 +1443,7 @@ class ip_address { * @param[in] size The size of the character array. * @return An ip_address object parsed from the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address operator""_ip(const char32_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address operator""_ip(const char32_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { if (size > ipv6_address::base_max_string_len) { raise_error(error_code::string_is_too_long, 0, address, size); } @@ -1445,7 +1464,7 @@ namespace std { template <> struct hash { - IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::size_t operator()(const IPADDRESS_NAMESPACE::ip_address& ip) const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t operator()(const IPADDRESS_NAMESPACE::ip_address& ip) const IPADDRESS_NOEXCEPT { return ip.hash(); } }; @@ -1458,7 +1477,12 @@ IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::string to_string(const IPADDRESS return ip.to_string(); } -IPADDRESS_FORCE_INLINE std::ostream& operator<<(std::ostream& stream, const IPADDRESS_NAMESPACE::ip_address& ip) { +IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::wstring to_wstring(const IPADDRESS_NAMESPACE::ip_address& ip) { + return ip.to_wstring(); +} + +template +IPADDRESS_FORCE_INLINE std::basic_ostream>& operator<<(std::basic_ostream>& stream, const IPADDRESS_NAMESPACE::ip_address& ip) { auto& iword = stream.iword(IPADDRESS_NAMESPACE::stream_index()); auto fmt = iword ? (IPADDRESS_NAMESPACE::format) (iword - 1) @@ -1471,11 +1495,12 @@ IPADDRESS_FORCE_INLINE std::ostream& operator<<(std::ostream& stream, const IPAD return std::toupper(c); }); } - return stream << str; + return stream << IPADDRESS_NAMESPACE::internal::string_converter::convert(str); } -IPADDRESS_FORCE_INLINE std::istream& operator>>(std::istream& stream, IPADDRESS_NAMESPACE::ip_address& ip) { - std::string str; +template +IPADDRESS_FORCE_INLINE std::basic_istream>& operator>>(std::basic_istream>& stream, IPADDRESS_NAMESPACE::ip_address& ip) { + std::basic_string, std::allocator> str; stream >> str; IPADDRESS_NAMESPACE::error_code err = IPADDRESS_NAMESPACE::error_code::no_error; ip = IPADDRESS_NAMESPACE::ip_address::parse(str, err); diff --git a/include/ipaddress/ip-any-iterator.hpp b/include/ipaddress/ip-any-iterator.hpp index 13a17fb..7ceb482 100644 --- a/include/ipaddress/ip-any-iterator.hpp +++ b/include/ipaddress/ip-any-iterator.hpp @@ -48,7 +48,7 @@ class ip_any_iterator { #endif using iterator_category = std::random_access_iterator_tag; /**< Iterator category. */ using value_type = T; /**< Value type iterated over. */ - using difference_type = std::int64_t; /**< Difference type between iterators. */ + using difference_type = int64_t; /**< Difference type between iterators. */ using pointer = const value_type*; /**< Pointer to value type. */ using reference = const value_type&; /**< Reference to value type. */ @@ -482,7 +482,7 @@ class ip_any_iterator { class hosts_any_sequence { public: using value_type = ip_address; /**< The type of the IP addresses in the sequence. */ - using size_type = std::size_t; /**< The type used for representing the size of the sequence. */ + using size_type = size_t; /**< The type used for representing the size of the sequence. */ using difference_type = uint128_t; /**< The type used for representing differences between iterators. */ using pointer = value_type*; /**< The pointer type for the value_type. */ using const_pointer = const value_type*; /**< The const pointer type for the value_type. */ @@ -661,7 +661,7 @@ template class subnets_any_sequence { public: using value_type = T; /**< The type of subnet value. */ - using size_type = std::size_t; /**< An unsigned integral type. */ + using size_type = size_t; /**< An unsigned integral type. */ using difference_type = uint128_t; /**< Unsigned integer type for differences. */ using pointer = value_type*; /**< Pointer to the subnet type. */ using const_pointer = const value_type*; /**< Const pointer to the subnet type. */ diff --git a/include/ipaddress/ip-any-network.hpp b/include/ipaddress/ip-any-network.hpp index 38af7de..90e18d6 100644 --- a/include/ipaddress/ip-any-network.hpp +++ b/include/ipaddress/ip-any-network.hpp @@ -29,9 +29,6 @@ namespace IPADDRESS_NAMESPACE { namespace internal { -// Parsing has been removed from the ip_network class due to a bug -// in the Clang compiler in version 14 and below -// https://bugs.llvm.org/show_bug.cgi?id=18781 template struct net_any_parser { template @@ -786,6 +783,62 @@ class ip_network { return is_v4() ? _ipv_net.ipv4.to_string(fmt) : _ipv_net.ipv6.to_string(fmt); } + /** + * Converts the network to a string representation. + * + * This method returns a string representation of the network, combining the network address + * and the prefix length, formatted according to the specified format. + * + * @param[in] fmt The format to use for the string representation. *Defaults to format::compressed*. + * @return A string representation of the network. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::wstring to_wstring(format fmt = format::compressed) const { + return is_v4() ? _ipv_net.ipv4.to_wstring(fmt) : _ipv_net.ipv6.to_wstring(fmt); + } + + /** + * Converts the network to a string representation. + * + * This method returns a string representation of the network, combining the network address + * and the prefix length, formatted according to the specified format. + * + * @param[in] fmt The format to use for the string representation. *Defaults to format::compressed*. + * @return A string representation of the network. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u16string to_u16string(format fmt = format::compressed) const { + return is_v4() ? _ipv_net.ipv4.to_u16string(fmt) : _ipv_net.ipv6.to_u16string(fmt); + } + + /** + * Converts the network to a string representation. + * + * This method returns a string representation of the network, combining the network address + * and the prefix length, formatted according to the specified format. + * + * @param[in] fmt The format to use for the string representation. *Defaults to format::compressed*. + * @return A string representation of the network. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u32string to_u32string(format fmt = format::compressed) const { + return is_v4() ? _ipv_net.ipv4.to_u32string(fmt) : _ipv_net.ipv6.to_u32string(fmt); + } + +#if __cpp_char8_t >= 201811L + + /** + * Converts the network to a string representation. + * + * This method returns a string representation of the network, combining the network address + * and the prefix length, formatted according to the specified format. + * + * @param[in] fmt The format to use for the string representation. *Defaults to format::compressed*. + * @return A string representation of the network. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u8string to_u8string(format fmt = format::compressed) const { + return is_v4() ? _ipv_net.ipv4.to_u8string(fmt) : _ipv_net.ipv6.to_u8string(fmt); + } + +#endif // __cpp_char8_t + /** * Swaps the contents of this network with another network. * @@ -1125,7 +1178,6 @@ class ip_network { */ template IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network parse(const T(&address)[N], bool strict = true) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { - internal::is_char_type(); auto code = error_code::no_error; auto result = internal::net_any_parser::parse(address, code, strict); if (code != error_code::no_error) { @@ -1149,7 +1201,6 @@ class ip_network { */ template static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network parse(const T(&address)[N], error_code& code, bool strict = true) IPADDRESS_NOEXCEPT { - internal::is_char_type(); code = error_code::no_error; const auto net4 = ipv4_network::parse(address, code, strict); @@ -1168,10 +1219,12 @@ class ip_network { /** * Converts the ip network object to a std::string. * + * @tparam T The character type of the string. * @return A `std::string` representation of the ip network object. */ - IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE explicit operator std::string() const { - return is_v4() ? _ipv_net.ipv4.to_string() : _ipv_net.ipv6.to_string(); + template + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE explicit operator std::basic_string, std::allocator>() const { + return internal::string_converter::convert(to_string()); } /** @@ -1278,35 +1331,6 @@ class ip_network { #endif // !IPADDRESS_HAS_SPACESHIP_OPERATOR private: - // not used due to clang bug in version 14 and below - // https://bugs.llvm.org/show_bug.cgi?id=18781 - // - // template - // IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network parse_string(const Str& address, bool strict) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { - // auto code = error_code::no_error; - // const auto net4 = ipv4_network::parse(address, code, strict); - // if (code == error_code::no_error) { - // return ip_network(net4); - // } - // return ip_network(ipv6_network::parse(address, strict)); - // } - // - // template - // static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network parse_string(const Str& address, error_code& code, bool strict) IPADDRESS_NOEXCEPT { - // code = error_code::no_error; - // const auto net4 = ipv4_network::parse(address, code, strict); - // if (code == error_code::no_error) { - // return ip_network(net4); - // } - // - // const auto net6 = ipv6_network::parse(address, code, strict); - // if (code == error_code::no_error) { - // return ip_network(net6); - // } - // - // return ip_network(); - // } - union ip_any_network { IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_any_network() IPADDRESS_NOEXCEPT : ipv4() { } @@ -1351,7 +1375,7 @@ class ip_network { * @param[in] size The size of the string literal. * @return An ip_network object representing the network specified by the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network operator""_net(const char* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network operator""_net(const char* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { const auto max_len = ipv6_address::base_max_string_len * 2 + 1; if (size > max_len) { raise_error(error_code::string_is_too_long, 0, address, size); @@ -1373,7 +1397,7 @@ class ip_network { * @param[in] size The size of the string literal. * @return An ip_network object representing the network specified by the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network operator""_net(const wchar_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network operator""_net(const wchar_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { const auto max_len = ipv6_address::base_max_string_len * 2 + 1; if (size > max_len) { raise_error(error_code::string_is_too_long, 0, address, size); @@ -1395,7 +1419,7 @@ class ip_network { * @param[in] size The size of the string literal. * @return An ip_network object representing the network specified by the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network operator""_net(const char16_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network operator""_net(const char16_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { const auto max_len = ipv6_address::base_max_string_len * 2 + 1; if (size > max_len) { raise_error(error_code::string_is_too_long, 0, address, size); @@ -1417,7 +1441,7 @@ class ip_network { * @param[in] size The size of the string literal. * @return An ip_network object representing the network specified by the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network operator""_net(const char32_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network operator""_net(const char32_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { const auto max_len = ipv6_address::base_max_string_len * 2 + 1; if (size > max_len) { raise_error(error_code::string_is_too_long, 0, address, size); @@ -1439,7 +1463,7 @@ namespace std { template <> struct hash { - IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::size_t operator()(const IPADDRESS_NAMESPACE::ip_network& network) const IPADDRESS_NOEXCEPT { + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t operator()(const IPADDRESS_NAMESPACE::ip_network& network) const IPADDRESS_NOEXCEPT { return network.hash(); } }; @@ -1452,7 +1476,12 @@ IPADDRESS_FORCE_INLINE std::string to_string(const IPADDRESS_NAMESPACE::ip_netwo return network.to_string(); } -IPADDRESS_FORCE_INLINE std::ostream& operator<<(std::ostream& stream, const IPADDRESS_NAMESPACE::ip_network& network) { +IPADDRESS_FORCE_INLINE std::wstring to_wstring(const IPADDRESS_NAMESPACE::ip_network& network) { + return network.to_wstring(); +} + +template +IPADDRESS_FORCE_INLINE std::basic_ostream>& operator<<(std::basic_ostream>& stream, const IPADDRESS_NAMESPACE::ip_network& network) { auto& iword = stream.iword(IPADDRESS_NAMESPACE::stream_index()); auto fmt = iword ? (IPADDRESS_NAMESPACE::format) (iword - 1) @@ -1465,15 +1494,16 @@ IPADDRESS_FORCE_INLINE std::ostream& operator<<(std::ostream& stream, const IPAD return std::toupper(c); }); } - return stream << str; + return stream << IPADDRESS_NAMESPACE::internal::string_converter::convert(str); } -IPADDRESS_FORCE_INLINE std::istream& operator>>(std::istream& stream, IPADDRESS_NAMESPACE::ip_network& network) { +template +IPADDRESS_FORCE_INLINE std::basic_istream>& operator>>(std::basic_istream>& stream, IPADDRESS_NAMESPACE::ip_network& network) { auto& iword = stream.iword(IPADDRESS_NAMESPACE::network_strict_index()); auto strict = iword == 0; iword = 0; - std::string str; + std::basic_string, std::allocator> str; stream >> str; IPADDRESS_NAMESPACE::error_code err = IPADDRESS_NAMESPACE::error_code::no_error; network = IPADDRESS_NAMESPACE::ip_network::parse(str, err, strict); diff --git a/include/ipaddress/ip-network-base.hpp b/include/ipaddress/ip-network-base.hpp index 76972fc..37124aa 100644 --- a/include/ipaddress/ip-network-base.hpp +++ b/include/ipaddress/ip-network-base.hpp @@ -65,10 +65,10 @@ class ip_network_base : public Base { IPADDRESS_NODISCARD static IPADDRESS_CONSTEVAL ip_network_base parse() IPADDRESS_NOEXCEPT { constexpr auto str = FixedString; auto code = error_code::no_error; - auto index = 0; - auto result = parse_address_with_prefix(str, Strict, code, index); + uint32_t value = 0; + auto result = parse_address_with_prefix(str, Strict, code, value); if (code != error_code::no_error) { - raise_error(code, index, str.data(), str.size()); + raise_error(code, value, str.data(), str.size()); } return result; } @@ -179,8 +179,8 @@ class ip_network_base : public Base { * @remark For C++ versions prior to C++17, member functions with `std::string` and C-strings will be used instead. */ static IPADDRESS_CONSTEXPR ip_network_base parse(std::string_view address, error_code& code, bool strict = true) IPADDRESS_NOEXCEPT { - auto index = 0; - return parse_address_with_prefix(address, strict, code, index); + uint32_t value = 0; + return parse_address_with_prefix(address, strict, code, value); } /** @@ -197,8 +197,8 @@ class ip_network_base : public Base { * @remark For C++ versions prior to C++17, member functions with `std::wstring` and C-strings will be used instead. */ static IPADDRESS_CONSTEXPR ip_network_base parse(std::wstring_view address, error_code& code, bool strict = true) IPADDRESS_NOEXCEPT { - auto index = 0; - return parse_address_with_prefix(address, strict, code, index); + uint32_t value = 0; + return parse_address_with_prefix(address, strict, code, value); } #if __cpp_char8_t >= 201811L @@ -216,8 +216,8 @@ class ip_network_base : public Base { * @note This method is available for C++20 and later versions where `char8_t` is supported. */ static IPADDRESS_CONSTEXPR ip_network_base parse(std::u8string_view address, error_code& code, bool strict = true) IPADDRESS_NOEXCEPT { - auto index = 0; - return parse_address_with_prefix(address, strict, code, index); + uint32_t value = 0; + return parse_address_with_prefix(address, strict, code, value); } #endif // __cpp_char8_t @@ -236,8 +236,8 @@ class ip_network_base : public Base { * @remark For C++ versions prior to C++17, member functions with `std::u16string` and C-strings will be used instead. */ static IPADDRESS_CONSTEXPR ip_network_base parse(std::u16string_view address, error_code& code, bool strict = true) IPADDRESS_NOEXCEPT { - auto index = 0; - return parse_address_with_prefix(address, strict, code, index); + uint32_t value = 0; + return parse_address_with_prefix(address, strict, code, value); } /** @@ -254,8 +254,8 @@ class ip_network_base : public Base { * @remark For C++ versions prior to C++17, member functions with `std::u32string` and C-strings will be used instead. */ static IPADDRESS_CONSTEXPR ip_network_base parse(std::u32string_view address, error_code& code, bool strict = true) IPADDRESS_NOEXCEPT { - auto index = 0; - return parse_address_with_prefix(address, strict, code, index); + uint32_t value = 0; + return parse_address_with_prefix(address, strict, code, value); } #else // IPADDRESS_CPP_VERSION < 17 @@ -317,8 +317,8 @@ class ip_network_base : public Base { * @return An ip network object representing the parsed network. */ static ip_network_base parse(const std::string& address, error_code& code, bool strict = true) IPADDRESS_NOEXCEPT { - auto index = 0; - return parse_address_with_prefix(address, strict, code, index); + uint32_t value = 0; + return parse_address_with_prefix(address, strict, code, value); } /** @@ -330,8 +330,8 @@ class ip_network_base : public Base { * @return An ip network object representing the parsed network. */ static ip_network_base parse(const std::wstring& address, error_code& code, bool strict = true) IPADDRESS_NOEXCEPT { - auto index = 0; - return parse_address_with_prefix(address, strict, code, index); + uint32_t value = 0; + return parse_address_with_prefix(address, strict, code, value); } /** @@ -343,8 +343,8 @@ class ip_network_base : public Base { * @return An ip network object representing the parsed network. */ static ip_network_base parse(const std::u16string& address, error_code& code, bool strict = true) IPADDRESS_NOEXCEPT { - auto index = 0; - return parse_address_with_prefix(address, strict, code, index); + uint32_t value = 0; + return parse_address_with_prefix(address, strict, code, value); } /** @@ -356,8 +356,8 @@ class ip_network_base : public Base { * @return An ip network object representing the parsed network. */ static ip_network_base parse(const std::u32string& address, error_code& code, bool strict = true) IPADDRESS_NOEXCEPT { - auto index = 0; - return parse_address_with_prefix(address, strict, code, index); + uint32_t value = 0; + return parse_address_with_prefix(address, strict, code, value); } #endif // IPADDRESS_CPP_VERSION < 17 @@ -376,7 +376,6 @@ class ip_network_base : public Base { */ template IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network_base parse(const T(&address)[N], bool strict = true) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { - internal::is_char_type(); auto str = make_fixed_string(address); return parse_address_with_prefix(str, strict); } @@ -396,10 +395,9 @@ class ip_network_base : public Base { */ template static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network_base parse(const T(&address)[N], error_code& code, bool strict = true) IPADDRESS_NOEXCEPT { - internal::is_char_type(); - auto str = make_fixed_string(address); - auto index = 0; - return parse_address_with_prefix(str, strict, code, index); + auto str = make_fixed_string(address, code); + uint32_t value = 0; + return code == error_code::no_error ? parse_address_with_prefix(str, strict, code, value) : ip_network_base{}; } /** @@ -580,6 +578,62 @@ class ip_network_base : public Base { return _network_address.to_string(fmt) + '/' + std::to_string(_prefixlen); } + /** + * Converts the network to a string representation. + * + * This method returns a string representation of the network, combining the network address + * and the prefix length, formatted according to the specified format. + * + * @param[in] fmt The format to use for the string representation. *Defaults to format::compressed*. + * @return A string representation of the network. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::wstring to_wstring(format fmt = format::compressed) const { + return internal::string_converter::convert(to_string(fmt)); + } + + /** + * Converts the network to a string representation. + * + * This method returns a string representation of the network, combining the network address + * and the prefix length, formatted according to the specified format. + * + * @param[in] fmt The format to use for the string representation. *Defaults to format::compressed*. + * @return A string representation of the network. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u16string to_u16string(format fmt = format::compressed) const { + return internal::string_converter::convert(to_string(fmt)); + } + + /** + * Converts the network to a string representation. + * + * This method returns a string representation of the network, combining the network address + * and the prefix length, formatted according to the specified format. + * + * @param[in] fmt The format to use for the string representation. *Defaults to format::compressed*. + * @return A string representation of the network. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u32string to_u32string(format fmt = format::compressed) const { + return internal::string_converter::convert(to_string(fmt)); + } + +#if __cpp_char8_t >= 201811L + + /** + * Converts the network to a string representation. + * + * This method returns a string representation of the network, combining the network address + * and the prefix length, formatted according to the specified format. + * + * @param[in] fmt The format to use for the string representation. *Defaults to format::compressed*. + * @return A string representation of the network. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u8string to_u8string(format fmt = format::compressed) const { + return internal::string_converter::convert(to_string(fmt)); + } + +#endif // __cpp_char8_t + /** * Swaps the contents of this network with another network. * @@ -1072,10 +1126,12 @@ class ip_network_base : public Base { /** * Converts the ip network object to a std::string. * - * @return A `std::string` representation of the ip network object. + * @tparam T The character type of the string. + * @return A string representation of the ip network object. */ - IPADDRESS_NODISCARD explicit operator std::string() const { - return to_string(); + template + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE explicit operator std::basic_string, std::allocator>() const { + return internal::string_converter::convert(to_string()); } /** @@ -1086,7 +1142,7 @@ class ip_network_base : public Base { * @param[in] rhs The other ip network object to compare with. * @return `true` if both objects are equal, `false` otherwise. */ - IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR bool operator==(const ip_network_base& rhs) const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator==(const ip_network_base& rhs) const IPADDRESS_NOEXCEPT { return _network_address == rhs._network_address && _netmask == rhs._netmask; } @@ -1098,7 +1154,7 @@ class ip_network_base : public Base { * @param[in] rhs The other ip network object to compare with. * @return `true` if both objects are not equal, `false` otherwise. */ - IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR bool operator!=(const ip_network_base& rhs) const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator!=(const ip_network_base& rhs) const IPADDRESS_NOEXCEPT { return !(*this == rhs); } @@ -1112,7 +1168,7 @@ class ip_network_base : public Base { * @param[in] rhs The other ip network object to compare with. * @return `std::strong_ordering` result of the comparison. */ - IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR std::strong_ordering operator<=>(const ip_network_base& rhs) const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::strong_ordering operator<=>(const ip_network_base& rhs) const IPADDRESS_NOEXCEPT { if (auto result = _network_address <=> rhs._network_address; result != std::strong_ordering::equivalent) { return result; } @@ -1129,7 +1185,7 @@ class ip_network_base : public Base { * @param[in] rhs The other ip network object to compare with. * @return `true` if this object is less than the other, `false` otherwise. */ - IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR bool operator<(const ip_network_base& rhs) const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator<(const ip_network_base& rhs) const IPADDRESS_NOEXCEPT { if (_network_address != rhs._network_address) { return _network_address < rhs._network_address; } @@ -1147,7 +1203,7 @@ class ip_network_base : public Base { * @param[in] rhs The other ip network object to compare with. * @return `true` if this object is greater than the other, `false` otherwise. */ - IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR bool operator>(const ip_network_base& rhs) const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator>(const ip_network_base& rhs) const IPADDRESS_NOEXCEPT { return rhs < *this; } @@ -1159,7 +1215,7 @@ class ip_network_base : public Base { * @param[in] rhs The other ip network object to compare with. * @return `true` if this object is less than or equal to the other, `false` otherwise. */ - IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR bool operator<=(const ip_network_base& rhs) const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator<=(const ip_network_base& rhs) const IPADDRESS_NOEXCEPT { return !(rhs < *this); } @@ -1171,7 +1227,7 @@ class ip_network_base : public Base { * @param[in] rhs The other ip network object to compare with. * @return `true` if this object is greater than or equal to the other, `false` otherwise. */ - IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR bool operator>=(const ip_network_base& rhs) const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool operator>=(const ip_network_base& rhs) const IPADDRESS_NOEXCEPT { return !(*this < rhs); } @@ -1181,34 +1237,38 @@ class ip_network_base : public Base { template static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network_base parse_address_with_prefix(const Str& str, bool strict) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { auto code = error_code::no_error; - auto index = 0; - auto result = parse_address_with_prefix(str, strict, code, index); + uint32_t value = 0; + auto result = parse_address_with_prefix(str, strict, code, value); if (code != error_code::no_error) { - raise_error(code, index, str.data(), str.size()); + raise_error(code, value, str.data(), str.size()); } return result; } template - static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network_base parse_address_with_prefix(const Str& str, bool strict, error_code& code, int& index) IPADDRESS_NOEXCEPT { + static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network_base parse_address_with_prefix(const Str& str, bool strict, error_code& code, uint32_t& code_value) IPADDRESS_NOEXCEPT { code = error_code::no_error; - + auto it = str.data(); + auto end = str.data() + str.size(); auto has_slash = false; - auto netmask = str.end(); + auto netmask = end; auto symbol = 0; char address[ip_address_type::base_max_string_len + 1] = {}; - for (auto it = str.begin(); it != str.end(); ++it) { - const auto c = char(*it); + while (it < end) { + const auto c = internal::next_char_or_error(it, end, code, code_value); + if (code != error_code::no_error) { + return ip_network_base(); + } if (c == '/') { if (has_slash) { code = error_code::only_one_slash_permitted; return ip_network_base(); } - if (it + 1 == str.end()) { + if (it == end) { code = error_code::empty_netmask; return ip_network_base(); } - netmask = it + 1; + netmask = it; has_slash = true; } if (!has_slash) { @@ -1219,7 +1279,7 @@ class ip_network_base : public Base { } } - auto netmask_result = ip_address_type::parse_netmask(netmask, str.end(), code, index); + auto netmask_result = ip_address_type::parse_netmask(netmask, end, code, code_value); if (code != error_code::no_error) { return ip_network_base(); @@ -1257,7 +1317,7 @@ class ip_network_base : public Base { namespace internal { template -IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network_base parse_net_from_literal(const TChar* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { +IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_network_base parse_net_from_literal(const TChar* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { if (size > MaxLen) { raise_error(error_code::string_is_too_long, 0, address, size); } @@ -1274,17 +1334,19 @@ IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLIN #ifndef IPADDRESS_NO_OVERLOAD_STD -inline int network_strict_index() { +IPADDRESS_FORCE_INLINE int network_strict_index() { static int i = std::ios_base::xalloc(); return i; } -inline std::istream& strict(std::istream& stream) { +template +IPADDRESS_FORCE_INLINE std::basic_istream>& strict(std::basic_istream>& stream) { stream.iword(network_strict_index()) = 0; return stream; } -inline std::istream& non_strict(std::istream& stream) { +template +IPADDRESS_FORCE_INLINE std::basic_istream>& non_strict(std::basic_istream>& stream) { stream.iword(network_strict_index()) = 1; return stream; } @@ -1299,38 +1361,50 @@ namespace std { template struct hash> { - IPADDRESS_CONSTEXPR std::size_t operator()(const IPADDRESS_NAMESPACE::ip_network_base& network) const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t operator()(const IPADDRESS_NAMESPACE::ip_network_base& network) const IPADDRESS_NOEXCEPT { return network.hash(); } }; template -inline IPADDRESS_CONSTEXPR void swap(IPADDRESS_NAMESPACE::ip_network_base& net1, IPADDRESS_NAMESPACE::ip_network_base& net2) IPADDRESS_NOEXCEPT { +IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void swap(IPADDRESS_NAMESPACE::ip_network_base& net1, IPADDRESS_NAMESPACE::ip_network_base& net2) IPADDRESS_NOEXCEPT { return net1.swap(net2); } template -inline std::string to_string(const IPADDRESS_NAMESPACE::ip_network_base& network) { +IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::string to_string(const IPADDRESS_NAMESPACE::ip_network_base& network) { return network.to_string(); } template -inline std::ostream& operator<<(std::ostream& stream, const IPADDRESS_NAMESPACE::ip_network_base& network) { +IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::wstring to_wstring(const IPADDRESS_NAMESPACE::ip_network_base& network) { + return network.to_wstring(); +} + +template +IPADDRESS_FORCE_INLINE std::basic_ostream>& operator<<(std::basic_ostream>& stream, const IPADDRESS_NAMESPACE::ip_network_base& network) { auto& iword = stream.iword(IPADDRESS_NAMESPACE::stream_index()); auto fmt = iword ? (IPADDRESS_NAMESPACE::format) (iword - 1) : IPADDRESS_NAMESPACE::format::compressed; iword = 0; - return stream << network.to_string(fmt); + auto str = network.to_string(fmt); + if (stream.flags() & ios_base::uppercase) { + auto end = std::find(str.cbegin(), str.cend(), '%'); + std::transform(str.cbegin(), end, str.begin(), [](char c){ + return std::toupper(c); + }); + } + return stream << IPADDRESS_NAMESPACE::internal::string_converter::convert(str); } -template -inline std::istream& operator>>(std::istream& stream, IPADDRESS_NAMESPACE::ip_network_base& network) { +template +IPADDRESS_FORCE_INLINE std::basic_istream>& operator>>(std::basic_istream>& stream, IPADDRESS_NAMESPACE::ip_network_base& network) { auto& iword = stream.iword(IPADDRESS_NAMESPACE::network_strict_index()); auto strict = iword == 0; iword = 0; - std::string str; + std::basic_string, std::allocator> str; stream >> str; IPADDRESS_NAMESPACE::error_code err = IPADDRESS_NAMESPACE::error_code::no_error; network = IPADDRESS_NAMESPACE::ip_network_base::parse(str, err, strict); diff --git a/include/ipaddress/ip-network-iterator.hpp b/include/ipaddress/ip-network-iterator.hpp index c97561d..331c682 100644 --- a/include/ipaddress/ip-network-iterator.hpp +++ b/include/ipaddress/ip-network-iterator.hpp @@ -34,7 +34,7 @@ class ip_network_iterator { public: using iterator_category = std::random_access_iterator_tag; /**< The category of the iterator. */ using value_type = T; /**< The type of value iterated over. */ - using difference_type = std::int64_t; /**< Type to represent the difference between two iterators. */ + using difference_type = int64_t; /**< Type to represent the difference between two iterators. */ using pointer = const value_type*; /**< Pointer to the value type. */ using reference = const value_type&; /**< Reference to the value type. */ @@ -463,7 +463,7 @@ class ip_exclude_network_iterator { public: using iterator_category = std::forward_iterator_tag; /**< The category of the iterator. */ using value_type = T; /**< The type of value iterated over. */ - using difference_type = std::int64_t; /**< Type to represent the difference between two iterators. */ + using difference_type = int64_t; /**< Type to represent the difference between two iterators. */ using pointer = const value_type*; /**< Pointer to the value type. */ using reference = const value_type&; /**< Reference to the value type. */ @@ -678,7 +678,7 @@ template class subnets_sequence { public: using value_type = T; /**< The type of subnet value. */ - using size_type = std::size_t; /**< An unsigned integral type. */ + using size_type = size_t; /**< An unsigned integral type. */ using difference_type = typename value_type::uint_type; /**< Unsigned integer type for differences. */ using pointer = value_type*; /**< Pointer to the subnet type. */ using const_pointer = const value_type*; /**< Const pointer to the subnet type. */ @@ -890,7 +890,7 @@ template class exclude_network_sequence { public: using value_type = T; /**< The type of network value. */ - using size_type = std::size_t; /**< An unsigned integral type. */ + using size_type = size_t; /**< An unsigned integral type. */ using difference_type = typename value_type::uint_type; /**< Unsigned integer type for differences. */ using pointer = value_type*; /**< Pointer to the network type. */ using const_pointer = const value_type*; /**< Const pointer to the network type. */ diff --git a/include/ipaddress/ip-networks.hpp b/include/ipaddress/ip-networks.hpp index f68b5ee..6820267 100644 --- a/include/ipaddress/ip-networks.hpp +++ b/include/ipaddress/ip-networks.hpp @@ -26,7 +26,7 @@ struct networks { static const ipv4_network ipv4_private_networks[]; static const ipv6_network ipv6_private_networks[]; - static const ipv4_network ipv4_is_global; + static const ipv4_network ipv4_is_public_network; static const ipv4_network ipv4_reserved_network; static const ipv6_network ipv6_reserved_networks[]; @@ -93,7 +93,7 @@ static constexpr ipv4_network template const ipv4_network networks:: #endif // NOLINTNEXTLINE(cert-err58-cpp): for C++11 - ipv4_is_global = ipv4_network::parse("100.64.0.0/10"); + ipv4_is_public_network = ipv4_network::parse("100.64.0.0/10"); // Reserved networks #if __cpp_constexpr >= 201304L @@ -190,7 +190,7 @@ template constexpr ipv6_network networks::ipv6_private_networks[]; template -constexpr ipv4_network networks::ipv4_is_global; +constexpr ipv4_network networks::ipv4_is_public_network; template constexpr ipv4_network networks::ipv4_reserved_network; @@ -248,7 +248,7 @@ IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool ipv6_network template<> IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool ipv4_network::is_global() const IPADDRESS_NOEXCEPT { - const auto& network = internal::nets::ipv4_is_global; + const auto& network = internal::nets::ipv4_is_public_network; const auto& address = network_address(); const auto broadcast = broadcast_address(); return !(network.contains(address) && network.contains(broadcast)) && !is_private(); @@ -298,7 +298,7 @@ IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool ipv6_address template<> IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool ipv4_address::is_global() const IPADDRESS_NOEXCEPT { - return !internal::nets::ipv4_is_global.contains(*this) && !is_private(); + return !internal::nets::ipv4_is_public_network.contains(*this) && !is_private(); } template<> diff --git a/include/ipaddress/ipv4-address.hpp b/include/ipaddress/ipv4-address.hpp index 710146a..5d2d80f 100644 --- a/include/ipaddress/ipv4-address.hpp +++ b/include/ipaddress/ipv4-address.hpp @@ -81,7 +81,7 @@ class ipv4_address_base : public base_v4 { lhs._bytes.swap(rhs._bytes); } - IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::size_t hash(const base_type& bytes) IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t hash(const base_type& bytes) IPADDRESS_NOEXCEPT { return internal::calc_hash(0, size_t(bytes[0]), size_t(bytes[1]), size_t(bytes[2]), size_t(bytes[3])); } @@ -151,7 +151,7 @@ using ipv4_address = ip_address_base; * @param[in] size The size of the character array. * @return An ipv4_address object parsed from the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_address operator""_ipv4(const char* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_address operator""_ipv4(const char* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_ip_from_literal(address, size); } @@ -162,7 +162,7 @@ using ipv4_address = ip_address_base; * @param[in] size The size of the character array. * @return An ipv4_address object parsed from the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_address operator""_ipv4(const wchar_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_address operator""_ipv4(const wchar_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_ip_from_literal(address, size); } @@ -173,7 +173,7 @@ using ipv4_address = ip_address_base; * @param[in] size The size of the character array. * @return An ipv4_address object parsed from the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_address operator""_ipv4(const char16_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_address operator""_ipv4(const char16_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_ip_from_literal(address, size); } @@ -184,7 +184,7 @@ using ipv4_address = ip_address_base; * @param[in] size The size of the character array. * @return An ipv4_address object parsed from the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_address operator""_ipv4(const char32_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_address operator""_ipv4(const char32_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_ip_from_literal(address, size); } diff --git a/include/ipaddress/ipv4-network.hpp b/include/ipaddress/ipv4-network.hpp index c9ef21e..42f482f 100644 --- a/include/ipaddress/ipv4-network.hpp +++ b/include/ipaddress/ipv4-network.hpp @@ -77,7 +77,7 @@ using ipv4_network = ip_network_base; * @param[in] size The size of the string literal. * @return An ipv4_network object representing the network specified by the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_network operator""_ipv4_net(const char* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_network operator""_ipv4_net(const char* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_net_from_literal(address, size); } @@ -91,7 +91,7 @@ using ipv4_network = ip_network_base; * @param[in] size The size of the string literal. * @return An ipv4_network object representing the network specified by the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_network operator""_ipv4_net(const wchar_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_network operator""_ipv4_net(const wchar_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_net_from_literal(address, size); } @@ -105,7 +105,7 @@ using ipv4_network = ip_network_base; * @param[in] size The size of the string literal. * @return An ipv4_network object representing the network specified by the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_network operator""_ipv4_net(const char16_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_network operator""_ipv4_net(const char16_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_net_from_literal(address, size); } @@ -119,7 +119,7 @@ using ipv4_network = ip_network_base; * @param[in] size The size of the string literal. * @return An ipv4_network object representing the network specified by the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_network operator""_ipv4_net(const char32_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv4_network operator""_ipv4_net(const char32_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_net_from_literal(address, size); } diff --git a/include/ipaddress/ipv6-address.hpp b/include/ipaddress/ipv6-address.hpp index 349e656..cd35370 100644 --- a/include/ipaddress/ipv6-address.hpp +++ b/include/ipaddress/ipv6-address.hpp @@ -20,20 +20,36 @@ namespace IPADDRESS_NAMESPACE { namespace internal { -// Parsing has been removed from the ipv6_address class due to a bug -// in the Clang compiler in version 14 and below -// https://bugs.llvm.org/show_bug.cgi?id=18781 template struct ipv6_set_scope { template static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void change(fixed_string& result, const Str& scope_id) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { - error_code err = error_code::no_error; - change(result, scope_id, err); - #ifndef IPADDRESS_NO_EXCEPTIONS - if (err != error_code::no_error) { - raise_error(err, 0, "", 7); + #if IPADDRESS_IPV6_SCOPE_MAX_LENGTH > 0 + if (scope_id.size() > IPADDRESS_IPV6_SCOPE_MAX_LENGTH) { + raise_error(error_code::scope_id_is_too_long, 0, scope_id.data(), scope_id.size()); + return; } - #endif + typename Str::value_type scope[IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1] = {}; + for (size_t i = 0; i < scope_id.size(); ++i) { + scope[i] = scope_id[i]; + } + #ifdef IPADDRESS_NO_EXCEPTIONS + auto code = error_code::no_error; + auto str = make_fixed_string(scope, code); + if (code != error_code::no_error) { + return; + } + #else // !IPADDRESS_NO_EXCEPTIONS + auto str = make_fixed_string(scope); + #endif // !IPADDRESS_NO_EXCEPTIONS + for (const char c : str) { + if (uint32_t(c) > 127) { + raise_error(error_code::invalid_scope_id, 0, scope_id.data(), scope_id.size()); + return; + } + } + result = str; + #endif // IPADDRESS_IPV6_SCOPE_MAX_LENGTH } template @@ -44,11 +60,20 @@ struct ipv6_set_scope { code = error_code::scope_id_is_too_long; return; } - char scope[IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1] = {}; + typename Str::value_type scope[IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1] = {}; for (size_t i = 0; i < scope_id.size(); ++i) { - scope[i] = char(scope_id[i]); + scope[i] = scope_id[i]; + } + const auto new_scope_id = make_fixed_string(scope, code); + if (code == error_code::no_error) { + for (const char c : new_scope_id) { + if (uint32_t(c) > 127) { + code = error_code::invalid_scope_id; + return; + } + } + result = new_scope_id; } - result = make_fixed_string(scope); #endif // IPADDRESS_IPV6_SCOPE_MAX_LENGTH } }; @@ -279,15 +304,62 @@ class ipv6_address_base : public base_v6 { * @remark If scope is disabled in settings (`IPADDRESS_IPV6_SCOPE_MAX_LENGTH == 0`) then this call will have no effect. */ template - IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void set_scope_id(const T(&scope_id)[N]) IPADDRESS_NOEXCEPT { - internal::is_char_type(); + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void set_scope_id(const T(&scope_id)[N]) IPADDRESS_NOEXCEPT(noexcept(internal::char_reader::has_throw())) { + #if IPADDRESS_IPV6_SCOPE_MAX_LENGTH > 0 + static_assert(N <= IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1, "scope id is too long"); + T str[IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1] = {}; + for (size_t i = 0; i < N; ++i) { + str[i] = scope_id[i]; + } + #ifdef IPADDRESS_NO_EXCEPTIONS + auto code = error_code::no_error; + auto result = make_fixed_string(str, code); + if (code != error_code::no_error) { + return; + } + #else // !IPADDRESS_NO_EXCEPTIONS + auto result = make_fixed_string(str); + for (const char c : result) { + if (uint32_t(c) > 127) { + raise_error(error_code::invalid_scope_id, 0, scope_id, N); + return; + } + } + #endif // !IPADDRESS_NO_EXCEPTIONS + _data.scope_id = result; + #endif // IPADDRESS_IPV6_SCOPE_MAX_LENGTH + } + + /** + * Sets the scope identifier of the IPv6 address and reports any errors encountered. + * + * This function sets the scope identifier using a character array. The length of the array + * should not exceed `IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1`. + * + * @tparam T The character type of the scope identifier. + * @tparam N The size of the scope identifier array. + * @param[in] scope_id The character array representing the scope identifier. + * @param[out] code An error_code object that will be set to the error that occurred, if any. + * @remark If scope is disabled in settings (`IPADDRESS_IPV6_SCOPE_MAX_LENGTH == 0`) then this call will have no effect. + */ + template + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void set_scope_id(const T(&scope_id)[N], error_code& code) IPADDRESS_NOEXCEPT { #if IPADDRESS_IPV6_SCOPE_MAX_LENGTH > 0 static_assert(N <= IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1, "scope id is too long"); - char str[IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1] = {}; + T str[IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1] = {}; for (size_t i = 0; i < N; ++i) { str[i] = scope_id[i]; } - _data.scope_id = make_fixed_string(str); + const auto result = make_fixed_string(str, code); + if (code == error_code::no_error) { + for (const char c : result) { + if (uint32_t(c) > 127) { + code = error_code::invalid_scope_id; + return; + } + } + _data.scope_id = result; + } #endif // IPADDRESS_IPV6_SCOPE_MAX_LENGTH } @@ -733,7 +805,7 @@ class ipv6_address_base : public base_v6 { #endif // IPADDRESS_IPV6_SCOPE_MAX_LENGTH } - IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::size_t hash(const base_type& bytes) const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t hash(const base_type& bytes) const IPADDRESS_NOEXCEPT { return internal::calc_hash( #if IPADDRESS_IPV6_SCOPE_MAX_LENGTH > 0 _data.scope_id.hash(), @@ -802,36 +874,6 @@ class ipv6_address_base : public base_v6 { template friend class ip_network_base; - // not used due to clang bug in version 14 and below - // https://bugs.llvm.org/show_bug.cgi?id=18781 - // - // template - // IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void change_scope_id(const Str& scope_id) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { - // error_code err = error_code::no_error; - // change_scope_id(scope_id, err); - // #ifndef IPADDRESS_NO_EXCEPTIONS - // if (err != error_code::no_error) { - // raise_error(err, 0, "", 7); - // } - // #endif - // } - // - // template - // IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void change_scope_id(const Str& scope_id, error_code& code) IPADDRESS_NOEXCEPT { - // code = error_code::no_error; - // #if IPADDRESS_IPV6_SCOPE_MAX_LENGTH > 0 - // if (scope_id.size() > IPADDRESS_IPV6_SCOPE_MAX_LENGTH) { - // code = error_code::scope_id_is_too_long; - // return; - // } - // char scope[IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1] = {}; - // for (size_t i = 0; i < scope_id.size(); ++i) { - // scope[i] = char(scope_id[i]); - // } - // _data.scope_id = make_fixed_string(scope); - // #endif // IPADDRESS_IPV6_SCOPE_MAX_LENGTH - // } - struct ipv6_data { IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_data() IPADDRESS_NOEXCEPT = default; IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_data(const base_type& b) IPADDRESS_NOEXCEPT : bytes(b) { @@ -876,7 +918,7 @@ using ipv6_address = ip_address_base; * @param[in] size The size of the character array. * @return An ipv6_address object parsed from the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_address operator""_ipv6(const char* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_address operator""_ipv6(const char* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_ip_from_literal(address, size); } @@ -887,7 +929,7 @@ using ipv6_address = ip_address_base; * @param[in] size The size of the character array. * @return An ipv6_address object parsed from the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_address operator""_ipv6(const wchar_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_address operator""_ipv6(const wchar_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_ip_from_literal(address, size); } @@ -898,7 +940,7 @@ using ipv6_address = ip_address_base; * @param[in] size The size of the character array. * @return An ipv6_address object parsed from the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_address operator""_ipv6(const char16_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_address operator""_ipv6(const char16_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_ip_from_literal(address, size); } @@ -909,7 +951,7 @@ using ipv6_address = ip_address_base; * @param[in] size The size of the character array. * @return An ipv6_address object parsed from the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_address operator""_ipv6(const char32_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_address operator""_ipv6(const char32_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_ip_from_literal(address, size); } diff --git a/include/ipaddress/ipv6-network.hpp b/include/ipaddress/ipv6-network.hpp index aba5017..ad6752e 100644 --- a/include/ipaddress/ipv6-network.hpp +++ b/include/ipaddress/ipv6-network.hpp @@ -93,7 +93,7 @@ using ipv6_network = ip_network_base; * @param[in] size The size of the string literal. * @return An ipv6_network object representing the network specified by the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_network operator""_ipv6_net(const char* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_network operator""_ipv6_net(const char* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_net_from_literal(address, size); } @@ -107,7 +107,7 @@ using ipv6_network = ip_network_base; * @param[in] size The size of the string literal. * @return An ipv6_network object representing the network specified by the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_network operator""_ipv6_net(const wchar_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_network operator""_ipv6_net(const wchar_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_net_from_literal(address, size); } @@ -121,7 +121,7 @@ using ipv6_network = ip_network_base; * @param[in] size The size of the string literal. * @return An ipv6_network object representing the network specified by the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_network operator""_ipv6_net(const char16_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_network operator""_ipv6_net(const char16_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_net_from_literal(address, size); } @@ -135,7 +135,7 @@ using ipv6_network = ip_network_base; * @param[in] size The size of the string literal. * @return An ipv6_network object representing the network specified by the string literal. */ - IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_network operator""_ipv6_net(const char32_t* address, std::size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ipv6_network operator""_ipv6_net(const char32_t* address, size_t size) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { return internal::parse_net_from_literal(address, size); } diff --git a/include/ipaddress/uint128.hpp b/include/ipaddress/uint128.hpp index 85e3e99..801cc88 100644 --- a/include/ipaddress/uint128.hpp +++ b/include/ipaddress/uint128.hpp @@ -68,7 +68,7 @@ class uint128_t final { // NOLINT(cppcoreguidelines-special-member-functions) * * Constructs a new `uint128_t` instance with default values. */ - IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE uint128_t() IPADDRESS_NOEXCEPT = default; /**< Default constructor */ + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE uint128_t() IPADDRESS_NOEXCEPT = default; /** * Copy constructor. @@ -77,7 +77,7 @@ class uint128_t final { // NOLINT(cppcoreguidelines-special-member-functions) * * @param[in] other The `uint128_t` instance to copy. */ - IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE uint128_t(const uint128_t& other) IPADDRESS_NOEXCEPT = default; /**< Copy constructor */ + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE uint128_t(const uint128_t& other) IPADDRESS_NOEXCEPT = default; /** * Move constructor. @@ -86,7 +86,7 @@ class uint128_t final { // NOLINT(cppcoreguidelines-special-member-functions) * * @param[in,out] other The `uint128_t` instance to move. */ - IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE uint128_t(uint128_t&& other) IPADDRESS_NOEXCEPT = default; /**< Move constructor */ + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE uint128_t(uint128_t&& other) IPADDRESS_NOEXCEPT = default; /** * Assignment operator. @@ -96,7 +96,7 @@ class uint128_t final { // NOLINT(cppcoreguidelines-special-member-functions) * @param[in] other The `uint128_t` instance to assign from. * @return A reference to the assigned `uint128_t` instance. */ - IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE uint128_t& operator=(const uint128_t& other) IPADDRESS_NOEXCEPT = default; /**< Assignment operator */ + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE uint128_t& operator=(const uint128_t& other) IPADDRESS_NOEXCEPT = default; /** * Move assignment operator. @@ -106,7 +106,7 @@ class uint128_t final { // NOLINT(cppcoreguidelines-special-member-functions) * @param[in,out] other The `uint128_t` instance to move from. * @return A reference to the moved `uint128_t` instance. */ - IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE uint128_t& operator=(uint128_t&& other) IPADDRESS_NOEXCEPT = default; /**< Move assignment operator */ + IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE uint128_t& operator=(uint128_t&& other) IPADDRESS_NOEXCEPT = default; /** * Constructs a `uint128_t` instance from upper and lower parts. @@ -236,7 +236,7 @@ class uint128_t final { // NOLINT(cppcoreguidelines-special-member-functions) * @param[in] value The floating-point value to convert. */ IPADDRESS_FORCE_INLINE explicit uint128_t(double value) IPADDRESS_NOEXCEPT { - const auto result = from_double(double(value)); + const auto result = from_double(value); _upper = result._upper; _lower = result._lower; } @@ -290,9 +290,9 @@ class uint128_t final { // NOLINT(cppcoreguidelines-special-member-functions) * * This method calculates a hash value for the `uint128_t` instance, which can be used in hash-based data structures. * - * @return A `std::size_t` representing the hash value of the `uint128_t` instance. + * @return A `size_t` representing the hash value of the `uint128_t` instance. */ - IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::size_t hash() const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t hash() const IPADDRESS_NOEXCEPT { internal::hash_combine<8> hasher{}; const auto seed = hasher(_upper); const auto hash = hasher(seed + 0x9e3779b9 + _lower); @@ -349,6 +349,62 @@ class uint128_t final { // NOLINT(cppcoreguidelines-special-member-functions) } } + /** + * Converts the `uint128_t` value to a string representation. + * + * This method converts the `uint128_t` instance to its string representation in the specified format. + * It supports decimal, octal, and hexadecimal formats. + * + * @param[in] fmt The format to use for the conversion, with `format::decimal` as the default. + * @return A `std::wstring` holding the converted value. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::wstring to_wstring(format fmt = format::decimal) const { + return internal::string_converter::convert(to_string(fmt)); + } + + /** + * Converts the `uint128_t` value to a string representation. + * + * This method converts the `uint128_t` instance to its string representation in the specified format. + * It supports decimal, octal, and hexadecimal formats. + * + * @param[in] fmt The format to use for the conversion, with `format::decimal` as the default. + * @return A `std::u16string` holding the converted value. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u16string to_u16string(format fmt = format::decimal) const { + return internal::string_converter::convert(to_string(fmt)); + } + + /** + * Converts the `uint128_t` value to a string representation. + * + * This method converts the `uint128_t` instance to its string representation in the specified format. + * It supports decimal, octal, and hexadecimal formats. + * + * @param[in] fmt The format to use for the conversion, with `format::decimal` as the default. + * @return A `std::u32string` holding the converted value. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u32string to_u32string(format fmt = format::decimal) const { + return internal::string_converter::convert(to_string(fmt)); + } + +#if __cpp_char8_t >= 201811L + + /** + * Converts the `uint128_t` value to a string representation. + * + * This method converts the `uint128_t` instance to its string representation in the specified format. + * It supports decimal, octal, and hexadecimal formats. + * + * @param[in] fmt The format to use for the conversion, with `format::decimal` as the default. + * @return A `std::u8string` holding the converted value. + */ + IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::u8string to_u8string(format fmt = format::decimal) const { + return internal::string_converter::convert(to_string(fmt)); + } + +#endif // __cpp_char8_t + /** * Parses a string to a `uint128_t` instance. * @@ -360,14 +416,84 @@ class uint128_t final { // NOLINT(cppcoreguidelines-special-member-functions) * @return An optional containing the parsed `uint128_t` value if successful, otherwise an empty optional. */ IPADDRESS_NODISCARD static IPADDRESS_FORCE_INLINE optional from_string(const std::string& str, format fmt = format::decimal) IPADDRESS_NOEXCEPT { - switch (fmt) { - case format::octal: - return oct_str_to_uint128(str); - case format::hexadecimal: - return hex_str_to_uint128(str); - default: - return dec_str_to_uint128(str); - } + return str_to_uint128(str.data(), str.data() + str.length(), fmt); + } + + /** + * Parses a string to a `uint128_t` instance. + * + * This static method attempts to parse a given string as a `uint128_t` value in the specified format. + * If the parsing is successful, it returns an optional containing the parsed `uint128_t` value. + * + * @param[in] str The input string to parse. + * @param[in] fmt The string format to interpret the input string (*defaults to decimal*). + * @return An optional containing the parsed `uint128_t` value if successful, otherwise an empty optional. + */ + IPADDRESS_NODISCARD static IPADDRESS_FORCE_INLINE optional from_string(const std::wstring& str, format fmt = format::decimal) IPADDRESS_NOEXCEPT { + return str_to_uint128(str.data(), str.data() + str.length(), fmt); + } + + /** + * Parses a string to a `uint128_t` instance. + * + * This static method attempts to parse a given string as a `uint128_t` value in the specified format. + * If the parsing is successful, it returns an optional containing the parsed `uint128_t` value. + * + * @param[in] str The input string to parse. + * @param[in] fmt The string format to interpret the input string (*defaults to decimal*). + * @return An optional containing the parsed `uint128_t` value if successful, otherwise an empty optional. + */ + IPADDRESS_NODISCARD static IPADDRESS_FORCE_INLINE optional from_string(const std::u16string& str, format fmt = format::decimal) IPADDRESS_NOEXCEPT { + return str_to_uint128(str.data(), str.data() + str.length(), fmt); + } + + /** + * Parses a string to a `uint128_t` instance. + * + * This static method attempts to parse a given string as a `uint128_t` value in the specified format. + * If the parsing is successful, it returns an optional containing the parsed `uint128_t` value. + * + * @param[in] str The input string to parse. + * @param[in] fmt The string format to interpret the input string (*defaults to decimal*). + * @return An optional containing the parsed `uint128_t` value if successful, otherwise an empty optional. + */ + IPADDRESS_NODISCARD static IPADDRESS_FORCE_INLINE optional from_string(const std::u32string& str, format fmt = format::decimal) IPADDRESS_NOEXCEPT { + return str_to_uint128(str.data(), str.data() + str.length(), fmt); + } + +#if __cpp_char8_t >= 201811L + + /** + * Parses a string to a `uint128_t` instance. + * + * This static method attempts to parse a given string as a `uint128_t` value in the specified format. + * If the parsing is successful, it returns an optional containing the parsed `uint128_t` value. + * + * @param[in] str The input string to parse. + * @param[in] fmt The string format to interpret the input string (*defaults to decimal*). + * @return An optional containing the parsed `uint128_t` value if successful, otherwise an empty optional. + */ + IPADDRESS_NODISCARD static IPADDRESS_FORCE_INLINE optional from_string(const std::u8string& str, format fmt = format::decimal) IPADDRESS_NOEXCEPT { + return str_to_uint128(str.data(), str.data() + str.length(), fmt); + } + +#endif // __cpp_char8_t + + /** + * Parses a string to a `uint128_t` instance. + * + * This static method attempts to parse a given string as a `uint128_t` value in the specified format. + * If the parsing is successful, it returns an optional containing the parsed `uint128_t` value. + * + * @tparam T The character type of the array. + * @tparam N The size of the character array. + * @param[in] str The input string to parse. + * @param[in] fmt The string format to interpret the input string (*defaults to decimal*). + * @return An optional containing the parsed `uint128_t` value if successful, otherwise an empty optional. + */ + template + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE optional from_string(const T(&str)[N], format fmt = format::decimal) IPADDRESS_NOEXCEPT { + return str_to_uint128(str, str + N, fmt); } /** @@ -1439,37 +1565,70 @@ class uint128_t final { // NOLINT(cppcoreguidelines-special-member-functions) return result; } - IPADDRESS_NODISCARD static IPADDRESS_FORCE_INLINE optional dec_str_to_uint128(const std::string& str) IPADDRESS_NOEXCEPT { +#pragma warning(push, 3) +#ifdef __clang__ + _Pragma("clang diagnostic push") + _Pragma("clang diagnostic ignored \"-Wundefined-inline\"") +#endif + template + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE optional str_to_uint128(const T* begin, const T* end, format fmt) IPADDRESS_NOEXCEPT { + switch (fmt) { + case format::octal: + return oct_str_to_uint128(begin, end); + case format::hexadecimal: + return hex_str_to_uint128(begin, end); + default: + return dec_str_to_uint128(begin, end); + } + } +#ifdef __clang__ + _Pragma("clang diagnostic pop") +#endif +#pragma warning(pop) + + template + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE optional dec_str_to_uint128(const T* begin, const T* end) IPADDRESS_NOEXCEPT { uint128_t result = 0; - for (const auto c : str) { - if (c < '0' || c > '9') { + for (const T* it = begin; it != end; ++it) { + auto c = *it; + if (c == T('\0')) { + break; + } else if (c < T('0') || c > T('9')) { return nullptr; } - result = result * 10 + (c - '0'); + result = result * 10 + (c - T('0')); } return result; } - IPADDRESS_NODISCARD static IPADDRESS_FORCE_INLINE optional oct_str_to_uint128(const std::string& str) IPADDRESS_NOEXCEPT { + template + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE optional oct_str_to_uint128(const T* begin, const T* end) IPADDRESS_NOEXCEPT { uint128_t result = 0; - for (const auto c : str) { - if (c < '0' || c > '7') { + for (const T* it = begin; it != end; ++it) { + auto c = *it; + if (c == T('\0')) { + break; + } else if (c < T('0') || c > T('7')) { return nullptr; } - result = result * 8 + (c - '0'); + result = result * 8 + (c - T('0')); } return result; } - IPADDRESS_NODISCARD static IPADDRESS_FORCE_INLINE optional hex_str_to_uint128(const std::string& str) IPADDRESS_NOEXCEPT { + template + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE optional hex_str_to_uint128(const T* begin, const T* end) IPADDRESS_NOEXCEPT { uint128_t result = 0; int digit = 0; - for (const auto c : str) { - if (c >= '0' && c <= '9') { - digit = c - '0'; - } else if (c >= 'A' && c <= 'F') { + for (const T* it = begin; it != end; ++it) { + auto c = *it; + if (c == T('\0')) { + break; + } else if (c >= T('0') && c <= T('9')) { + digit = c - T('0'); + } else if (c >= T('A') && c <= T('F')) { digit = c - 55; - } else if (c >= 'a' && c <= 'f') { + } else if (c >= T('a') && c <= T('f')) { digit = c - 87; } else { return nullptr; @@ -1727,29 +1886,29 @@ namespace std { template struct _numeric_limits_uint128 { - static constexpr bool is_bounded = true; - static constexpr bool is_exact = true; - static constexpr bool is_integer = true; - static constexpr bool is_modulo = true; - static constexpr bool is_specialized = true; - static constexpr bool is_iec559 = false; - static constexpr bool is_signed = false; - static constexpr bool has_denorm_loss = false; - static constexpr bool has_infinity = false; - static constexpr bool has_quiet_NaN = false; - static constexpr bool has_signaling_NaN = false; - static constexpr bool tinyness_before = false; - static constexpr bool traps = false; - static constexpr int max_digits10 = 0; - static constexpr int max_exponent = 0; - static constexpr int max_exponent10 = 0; - static constexpr int min_exponent = 0; - static constexpr int min_exponent10 = 0; - static constexpr int digits = 128; - static constexpr int digits10 = 38; - static constexpr int radix = 2; - static constexpr float_denorm_style has_denorm = denorm_absent; - static constexpr float_round_style round_style = round_toward_zero; + static constexpr bool is_bounded = true; + static constexpr bool is_exact = true; + static constexpr bool is_integer = true; + static constexpr bool is_modulo = true; + static constexpr bool is_specialized = true; + static constexpr bool is_iec559 = false; + static constexpr bool is_signed = false; + static constexpr bool has_denorm_loss = false; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + static constexpr bool tinyness_before = false; + static constexpr bool traps = false; + static constexpr int max_digits10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int digits = 128; + static constexpr int digits10 = 38; + static constexpr int radix = 2; + static constexpr std::float_denorm_style has_denorm = std::denorm_absent; + static constexpr std::float_round_style round_style = std::round_toward_zero; IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE T (min)() IPADDRESS_NOEXCEPT { return 0; @@ -1852,10 +2011,10 @@ template constexpr int _numeric_limits_uint128::radix; template -constexpr float_denorm_style _numeric_limits_uint128::has_denorm; +constexpr std::float_denorm_style _numeric_limits_uint128::has_denorm; template -constexpr float_round_style _numeric_limits_uint128::round_style; +constexpr std::float_round_style _numeric_limits_uint128::round_style; #ifdef __clang__ _Pragma("clang diagnostic pop") @@ -1871,7 +2030,7 @@ struct numeric_limits : _numeric_limits_uint128< template <> struct hash { - IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::size_t operator()(const IPADDRESS_NAMESPACE::uint128_t& value) const IPADDRESS_NOEXCEPT { + IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t operator()(const IPADDRESS_NAMESPACE::uint128_t& value) const IPADDRESS_NOEXCEPT { return value.hash(); } }; @@ -1890,7 +2049,12 @@ IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::string to_string(const IPADDRESS return value.to_string(); } -IPADDRESS_FORCE_INLINE std::ostream& operator<<(std::ostream& stream, const IPADDRESS_NAMESPACE::uint128_t& value) { +IPADDRESS_NODISCARD IPADDRESS_FORCE_INLINE std::wstring to_wstring(const IPADDRESS_NAMESPACE::uint128_t& value) { + return value.to_wstring(); +} + +template +IPADDRESS_FORCE_INLINE std::basic_ostream>& operator<<(std::basic_ostream>& stream, const IPADDRESS_NAMESPACE::uint128_t& value) { auto fmt = IPADDRESS_NAMESPACE::uint128_t::format::decimal; if (stream.flags() & ios_base::hex) { fmt = IPADDRESS_NAMESPACE::uint128_t::format::hexadecimal; @@ -1903,17 +2067,18 @@ IPADDRESS_FORCE_INLINE std::ostream& operator<<(std::ostream& stream, const IPAD return std::toupper(c); }); } - return stream << str; + return stream << IPADDRESS_NAMESPACE::internal::string_converter::convert(str); } -IPADDRESS_FORCE_INLINE std::istream& operator>>(std::istream& stream, IPADDRESS_NAMESPACE::uint128_t& value) { +template +IPADDRESS_FORCE_INLINE std::basic_istream>& operator>>(std::basic_istream>& stream, IPADDRESS_NAMESPACE::uint128_t& value) { auto fmt = IPADDRESS_NAMESPACE::uint128_t::format::decimal; if (stream.flags() & ios_base::hex) { fmt = IPADDRESS_NAMESPACE::uint128_t::format::hexadecimal; } else if (stream.flags() & ios_base::oct) { fmt = IPADDRESS_NAMESPACE::uint128_t::format::octal; } - std::string str; + std::basic_string, std::allocator> str; stream >> str; const auto result = IPADDRESS_NAMESPACE::uint128_t::from_string(str, fmt); if (result) { diff --git a/include/ipaddress/unicode.hpp b/include/ipaddress/unicode.hpp new file mode 100644 index 0000000..decdfb6 --- /dev/null +++ b/include/ipaddress/unicode.hpp @@ -0,0 +1,321 @@ +/** + * @file unicode.hpp + * @brief Unicode character processing + * @author Vladimir Shaleev + * @copyright MIT License + * + * This file contains definitions and templates for working with various + * Unicode encodings such as UTF-8, UTF-16, UTF-32, and Wide chars. + * It provides functionality to convert unicode characters to char and + * handling errors associated with invalid Unicode characters. + */ + +#ifndef IPADDRESS_UNICODE_HPP +#define IPADDRESS_UNICODE_HPP + +#include "errors.hpp" + +namespace IPADDRESS_NAMESPACE { + +namespace internal { + +template +struct char_reader; + +template +struct char_or_throw { + IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE char next(const T*& it, const T* begin, const T* end) IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS { + uint32_t error_symbol = 0; + auto code = error_code::no_error; + auto prev_it = it; + const auto result = char_reader::next_or_error(it, end, code, error_symbol); + if (code != error_code::no_error) { + raise_error(code, error_symbol, begin, end - begin); + } + return result; + } + + static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void has_throw() IPADDRESS_NOEXCEPT_WHEN_NO_EXCEPTIONS; +}; + +struct symbol { + uint32_t value; + uint32_t length; +}; + +template +struct utf8_reader { + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE char utf8_next_or_error(const T*& it, const T* end, error_code& error, uint32_t& error_symbol) { + error = error_code::no_error; + error_symbol = 0; + auto correct = true; + auto symbol = utf8_code_point(uint8_t(*it), correct); + if (correct) { + switch (symbol.length) { + case 1: + break; + + case 2: + symbol.value = (symbol.value << 6) | trailing_utf8_code_point(it, end, correct); + break; + + case 3: + symbol.value = (symbol.value << 6) | trailing_utf8_code_point(it, end, correct); + symbol.value = (symbol.value << 6) | trailing_utf8_code_point(it, end, correct); + break; + + case 4: + symbol.value = (symbol.value << 6) | trailing_utf8_code_point(it, end, correct); + symbol.value = (symbol.value << 6) | trailing_utf8_code_point(it, end, correct); + symbol.value = (symbol.value << 6) | trailing_utf8_code_point(it, end, correct); + break; + + default: + correct = false; + break; + } + } + ++it; + if (!correct) { + error = error_code::wrong_encoding_sequence; + return '\0'; + } else if (symbol.value > 127) { + error = error_code::unexpected_symbol; + error_symbol = symbol.value; + return '\0'; + } + return char(symbol.value); + } + + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE symbol utf8_code_point(uint8_t byte, bool& correct) IPADDRESS_NOEXCEPT { + if ((byte & 0b10000000) == 0b00000000) { + return {static_cast(byte), 1}; + } + if ((byte & 0b11100000) == 0b11000000) { + return {static_cast(byte & 0b00011111), 2}; + } + if ((byte & 0b11110000) == 0b11100000) { + return {static_cast(byte & 0b00001111), 3}; + } + if ((byte & 0b11111000) == 0b11110000) { + return {static_cast(byte & 0b00000111), 4}; + } + correct = false; + return {0, 0}; + } + + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE uint32_t trailing_utf8_code_point(const T*& it, const T* end, bool& correct) IPADDRESS_NOEXCEPT { + if (++it >= end) { + correct = false; + return 0; + } + if ((uint8_t(*it) & 0b11000000) == 0b10000000) { + correct = true; + return uint8_t(*it) & 0b00111111; + } + correct = false; + return 0; + } +}; + +template +struct utf16_reader { + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE char utf16_next_or_error(const T*& it, const T* end, error_code& error, uint32_t& error_symbol) IPADDRESS_NOEXCEPT { + error = error_code::no_error; + error_symbol = 0; + bool correct = true; + auto symbol = utf16_code_point(uint16_t(*it)); + switch (symbol.length) { + case 1: + symbol.value = uint32_t(*it); + break; + + case 2: + if (++it >= end) { + correct = false; + break; + } + if ((*it & 0b1111110000000000) == 0b1101110000000000) { + symbol.value = ((symbol.value << 10) | (uint16_t(*it) & 0b0000001111111111)) + 0x10000; + } else { + correct = false; + } + break; + + default: + correct = false; + break; + } + ++it; + if (!correct) { + error = error_code::wrong_encoding_sequence; + return '\0'; + } else if (symbol.value > 127) { + error = error_code::unexpected_symbol; + error_symbol = symbol.value; + return '\0'; + } + return char(symbol.value); + } + + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE symbol utf16_code_point(uint16_t value) IPADDRESS_NOEXCEPT { + if ((value & 0b1111110000000000) == 0b1101100000000000) { + return {static_cast(value & 0b0000001111111111), 2}; + } else { + return {value, 1}; + } + } +}; + +template +struct utf32_reader { + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE char utf32_next_or_error(const T*& it, const T* /*end*/, error_code& error, uint32_t& error_symbol) IPADDRESS_NOEXCEPT { + error = error_code::no_error; + error_symbol = 0; + if (uint32_t(*it) > 127) { + error = error_code::unexpected_symbol; + error_symbol = uint32_t(*it++); // NOLINT + return '\0'; + } + return char(*it++); + } +}; + +template <> +struct char_reader +#ifdef IPADDRESS_CHAR_IS_UTF8 +: utf8_reader, char_or_throw +#endif +{ +#ifdef IPADDRESS_CHAR_IS_UTF8 + + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE char next_or_error(const char*& it, const char* end, error_code& error, uint32_t& error_symbol) IPADDRESS_NOEXCEPT { + return utf8_next_or_error(it, end, error, error_symbol); + } + +#else // !IPADDRESS_CHAR_IS_UTF8 + + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE char next(const char*& it, const char* /*begin*/, const char* /*end*/) IPADDRESS_NOEXCEPT { + return *it++; + } + + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE char next_or_error(const char*& it, const char* /*end*/, error_code& error, uint32_t& error_symbol) IPADDRESS_NOEXCEPT { + error = error_code::no_error; + error_symbol = 0; + return *it++; + } + + static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void has_throw() IPADDRESS_NOEXCEPT; + +#endif // !IPADDRESS_CHAR_IS_UTF8 +}; + +#if __cpp_char8_t >= 201811L + +template <> +struct char_reader : utf8_reader, char_or_throw { + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE char next_or_error(const char8_t*& it, const char8_t* end, error_code& error, uint32_t& error_symbol) IPADDRESS_NOEXCEPT { + return utf8_next_or_error(it, end, error, error_symbol); + } +}; + +#endif // __cpp_char8_t + +template <> +struct char_reader : utf16_reader, char_or_throw { + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE char next_or_error(const char16_t*& it, const char16_t* end, error_code& error, uint32_t& error_symbol) IPADDRESS_NOEXCEPT { + return utf16_next_or_error(it, end, error, error_symbol); + } +}; + +template <> +struct char_reader : utf32_reader, char_or_throw { + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE char next_or_error(const char32_t*& it, const char32_t* end, error_code& error, uint32_t& error_symbol) IPADDRESS_NOEXCEPT { + return utf32_next_or_error(it, end, error, error_symbol); + } +}; + +template <> +struct char_reader : utf16_reader, utf32_reader, char_or_throw { + IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE char next_or_error(const wchar_t*& it, const wchar_t* end, error_code& error, uint32_t& error_symbol) IPADDRESS_NOEXCEPT { + error = error_code::no_error; + error_symbol = 0; + #ifdef _WIN32 + if (sizeof(wchar_t) == sizeof(char16_t)) { + return utf16_next_or_error(it, end, error, error_symbol); + } + #else + if (sizeof(wchar_t) == sizeof(char32_t)) { + return utf32_next_or_error(it, end, error, error_symbol); + } + #endif + if (*it > 127) { + error = error_code::unexpected_symbol; + error_symbol = uint32_t(*it++); // NOLINT + return '\0'; + } + return char(*it++); + } +}; + +template +IPADDRESS_NODISCARD_WHEN_NO_EXCEPTIONS IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE char next_char(const T*& it, const T* begin, const T* end) IPADDRESS_NOEXCEPT(noexcept(internal::char_reader::next(it, begin, end))) { + return internal::char_reader::next(it, begin, end); +} + +template +IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE char next_char_or_error(const T*& it, const T* end, error_code& error, uint32_t& error_symbol) IPADDRESS_NOEXCEPT { + return internal::char_reader::next_or_error(it, end, error, error_symbol); +} + +template +struct string_converter { + IPADDRESS_NODISCARD static IPADDRESS_FORCE_INLINE std::basic_string, std::allocator> convert(const std::string& str) { + return std::basic_string, std::allocator>(str.cbegin(), str.cend()); + } +}; + +template <> +struct string_converter { + IPADDRESS_NODISCARD static IPADDRESS_FORCE_INLINE const std::string& convert(const std::string& str) IPADDRESS_NOEXCEPT { + return str; + } +}; + +IPADDRESS_FORCE_INLINE void print_symbol_code(std::ostringstream& out, uint32_t symbol) { + out << "{U+" << std::setw(4) << std::setfill('0') << std::hex << symbol << '}'; +} + +} // IPADDRESS_NAMESPACE::internal + +IPADDRESS_FORCE_INLINE std::ostringstream& error::print(std::ostringstream& out, const symbol& arg) { + internal::print_symbol_code(out, arg.value); + return out; +} + +template +IPADDRESS_FORCE_INLINE std::ostringstream& error::print(std::ostringstream& out, const T (&str)[N]) { + auto code = error_code::no_error; + uint32_t error_symbol = 0; + const T* it = str; + const T* end = str + N; + while (it < end) { + const auto result = internal::next_char_or_error(it, end, code, error_symbol); + if (code == error_code::no_error) { + if (result == '\0') { + break; + } + out << result; + } else { + if (error_symbol == 0) { + break; + } + internal::print_symbol_code(out, error_symbol); + } + } + return out; +} + +} // namespace IPADDRESS_NAMESPACE + +#endif // IPADDRESS_UNICODE_HPP diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 01f3c14..9052b84 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,6 +15,7 @@ endif() enable_testing() add_executable(ipaddress-tests "uint128-tests.cpp" + "unicode-tests.cpp" "fixed-string-tests.cpp" "ipv4-address-tests.cpp" "ipv6-address-tests.cpp" diff --git a/tests/fixed-string-tests.cpp b/tests/fixed-string-tests.cpp index bc7219d..2debe62 100644 --- a/tests/fixed-string-tests.cpp +++ b/tests/fixed-string-tests.cpp @@ -1,7 +1,9 @@ #include +#include #include +using namespace testing; using namespace ipaddress; #if IPADDRESS_CPP_VERSION >= 17 @@ -358,3 +360,167 @@ TEST(fixed_string, ConstexprCompare) { ASSERT_TRUE(b_53); ASSERT_TRUE(b_54); } + +TEST(fixed_string, InvalidUnicodeString) { +#if __cpp_char8_t >= 201811L + + const char8_t utf8_1[2] = { char8_t(240), char8_t(0) }; + const char8_t utf8_2[3] = { char8_t(240), char8_t(144), char8_t(0) }; + const char8_t utf8_3[4] = { char8_t(240), char8_t(144), char8_t(141), char8_t(0) }; + const char8_t utf8_4[5] = { char8_t(240), char8_t(144), char8_t(141), char8_t(136), char8_t(0) }; + +#ifdef IPADDRESS_NO_EXCEPTIONS + VAL_FIXED_STRING(str_utf8_1, utf8_1); + EXPECT_EQ(str_utf8_1.size(), 0); +#else + EXPECT_THAT( + [&]() { VAL_FIXED_STRING(str_utf8_1, utf8_1); }, + ThrowsMessage(StrEq("incorrect sequence of bytes in unicode encoding for string "))); +#endif + +#ifdef IPADDRESS_NO_EXCEPTIONS + VAL_FIXED_STRING(str_utf8_2, utf8_2); + EXPECT_EQ(str_utf8_2.size(), 0); +#else + EXPECT_THAT( + [&]() { VAL_FIXED_STRING(str_utf8_2, utf8_2); }, + ThrowsMessage(StrEq("incorrect sequence of bytes in unicode encoding for string "))); +#endif + +#ifdef IPADDRESS_NO_EXCEPTIONS + VAL_FIXED_STRING(str_utf8_3, utf8_3); + EXPECT_EQ(str_utf8_3.size(), 0); +#else + EXPECT_THAT( + [&]() { VAL_FIXED_STRING(str_utf8_3, utf8_3); }, + ThrowsMessage(StrEq("incorrect sequence of bytes in unicode encoding for string "))); +#endif + +#ifdef IPADDRESS_NO_EXCEPTIONS + VAL_FIXED_STRING(str_utf8_4, utf8_4); + EXPECT_EQ(str_utf8_4.size(), 0); +#else + EXPECT_THAT( + [&]() { VAL_FIXED_STRING(str_utf8_4, utf8_4); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+10348} in string {U+10348}"))); +#endif + +#endif // __cpp_char8_t + + const char16_t utf16_2[2] = { char16_t(55296), char16_t(0) }; + const char16_t utf16_3[3] = { char16_t(55296), char16_t(48), char16_t(0) }; + const char16_t utf16_4[3] = { char16_t(55296), char16_t(57160), char16_t(0) }; + +#ifdef IPADDRESS_NO_EXCEPTIONS + VAL_FIXED_STRING(str_utf16_2, utf16_2); + EXPECT_EQ(str_utf16_2.size(), 0); +#elif IPADDRESS_CPP_VERSION >= 14 + EXPECT_THAT( + [&]() { VAL_FIXED_STRING(str_utf16_2, utf16_2); }, + ThrowsMessage(StrEq("incorrect sequence of bytes in unicode encoding for string "))); +#else + ASSERT_THROW((make_fixed_string(utf16_2)), parse_error); +#endif + +#ifdef IPADDRESS_NO_EXCEPTIONS + VAL_FIXED_STRING(str_utf16_3, utf16_3); + EXPECT_EQ(str_utf16_3.size(), 0); +#elif IPADDRESS_CPP_VERSION >= 14 + EXPECT_THAT( + [&]() { VAL_FIXED_STRING(str_utf16_3, utf16_3); }, + ThrowsMessage(StrEq("incorrect sequence of bytes in unicode encoding for string "))); +#else + ASSERT_THROW((make_fixed_string(utf16_3)), parse_error); +#endif + +#ifdef IPADDRESS_NO_EXCEPTIONS + VAL_FIXED_STRING(str_utf16_4, utf16_4); + EXPECT_EQ(str_utf16_4.size(), 0); +#elif IPADDRESS_CPP_VERSION >= 14 + EXPECT_THAT( + [&]() { VAL_FIXED_STRING(str_utf16_4, utf16_4); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+10348} in string {U+10348}"))); +#else + ASSERT_THROW((make_fixed_string(utf16_4)), parse_error); +#endif + + const char32_t utf32_3[2] = { char32_t(259), char32_t(0) }; + const char32_t utf32_4[2] = { char32_t(66376), char32_t(0) }; + +#ifdef IPADDRESS_NO_EXCEPTIONS + VAL_FIXED_STRING(str_utf32_3, utf32_3); + EXPECT_EQ(str_utf32_3.size(), 0); +#elif IPADDRESS_CPP_VERSION >= 14 + EXPECT_THAT( + [&]() { VAL_FIXED_STRING(str_utf32_3, utf32_3); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+0103} in string {U+0103}"))); +#else + ASSERT_THROW((make_fixed_string(utf32_3)), parse_error); +#endif + +#ifdef IPADDRESS_NO_EXCEPTIONS + VAL_FIXED_STRING(str_utf32_4, utf32_4); + EXPECT_EQ(str_utf32_4.size(), 0); +#elif IPADDRESS_CPP_VERSION >= 14 + EXPECT_THAT( + [&]() { VAL_FIXED_STRING(str_utf32_4, utf32_4); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+10348} in string {U+10348}"))); +#else + ASSERT_THROW((make_fixed_string(utf32_4)), parse_error); +#endif +} + +template +IPADDRESS_CONSTEXPR auto make_fixed_string_and_code(const T (&str)[N]) noexcept -> decltype(std::make_pair(make_fixed_string(str), error_code::no_error)) { + auto code = error_code::no_error; + auto result = make_fixed_string(str, code); + return std::make_pair(result, code); +} + +TEST(fixed_string, make_fixed_string) { + IPADDRESS_CONSTEXPR auto result1 = make_fixed_string_and_code(""); + IPADDRESS_CONSTEXPR auto result2 = make_fixed_string_and_code(u""); + IPADDRESS_CONSTEXPR auto result3 = make_fixed_string_and_code(U""); + IPADDRESS_CONSTEXPR auto result4 = make_fixed_string_and_code(L""); + IPADDRESS_CONSTEXPR auto result5 = make_fixed_string_and_code("1"); + IPADDRESS_CONSTEXPR auto result6 = make_fixed_string_and_code(u"1"); + IPADDRESS_CONSTEXPR auto result7 = make_fixed_string_and_code(U"1"); + IPADDRESS_CONSTEXPR auto result8 = make_fixed_string_and_code(L"1"); + IPADDRESS_CONSTEXPR auto result9 = make_fixed_string_and_code(u"1\U00010348"); + IPADDRESS_CONSTEXPR auto result10 = make_fixed_string_and_code(U"1\U00010348"); + IPADDRESS_CONSTEXPR auto result11 = make_fixed_string_and_code(L"1\U00010348"); + ASSERT_EQ(result1.first.size(), 0); + ASSERT_EQ(result2.first.size(), 0); + ASSERT_EQ(result3.first.size(), 0); + ASSERT_EQ(result4.first.size(), 0); + ASSERT_EQ(result5.first.size(), 1); + ASSERT_EQ(result6.first.size(), 1); + ASSERT_EQ(result7.first.size(), 1); + ASSERT_EQ(result8.first.size(), 1); + ASSERT_EQ(result9.first.size(), 1); + ASSERT_EQ(result10.first.size(), 1); + ASSERT_EQ(result11.first.size(), 1); + ASSERT_EQ(result1.second, error_code::no_error); + ASSERT_EQ(result2.second, error_code::no_error); + ASSERT_EQ(result3.second, error_code::no_error); + ASSERT_EQ(result4.second, error_code::no_error); + ASSERT_EQ(result5.second, error_code::no_error); + ASSERT_EQ(result6.second, error_code::no_error); + ASSERT_EQ(result7.second, error_code::no_error); + ASSERT_EQ(result8.second, error_code::no_error); + ASSERT_EQ(result9.second, error_code::unexpected_symbol); + ASSERT_EQ(result10.second, error_code::unexpected_symbol); + ASSERT_EQ(result11.second, error_code::unexpected_symbol); + +#if __cpp_char8_t >= 201811L + constexpr auto result12 = make_fixed_string_and_code(u8""); + constexpr auto result13 = make_fixed_string_and_code(u8"1"); + constexpr auto result14 = make_fixed_string_and_code(u8"1\U00010348"); + ASSERT_EQ(result12.first.size(), 0); + ASSERT_EQ(result13.first.size(), 1); + ASSERT_EQ(result14.first.size(), 1); + ASSERT_EQ(result12.second, error_code::no_error); + ASSERT_EQ(result13.second, error_code::no_error); + ASSERT_EQ(result14.second, error_code::unexpected_symbol); +#endif +} diff --git a/tests/ip-address-tests.cpp b/tests/ip-address-tests.cpp index d8fe7ae..8a24fe4 100644 --- a/tests/ip-address-tests.cpp +++ b/tests/ip-address-tests.cpp @@ -387,7 +387,7 @@ TEST_P(InvalidAddressParams, parse) { #ifdef IPADDRESS_NO_EXCEPTIONS auto error_ip = ip_address::parse(expected_address); - EXPECT_EQ(error_ip.v4().value().to_uint(), 0); + EXPECT_EQ(error_ip.to_uint128(), 0); #elif IPADDRESS_CPP_VERSION >= 14 EXPECT_THAT( [address=expected_address]() { ip_address::parse(address); }, @@ -423,6 +423,89 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple("::1%scope_id/24", error_code::invalid_scope_id, "invalid scope id in address ::1%scope_id/24") )); +template +static void parse_unexpected_symbol(const T (&expected_address)[N1], const T (&expected_scope)[N2]) { // NOLINT(readability-function-cognitive-complexity) + using tstring = std::basic_string, std::allocator>; + using tistringstream = std::basic_istringstream, std::allocator>; + + auto ip = ip_address::parse("2001:db8::1"); + error_code err1 = error_code::no_error; + error_code err2 = error_code::no_error; + error_code err3 = error_code::no_error; + error_code err4 = error_code::no_error; + + ip_address::parse(expected_address, err1); + ip_address::parse(tstring(expected_address), err2); + ip.set_scope_id(expected_scope, err3); + ASSERT_FALSE(ip.get_scope_id()); + ip.set_scope_id(tstring(expected_scope), err4); + ASSERT_FALSE(ip.get_scope_id()); + ASSERT_EQ(err1, error_code::unexpected_symbol); + ASSERT_EQ(err2, error_code::unexpected_symbol); + ASSERT_EQ(err3, error_code::unexpected_symbol); + ASSERT_EQ(err4, error_code::unexpected_symbol); + +#ifdef IPADDRESS_NO_EXCEPTIONS + auto error_ip1 = ip_address::parse(expected_address); + auto error_ip2 = ip_address::parse(tstring(expected_address)); + ASSERT_EQ(error_ip1.to_uint128(), 0); + ASSERT_EQ(error_ip2.to_uint128(), 0); + ip.set_scope_id(expected_scope); + ASSERT_FALSE(ip.get_scope_id()); + ip.set_scope_id(tstring(expected_scope)); + ASSERT_FALSE(ip.get_scope_id()); +#elif IPADDRESS_CPP_VERSION >= 14 + EXPECT_THAT( + [address=expected_address]() { ip_address::parse(address); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+10348} in string 200{U+10348}:d{U+d55c}8::1"))); + EXPECT_THAT( + [address=expected_address]() { ip_address::parse(tstring(address)); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+10348} in string 200{U+10348}:d{U+d55c}8::1"))); + EXPECT_THAT( + [address=expected_address]() { ip_address::parse(address); }, + Throws(Property(&parse_error::code, Eq(error_code::unexpected_symbol)))); + EXPECT_THAT( + [address=expected_address]() { ip_address::parse(tstring(address)); }, + Throws(Property(&parse_error::code, Eq(error_code::unexpected_symbol)))); + EXPECT_THAT( + [=]() { ip_address(ip).set_scope_id(expected_scope); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+d55c} in string 12{U+d55c}3"))); + EXPECT_THAT( + [=]() { ip_address(ip).set_scope_id(tstring(expected_scope)); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+d55c} in string 12{U+d55c}3"))); + EXPECT_THAT( + [=]() { ip_address(ip).set_scope_id(expected_scope); }, + Throws(Property(&parse_error::code, Eq(error_code::unexpected_symbol)))); + EXPECT_THAT( + [=]() { ip_address(ip).set_scope_id(tstring(expected_scope)); }, + Throws(Property(&parse_error::code, Eq(error_code::unexpected_symbol)))); +#else // googletest EXPECT_THAT is not supported in cpp less than 14 + ASSERT_THROW(ip_address::parse(expected_address), parse_error); + ASSERT_THROW((ip_address::parse(tstring(expected_address))), parse_error); + ASSERT_THROW(ip.set_scope_id(expected_scope), parse_error); + ASSERT_THROW((ip.set_scope_id(tstring(expected_scope))), parse_error); +#endif +} +#define PARSE_UNEXPECTED_SYMBOL(unicode) parse_unexpected_symbol(unicode##"200\U00010348:d\ud55c8::1", unicode##"12\ud55c3") + +#if __cpp_char8_t >= 201811L +TEST(ip_address, ParseUnexpectedUtf8) { + PARSE_UNEXPECTED_SYMBOL(u8); +} +#endif + +TEST(ip_address, ParseUnexpectedUtf16) { + PARSE_UNEXPECTED_SYMBOL(u); +} + +TEST(ip_address, ParseUnexpectedUtf32) { + PARSE_UNEXPECTED_SYMBOL(U); +} + +TEST(ip_address, ParseUnexpectedWideChar) { + PARSE_UNEXPECTED_SYMBOL(L); +} + TEST(ip_address, Comparison) { IPADDRESS_CONSTEXPR auto ip1 = ip_address::parse("127.240.0.1"); IPADDRESS_CONSTEXPR auto ip2 = ip_address::parse("2001:db8::1"); @@ -497,6 +580,121 @@ TEST(ip_address, to_string) { ASSERT_EQ(ss_compressed_upper_2.str(), expected_compressed_upper_2); } +TEST(ip_address, to_wstring) { + IPADDRESS_CONSTEXPR auto ip1 = ip_address::parse("127.240.0.1"); + IPADDRESS_CONSTEXPR auto ip2 = ip_address::parse("fe80::1ff:fe23:4567:890a%eth2"); + + const std::wstring expected_address = L"127.240.0.1"; + const wchar_t* expected_full_2 = L"fe80:0000:0000:0000:01ff:fe23:4567:890a%eth2"; + const wchar_t* expected_compact_2 = L"fe80:0:0:0:1ff:fe23:4567:890a%eth2"; + const wchar_t* expected_compressed_2 = L"fe80::1ff:fe23:4567:890a%eth2"; + const wchar_t* expected_compressed_upper_2 = L"FE80::1FF:FE23:4567:890A%eth2"; + + std::wostringstream ss_full; ss_full << full << ip1; + std::wostringstream ss_default; ss_default << ip1; + std::wostringstream ss_compact; ss_compact << compact << ip1; + std::wostringstream ss_compressed; ss_compressed << compressed << ip1; + std::wostringstream ss_compressed_upper; ss_compressed_upper << std::uppercase << compressed << ip1; + + std::wostringstream ss_full_2; ss_full_2 << full << ip2; + std::wostringstream ss_default_2; ss_default_2 << ip2; + std::wostringstream ss_compact_2; ss_compact_2 << compact << ip2; + std::wostringstream ss_compressed_2; ss_compressed_2 << compressed << ip2; + std::wostringstream ss_compressed_upper_2; ss_compressed_upper_2 << std::uppercase << compressed << ip2; + + ASSERT_EQ(ip1.to_wstring(format::full), expected_address); + ASSERT_EQ(ip1.to_wstring(format::compact), expected_address); + ASSERT_EQ(ip1.to_wstring(format::compressed),expected_address); + ASSERT_EQ(ip1.to_wstring(), expected_address); + ASSERT_EQ((std::wstring) ip1, expected_address); + ASSERT_EQ(std::to_wstring(ip1), expected_address); + ASSERT_EQ(ss_full.str(),expected_address); + ASSERT_EQ(ss_default.str(), expected_address); + ASSERT_EQ(ss_compact.str(), expected_address); + ASSERT_EQ(ss_compressed.str(), expected_address); + ASSERT_EQ(ss_compressed_upper.str(), expected_address); + + ASSERT_EQ(ip2.to_wstring(format::full), expected_full_2); + ASSERT_EQ(ip2.to_wstring(format::compact), expected_compact_2); + ASSERT_EQ(ip2.to_wstring(format::compressed), expected_compressed_2); + ASSERT_EQ(ip2.to_wstring(), expected_compressed_2); + ASSERT_EQ((std::wstring) ip2, expected_compressed_2); + ASSERT_EQ(std::to_wstring(ip2), expected_compressed_2); + ASSERT_EQ(ss_full_2.str(), expected_full_2); + ASSERT_EQ(ss_default_2.str(), expected_compressed_2); + ASSERT_EQ(ss_compact_2.str(), expected_compact_2); + ASSERT_EQ(ss_compressed_2.str(), expected_compressed_2); + ASSERT_EQ(ss_compressed_upper_2.str(), expected_compressed_upper_2); +} + +TEST(ip_address, to_u16string) { + IPADDRESS_CONSTEXPR auto ip1 = ip_address::parse("127.240.0.1"); + IPADDRESS_CONSTEXPR auto ip2 = ip_address::parse("fe80::1ff:fe23:4567:890a%eth2"); + + const std::u16string expected_address = u"127.240.0.1"; + const char16_t* expected_full_2 = u"fe80:0000:0000:0000:01ff:fe23:4567:890a%eth2"; + const char16_t* expected_compact_2 = u"fe80:0:0:0:1ff:fe23:4567:890a%eth2"; + const char16_t* expected_compressed_2 = u"fe80::1ff:fe23:4567:890a%eth2"; + + ASSERT_EQ(ip1.to_u16string(format::full), expected_address); + ASSERT_EQ(ip1.to_u16string(format::compact), expected_address); + ASSERT_EQ(ip1.to_u16string(format::compressed),expected_address); + ASSERT_EQ(ip1.to_u16string(), expected_address); + ASSERT_EQ((std::u16string) ip1, expected_address); + + ASSERT_EQ(ip2.to_u16string(format::full), expected_full_2); + ASSERT_EQ(ip2.to_u16string(format::compact), expected_compact_2); + ASSERT_EQ(ip2.to_u16string(format::compressed), expected_compressed_2); + ASSERT_EQ(ip2.to_u16string(), expected_compressed_2); + ASSERT_EQ((std::u16string) ip2, expected_compressed_2); +} + +TEST(ip_address, to_u32string) { + IPADDRESS_CONSTEXPR auto ip1 = ip_address::parse("127.240.0.1"); + IPADDRESS_CONSTEXPR auto ip2 = ip_address::parse("fe80::1ff:fe23:4567:890a%eth2"); + + const std::u32string expected_address = U"127.240.0.1"; + const char32_t* expected_full_2 = U"fe80:0000:0000:0000:01ff:fe23:4567:890a%eth2"; + const char32_t* expected_compact_2 = U"fe80:0:0:0:1ff:fe23:4567:890a%eth2"; + const char32_t* expected_compressed_2 = U"fe80::1ff:fe23:4567:890a%eth2"; + + ASSERT_EQ(ip1.to_u32string(format::full), expected_address); + ASSERT_EQ(ip1.to_u32string(format::compact), expected_address); + ASSERT_EQ(ip1.to_u32string(format::compressed),expected_address); + ASSERT_EQ(ip1.to_u32string(), expected_address); + ASSERT_EQ((std::u32string) ip1, expected_address); + + ASSERT_EQ(ip2.to_u32string(format::full), expected_full_2); + ASSERT_EQ(ip2.to_u32string(format::compact), expected_compact_2); + ASSERT_EQ(ip2.to_u32string(format::compressed), expected_compressed_2); + ASSERT_EQ(ip2.to_u32string(), expected_compressed_2); + ASSERT_EQ((std::u32string) ip2, expected_compressed_2); +} + +#if __cpp_char8_t >= 201811L +TEST(ip_address, to_u8string) { + IPADDRESS_CONSTEXPR auto ip1 = ip_address::parse("127.240.0.1"); + IPADDRESS_CONSTEXPR auto ip2 = ip_address::parse("fe80::1ff:fe23:4567:890a%eth2"); + + const std::u8string expected_address = u8"127.240.0.1"; + const char8_t* expected_full_2 = u8"fe80:0000:0000:0000:01ff:fe23:4567:890a%eth2"; + const char8_t* expected_compact_2 = u8"fe80:0:0:0:1ff:fe23:4567:890a%eth2"; + const char8_t* expected_compressed_2 = u8"fe80::1ff:fe23:4567:890a%eth2"; + + ASSERT_EQ(ip1.to_u8string(format::full), expected_address); + ASSERT_EQ(ip1.to_u8string(format::compact), expected_address); + ASSERT_EQ(ip1.to_u8string(format::compressed),expected_address); + ASSERT_EQ(ip1.to_u8string(), expected_address); + ASSERT_EQ((std::u8string) ip1, expected_address); + + ASSERT_EQ(ip2.to_u8string(format::full), expected_full_2); + ASSERT_EQ(ip2.to_u8string(format::compact), expected_compact_2); + ASSERT_EQ(ip2.to_u8string(format::compressed), expected_compressed_2); + ASSERT_EQ(ip2.to_u8string(), expected_compressed_2); + ASSERT_EQ((std::u8string) ip2, expected_compressed_2); +} +#endif + TEST(ip_address, Hash) { constexpr auto hash_functor = std::hash{}; diff --git a/tests/ip-network-tests.cpp b/tests/ip-network-tests.cpp index 2952dbf..51fd342 100644 --- a/tests/ip-network-tests.cpp +++ b/tests/ip-network-tests.cpp @@ -308,7 +308,7 @@ TEST_P(InvalidNetworkParams, parse) { #ifdef IPADDRESS_NO_EXCEPTIONS auto error_net = ip_network::parse(expected_address); - EXPECT_EQ(error_net.network_address().v4().value().to_uint(), 0); + EXPECT_EQ(error_net.network_address().to_uint128(), 0); #elif IPADDRESS_CPP_VERSION >= 14 EXPECT_THAT( [address=expected_address]() { ip_network::parse(address); }, @@ -344,6 +344,59 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple("1234:axy::b%scope", error_code::part_has_invalid_symbol, "in part 0 of address 1234:axy::b%scope has invalid symbols") )); +template +static void parse_unexpected_symbol(const T (&expected_address)[N]) { + using tstring = std::basic_string, std::allocator>; + using tistringstream = std::basic_istringstream, std::allocator>; + + error_code err1 = error_code::no_error; + error_code err2 = error_code::no_error; + + ip_network::parse(expected_address, err1); + ip_network::parse(tstring(expected_address), err2); + ASSERT_EQ(err1, error_code::unexpected_symbol); + ASSERT_EQ(err2, error_code::unexpected_symbol); + +#ifdef IPADDRESS_NO_EXCEPTIONS + auto error_ip1 = ip_network::parse(expected_address); + auto error_ip2 = ip_network::parse(tstring(expected_address)); + ASSERT_EQ(error_ip1.network_address().to_uint128(), 0); + ASSERT_EQ(error_ip2.network_address().to_uint128(), 0); +#elif IPADDRESS_CPP_VERSION >= 14 + EXPECT_THAT( + [address=expected_address]() { ip_network::parse(address); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+d55c} in string 2001:dc8::/1{U+d55c}2{U+d55c}"))); + EXPECT_THAT( + [address=expected_address]() { ip_network::parse(tstring(address)); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+d55c} in string 2001:dc8::/1{U+d55c}2{U+d55c}"))); + EXPECT_THAT( + [address=expected_address]() { ip_network::parse(address); }, + Throws(Property(&parse_error::code, Eq(error_code::unexpected_symbol)))); +#else // googletest EXPECT_THAT is not supported in cpp less than 14 + ASSERT_THROW(ip_network::parse(expected_address), parse_error); + ASSERT_THROW((ip_network::parse(tstring(expected_address))), parse_error); +#endif +} +#define PARSE_UNEXPECTED_SYMBOL(unicode) parse_unexpected_symbol(unicode##"2001:dc8::/1\ud55c2\ud55c") + +#if __cpp_char8_t >= 201811L +TEST(ip_network, ParseUnexpectedUtf8) { + PARSE_UNEXPECTED_SYMBOL(u8); +} +#endif + +TEST(ip_network, ParseUnexpectedUtf16) { + PARSE_UNEXPECTED_SYMBOL(u); +} + +TEST(ip_network, ParseUnexpectedUtf32) { + PARSE_UNEXPECTED_SYMBOL(U); +} + +TEST(ip_network, ParseUnexpectedWideChar) { + PARSE_UNEXPECTED_SYMBOL(L); +} + TEST(ip_network, Comparison) { IPADDRESS_CONSTEXPR auto net1 = ip_network::parse("127.240.0.0/32"); IPADDRESS_CONSTEXPR auto net2 = ip_network::parse("2001:db8::/64"); @@ -418,6 +471,121 @@ TEST(ip_network, to_string) { ASSERT_EQ(ss_compressed_upper_2.str(), expected_compressed_upper_2); } +TEST(ip_network, to_wstring) { + IPADDRESS_CONSTEXPR auto net1 = ip_network::parse("127.240.0.0/24"); + IPADDRESS_CONSTEXPR auto net2 = ip_network::parse("fe80::1ff:fe23:4567:890a%eth2"); + + const std::wstring expected_address = L"127.240.0.0/24"; + const wchar_t* expected_full_2 = L"fe80:0000:0000:0000:01ff:fe23:4567:890a%eth2/128"; + const wchar_t* expected_compact_2 = L"fe80:0:0:0:1ff:fe23:4567:890a%eth2/128"; + const wchar_t* expected_compressed_2 = L"fe80::1ff:fe23:4567:890a%eth2/128"; + const wchar_t* expected_compressed_upper_2 = L"FE80::1FF:FE23:4567:890A%eth2/128"; + + std::wostringstream ss_full; ss_full << full << net1; + std::wostringstream ss_default; ss_default << net1; + std::wostringstream ss_compact; ss_compact << compact << net1; + std::wostringstream ss_compressed; ss_compressed << compressed << net1; + std::wostringstream ss_compressed_upper; ss_compressed_upper << std::uppercase << compressed << net1; + + std::wostringstream ss_full_2; ss_full_2 << full << net2; + std::wostringstream ss_default_2; ss_default_2 << net2; + std::wostringstream ss_compact_2; ss_compact_2 << compact << net2; + std::wostringstream ss_compressed_2; ss_compressed_2 << compressed << net2; + std::wostringstream ss_compressed_upper_2; ss_compressed_upper_2 << std::uppercase << compressed << net2; + + ASSERT_EQ(net1.to_wstring(format::full), expected_address); + ASSERT_EQ(net1.to_wstring(format::compact), expected_address); + ASSERT_EQ(net1.to_wstring(format::compressed),expected_address); + ASSERT_EQ(net1.to_wstring(), expected_address); + ASSERT_EQ((std::wstring) net1, expected_address); + ASSERT_EQ(std::to_wstring(net1), expected_address); + ASSERT_EQ(ss_full.str(),expected_address); + ASSERT_EQ(ss_default.str(), expected_address); + ASSERT_EQ(ss_compact.str(), expected_address); + ASSERT_EQ(ss_compressed.str(), expected_address); + ASSERT_EQ(ss_compressed_upper.str(), expected_address); + + ASSERT_EQ(net2.to_wstring(format::full), expected_full_2); + ASSERT_EQ(net2.to_wstring(format::compact), expected_compact_2); + ASSERT_EQ(net2.to_wstring(format::compressed), expected_compressed_2); + ASSERT_EQ(net2.to_wstring(), expected_compressed_2); + ASSERT_EQ((std::wstring) net2, expected_compressed_2); + ASSERT_EQ(std::to_wstring(net2), expected_compressed_2); + ASSERT_EQ(ss_full_2.str(), expected_full_2); + ASSERT_EQ(ss_default_2.str(), expected_compressed_2); + ASSERT_EQ(ss_compact_2.str(), expected_compact_2); + ASSERT_EQ(ss_compressed_2.str(), expected_compressed_2); + ASSERT_EQ(ss_compressed_upper_2.str(), expected_compressed_upper_2); +} + +TEST(ip_network, to_u16string) { + IPADDRESS_CONSTEXPR auto net1 = ip_network::parse("127.240.0.0/24"); + IPADDRESS_CONSTEXPR auto net2 = ip_network::parse("fe80::1ff:fe23:4567:890a%eth2"); + + const std::u16string expected_address = u"127.240.0.0/24"; + const char16_t* expected_full_2 = u"fe80:0000:0000:0000:01ff:fe23:4567:890a%eth2/128"; + const char16_t* expected_compact_2 = u"fe80:0:0:0:1ff:fe23:4567:890a%eth2/128"; + const char16_t* expected_compressed_2 = u"fe80::1ff:fe23:4567:890a%eth2/128"; + + ASSERT_EQ(net1.to_u16string(format::full), expected_address); + ASSERT_EQ(net1.to_u16string(format::compact), expected_address); + ASSERT_EQ(net1.to_u16string(format::compressed),expected_address); + ASSERT_EQ(net1.to_u16string(), expected_address); + ASSERT_EQ((std::u16string) net1, expected_address); + + ASSERT_EQ(net2.to_u16string(format::full), expected_full_2); + ASSERT_EQ(net2.to_u16string(format::compact), expected_compact_2); + ASSERT_EQ(net2.to_u16string(format::compressed), expected_compressed_2); + ASSERT_EQ(net2.to_u16string(), expected_compressed_2); + ASSERT_EQ((std::u16string) net2, expected_compressed_2); +} + +TEST(ip_network, to_u32string) { + IPADDRESS_CONSTEXPR auto net1 = ip_network::parse("127.240.0.0/24"); + IPADDRESS_CONSTEXPR auto net2 = ip_network::parse("fe80::1ff:fe23:4567:890a%eth2"); + + const std::u32string expected_address = U"127.240.0.0/24"; + const char32_t* expected_full_2 = U"fe80:0000:0000:0000:01ff:fe23:4567:890a%eth2/128"; + const char32_t* expected_compact_2 = U"fe80:0:0:0:1ff:fe23:4567:890a%eth2/128"; + const char32_t* expected_compressed_2 = U"fe80::1ff:fe23:4567:890a%eth2/128"; + + ASSERT_EQ(net1.to_u32string(format::full), expected_address); + ASSERT_EQ(net1.to_u32string(format::compact), expected_address); + ASSERT_EQ(net1.to_u32string(format::compressed),expected_address); + ASSERT_EQ(net1.to_u32string(), expected_address); + ASSERT_EQ((std::u32string) net1, expected_address); + + ASSERT_EQ(net2.to_u32string(format::full), expected_full_2); + ASSERT_EQ(net2.to_u32string(format::compact), expected_compact_2); + ASSERT_EQ(net2.to_u32string(format::compressed), expected_compressed_2); + ASSERT_EQ(net2.to_u32string(), expected_compressed_2); + ASSERT_EQ((std::u32string) net2, expected_compressed_2); +} + +#if __cpp_char8_t >= 201811L +TEST(ip_network, to_u8string) { + IPADDRESS_CONSTEXPR auto net1 = ip_network::parse("127.240.0.0/24"); + IPADDRESS_CONSTEXPR auto net2 = ip_network::parse("fe80::1ff:fe23:4567:890a%eth2"); + + const std::u8string expected_address = u8"127.240.0.0/24"; + const char8_t* expected_full_2 = u8"fe80:0000:0000:0000:01ff:fe23:4567:890a%eth2/128"; + const char8_t* expected_compact_2 = u8"fe80:0:0:0:1ff:fe23:4567:890a%eth2/128"; + const char8_t* expected_compressed_2 = u8"fe80::1ff:fe23:4567:890a%eth2/128"; + + ASSERT_EQ(net1.to_u8string(format::full), expected_address); + ASSERT_EQ(net1.to_u8string(format::compact), expected_address); + ASSERT_EQ(net1.to_u8string(format::compressed),expected_address); + ASSERT_EQ(net1.to_u8string(), expected_address); + ASSERT_EQ((std::u8string) net1, expected_address); + + ASSERT_EQ(net2.to_u8string(format::full), expected_full_2); + ASSERT_EQ(net2.to_u8string(format::compact), expected_compact_2); + ASSERT_EQ(net2.to_u8string(format::compressed), expected_compressed_2); + ASSERT_EQ(net2.to_u8string(), expected_compressed_2); + ASSERT_EQ((std::u8string) net2, expected_compressed_2); +} +#endif + TEST(ip_network, Hash) { IPADDRESS_CONSTEXPR auto hash_functor = std::hash{}; diff --git a/tests/ipv4-address-tests.cpp b/tests/ipv4-address-tests.cpp index bdfe4a4..7eba478 100644 --- a/tests/ipv4-address-tests.cpp +++ b/tests/ipv4-address-tests.cpp @@ -379,6 +379,59 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple("1.2.3.040", error_code::leading_0_are_not_permitted, "leading zeros are not permitted in octet 3 of address 1.2.3.040") )); +template +static void parse_unexpected_symbol(const T (&expected_address)[N]) { + using tstring = std::basic_string, std::allocator>; + using tistringstream = std::basic_istringstream, std::allocator>; + + error_code err1 = error_code::no_error; + error_code err2 = error_code::no_error; + + ipv4_address::parse(expected_address, err1); + ipv4_address::parse(tstring(expected_address), err2); + ASSERT_EQ(err1, error_code::unexpected_symbol); + ASSERT_EQ(err2, error_code::unexpected_symbol); + +#ifdef IPADDRESS_NO_EXCEPTIONS + auto error_ip1 = ipv4_address::parse(expected_address); + auto error_ip2 = ipv4_address::parse(tstring(expected_address)); + ASSERT_EQ(error_ip1.to_uint(), 0); + ASSERT_EQ(error_ip2.to_uint(), 0); +#elif IPADDRESS_CPP_VERSION >= 14 + EXPECT_THAT( + [address=expected_address]() { ipv4_address::parse(address); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+10348} in string 127.{U+10348}.{U+d55c}.1"))); + EXPECT_THAT( + [address=expected_address]() { ipv4_address::parse(tstring(address)); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+10348} in string 127.{U+10348}.{U+d55c}.1"))); + EXPECT_THAT( + [address=expected_address]() { ipv4_address::parse(address); }, + Throws(Property(&parse_error::code, Eq(error_code::unexpected_symbol)))); +#else // googletest EXPECT_THAT is not supported in cpp less than 14 + ASSERT_THROW(ipv4_address::parse(expected_address), parse_error); + ASSERT_THROW((ipv4_address::parse(tstring(expected_address))), parse_error); +#endif +} +#define PARSE_UNEXPECTED_SYMBOL(unicode) parse_unexpected_symbol(unicode##"127.\U00010348.\ud55c.1") + +#if __cpp_char8_t >= 201811L +TEST(ipv4_address, ParseUnexpectedUtf8) { + PARSE_UNEXPECTED_SYMBOL(u8); +} +#endif + +TEST(ipv4_address, ParseUnexpectedUtf16) { + PARSE_UNEXPECTED_SYMBOL(u); +} + +TEST(ipv4_address, ParseUnexpectedUtf32) { + PARSE_UNEXPECTED_SYMBOL(U); +} + +TEST(ipv4_address, ParseUnexpectedWideChar) { + PARSE_UNEXPECTED_SYMBOL(L); +} + TEST(ipv4_address, Comparison) { auto ip1 = ipv4_address::parse("127.239.0.1"); auto ip2 = ipv4_address::parse("127.240.0.1"); @@ -423,6 +476,7 @@ TEST_P(ToStringIpv4Params, to_string) { ss << actual; ASSERT_EQ(actual.to_string(), std::string(expected)); + ASSERT_EQ((std::string) actual, std::string(expected)); ASSERT_EQ(std::to_string(actual), std::string(expected)); ASSERT_EQ(ss.str(), std::string(expected)); } @@ -434,6 +488,81 @@ INSTANTIATE_TEST_SUITE_P( "255.0.42.42" )); +using ToWStringIpv4Params = TestWithParam; +TEST_P(ToWStringIpv4Params, to_wstring) { + const auto expected = GetParam(); + + const auto actual = ipv4_address::parse(expected); + + std::wostringstream ss; + ss << actual; + + ASSERT_EQ(actual.to_wstring(), std::wstring(expected)); + ASSERT_EQ((std::wstring) actual, std::wstring(expected)); + ASSERT_EQ(std::to_wstring(actual), std::wstring(expected)); + ASSERT_EQ(ss.str(), std::wstring(expected)); +} +INSTANTIATE_TEST_SUITE_P( + ipv4_address, ToWStringIpv4Params, + testing::Values( + L"0.0.0.0", + L"127.0.0.1", + L"255.0.42.42" + )); + +using ToUtf16StringIpv4Params = TestWithParam; +TEST_P(ToUtf16StringIpv4Params, to_u16string) { + const auto expected = GetParam(); + + const auto actual = ipv4_address::parse(expected); + + ASSERT_EQ(actual.to_u16string(), std::u16string(expected)); + ASSERT_EQ((std::u16string) actual, std::u16string(expected)); +} +INSTANTIATE_TEST_SUITE_P( + ipv4_address, ToUtf16StringIpv4Params, + testing::Values( + u"0.0.0.0", + u"127.0.0.1", + u"255.0.42.42" + )); + +using ToUtf32StringIpv4Params = TestWithParam; +TEST_P(ToUtf32StringIpv4Params, to_u32string) { + const auto expected = GetParam(); + + const auto actual = ipv4_address::parse(expected); + + ASSERT_EQ(actual.to_u32string(), std::u32string(expected)); + ASSERT_EQ((std::u32string) actual, std::u32string(expected)); +} +INSTANTIATE_TEST_SUITE_P( + ipv4_address, ToUtf32StringIpv4Params, + testing::Values( + U"0.0.0.0", + U"127.0.0.1", + U"255.0.42.42" + )); + +#if __cpp_char8_t >= 201811L +using ToUtf8StringIpv4Params = TestWithParam; +TEST_P(ToUtf8StringIpv4Params, to_u8string) { + const auto expected = GetParam(); + + const auto actual = ipv4_address::parse(expected); + + ASSERT_EQ(actual.to_u8string(), std::u8string(expected)); + ASSERT_EQ((std::u8string) actual, std::u8string(expected)); +} +INSTANTIATE_TEST_SUITE_P( + ipv4_address, ToUtf8StringIpv4Params, + testing::Values( + u8"0.0.0.0", + u8"127.0.0.1", + u8"255.0.42.42" + )); +#endif + TEST(ipv4_address, Hash) { auto ip1 = ipv4_address::parse("127.0.0.1"); auto ip2 = ipv4_address::parse("127.0.0.2"); diff --git a/tests/ipv4-network-tests.cpp b/tests/ipv4-network-tests.cpp index 59fae83..c7e623b 100644 --- a/tests/ipv4-network-tests.cpp +++ b/tests/ipv4-network-tests.cpp @@ -634,6 +634,59 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple("1.2.3.256", error_code::octet_exceeded_255, "octet 0 of address 1.2.3.256 exceeded 255") )); +template +static void parse_unexpected_symbol(const T (&expected_address)[N]) { + using tstring = std::basic_string, std::allocator>; + using tistringstream = std::basic_istringstream, std::allocator>; + + error_code err1 = error_code::no_error; + error_code err2 = error_code::no_error; + + ipv4_network::parse(expected_address, err1); + ipv4_network::parse(tstring(expected_address), err2); + ASSERT_EQ(err1, error_code::unexpected_symbol); + ASSERT_EQ(err2, error_code::unexpected_symbol); + +#ifdef IPADDRESS_NO_EXCEPTIONS + auto error_ip1 = ipv4_network::parse(expected_address); + auto error_ip2 = ipv4_network::parse(tstring(expected_address)); + ASSERT_EQ(error_ip1.network_address().to_uint(), 0); + ASSERT_EQ(error_ip2.network_address().to_uint(), 0); +#elif IPADDRESS_CPP_VERSION >= 14 + EXPECT_THAT( + [address=expected_address]() { ipv4_network::parse(address); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+d55c} in string 127.0.0.1/3{U+d55c}"))); + EXPECT_THAT( + [address=expected_address]() { ipv4_network::parse(tstring(address)); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+d55c} in string 127.0.0.1/3{U+d55c}"))); + EXPECT_THAT( + [address=expected_address]() { ipv4_network::parse(address); }, + Throws(Property(&parse_error::code, Eq(error_code::unexpected_symbol)))); +#else // googletest EXPECT_THAT is not supported in cpp less than 14 + ASSERT_THROW(ipv4_network::parse(expected_address), parse_error); + ASSERT_THROW((ipv4_network::parse(tstring(expected_address))), parse_error); +#endif +} +#define PARSE_UNEXPECTED_SYMBOL(unicode) parse_unexpected_symbol(unicode##"127.0.0.1/3\ud55c") + +#if __cpp_char8_t >= 201811L +TEST(ipv4_network, ParseUnexpectedUtf8) { + PARSE_UNEXPECTED_SYMBOL(u8); +} +#endif + +TEST(ipv4_network, ParseUnexpectedUtf16) { + PARSE_UNEXPECTED_SYMBOL(u); +} + +TEST(ipv4_network, ParseUnexpectedUtf32) { + PARSE_UNEXPECTED_SYMBOL(U); +} + +TEST(ipv4_network, ParseUnexpectedWideChar) { + PARSE_UNEXPECTED_SYMBOL(L); +} + TEST(ipv4_network, Comparison) { auto net1 = ipv4_network::parse("127.240.1.0/24"); auto net2 = ipv4_network::parse("127.240.1.0"); @@ -690,6 +743,81 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple("1.2.3.4/255.255.255.255", "1.2.3.4/32") )); +using ToWStringNetworkIpv4Params = TestWithParam>; +TEST_P(ToWStringNetworkIpv4Params, to_wstring) { + const auto expected = std::get<1>(GetParam()); + + const auto actual = ipv4_network::parse(std::get<0>(GetParam())); + + std::wostringstream ss; + ss << actual; + + ASSERT_EQ(actual.to_wstring(), std::wstring(expected)); + ASSERT_EQ(std::wstring(actual), std::wstring(expected)); + ASSERT_EQ(std::to_wstring(actual), std::wstring(expected)); + ASSERT_EQ(ss.str(), std::wstring(expected)); +} +INSTANTIATE_TEST_SUITE_P( + ipv4_network, ToWStringNetworkIpv4Params, + Values( + std::make_tuple("1.2.3.4", L"1.2.3.4/32"), + std::make_tuple("1.2.3.4/32", L"1.2.3.4/32"), + std::make_tuple("1.2.3.4/255.255.255.255", L"1.2.3.4/32") + )); + +using ToUtf16StringNetworkIpv4Params = TestWithParam>; +TEST_P(ToUtf16StringNetworkIpv4Params, to_u16string) { + const auto expected = std::get<1>(GetParam()); + + const auto actual = ipv4_network::parse(std::get<0>(GetParam())); + + ASSERT_EQ(actual.to_u16string(), std::u16string(expected)); + ASSERT_EQ(std::u16string(actual), std::u16string(expected)); +} +INSTANTIATE_TEST_SUITE_P( + ipv4_network, ToUtf16StringNetworkIpv4Params, + Values( + std::make_tuple("1.2.3.4", u"1.2.3.4/32"), + std::make_tuple("1.2.3.4/32", u"1.2.3.4/32"), + std::make_tuple("1.2.3.4/255.255.255.255", u"1.2.3.4/32") + )); + +using ToUtf32StringNetworkIpv4Params = TestWithParam>; +TEST_P(ToUtf32StringNetworkIpv4Params, to_u32string) { + const auto expected = std::get<1>(GetParam()); + + const auto actual = ipv4_network::parse(std::get<0>(GetParam())); + + ASSERT_EQ(actual.to_u32string(), std::u32string(expected)); + ASSERT_EQ(std::u32string(actual), std::u32string(expected)); +} +INSTANTIATE_TEST_SUITE_P( + ipv4_network, ToUtf32StringNetworkIpv4Params, + Values( + std::make_tuple("1.2.3.4", U"1.2.3.4/32"), + std::make_tuple("1.2.3.4/32", U"1.2.3.4/32"), + std::make_tuple("1.2.3.4/255.255.255.255", U"1.2.3.4/32") + )); + +#if __cpp_char8_t >= 201811L +using ToUtf8StringNetworkIpv4Params = TestWithParam>; +TEST_P(ToUtf8StringNetworkIpv4Params, to_u8string) { + const auto expected = std::get<1>(GetParam()); + + const auto actual = ipv4_network::parse(std::get<0>(GetParam())); + + ASSERT_EQ(actual.to_u8string(), std::u8string(expected)); + ASSERT_EQ(std::u8string(actual), std::u8string(expected)); +} +INSTANTIATE_TEST_SUITE_P( + ipv4_network, ToUtf8StringNetworkIpv4Params, + Values( + std::make_tuple("1.2.3.4", u8"1.2.3.4/32"), + std::make_tuple("1.2.3.4/32", u8"1.2.3.4/32"), + std::make_tuple("1.2.3.4/255.255.255.255", u8"1.2.3.4/32") + )); +#endif + TEST(ipv4_network, Hash) { auto net1 = ipv4_network::parse("127.0.0.1"); auto net2 = ipv4_network::parse("127.0.0.1/32"); diff --git a/tests/ipv6-address-tests.cpp b/tests/ipv6-address-tests.cpp index d3bdc3b..5c72355 100644 --- a/tests/ipv6-address-tests.cpp +++ b/tests/ipv6-address-tests.cpp @@ -620,6 +620,89 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple("::1%scope_id/24", error_code::invalid_scope_id, "invalid scope id in address ::1%scope_id/24") )); +template +static void parse_unexpected_symbol(const T (&expected_address)[N1], const T (&expected_scope)[N2]) { // NOLINT(readability-function-cognitive-complexity) + using tstring = std::basic_string, std::allocator>; + using tistringstream = std::basic_istringstream, std::allocator>; + + auto ip = ipv6_address::parse("2001:db8::1"); + error_code err1 = error_code::no_error; + error_code err2 = error_code::no_error; + error_code err3 = error_code::no_error; + error_code err4 = error_code::no_error; + + ipv6_address::parse(expected_address, err1); + ipv6_address::parse(tstring(expected_address), err2); + ip.set_scope_id(expected_scope, err3); + ASSERT_FALSE(ip.get_scope_id()); + ip.set_scope_id(tstring(expected_scope), err4); + ASSERT_FALSE(ip.get_scope_id()); + ASSERT_EQ(err1, error_code::unexpected_symbol); + ASSERT_EQ(err2, error_code::unexpected_symbol); + ASSERT_EQ(err3, error_code::unexpected_symbol); + ASSERT_EQ(err4, error_code::unexpected_symbol); + +#ifdef IPADDRESS_NO_EXCEPTIONS + auto error_ip1 = ipv6_address::parse(expected_address); + auto error_ip2 = ipv6_address::parse(tstring(expected_address)); + ASSERT_EQ(error_ip1.to_uint(), 0); + ASSERT_EQ(error_ip2.to_uint(), 0); + ip.set_scope_id(expected_scope); + ASSERT_FALSE(ip.get_scope_id()); + ip.set_scope_id(tstring(expected_scope)); + ASSERT_FALSE(ip.get_scope_id()); +#elif IPADDRESS_CPP_VERSION >= 14 + EXPECT_THAT( + [address=expected_address]() { ipv6_address::parse(address); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+10348} in string 200{U+10348}:d{U+d55c}8::1"))); + EXPECT_THAT( + [address=expected_address]() { ipv6_address::parse(tstring(address)); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+10348} in string 200{U+10348}:d{U+d55c}8::1"))); + EXPECT_THAT( + [address=expected_address]() { ipv6_address::parse(address); }, + Throws(Property(&parse_error::code, Eq(error_code::unexpected_symbol)))); + EXPECT_THAT( + [address=expected_address]() { ipv6_address::parse(tstring(address)); }, + Throws(Property(&parse_error::code, Eq(error_code::unexpected_symbol)))); + EXPECT_THAT( + [=]() { ipv6_address(ip).set_scope_id(expected_scope); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+d55c} in string 12{U+d55c}3"))); + EXPECT_THAT( + [=]() { ipv6_address(ip).set_scope_id(tstring(expected_scope)); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+d55c} in string 12{U+d55c}3"))); + EXPECT_THAT( + [=]() { ipv6_address(ip).set_scope_id(expected_scope); }, + Throws(Property(&parse_error::code, Eq(error_code::unexpected_symbol)))); + EXPECT_THAT( + [=]() { ipv6_address(ip).set_scope_id(tstring(expected_scope)); }, + Throws(Property(&parse_error::code, Eq(error_code::unexpected_symbol)))); +#else // googletest EXPECT_THAT is not supported in cpp less than 14 + ASSERT_THROW(ipv6_address::parse(expected_address), parse_error); + ASSERT_THROW((ipv6_address::parse(tstring(expected_address))), parse_error); + ASSERT_THROW(ip.set_scope_id(expected_scope), parse_error); + ASSERT_THROW((ip.set_scope_id(tstring(expected_scope))), parse_error); +#endif +} +#define PARSE_UNEXPECTED_SYMBOL(unicode) parse_unexpected_symbol(unicode##"200\U00010348:d\ud55c8::1", unicode##"12\ud55c3") + +#if __cpp_char8_t >= 201811L +TEST(ipv6_address, ParseUnexpectedUtf8) { + PARSE_UNEXPECTED_SYMBOL(u8); +} +#endif + +TEST(ipv6_address, ParseUnexpectedUtf16) { + PARSE_UNEXPECTED_SYMBOL(u); +} + +TEST(ipv6_address, ParseUnexpectedUtf32) { + PARSE_UNEXPECTED_SYMBOL(U); +} + +TEST(ipv6_address, ParseUnexpectedWideChar) { + PARSE_UNEXPECTED_SYMBOL(L); +} + TEST(ipv6_address, Comparison) { auto ip1 = ipv6_address::parse("2001:db8::1"); auto ip2 = ipv6_address::parse("2001:db8::2"); @@ -755,6 +838,61 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple("0:0:0:4:5:6:42.42.42.1%test", "0000:0000:0000:0004:0005:0006:2a2a:2a01%test", "0:0:0:4:5:6:2a2a:2a01%test", "::4:5:6:2a2a:2a01%test", "::4:5:6:2A2A:2A01%test") )); +using ToWStringIpv6Params = TestWithParam>; +TEST_P(ToWStringIpv6Params, to_wstring) { + const wchar_t* expected_full = std::get<1>(GetParam()); + const wchar_t* expected_compact = std::get<2>(GetParam()); + const wchar_t* expected_compressed = std::get<3>(GetParam()); + const wchar_t* expected_compressed_upper = std::get<4>(GetParam()); + + const auto actual = ipv6_address::parse(std::get<0>(GetParam())); + + std::wostringstream ss_full; ss_full << full << actual; + std::wostringstream ss_default; ss_default << actual; + std::wostringstream ss_compact; ss_compact << compact << actual; + std::wostringstream ss_compressed; ss_compressed << compressed << actual; + std::wostringstream ss_compressed_upper; ss_compressed_upper << std::uppercase << compressed << actual; + + ASSERT_EQ(actual.to_wstring(format::full), std::wstring(expected_full)); + ASSERT_EQ(actual.to_wstring(format::compact), std::wstring(expected_compact)); + ASSERT_EQ(actual.to_wstring(format::compressed), std::wstring(expected_compressed)); + ASSERT_EQ(actual.to_wstring(), std::wstring(expected_compressed)); + ASSERT_EQ((std::wstring) actual, std::wstring(expected_compressed)); + ASSERT_EQ(std::to_wstring(actual), std::wstring(expected_compressed)); + ASSERT_EQ(ss_full.str(), std::wstring(expected_full)); + ASSERT_EQ(ss_default.str(), std::wstring(expected_compressed)); + ASSERT_EQ(ss_compact.str(), std::wstring(expected_compact)); + ASSERT_EQ(ss_compressed.str(), std::wstring(expected_compressed)); + ASSERT_EQ(ss_compressed_upper.str(), std::wstring(expected_compressed_upper)); +} +INSTANTIATE_TEST_SUITE_P( + ipv6_address, ToWStringIpv6Params, + testing::Values( + std::make_tuple("2001:db8:0:0:1:0:0:1", L"2001:0db8:0000:0000:0001:0000:0000:0001", L"2001:db8:0:0:1:0:0:1", L"2001:db8::1:0:0:1", L"2001:DB8::1:0:0:1"), + std::make_tuple("2001:DB8::1", L"2001:0db8:0000:0000:0000:0000:0000:0001", L"2001:db8:0:0:0:0:0:1", L"2001:db8::1", L"2001:DB8::1"), + std::make_tuple("2001:db8::1", L"2001:0db8:0000:0000:0000:0000:0000:0001", L"2001:db8:0:0:0:0:0:1", L"2001:db8::1", L"2001:DB8::1"), + std::make_tuple("2001:0db8:85a3:0000:0000:8a2e:0370:7334", L"2001:0db8:85a3:0000:0000:8a2e:0370:7334", L"2001:db8:85a3:0:0:8a2e:370:7334", L"2001:db8:85a3::8a2e:370:7334", L"2001:DB8:85A3::8A2E:370:7334"), + std::make_tuple("fe80::1ff:fe23:4567:890a", L"fe80:0000:0000:0000:01ff:fe23:4567:890a", L"fe80:0:0:0:1ff:fe23:4567:890a", L"fe80::1ff:fe23:4567:890a", L"FE80::1FF:FE23:4567:890A"), + std::make_tuple("::", L"0000:0000:0000:0000:0000:0000:0000:0000", L"0:0:0:0:0:0:0:0", L"::", L"::"), + std::make_tuple("::127.0.0.1", L"0000:0000:0000:0000:0000:0000:7f00:0001", L"0:0:0:0:0:0:7f00:1", L"::7f00:1", L"::7F00:1"), + std::make_tuple("0000::0000", L"0000:0000:0000:0000:0000:0000:0000:0000", L"0:0:0:0:0:0:0:0", L"::", L"::"), + std::make_tuple("::c0a8:1", L"0000:0000:0000:0000:0000:0000:c0a8:0001", L"0:0:0:0:0:0:c0a8:1", L"::c0a8:1", L"::C0A8:1"), + std::make_tuple("000::c0a8:0001", L"0000:0000:0000:0000:0000:0000:c0a8:0001", L"0:0:0:0:0:0:c0a8:1", L"::c0a8:1", L"::C0A8:1"), + std::make_tuple("::1", L"0000:0000:0000:0000:0000:0000:0000:0001", L"0:0:0:0:0:0:0:1", L"::1", L"::1"), + std::make_tuple("::ffff:0:0", L"0000:0000:0000:0000:0000:ffff:0000:0000", L"0:0:0:0:0:ffff:0:0", L"::ffff:0:0", L"::FFFF:0:0"), + std::make_tuple("::ffff:0:0:0", L"0000:0000:0000:0000:ffff:0000:0000:0000", L"0:0:0:0:ffff:0:0:0", L"::ffff:0:0:0", L"::FFFF:0:0:0"), + std::make_tuple("64:ff9b::", L"0064:ff9b:0000:0000:0000:0000:0000:0000", L"64:ff9b:0:0:0:0:0:0", L"64:ff9b::", L"64:FF9B::"), + std::make_tuple("64:ff9b:1::", L"0064:ff9b:0001:0000:0000:0000:0000:0000", L"64:ff9b:1:0:0:0:0:0", L"64:ff9b:1::", L"64:FF9B:1::"), + std::make_tuple("100::", L"0100:0000:0000:0000:0000:0000:0000:0000", L"100:0:0:0:0:0:0:0", L"100::", L"100::"), + std::make_tuple("ff02::1:3", L"ff02:0000:0000:0000:0000:0000:0001:0003", L"ff02:0:0:0:0:0:1:3", L"ff02::1:3", L"FF02::1:3"), + std::make_tuple("fe80::1ff:fe23:4567:890a%eth2", L"fe80:0000:0000:0000:01ff:fe23:4567:890a%eth2", L"fe80:0:0:0:1ff:fe23:4567:890a%eth2", L"fe80::1ff:fe23:4567:890a%eth2", L"FE80::1FF:FE23:4567:890A%eth2"), + std::make_tuple("fe80::1ff:fe23:4567:890a%25eth01234567", L"fe80:0000:0000:0000:01ff:fe23:4567:890a%25eth01234567", L"fe80:0:0:0:1ff:fe23:4567:890a%25eth01234567", L"fe80::1ff:fe23:4567:890a%25eth01234567", L"FE80::1FF:FE23:4567:890A%25eth01234567"), + std::make_tuple("fe80::1ff:fe23:4567:890a%3", L"fe80:0000:0000:0000:01ff:fe23:4567:890a%3", L"fe80:0:0:0:1ff:fe23:4567:890a%3", L"fe80::1ff:fe23:4567:890a%3", L"FE80::1FF:FE23:4567:890A%3"), + std::make_tuple("fe80::1ff:fe23:4567:890a%31", L"fe80:0000:0000:0000:01ff:fe23:4567:890a%31", L"fe80:0:0:0:1ff:fe23:4567:890a%31", L"fe80::1ff:fe23:4567:890a%31", L"FE80::1FF:FE23:4567:890A%31"), + std::make_tuple("1:2:3:4:5:6:42.42.42.1", L"0001:0002:0003:0004:0005:0006:2a2a:2a01", L"1:2:3:4:5:6:2a2a:2a01", L"1:2:3:4:5:6:2a2a:2a01", L"1:2:3:4:5:6:2A2A:2A01"), + std::make_tuple("0:0:0:4:5:6:42.42.42.1%test", L"0000:0000:0000:0004:0005:0006:2a2a:2a01%test", L"0:0:0:4:5:6:2a2a:2a01%test", L"::4:5:6:2a2a:2a01%test", L"::4:5:6:2A2A:2A01%test") + )); + TEST(ipv6_address, Hash) { auto ip1 = ipv6_address::parse("2001:db8::1"); auto ip2 = ipv6_address::parse("2001:db8::2"); diff --git a/tests/ipv6-network-tests.cpp b/tests/ipv6-network-tests.cpp index 8a341ef..7022bd7 100644 --- a/tests/ipv6-network-tests.cpp +++ b/tests/ipv6-network-tests.cpp @@ -633,6 +633,59 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple("1234:axy::b%scope", error_code::part_has_invalid_symbol, "in part 0 of address 1234:axy::b%scope has invalid symbols") )); +template +static void parse_unexpected_symbol(const T (&expected_address)[N]) { + using tstring = std::basic_string, std::allocator>; + using tistringstream = std::basic_istringstream, std::allocator>; + + error_code err1 = error_code::no_error; + error_code err2 = error_code::no_error; + + ipv6_network::parse(expected_address, err1); + ipv6_network::parse(tstring(expected_address), err2); + ASSERT_EQ(err1, error_code::unexpected_symbol); + ASSERT_EQ(err2, error_code::unexpected_symbol); + +#ifdef IPADDRESS_NO_EXCEPTIONS + auto error_ip1 = ipv6_network::parse(expected_address); + auto error_ip2 = ipv6_network::parse(tstring(expected_address)); + ASSERT_EQ(error_ip1.network_address().to_uint(), 0); + ASSERT_EQ(error_ip2.network_address().to_uint(), 0); +#elif IPADDRESS_CPP_VERSION >= 14 + EXPECT_THAT( + [address=expected_address]() { ipv6_network::parse(address); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+d55c} in string 2001:dc8::/1{U+d55c}2{U+d55c}"))); + EXPECT_THAT( + [address=expected_address]() { ipv6_network::parse(tstring(address)); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+d55c} in string 2001:dc8::/1{U+d55c}2{U+d55c}"))); + EXPECT_THAT( + [address=expected_address]() { ipv6_network::parse(address); }, + Throws(Property(&parse_error::code, Eq(error_code::unexpected_symbol)))); +#else // googletest EXPECT_THAT is not supported in cpp less than 14 + ASSERT_THROW(ipv6_network::parse(expected_address), parse_error); + ASSERT_THROW((ipv6_network::parse(tstring(expected_address))), parse_error); +#endif +} +#define PARSE_UNEXPECTED_SYMBOL(unicode) parse_unexpected_symbol(unicode##"2001:dc8::/1\ud55c2\ud55c") + +#if __cpp_char8_t >= 201811L +TEST(ipv6_network, ParseUnexpectedUtf8) { + PARSE_UNEXPECTED_SYMBOL(u8); +} +#endif + +TEST(ipv6_network, ParseUnexpectedUtf16) { + PARSE_UNEXPECTED_SYMBOL(u); +} + +TEST(ipv6_network, ParseUnexpectedUtf32) { + PARSE_UNEXPECTED_SYMBOL(U); +} + +TEST(ipv6_network, ParseUnexpectedWideChar) { + PARSE_UNEXPECTED_SYMBOL(L); +} + TEST(ipv6_network, Comparison) { auto net1 = ipv6_network::parse("2001:db8::/96"); auto net2 = ipv6_network::parse("2001:dc8::"); @@ -699,6 +752,106 @@ INSTANTIATE_TEST_SUITE_P( std::make_tuple("2001:db8::%scope/32", "2001:0db8:0000:0000:0000:0000:0000:0000%scope/32", "2001:db8:0:0:0:0:0:0%scope/32", "2001:db8::%scope/32") )); +using ToWStringNetworkIpv6Params = TestWithParam>; +TEST_P(ToWStringNetworkIpv6Params, to_wstring) { + const auto expected_full = std::get<1>(GetParam()); + const auto expected_compact = std::get<2>(GetParam()); + const auto expected_compressed = std::get<3>(GetParam()); + + const auto actual = ipv6_network::parse(std::get<0>(GetParam())); + + std::wostringstream ss_full; ss_full << full << actual; + std::wostringstream ss_default; ss_default << actual; + std::wostringstream ss_compact; ss_compact << compact << actual; + std::wostringstream ss_compressed; ss_compressed << compressed << actual; + + ASSERT_EQ(actual.to_wstring(format::full), std::wstring(expected_full)); + ASSERT_EQ(actual.to_wstring(format::compact), std::wstring(expected_compact)); + ASSERT_EQ(actual.to_wstring(format::compressed), std::wstring(expected_compressed)); + ASSERT_EQ(actual.to_wstring(), std::wstring(expected_compressed)); + ASSERT_EQ((std::wstring) actual, std::wstring(expected_compressed)); + ASSERT_EQ(std::to_wstring(actual), std::wstring(expected_compressed)); + ASSERT_EQ(ss_full.str(), std::wstring(expected_full)); + ASSERT_EQ(ss_default.str(), std::wstring(expected_compressed)); + ASSERT_EQ(ss_compact.str(), std::wstring(expected_compact)); + ASSERT_EQ(ss_compressed.str(), std::wstring(expected_compressed)); +} +INSTANTIATE_TEST_SUITE_P( + ipv6_network, ToWStringNetworkIpv6Params, + testing::Values( + std::make_tuple("2001:db8::", L"2001:0db8:0000:0000:0000:0000:0000:0000/128", L"2001:db8:0:0:0:0:0:0/128", L"2001:db8::/128"), + std::make_tuple("2001:db8::/32", L"2001:0db8:0000:0000:0000:0000:0000:0000/32", L"2001:db8:0:0:0:0:0:0/32", L"2001:db8::/32"), + std::make_tuple("2001:db8::%scope/32", L"2001:0db8:0000:0000:0000:0000:0000:0000%scope/32", L"2001:db8:0:0:0:0:0:0%scope/32", L"2001:db8::%scope/32") + )); + +using ToUtf16StringNetworkIpv6Params = TestWithParam>; +TEST_P(ToUtf16StringNetworkIpv6Params, to_u16string) { + const auto expected_full = std::get<1>(GetParam()); + const auto expected_compact = std::get<2>(GetParam()); + const auto expected_compressed = std::get<3>(GetParam()); + + const auto actual = ipv6_network::parse(std::get<0>(GetParam())); + + ASSERT_EQ(actual.to_u16string(format::full), std::u16string(expected_full)); + ASSERT_EQ(actual.to_u16string(format::compact), std::u16string(expected_compact)); + ASSERT_EQ(actual.to_u16string(format::compressed), std::u16string(expected_compressed)); + ASSERT_EQ(actual.to_u16string(), std::u16string(expected_compressed)); + ASSERT_EQ((std::u16string) actual, std::u16string(expected_compressed)); +} +INSTANTIATE_TEST_SUITE_P( + ipv6_network, ToUtf16StringNetworkIpv6Params, + testing::Values( + std::make_tuple("2001:db8::", u"2001:0db8:0000:0000:0000:0000:0000:0000/128", u"2001:db8:0:0:0:0:0:0/128", u"2001:db8::/128"), + std::make_tuple("2001:db8::/32", u"2001:0db8:0000:0000:0000:0000:0000:0000/32", u"2001:db8:0:0:0:0:0:0/32", u"2001:db8::/32"), + std::make_tuple("2001:db8::%scope/32", u"2001:0db8:0000:0000:0000:0000:0000:0000%scope/32", u"2001:db8:0:0:0:0:0:0%scope/32", u"2001:db8::%scope/32") + )); + +using ToUtf32StringNetworkIpv6Params = TestWithParam>; +TEST_P(ToUtf32StringNetworkIpv6Params, to_u32string) { + const auto expected_full = std::get<1>(GetParam()); + const auto expected_compact = std::get<2>(GetParam()); + const auto expected_compressed = std::get<3>(GetParam()); + + const auto actual = ipv6_network::parse(std::get<0>(GetParam())); + + ASSERT_EQ(actual.to_u32string(format::full), std::u32string(expected_full)); + ASSERT_EQ(actual.to_u32string(format::compact), std::u32string(expected_compact)); + ASSERT_EQ(actual.to_u32string(format::compressed), std::u32string(expected_compressed)); + ASSERT_EQ(actual.to_u32string(), std::u32string(expected_compressed)); + ASSERT_EQ((std::u32string) actual, std::u32string(expected_compressed)); +} +INSTANTIATE_TEST_SUITE_P( + ipv6_network, ToUtf32StringNetworkIpv6Params, + testing::Values( + std::make_tuple("2001:db8::", U"2001:0db8:0000:0000:0000:0000:0000:0000/128", U"2001:db8:0:0:0:0:0:0/128", U"2001:db8::/128"), + std::make_tuple("2001:db8::/32", U"2001:0db8:0000:0000:0000:0000:0000:0000/32", U"2001:db8:0:0:0:0:0:0/32", U"2001:db8::/32"), + std::make_tuple("2001:db8::%scope/32", U"2001:0db8:0000:0000:0000:0000:0000:0000%scope/32", U"2001:db8:0:0:0:0:0:0%scope/32", U"2001:db8::%scope/32") + )); + +#if __cpp_char8_t >= 201811L +using ToUtf8StringNetworkIpv6Params = TestWithParam>; +TEST_P(ToUtf8StringNetworkIpv6Params, to_u8string) { + const auto expected_full = std::get<1>(GetParam()); + const auto expected_compact = std::get<2>(GetParam()); + const auto expected_compressed = std::get<3>(GetParam()); + + const auto actual = ipv6_network::parse(std::get<0>(GetParam())); + + ASSERT_EQ(actual.to_u8string(format::full), std::u8string(expected_full)); + ASSERT_EQ(actual.to_u8string(format::compact), std::u8string(expected_compact)); + ASSERT_EQ(actual.to_u8string(format::compressed), std::u8string(expected_compressed)); + ASSERT_EQ(actual.to_u8string(), std::u8string(expected_compressed)); + ASSERT_EQ((std::u8string) actual, std::u8string(expected_compressed)); +} +INSTANTIATE_TEST_SUITE_P( + ipv6_network, ToUtf8StringNetworkIpv6Params, + testing::Values( + std::make_tuple("2001:db8::", u8"2001:0db8:0000:0000:0000:0000:0000:0000/128", u8"2001:db8:0:0:0:0:0:0/128", u8"2001:db8::/128"), + std::make_tuple("2001:db8::/32", u8"2001:0db8:0000:0000:0000:0000:0000:0000/32", u8"2001:db8:0:0:0:0:0:0/32", u8"2001:db8::/32"), + std::make_tuple("2001:db8::%scope/32", u8"2001:0db8:0000:0000:0000:0000:0000:0000%scope/32", u8"2001:db8:0:0:0:0:0:0%scope/32", u8"2001:db8::%scope/32") + )); +#endif + TEST(ipv6_network, Hash) { auto net1 = ipv6_network::parse("2001:db8::"); auto net2 = ipv6_network::parse("2001:db8::/128"); diff --git a/tests/uint128-tests.cpp b/tests/uint128-tests.cpp index efc3266..15baab5 100644 --- a/tests/uint128-tests.cpp +++ b/tests/uint128-tests.cpp @@ -483,6 +483,24 @@ TEST(uint128_t, ToString) { ASSERT_EQ(ss10.str(), std::string("400000000000045BC")); } +TEST(uint128_t, ToStringUnicode) { + IPADDRESS_CONSTEXPR auto value = uint128_t::from_string("10000000000000000042674").value(); + ASSERT_EQ(std::to_string(value), "10000000000000000042674"); + ASSERT_EQ(std::to_wstring(value), L"10000000000000000042674"); + + std::ostringstream ss1; ss1 << value; + std::wstringstream ss2; ss2 << value; + ASSERT_EQ(ss1.str(), "10000000000000000042674"); + ASSERT_EQ(ss2.str(), L"10000000000000000042674"); + ASSERT_EQ(value.to_string(), "10000000000000000042674"); + ASSERT_EQ(value.to_wstring(), L"10000000000000000042674"); + ASSERT_EQ(value.to_u16string(), u"10000000000000000042674"); + ASSERT_EQ(value.to_u32string(), U"10000000000000000042674"); +#if __cpp_char8_t >= 201811L + ASSERT_EQ(value.to_u8string(), u8"10000000000000000042674"); +#endif +} + TEST(uint128_t, FromString) { const uint128_t expected1 = 17852; const uint128_t expected2 = { 4, 17852 }; @@ -515,6 +533,118 @@ TEST(uint128_t, FromString) { ASSERT_EQ(read11, uint128_t(0)); } +TEST(uint128_t, FromStringWideChar) { + IPADDRESS_CONSTEXPR auto value1 = uint128_t::from_string(L"10000000000000000042674"); + IPADDRESS_CONSTEXPR auto has_value1 = value1.has_value(); + IPADDRESS_CONSTEXPR auto lower_value1 = value1.value().lower(); + IPADDRESS_CONSTEXPR auto upper_value1 = value1.value().upper(); + ASSERT_TRUE(has_value1); + ASSERT_EQ(lower_value1, 1864712049423066802ULL); + ASSERT_EQ(upper_value1, 542ULL); + + IPADDRESS_CONSTEXPR auto value2 = uint128_t::from_string(L"100000000000a00000042674"); + IPADDRESS_CONSTEXPR auto has_value2 = value2.has_value(); + IPADDRESS_CONSTEXPR auto lower_value2 = value2.value().lower(); + IPADDRESS_CONSTEXPR auto upper_value2 = value2.value().upper(); + ASSERT_FALSE(has_value2); + ASSERT_EQ(lower_value2, 0); + ASSERT_EQ(upper_value2, 0); + + const auto str = L"10000000000000000042674"; + const auto value3 = uint128_t::from_string(str); + ASSERT_TRUE(value3.has_value()); + ASSERT_EQ(value3.value().lower(), 1864712049423066802ULL); + ASSERT_EQ(value3.value().upper(), 542ULL); + + uint128_t value4; + std::wistringstream ss1(L"10000000000000000042674"); + ss1 >> value4; + ASSERT_FALSE(ss1.fail()); + ASSERT_EQ(value4.lower(), 1864712049423066802ULL); + ASSERT_EQ(value4.upper(), 542ULL); + + uint128_t value5; + std::wistringstream ss2(L"1000c0000000000000042674"); + ss2 >> value5; + ASSERT_TRUE(ss2.fail()); + ASSERT_EQ(value5.lower(), 0); + ASSERT_EQ(value5.upper(), 0); +} + +TEST(uint128_t, FromStringUtf16) { + IPADDRESS_CONSTEXPR auto value1 = uint128_t::from_string(u"10000000000000000042674"); + IPADDRESS_CONSTEXPR auto has_value1 = value1.has_value(); + IPADDRESS_CONSTEXPR auto lower_value1 = value1.value().lower(); + IPADDRESS_CONSTEXPR auto upper_value1 = value1.value().upper(); + ASSERT_TRUE(has_value1); + ASSERT_EQ(lower_value1, 1864712049423066802ULL); + ASSERT_EQ(upper_value1, 542ULL); + + IPADDRESS_CONSTEXPR auto value2 = uint128_t::from_string(u"100000000000a00000042674"); + IPADDRESS_CONSTEXPR auto has_value2 = value2.has_value(); + IPADDRESS_CONSTEXPR auto lower_value2 = value2.value().lower(); + IPADDRESS_CONSTEXPR auto upper_value2 = value2.value().upper(); + ASSERT_FALSE(has_value2); + ASSERT_EQ(lower_value2, 0); + ASSERT_EQ(upper_value2, 0); + + const auto str = u"10000000000000000042674"; + const auto value3 = uint128_t::from_string(str); + ASSERT_TRUE(value3.has_value()); + ASSERT_EQ(value3.value().lower(), 1864712049423066802ULL); + ASSERT_EQ(value3.value().upper(), 542ULL); +} + +TEST(uint128_t, FromStringUtf32) { + IPADDRESS_CONSTEXPR auto value1 = uint128_t::from_string(U"10000000000000000042674"); + IPADDRESS_CONSTEXPR auto has_value1 = value1.has_value(); + IPADDRESS_CONSTEXPR auto lower_value1 = value1.value().lower(); + IPADDRESS_CONSTEXPR auto upper_value1 = value1.value().upper(); + ASSERT_TRUE(has_value1); + ASSERT_EQ(lower_value1, 1864712049423066802ULL); + ASSERT_EQ(upper_value1, 542ULL); + + IPADDRESS_CONSTEXPR auto value2 = uint128_t::from_string(U"100000000000a00000042674"); + IPADDRESS_CONSTEXPR auto has_value2 = value2.has_value(); + IPADDRESS_CONSTEXPR auto lower_value2 = value2.value().lower(); + IPADDRESS_CONSTEXPR auto upper_value2 = value2.value().upper(); + ASSERT_FALSE(has_value2); + ASSERT_EQ(lower_value2, 0); + ASSERT_EQ(upper_value2, 0); + + const auto str = U"10000000000000000042674"; + const auto value3 = uint128_t::from_string(str); + ASSERT_TRUE(value3.has_value()); + ASSERT_EQ(value3.value().lower(), 1864712049423066802ULL); + ASSERT_EQ(value3.value().upper(), 542ULL); +} + +#if __cpp_char8_t >= 201811L +TEST(uint128_t, FromStringUtf8) { + IPADDRESS_CONSTEXPR auto value1 = uint128_t::from_string(u8"10000000000000000042674"); + IPADDRESS_CONSTEXPR auto has_value1 = value1.has_value(); + IPADDRESS_CONSTEXPR auto lower_value1 = value1.value().lower(); + IPADDRESS_CONSTEXPR auto upper_value1 = value1.value().upper(); + ASSERT_TRUE(has_value1); + ASSERT_EQ(lower_value1, 1864712049423066802ULL); + ASSERT_EQ(upper_value1, 542ULL); + + IPADDRESS_CONSTEXPR auto value2 = uint128_t::from_string(u8"100000000000a00000042674"); + IPADDRESS_CONSTEXPR auto has_value2 = value2.has_value(); + IPADDRESS_CONSTEXPR auto lower_value2 = value2.value().lower(); + IPADDRESS_CONSTEXPR auto upper_value2 = value2.value().upper(); + ASSERT_FALSE(has_value2); + ASSERT_EQ(lower_value2, 0); + ASSERT_EQ(upper_value2, 0); + + const auto str = u8"10000000000000000042674"; + const auto value3 = uint128_t::from_string(str); + ASSERT_TRUE(value3.has_value()); + ASSERT_EQ(value3.value().lower(), 1864712049423066802ULL); + ASSERT_EQ(value3.value().upper(), 542ULL); +} +#endif + TEST(uint128_t, NumericLimits) { ASSERT_TRUE(std::numeric_limits::is_integer); ASSERT_EQ(std::numeric_limits::digits, 128); diff --git a/tests/unicode-tests.cpp b/tests/unicode-tests.cpp new file mode 100644 index 0000000..aff5214 --- /dev/null +++ b/tests/unicode-tests.cpp @@ -0,0 +1,243 @@ +#include +#include + +#include + +using namespace testing; +using namespace ipaddress; +using ipaddress::internal::char_reader; + +template +IPADDRESS_CONSTEXPR bool constexpr_test_error(const T (&str)[N]) noexcept { + const T* it = str; + const T* end = it + N; + while (it != end) { + auto code = error_code::no_error; + uint32_t error_symbol = 0; + char symbol = char_reader::next_or_error(it, end, code, error_symbol); + if (code != error_code::no_error) { + return false; + } + if (symbol == '\0') { + break; + } + } + return true; +} + +template +IPADDRESS_CONSTEXPR bool constexpr_test(const T (&str)[N]) { + const T* it = str; + const T* begin = str; + const T* end = it + N; + while (it != end) { + char symbol = char_reader::next(it, begin, end); + if (symbol == '\0') { + break; + } + } + return true; +} + +template +void test(const T (&str)[N]) { // NOLINT(readability-function-cognitive-complexity) + const T* it = str; + const T* end = it + N; + + auto code = error_code::no_error; + char symbol = '\0'; + uint32_t error_symbol = 0; + + symbol = char_reader::next_or_error(it, end, code, error_symbol); + EXPECT_EQ(symbol, '1'); + EXPECT_EQ(code, error_code::no_error); + EXPECT_EQ(error_symbol, 0); + + symbol = char_reader::next_or_error(it, end, code, error_symbol); + EXPECT_EQ(symbol, '2'); + EXPECT_EQ(code, error_code::no_error); + EXPECT_EQ(error_symbol, 0); + + symbol = char_reader::next_or_error(it, end, code, error_symbol); + EXPECT_EQ(symbol, '\0'); + EXPECT_EQ(code, error_code::unexpected_symbol); + EXPECT_EQ(error_symbol, 0x10348); + + symbol = char_reader::next_or_error(it, end, code, error_symbol); + EXPECT_EQ(symbol, '\0'); + EXPECT_EQ(code, error_code::unexpected_symbol); + EXPECT_EQ(error_symbol, 0xd55c); + + symbol = char_reader::next_or_error(it, end, code, error_symbol); + EXPECT_EQ(symbol, '\0'); + EXPECT_EQ(code, error_code::unexpected_symbol); + EXPECT_EQ(error_symbol, 0x0418); + + symbol = char_reader::next_or_error(it, end, code, error_symbol); + EXPECT_EQ(symbol, '$'); + EXPECT_EQ(code, error_code::no_error); + EXPECT_EQ(error_symbol, 0); + + symbol = char_reader::next_or_error(it, end, code, error_symbol); + EXPECT_EQ(symbol, '\0'); + EXPECT_EQ(code, error_code::no_error); + EXPECT_EQ(error_symbol, 0); +} + +template +void test_exception(const T (&str)[N]) { + const T* it = str; + const T* begin = it; + const T* end = it + sizeof(str) / sizeof(str[0]); + + char symbol = '\0'; + + symbol = char_reader::next(it, begin, end); + EXPECT_EQ(symbol, '1'); + + symbol = char_reader::next(it, begin, end); + EXPECT_EQ(symbol, '2'); + +#ifdef IPADDRESS_NO_EXCEPTIONS + const auto actual = char_reader::next(it, begin, end); + EXPECT_EQ(actual, '\0'); +#elif IPADDRESS_CPP_VERSION >= 14 + EXPECT_THAT( + [=]() { auto curr_it = it; char_reader::next(curr_it, begin, end); }, + ThrowsMessage(StrEq("unexpected next unicode symbol {U+10348} in string 12{U+10348}{U+d55c}{U+0418}$"))); +#else + ASSERT_THROW((char_reader::next(it, begin, end)), parse_error); +#endif +} + +TEST(char_reader, CompileTime) { + IPADDRESS_CONSTEXPR auto result1 = constexpr_test_error("1234"); + IPADDRESS_CONSTEXPR auto result2 = constexpr_test_error(u"1234"); + IPADDRESS_CONSTEXPR auto result3 = constexpr_test_error(u"1猫4"); + IPADDRESS_CONSTEXPR auto result4 = constexpr_test_error(U"1234"); + IPADDRESS_CONSTEXPR auto result5 = constexpr_test_error(U"1猫4"); + IPADDRESS_CONSTEXPR auto result6 = constexpr_test_error(L"1234"); + IPADDRESS_CONSTEXPR auto result7 = constexpr_test_error(L"1猫4"); + ASSERT_TRUE(result1); + ASSERT_TRUE(result2); + ASSERT_FALSE(result3); + ASSERT_TRUE(result4); + ASSERT_FALSE(result5); + ASSERT_TRUE(result6); + ASSERT_FALSE(result7); + + IPADDRESS_CONSTEXPR auto result8 = constexpr_test("1234"); + IPADDRESS_CONSTEXPR auto result9 = constexpr_test(u"1234"); + IPADDRESS_CONSTEXPR auto result10 = constexpr_test(U"1234"); + IPADDRESS_CONSTEXPR auto result11 = constexpr_test(L"1234"); + ASSERT_TRUE(result8); + ASSERT_TRUE(result9); + ASSERT_TRUE(result10); + ASSERT_TRUE(result11); + +#if __cpp_char8_t >= 201811L + constexpr auto result12 = constexpr_test_error(u8"1234"); + constexpr auto result13 = constexpr_test_error(u8"12\ud55c34"); + constexpr auto result14 = constexpr_test(u8"1234"); + ASSERT_TRUE(result12); + ASSERT_FALSE(result13); + ASSERT_TRUE(result14); +#endif // __cpp_char8_t +} + +#if __cpp_char8_t >= 201811L +TEST(char_reader, Utf8) { + test(u8"12\U00010348\ud55c\u0418$"); +} +#endif // __cpp_char8_t + +TEST(char_reader, Utf16) { + test(u"12\U00010348\ud55c\u0418$"); +} + +TEST(char_reader, Utf32) { + test(U"12\U00010348\ud55c\u0418$"); +} + +TEST(char_reader, WideChar) { + test(L"12\U00010348\ud55c\u0418$"); +} + +#if __cpp_char8_t >= 201811L +TEST(char_reader, Utf8Exception) { + test_exception(u8"12\U00010348\ud55c\u0418$"); +} +#endif // __cpp_char8_t + +TEST(char_reader, Utf16Exception) { + test_exception(u"12\U00010348\ud55c\u0418$"); +} + +TEST(char_reader, Utf32Exception) { + test_exception(U"12\U00010348\ud55c\u0418$"); +} + +TEST(char_reader, WideCharException) { + test_exception(L"12\U00010348\ud55c\u0418$"); +} + +#ifdef IPADDRESS_CHAR_IS_UTF8 +TEST(char_reader, Char) { + char str[13] = { + char(49), + char(50), + char(240), char(144), char(141), char(136), + char(237), char(149), char(156), + char(208), char(152), + char(36), + char(0) + }; + test(str); +} +#else // IPADDRESS_CHAR_IS_UTF8 +TEST(char_reader, Char) { + constexpr char str[] = "abc123"; + + const char* it = str; + const char* end = it + sizeof(str) / sizeof(str[0]); + + auto code = error_code::no_error; + char symbol = '\0'; + uint32_t error_symbol = 0; + + symbol = char_reader::next_or_error(it, end, code, error_symbol); + EXPECT_EQ(symbol, 'a'); + EXPECT_EQ(code, error_code::no_error); + EXPECT_EQ(error_symbol, 0); + + symbol = char_reader::next_or_error(it, end, code, error_symbol); + EXPECT_EQ(symbol, 'b'); + EXPECT_EQ(code, error_code::no_error); + EXPECT_EQ(error_symbol, 0); + + symbol = char_reader::next_or_error(it, end, code, error_symbol); + EXPECT_EQ(symbol, 'c'); + EXPECT_EQ(code, error_code::no_error); + EXPECT_EQ(error_symbol, 0); + + symbol = char_reader::next_or_error(it, end, code, error_symbol); + EXPECT_EQ(symbol, '1'); + EXPECT_EQ(code, error_code::no_error); + EXPECT_EQ(error_symbol, 0); + + symbol = char_reader::next_or_error(it, end, code, error_symbol); + EXPECT_EQ(symbol, '2'); + EXPECT_EQ(code, error_code::no_error); + EXPECT_EQ(error_symbol, 0); + + symbol = char_reader::next_or_error(it, end, code, error_symbol); + EXPECT_EQ(symbol, '3'); + EXPECT_EQ(code, error_code::no_error); + EXPECT_EQ(error_symbol, 0); + + symbol = char_reader::next_or_error(it, end, code, error_symbol); + EXPECT_EQ(symbol, '\0'); + EXPECT_EQ(code, error_code::no_error); + EXPECT_EQ(error_symbol, 0); +} +#endif