ipaddress 1.2.0
Loading...
Searching...
No Matches
base-v6.hpp
Go to the documentation of this file.
1/**
2 * @file base-v6.hpp
3 * @brief Adds basic functionality for working with IPv6
4 * @author Vladimir Shaleev
5 * @copyright MIT License
6 *
7 * This file provides foundational classes and functions necessary for handling IPv6 addresses,
8 * including parsing, constructing, and manipulating IPv6 address representations. It serves as
9 * a base for higher-level IPv6 address operations and utilities.
10 */
11
12#ifndef IPADDRESS_BASE_V6_HPP
13#define IPADDRESS_BASE_V6_HPP
14
15#include "ipv4-address.hpp"
16#include "uint128.hpp"
17
18namespace IPADDRESS_NAMESPACE {
19
20namespace internal {
21
22IPADDRESS_NODISCARD IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE bool is_invalid_scope_id_symbol(char c) IPADDRESS_NOEXCEPT {
23 return c == '%' || c == '/'
24#ifndef IPADDRESS_SCOPE_ID_SUPPORT_SPACES
25 || c == ' ' || (c >= '\t' && c <= '\r')
26#endif // IPADDRESS_SCOPE_ID_SUPPORT_SPACES
27 ;
28}
29
30} // namespace IPADDRESS_NAMESPACE::internal
31
32/**
33 * A template class providing the base functionality for IPv6 addresses.
34 *
35 * This class encapsulates the basic properties and operations for IPv6 addresses,
36 * such as version identification, size, and conversion from string representations.
37 * It is intended to be extended by more specialized IPv6 address classes.
38 *
39 * @tparam Ext The extension type that provides additional functionality.
40 */
41IPADDRESS_EXPORT template <typename Ext>
42class base_v6 {
43public:
44 using base_type = byte_array<16>;
45 using uint_type = ::IPADDRESS_NAMESPACE::uint128_t;
46
47 static constexpr ip_version base_version = ip_version::V6;
48 static constexpr size_t base_size = 16;
49 static constexpr size_t base_max_string_len = 41 + IPADDRESS_IPV6_SCOPE_MAX_LENGTH;
50 static constexpr size_t base_max_prefixlen = base_size * 8;
51
52 /**
53 * Retrieves the IP version of the address.
54 *
55 * @return The IP version enumeration value for IPv6.
56 */
58 return base_version;
59 }
60
61 /**
62 * Retrieves the size of the IPv6 address.
63 *
64 * @return The size of the IPv6 address in bytes.
65 */
67 return base_size;
68 }
69
70protected:
71 static constexpr size_t _min_parts = 3;
72 static constexpr size_t _max_parts = 8;
73
74 template <typename Iter>
75 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base<Ext> ip_from_string(Iter begin, Iter end, error_code& code, uint32_t& value) IPADDRESS_NOEXCEPT {
76 if (begin == end) {
78 return {};
79 }
80
81 auto ip_and_scope = split_scope_id(begin, end, code, value);
82
83 if (code != error_code::no_error) {
84 return {};
85 }
86
87 const auto parts = split_parts(begin, ip_and_scope.end_ip, value, code);
88
89 if (code != error_code::no_error) {
90 return {};
91 }
92
93 const auto result = get_parts_bound(parts, value, code);
94
95 if (code != error_code::no_error) {
96 return {};
97 }
98
99 auto ip = ip_address_base<Ext>(parse_parts(parts, value, std::get<0>(result), std::get<1>(result), std::get<2>(result), code));
100
101 #if IPADDRESS_IPV6_SCOPE_MAX_LENGTH > 0
102 if (code != error_code::no_error) {
103 return {};
104 }
105
106 if (ip_and_scope.scope_id[0] != '\0') {
107 ip._data.scope_id = make_fixed_string(ip_and_scope.scope_id, code);
108 }
109 #endif // IPADDRESS_IPV6_SCOPE_MAX_LENGTH
110
111 return ip;
112 }
113
114 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base<Ext> ip_from_prefix(size_t prefixlen) {
115 base_type bytes {};
116 for (size_t i = 0; i < (prefixlen >> 3); ++i) {
117 bytes[i] = 0xFF;
118 }
119 auto shift = (prefixlen - ((prefixlen >> 3) << 3));
120 if (shift > 0) {
121 auto byte = 0xFF ^ uint8_t(uint8_t(0xFF) >> shift);
122 bytes[prefixlen >> 3] = byte;
123 }
124 return ip_address_base<Ext>(bytes);
125 }
126
127 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE size_t ip_to_chars(const base_type& bytes, const fixed_string<IPADDRESS_IPV6_SCOPE_MAX_LENGTH>& scope_id, format fmt, char (&result)[base_max_string_len + 1]) IPADDRESS_NOEXCEPT {
128 constexpr auto hextets_count = size_t(base_size >> 1);
129 char hextets[hextets_count][5] = {};
130 const size_t max_hextets = base_size >> 1;
131 for (size_t i = 0; i < max_hextets; ++i) {
132 const uint16_t value = (uint16_t(bytes[i * 2]) << 8) | uint16_t(bytes[i * 2 + 1]);
133 to_hex(value, hextets[i]);
134 }
135
136 auto count = max_hextets;
137 auto has_doublecolon_start = false;
138 auto has_doublecolon_end = false;
139
140 if (fmt == format::compact || fmt == format::compressed) {
141 for (size_t i = 0; i < hextets_count; ++i) {
142 auto& hextet = hextets[i];
143 for (size_t h = 0; h < 3; ++h) {
144 if (hextet[0] != '0') {
145 break;
146 }
147 hextet[0] = hextet[1];
148 hextet[1] = hextet[2];
149 hextet[2] = hextet[3];
150 hextet[3] = hextet[4];
151 }
152 }
153
154 if (fmt == format::compressed) {
155 int best_doublecolon_len = 0;
156 int best_doublecolon_start = -1;
157 int doublecolon_len = 0;
158 int doublecolon_start = -1;
159
160 for (size_t i = 0; i < max_hextets; ++i) {
161 const auto& hextet = hextets[i];
162 if (hextet[0] == '0') {
163 doublecolon_len += 1;
164
165 if (doublecolon_start == -1) {
166 doublecolon_start = int(i);
167 }
168 if (doublecolon_len > best_doublecolon_len) {
169 best_doublecolon_len = doublecolon_len;
170 best_doublecolon_start = doublecolon_start;
171 }
172 } else {
173 doublecolon_len = 0;
174 doublecolon_start = -1;
175 }
176 }
177
178 if (best_doublecolon_len > 1) {
179 const int best_doublecolon_end = best_doublecolon_start + best_doublecolon_len;
180 if (best_doublecolon_end == max_hextets) {
181 has_doublecolon_end = true;
182 }
183 hextets[best_doublecolon_start][0] = '\0';
184 for (int h = best_doublecolon_start; h < max_hextets - 1; ++h) {
185 const auto lindex = h + 1;
186 const auto rindex = h + best_doublecolon_len;
187 if (rindex >= max_hextets) {
188 break;
189 }
190 hextets[lindex][0] = hextets[rindex][0];
191 hextets[lindex][1] = hextets[rindex][1];
192 hextets[lindex][2] = hextets[rindex][2];
193 hextets[lindex][3] = hextets[rindex][3];
194 }
195 count -= best_doublecolon_len - 1;
196
197 if (best_doublecolon_start == 0) {
198 has_doublecolon_start = true;
199 }
200 }
201 }
202 }
203
204 size_t offset = 0;
205 if (has_doublecolon_start) {
206 result[offset++] = ':';
207 }
208 for (size_t i = 0; i < count - 1; ++i) {
209 const auto& hextet = hextets[i];
210 for (size_t c = 0; hextet[c] != '\0'; ++c) {
211 result[offset++] = hextet[c];
212 }
213 result[offset++] = ':';
214 }
215 const auto hextet = hextets[count - 1];
216 for (size_t c = 0; hextet[c] != '\0'; ++c) {
217 result[offset++] = hextet[c];
218 }
219 if (has_doublecolon_end) {
220 result[offset++] = ':';
221 }
222 if (!scope_id.empty()) {
223 result[offset++] = '%';
224 const auto scope_id_size = scope_id.size();
225 for (size_t i = 0; i < scope_id_size; ++i) {
226 result[offset++] = scope_id[i];
227 }
228 }
229 result[offset] = '\0';
230 return offset;
231 }
232
233 IPADDRESS_NODISCARD static IPADDRESS_FORCE_INLINE std::string ip_reverse_pointer(const base_type& bytes) {
234 char result[base_max_string_len + 1] {};
235 const auto len = ip_to_chars(bytes, {}, format::full, result);
236 auto ip = std::string(result, len);
237 ip.erase(std::remove(ip.begin(), ip.end(), ':'), ip.end());
238 auto res = std::accumulate(std::next(ip.rbegin()), ip.rend(), std::string{ip.back()}, [](std::string a, char b) {
239 return std::move(a) + '.' + b;
240 });
241 return res + ".ip6.arpa";
242 }
243
244 template <typename Iter>
245 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::tuple<ip_address_base<Ext>, size_t> parse_netmask(Iter begin, Iter end, error_code& code, uint32_t& code_value) IPADDRESS_NOEXCEPT {
246 size_t prefixlen = 0;
247 auto it = begin;
248 auto has_prefixlen = false;
249 while (it < end) {
250 has_prefixlen = true;
251 const auto c = internal::next_char_or_error(it, end, code, code_value);
252 if (code != error_code::no_error) {
253 return std::make_tuple(ip_address_base<Ext>(), 0);
254 }
255 if (c >= '0' && c <= '9') {
256 prefixlen = prefixlen * 10 + (c - '0');
257 } else {
259 return std::make_tuple(ip_address_base<Ext>(), 0);
260 }
261 }
262
263 if (prefixlen > base_max_prefixlen) {
265 return std::make_tuple(ip_address_base<Ext>(), 0);
266 }
267
268 prefixlen = has_prefixlen ? prefixlen : base_max_prefixlen;
269
270 auto netmask = ip_from_prefix(prefixlen);
271 return std::make_tuple(netmask, prefixlen);
272 }
273
274 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_address_base<Ext> strict_netmask(const ip_address_base<Ext>& address, const ip_address_base<Ext>& netmask, bool strict, error_code& code) IPADDRESS_NOEXCEPT {
275 const auto& bytes_address = address.bytes();
276 const auto& bytes_netmask = netmask.bytes();
277 base_type bytes{};
278
279 for (auto i = 0; i < base_size; ++i) {
280 bytes[i] = bytes_address[i] & bytes_netmask[i];
281 }
282
283 if (bytes != bytes_address) {
284 if (strict) {
286 return ip_address_base<Ext>();
287 } else {
288 #if IPADDRESS_IPV6_SCOPE_MAX_LENGTH > 0
289 auto ip = ip_address_base<Ext>(bytes);
290 if (address._data.scope_id[0] != '\0') {
291 ip._data.scope_id = address._data.scope_id;
292 }
293 return ip;
294 #else // IPADDRESS_IPV6_SCOPE_MAX_LENGTH
295 return ip_address_base<Ext>(bytes);
296 #endif // IPADDRESS_IPV6_SCOPE_MAX_LENGTH
297 }
298 }
299 return address;
300 }
301
302private:
303 template <typename Iter>
304 struct ip_and_scope {
305 Iter end_ip;
306 char scope_id[IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1];
307 };
308
309 template <typename Iter>
310 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE ip_and_scope<Iter> split_scope_id(Iter begin, Iter end, error_code& error, uint32_t& error_value) IPADDRESS_NOEXCEPT {
311 #if IPADDRESS_IPV6_SCOPE_MAX_LENGTH > 0
312 auto index = 0;
313 #endif // IPADDRESS_IPV6_SCOPE_MAX_LENGTH
314 auto it = begin;
315 auto scope = false;
316 ip_and_scope<Iter> result{};
317 while (it < end) {
318 const auto c = internal::next_char_or_error(it, end, error, error_value);
319 if (error != error_code::no_error) {
320 return result;
321 }
322 if (!scope && c != '%') {
323 result.end_ip = it;
324 } else if (scope) {
325 #if IPADDRESS_IPV6_SCOPE_MAX_LENGTH > 0
326 if (index > IPADDRESS_IPV6_SCOPE_MAX_LENGTH - 1) {
328 return result;
329 }
330 #endif // IPADDRESS_IPV6_SCOPE_MAX_LENGTH
331 if (internal::is_invalid_scope_id_symbol(c)) {
333 return result;
334 }
335 #if IPADDRESS_IPV6_SCOPE_MAX_LENGTH > 0
336 result.scope_id[index++] = c;
337 #endif // IPADDRESS_IPV6_SCOPE_MAX_LENGTH
338 } else {
339 scope = true;
340 }
341 }
342 #if IPADDRESS_IPV6_SCOPE_MAX_LENGTH > 0
343 if (scope && result.scope_id[0] == '\0') {
345 return result;
346 }
347 #endif // IPADDRESS_IPV6_SCOPE_MAX_LENGTH
348 return result;
349 }
350
351 template <typename Iter>
352 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::array<fixed_string<4>, _max_parts + 1> split_parts(Iter begin, Iter end, uint32_t& parts_count, error_code& error) IPADDRESS_NOEXCEPT {
353 IPADDRESS_CONSTEXPR std::array<fixed_string<4>, _max_parts + 1> empty_parts = {
354 make_fixed_string("\0\0\0\0"),
355 make_fixed_string("\0\0\0\0"),
356 make_fixed_string("\0\0\0\0"),
357 make_fixed_string("\0\0\0\0"),
358 make_fixed_string("\0\0\0\0"),
359 make_fixed_string("\0\0\0\0"),
360 make_fixed_string("\0\0\0\0"),
361 make_fixed_string("\0\0\0\0"),
362 make_fixed_string("\0\0\0\0")};
363
364 char parts[_max_parts + 1][5] = {};
365 char last_part[17] = {};
366
367 parts_count = 0;
368 int symbol = 0;
369 char prev_c = '\0';
370 bool has_double_colon = false;
371
372 Iter it = begin;
373 uint32_t error_symbol = 0;
374
375 while (it < end) {
376 auto c = internal::next_char_or_error(it, end, error, error_symbol);
377 if (error != error_code::no_error) {
378 parts_count = error_symbol;
379 return empty_parts;
380 }
381 if (!has_double_colon && c == ':' && prev_c == ':') {
382 has_double_colon = true;
383 }
384 if (parts_count > _max_parts) {
385 error = has_double_colon
388 return empty_parts;
389 }
390 if (c != ':') {
391 if (symbol > 15) {
393 return empty_parts;
394 }
395 last_part[symbol++] = c;
396 last_part[symbol] = '\0';
397 } else {
398 if (symbol > 4) {
400 return empty_parts;
401 }
402
403 auto& current_part = parts[parts_count++];
404 current_part[0] = last_part[0];
405 current_part[1] = last_part[1];
406 current_part[2] = last_part[2];
407 current_part[3] = last_part[3];
408
409 symbol = 0;
410 last_part[0] = '\0';
411 }
412 prev_c = c;
413 }
414
415 if (parts_count > _max_parts) {
416 if (parts[0][0] == '\0' && parts[1][0] != '\0') {
418 } else if (last_part[0] == '\0') {
420 } else {
422 }
423 return empty_parts;
424 }
425
426 auto has_ipv4 = false;
427
428 for (auto i = 0; i < symbol; ++i) {
429 if (last_part[i] == '.') {
430 has_ipv4 = true;
431 break;
432 }
433 }
434
435 if (has_ipv4) {
436 if (parts_count + 1 >= _max_parts) {
438 return empty_parts;
439 }
440
441 auto ipv4 = ipv4_address::parse(last_part, error).to_uint();
442
443 if (error != error_code::no_error) {
444 return empty_parts;
445 }
446
447 to_hex(uint16_t((ipv4 >> 16) & 0xFFFF), parts[parts_count++]);
448 to_hex(uint16_t(ipv4 & 0xFFFF), parts[parts_count++]);
449 } else {
450 if (symbol > 4) {
452 return empty_parts;
453 }
454
455 auto& current_part = parts[parts_count++];
456 current_part[0] = last_part[0];
457 current_part[1] = last_part[1];
458 current_part[2] = last_part[2];
459 current_part[3] = last_part[3];
460 }
461
462 if (parts_count < _min_parts) {
464 return empty_parts;
465 }
466
467 return {
468 make_fixed_string(parts[0]),
469 make_fixed_string(parts[1]),
470 make_fixed_string(parts[2]),
471 make_fixed_string(parts[3]),
472 make_fixed_string(parts[4]),
473 make_fixed_string(parts[5]),
474 make_fixed_string(parts[6]),
475 make_fixed_string(parts[7]),
476 make_fixed_string(parts[8])};
477 }
478
479 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE std::tuple<size_t, size_t, size_t> get_parts_bound(const std::array<fixed_string<4>, _max_parts + 1>& parts, size_t parts_count, error_code& error) IPADDRESS_NOEXCEPT {
480 size_t skip = 0;
481 for (size_t i = 1; i < parts_count - 1; ++i) {
482 if (parts[i].empty()) {
483 if (skip) {
484 error = error_code::most_one_double_colon_permitted;
485 return { 0, 0, 0 };
486 }
487 skip = i;
488 }
489 }
490
491 if (skip) {
492 auto parts_hi = skip;
493 auto parts_lo = parts_count - skip - 1;
494
495 if (parts[0].empty()) {
496 if (--parts_hi) {
498 return { 0, 0, 0 };
499 }
500 }
501
502 if (parts[parts_count - 1].empty()) {
503 if (--parts_lo) {
505 return { 0, 0, 0 };
506 }
507 }
508
509 const auto parts_skipped = _max_parts - (parts_hi + parts_lo);
510
511 if (parts_skipped < 1) {
513 return { 0, 0, 0 };
514 }
515
516 return { parts_hi, parts_lo, parts_skipped };
517 } else {
518 if (parts_count != _max_parts) {
520 return { 0, 0, 0 };
521 }
522
523 if (parts[0].empty()) {
525 return { 0, 0, 0 };
526 }
527
528 if (parts[parts_count - 1].empty()) {
530 return { 0, 0, 0 };
531 }
532
533 return { parts_count, 0, 0 };
534 }
535 }
536
537 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE base_type parse_parts(const std::array<fixed_string<4>, _max_parts + 1>& parts, size_t parts_count, size_t hi, size_t lo, size_t skipped, error_code& error) IPADDRESS_NOEXCEPT {
538 base_type result = {};
539 size_t index = 0;
540
541 for (size_t i = 0; i < hi; ++i) {
542 const auto part = parse_part(parts[i], error);
543 result[index++] = uint8_t(part >> 8);
544 result[index++] = uint8_t(part & 0xFF);
545
546 if (error != error_code::no_error) {
547 return {};
548 }
549 }
550 index += skipped << 1;
551
552 assert(parts_count > lo);
553 for (size_t i = parts_count - lo; i < parts_count; ++i) {
554 const auto part = parse_part(parts[i], error);
555 result[index++] = uint8_t(part >> 8);
556 result[index++] = uint8_t(part & 0xFF);
557
558 if (error != error_code::no_error) {
559 return {};
560 }
561 }
562 return result;
563 }
564
565 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE uint16_t parse_part(const fixed_string<4>& part, error_code& error) IPADDRESS_NOEXCEPT {
566 uint16_t value = 0;
567 for (size_t i = 0; i < part.size(); ++i) {
568 const auto c = part[i];
569 const auto power = pow16(part.size() - i - 1);
570 if (c >= '0' && c <= '9') {
571 value += (c - '0') * power;
572 } else if(c >= 'A' && c <= 'F') {
573 value += (c - 55) * power;
574 } else if (c >= 'a' && c <= 'f') {
575 value += (c - 87) * power;
576 } else {
577 error = error_code::part_has_invalid_symbol;
578 return 0;
579 }
580 }
581 return value;
582 }
583
584 IPADDRESS_NODISCARD static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE uint16_t pow16(size_t power) IPADDRESS_NOEXCEPT {
585 switch (power) {
586 case 0: return 1;
587 case 1: return 16;
588 case 2: return 16 * 16;
589 case 3: return 16 * 16 * 16;
590 default:
591 assert(!"Unreachable code");
592 return 0;
593 }
594 }
595
596 static IPADDRESS_CONSTEXPR IPADDRESS_FORCE_INLINE void to_hex(uint16_t value, char(&result)[5]) IPADDRESS_NOEXCEPT {
597 constexpr char digits[] = "0123456789abcdef";
598 for (auto i = 0, j = (4 - 1) * 4; i < 4; ++i, j -= 4) {
599 result[i] = digits[(value >> j) & 0x0f];
600 result[i + 1] = '\0';
601 }
602 }
603};
604
605template <typename Ext>
606constexpr ip_version base_v6<Ext>::base_version;
607
608template <typename Ext>
609constexpr size_t base_v6<Ext>::base_size;
610
611template <typename Ext>
612constexpr size_t base_v6<Ext>::base_max_string_len;
613
614template <typename Ext>
615constexpr size_t base_v6<Ext>::base_max_prefixlen;
616
617template <typename Ext>
618constexpr size_t base_v6<Ext>::_min_parts;
619
620template <typename Ext>
621constexpr size_t base_v6<Ext>::_max_parts;
622
623} // namespace IPADDRESS_NAMESPACE
624
625#endif // IPADDRESS_BASE_V6_HPP
A template class providing the base functionality for IPv6 addresses.
Definition base-v6.hpp:42
constexpr inline ip_version version() const noexcept
Retrieves the IP version of the address.
Definition base-v6.hpp:57
constexpr inline size_t size() const noexcept
Retrieves the size of the IPv6 address.
Definition base-v6.hpp:66
A template base class for IP address representations.
Definition ip-address-base.hpp:56
inline std::u8string to_u8string(format fmt=format::decimal) const
Converts the uint128_t value to a string representation.
Definition uint128.hpp:403
#define IPADDRESS_EXPORT
Definition config.hpp:45
#define IPADDRESS_NODISCARD
Definition config.hpp:101
#define IPADDRESS_FORCE_INLINE
Definition config.hpp:115
#define IPADDRESS_NAMESPACE
Definition config.hpp:41
#define IPADDRESS_NOEXCEPT
Definition config.hpp:92
ip_version
Enumerates the IP address versions.
Definition ip-address-base.hpp:29
@ V6
IPv6 version identifier.
format
Enumerates the formatting options for IP address strings.
Definition ip-address-base.hpp:40
@ compressed
Compressed format with maximal omission of segments or octets.
@ compact
Compact format with possible omission of leading zeros.
error_code
Enumeration of error codes for IP address parsing and validation.
Definition errors.hpp:52
@ trailing_colon_only_permitted_as_part_of_double_colon
A trailing colon is only permitted as part of a double colon.
@ empty_address
The IP address string is empty when it should contain a valid address.
@ exactly_8_parts_expected_without_double_colon
Without a double colon, exactly eight parts are expected.
@ leading_colon_only_permitted_as_part_of_double_colon
A leading colon is only permitted as part of a double colon.
@ invalid_scope_id
The scope ID in the IPv6 address is invalid.
@ no_error
Indicates the absence of any errors.
@ scope_id_is_too_long
The scope ID in the IPv6 address exceeds the maximum length.
@ part_is_more_4_chars
A part of the IPv6 address contains more than four characters.
@ has_host_bits_set
The address has host bits set when they are expected to be clear.
@ most_8_colons_permitted
The IPv6 address contains more than the maximum allowed number of colons.
@ least_3_parts
The IPv6 address contains fewer than the minimum required parts.
@ expected_at_most_7_other_parts_with_double_colon
With a double colon present, at most seven other parts are expected.
@ invalid_netmask
The provided netmask is not valid according to standard netmask formatting rules.
A template class for creating and managing a fixed-size array of bytes.
Definition byte-array.hpp:33
Fixed size string class.
Definition fixed-string.hpp:29