2
3
4
5
6
7
8
9
10
12#ifndef IPADDRESS_BASE_V6_HPP
13#define IPADDRESS_BASE_V6_HPP
23 return c ==
'%' || c ==
'/'
24#ifndef IPADDRESS_SCOPE_ID_SUPPORT_SPACES
25 || c ==
' ' || (c >=
'\t' && c <=
'\r')
33
34
35
36
37
38
39
40
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;
53
54
55
56
62
63
64
65
71 static constexpr size_t _min_parts = 3;
72 static constexpr size_t _max_parts = 8;
74 template <
typename Iter>
81 auto ip_and_scope = split_scope_id(begin, end, code, value);
87 const auto parts = split_parts(begin, ip_and_scope.end_ip, value, code);
93 const auto result = get_parts_bound(parts, value, code);
99 auto ip = ip_address_base<Ext>(parse_parts(parts, value, std::get<0>(result), std::get<1>(result), std::get<2>(result), code));
101 #if IPADDRESS_IPV6_SCOPE_MAX_LENGTH
> 0
106 if (ip_and_scope.scope_id[0] !=
'\0') {
107 ip._data.scope_id = make_fixed_string(ip_and_scope.scope_id, code);
116 for (size_t i = 0; i < (prefixlen >> 3); ++i) {
119 auto shift = (prefixlen - ((prefixlen >> 3) << 3));
121 auto byte = 0xFF ^ uint8_t(uint8_t(0xFF) >> shift);
122 bytes[prefixlen >> 3] = byte;
124 return ip_address_base<Ext>(bytes);
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]);
136 auto count = max_hextets;
137 auto has_doublecolon_start =
false;
138 auto has_doublecolon_end =
false;
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') {
147 hextet[0] = hextet[1];
148 hextet[1] = hextet[2];
149 hextet[2] = hextet[3];
150 hextet[3] = hextet[4];
155 int best_doublecolon_len = 0;
156 int best_doublecolon_start = -1;
157 int doublecolon_len = 0;
158 int doublecolon_start = -1;
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;
165 if (doublecolon_start == -1) {
166 doublecolon_start =
int(i);
168 if (doublecolon_len > best_doublecolon_len) {
169 best_doublecolon_len = doublecolon_len;
170 best_doublecolon_start = doublecolon_start;
174 doublecolon_start = -1;
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;
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) {
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];
195 count -= best_doublecolon_len - 1;
197 if (best_doublecolon_start == 0) {
198 has_doublecolon_start =
true;
205 if (has_doublecolon_start) {
206 result[offset++] =
':';
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];
213 result[offset++] =
':';
215 const auto hextet = hextets[count - 1];
216 for (size_t c = 0; hextet[c] !=
'\0'; ++c) {
217 result[offset++] = hextet[c];
219 if (has_doublecolon_end) {
220 result[offset++] =
':';
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];
229 result[offset] =
'\0';
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;
241 return res +
".ip6.arpa";
244 template <
typename Iter>
246 size_t prefixlen = 0;
248 auto has_prefixlen =
false;
250 has_prefixlen =
true;
251 const auto c = internal::next_char_or_error(it, end, code, code_value);
255 if (c >=
'0' && c <=
'9') {
256 prefixlen = prefixlen * 10 + (c -
'0');
263 if (prefixlen > base_max_prefixlen) {
268 prefixlen = has_prefixlen ? prefixlen : base_max_prefixlen;
270 auto netmask = ip_from_prefix(prefixlen);
271 return std::make_tuple(netmask, prefixlen);
275 const auto& bytes_address = address.bytes();
276 const auto& bytes_netmask = netmask.bytes();
279 for (
auto i = 0; i < base_size; ++i) {
280 bytes[i] = bytes_address[i] & bytes_netmask[i];
283 if (bytes != bytes_address) {
288 return ip_address_base<Ext>(bytes);
295 template <
typename Iter>
296 struct ip_and_scope {
298 char scope_id[IPADDRESS_IPV6_SCOPE_MAX_LENGTH + 1];
301 template <
typename Iter>
306 ip_and_scope<Iter> result{};
308 const auto c = internal::next_char_or_error(it, end, error, error_value);
312 if (!scope && c !=
'%') {
315 if (index > IPADDRESS_IPV6_SCOPE_MAX_LENGTH - 1) {
319 if (internal::is_invalid_scope_id_symbol(c)) {
323 result.scope_id[index++] = c;
328 if (scope && result.scope_id[0] ==
'\0') {
335 template <
typename Iter>
337 IPADDRESS_CONSTEXPR std::array<fixed_string<4>, _max_parts + 1> empty_parts = {
338 make_fixed_string(
"\0\0\0\0"),
339 make_fixed_string(
"\0\0\0\0"),
340 make_fixed_string(
"\0\0\0\0"),
341 make_fixed_string(
"\0\0\0\0"),
342 make_fixed_string(
"\0\0\0\0"),
343 make_fixed_string(
"\0\0\0\0"),
344 make_fixed_string(
"\0\0\0\0"),
345 make_fixed_string(
"\0\0\0\0"),
346 make_fixed_string(
"\0\0\0\0")};
348 char parts[_max_parts + 1][5] = {};
349 char last_part[17] = {};
354 bool has_double_colon =
false;
357 uint32_t error_symbol = 0;
360 auto c = internal::next_char_or_error(it, end, error, error_symbol);
362 parts_count = error_symbol;
365 if (!has_double_colon && c ==
':' && prev_c ==
':') {
366 has_double_colon =
true;
368 if (parts_count > _max_parts) {
369 error = has_double_colon
379 last_part[symbol++] = c;
380 last_part[symbol] =
'\0';
387 auto& current_part = parts[parts_count++];
388 current_part[0] = last_part[0];
389 current_part[1] = last_part[1];
390 current_part[2] = last_part[2];
391 current_part[3] = last_part[3];
399 if (parts_count > _max_parts) {
400 if (parts[0][0] ==
'\0' && parts[1][0] !=
'\0') {
402 }
else if (last_part[0] ==
'\0') {
410 auto has_ipv4 =
false;
412 for (
auto i = 0; i < symbol; ++i) {
413 if (last_part[i] ==
'.') {
420 if (parts_count + 1 >= _max_parts) {
425 auto ipv4 = ipv4_address::parse(last_part, error).to_uint();
431 to_hex(uint16_t((ipv4 >> 16) & 0xFFFF), parts[parts_count++]);
432 to_hex(uint16_t(ipv4 & 0xFFFF), parts[parts_count++]);
439 auto& current_part = parts[parts_count++];
440 current_part[0] = last_part[0];
441 current_part[1] = last_part[1];
442 current_part[2] = last_part[2];
443 current_part[3] = last_part[3];
446 if (parts_count < _min_parts) {
452 make_fixed_string(parts[0]),
453 make_fixed_string(parts[1]),
454 make_fixed_string(parts[2]),
455 make_fixed_string(parts[3]),
456 make_fixed_string(parts[4]),
457 make_fixed_string(parts[5]),
458 make_fixed_string(parts[6]),
459 make_fixed_string(parts[7]),
460 make_fixed_string(parts[8])};
465 for (size_t i = 1; i < parts_count - 1; ++i) {
466 if (parts[i].empty()) {
468 error = error_code::most_one_double_colon_permitted;
476 auto parts_hi = skip;
477 auto parts_lo = parts_count - skip - 1;
479 if (parts[0].empty()) {
486 if (parts[parts_count - 1].empty()) {
493 const auto parts_skipped = _max_parts - (parts_hi + parts_lo);
495 if (parts_skipped < 1) {
500 return { parts_hi, parts_lo, parts_skipped };
502 if (parts_count != _max_parts) {
507 if (parts[0].empty()) {
512 if (parts[parts_count - 1].empty()) {
517 return { parts_count, 0, 0 };
522 base_type result = {};
525 for (size_t i = 0; i < hi; ++i) {
526 const auto part = parse_part(parts[i], error);
527 result[index++] = uint8_t(part >> 8);
528 result[index++] = uint8_t(part & 0xFF);
530 if (error != error_code::no_error) {
534 index += skipped << 1;
536 assert(parts_count > lo);
537 for (size_t i = parts_count - lo; i < parts_count; ++i) {
538 const auto part = parse_part(parts[i], error);
539 result[index++] = uint8_t(part >> 8);
540 result[index++] = uint8_t(part & 0xFF);
542 if (error != error_code::no_error) {
551 for (size_t i = 0; i < part.size(); ++i) {
552 const auto c = part[i];
553 const auto power = pow16(part.size() - i - 1);
554 if (c >=
'0' && c <=
'9') {
555 value += (c -
'0') * power;
556 }
else if(c >=
'A' && c <=
'F') {
557 value += (c - 55) * power;
558 }
else if (c >=
'a' && c <=
'f') {
559 value += (c - 87) * power;
561 error = error_code::part_has_invalid_symbol;
572 case 2:
return 16 * 16;
573 case 3:
return 16 * 16 * 16;
575 assert(!
"Unreachable code");
581 constexpr char digits[] =
"0123456789abcdef";
582 for (
auto i = 0, j = (4 - 1) * 4; i < 4; ++i, j -= 4) {
583 result[i] = digits[(value >> j) & 0x0f];
584 result[i + 1] =
'\0';
589template <
typename Ext>
592template <
typename Ext>
593constexpr size_t
base_v6<Ext>::base_size;
595template <
typename Ext>
596constexpr size_t
base_v6<Ext>::base_max_string_len;
598template <
typename Ext>
599constexpr size_t
base_v6<Ext>::base_max_prefixlen;
601template <
typename Ext>
602constexpr size_t
base_v6<Ext>::_min_parts;
604template <
typename Ext>
605constexpr size_t
base_v6<Ext>::_max_parts;
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:402
#define IPADDRESS_EXPORT
Definition config.hpp:42
#define IPADDRESS_NODISCARD
Definition config.hpp:98
#define IPADDRESS_FORCE_INLINE
Definition config.hpp:112
#define IPADDRESS_NAMESPACE
Definition config.hpp:38
#define IPADDRESS_NOEXCEPT
Definition config.hpp:89
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