src/corosio/src/ipv6_address.cpp

100.0% Lines (287/291) 100.0% List of functions (22/22) 93.0% Branches (160/172)
ipv6_address.cpp
f(x) Functions (22)
Function Calls Lines Branches Blocks
boost::corosio::ipv6_address::ipv6_address(std::array<unsigned char, 16ull> const&, unsigned int) :26 244x 100.0% – 100.0% boost::corosio::ipv6_address::ipv6_address(boost::corosio::ipv4_address const&) :33 12x 100.0% – 100.0% boost::corosio::ipv6_address::ipv6_address(std::basic_string_view<char, std::char_traits<char> >) :40 19x 100.0% 100.0% 100.0% boost::corosio::ipv6_address::to_string[abi:cxx11]() const :49 21x 100.0% 100.0% 72.7% boost::corosio::ipv6_address::to_buffer(char*, unsigned long long) const :57 11x 100.0% 100.0% 88.9% boost::corosio::ipv6_address::is_unspecified() const :66 6x 100.0% – 100.0% boost::corosio::ipv6_address::is_loopback() const :74 14x 100.0% – 100.0% boost::corosio::ipv6_address::is_multicast() const :80 6x 100.0% – 100.0% boost::corosio::ipv6_address::is_v4_mapped() const :86 44x 100.0% 62.5% 100.0% boost::corosio::ipv6_address::to_v4() const :95 8x 100.0% 100.0% 100.0% boost::corosio::ipv6_address::loopback() :106 94x 100.0% – 100.0% boost::corosio::operator<<(std::ostream&, boost::corosio::ipv6_address const&) :114 3x 100.0% 100.0% 100.0% boost::corosio::ipv6_address::print_impl(char*) const :122 29x 100.0% 96.2% 100.0% boost::corosio::ipv6_address::print_impl(char*) const::{lambda(unsigned char const*, unsigned char const*)#1}::operator()(unsigned char const*, unsigned char const*) const :124 91x 100.0% 100.0% 100.0% boost::corosio::ipv6_address::print_impl(char*) const::{lambda(char*, unsigned short)#1}::operator()(char*, unsigned short) const :137 66x 100.0% 100.0% 100.0% boost::corosio::(anonymous namespace)::hexdig_value(char) :262 799x 100.0% 100.0% 100.0% boost::corosio::(anonymous namespace)::parse_h16(char const*&, char const*, unsigned char&, unsigned char&) :276 287x 100.0% 100.0% 92.9% boost::corosio::(anonymous namespace)::maybe_octet(unsigned char const*) :308 17x 100.0% 100.0% 100.0% boost::corosio::parse_ipv6_impl(std::basic_string_view<char, std::char_traits<char> >, boost::corosio::ipv6_address&) :324 137x 100.0% 96.4% 99.0% boost::corosio::(anonymous namespace)::parse_zone_numeric(std::basic_string_view<char, std::char_traits<char> >, unsigned int&) :493 19x 100.0% 100.0% 100.0% boost::corosio::(anonymous namespace)::parse_zone(std::basic_string_view<char, std::char_traits<char> >, unsigned int&) :514 19x 100.0% 100.0% 100.0% boost::corosio::make_ipv6_address(std::basic_string_view<char, std::char_traits<char> >) :537 143x 100.0% 100.0% 100.0%
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Vinnie Falco ([email protected])
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/corosio
8 //
9
10 #include <boost/corosio/ipv6_address.hpp>
11 #include <boost/corosio/ipv4_address.hpp>
12
13 #include <boost/corosio/detail/except.hpp>
14 #include <boost/corosio/detail/platform.hpp>
15
16 #if BOOST_COROSIO_POSIX
17 #include <net/if.h>
18 #endif
19
20 #include <cstring>
21 #include <ostream>
22 #include <stdexcept>
23
24 namespace boost::corosio {
25
26 244x ipv6_address::ipv6_address(
27 244x bytes_type const& bytes, std::uint32_t scope_id) noexcept
28 244x : scope_id_(scope_id)
29 {
30 244x std::memcpy(addr_.data(), bytes.data(), 16);
31 244x }
32
33 12x ipv6_address::ipv6_address(ipv4_address const& addr) noexcept
34 {
35 12x auto const v = addr.to_bytes();
36 48x addr_ = {
37 12x {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, v[0], v[1], v[2], v[3]}};
38 12x }
39
40 19x ipv6_address::ipv6_address(std::string_view s)
41 {
42 19x auto [ec, addr] = make_ipv6_address(s);
43
2/2
✓ Branch 6 → 7 taken 3 times.
✓ Branch 6 → 8 taken 16 times.
19x if (ec)
44 3x detail::throw_system_error(ec, "invalid IPv6 address");
45 16x *this = addr;
46 16x }
47
48 std::string
49 21x ipv6_address::to_string() const
50 {
51 char buf[max_str_len];
52 21x auto n = print_impl(buf);
53
1/1
✓ Branch 5 → 6 taken 21 times.
42x return std::string(buf, n);
54 }
55
56 std::string_view
57 11x ipv6_address::to_buffer(char* dest, std::size_t dest_size) const
58 {
59
2/2
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 6 taken 8 times.
11x if (dest_size < max_str_len)
60
1/1
✓ Branch 4 → 5 taken 3 times.
3x throw std::length_error("buffer too small for IPv6 address");
61 8x auto n = print_impl(dest);
62 8x return std::string_view(dest, n);
63 }
64
65 bool
66 6x ipv6_address::is_unspecified() const noexcept
67 {
68 // Classification tests the address bits; the zone names a link,
69 // not a different kind of address
70 6x return addr_ == bytes_type{};
71 }
72
73 bool
74 14x ipv6_address::is_loopback() const noexcept
75 {
76 14x return addr_ == loopback().addr_;
77 }
78
79 bool
80 6x ipv6_address::is_multicast() const noexcept
81 {
82 6x return addr_[0] == 0xff;
83 }
84
85 bool
86 44x ipv6_address::is_v4_mapped() const noexcept
87 {
88
4/6
✓ Branch 5 → 6 taken 31 times.
✓ Branch 5 → 27 taken 3 times.
✓ Branch 7 → 8 taken 31 times.
✗ Branch 7 → 27 not taken.
✓ Branch 9 → 10 taken 31 times.
✗ Branch 9 → 27 not taken.
78x return addr_[0] == 0 && addr_[1] == 0 && addr_[2] == 0 && addr_[3] == 0 &&
89
4/8
✓ Branch 11 → 12 taken 31 times.
✗ Branch 11 → 27 not taken.
✓ Branch 13 → 14 taken 31 times.
✗ Branch 13 → 27 not taken.
✓ Branch 15 → 16 taken 31 times.
✗ Branch 15 → 27 not taken.
✓ Branch 17 → 18 taken 31 times.
✗ Branch 17 → 27 not taken.
31x addr_[4] == 0 && addr_[5] == 0 && addr_[6] == 0 && addr_[7] == 0 &&
90
6/8
✓ Branch 3 → 4 taken 34 times.
✓ Branch 3 → 27 taken 10 times.
✓ Branch 19 → 20 taken 31 times.
✗ Branch 19 → 27 not taken.
✓ Branch 21 → 22 taken 31 times.
✗ Branch 21 → 27 not taken.
✓ Branch 23 → 24 taken 11 times.
✓ Branch 23 → 27 taken 20 times.
89x addr_[8] == 0 && addr_[9] == 0 && addr_[10] == 0xff &&
91
1/2
✓ Branch 25 → 26 taken 11 times.
✗ Branch 25 → 27 not taken.
55x addr_[11] == 0xff;
92 }
93
94 ipv4_address
95 8x ipv6_address::to_v4() const
96 {
97
2/2
✓ Branch 3 → 4 taken 4 times.
✓ Branch 3 → 6 taken 4 times.
8x if (!is_v4_mapped())
98 4x detail::throw_system_error(
99 4x std::make_error_code(std::errc::address_family_not_supported),
100 "address is not v4-mapped");
101 4x return ipv4_address(
102 4x ipv4_address::bytes_type{{addr_[12], addr_[13], addr_[14], addr_[15]}});
103 }
104
105 ipv6_address
106 94x ipv6_address::loopback() noexcept
107 {
108 94x ipv6_address a;
109 94x a.addr_[15] = 1;
110 94x return a;
111 }
112
113 std::ostream&
114 3x operator<<(std::ostream& os, ipv6_address const& addr)
115 {
116 char buf[ipv6_address::max_str_len];
117
2/2
✓ Branch 2 → 3 taken 3 times.
✓ Branch 3 → 4 taken 3 times.
3x os << addr.to_buffer(buf, sizeof(buf));
118 3x return os;
119 }
120
121 std::size_t
122 29x ipv6_address::print_impl(char* dest) const noexcept
123 {
124 91x auto const count_zeroes = [](unsigned char const* first,
125 unsigned char const* const last) {
126 91x std::size_t n = 0;
127
2/2
✓ Branch 6 → 3 taken 248 times.
✓ Branch 6 → 7 taken 3 times.
251x while (first != last)
128 {
129
4/4
✓ Branch 3 → 4 taken 215 times.
✓ Branch 3 → 7 taken 33 times.
✓ Branch 4 → 5 taken 160 times.
✓ Branch 4 → 7 taken 55 times.
248x if (first[0] != 0 || first[1] != 0)
130 break;
131 160x n += 2;
132 160x first += 2;
133 }
134 91x return n;
135 };
136
137 66x auto const print_hex = [](char* dest, unsigned short v) {
138 66x char const* const dig = "0123456789abcdef";
139
2/2
✓ Branch 2 → 3 taken 25 times.
✓ Branch 2 → 4 taken 41 times.
66x if (v >= 0x1000)
140 {
141 25x *dest++ = dig[v >> 12];
142 25x v &= 0x0fff;
143 25x *dest++ = dig[v >> 8];
144 25x v &= 0x0ff;
145 25x *dest++ = dig[v >> 4];
146 25x v &= 0x0f;
147 25x *dest++ = dig[v];
148 }
149
2/2
✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 6 taken 37 times.
41x else if (v >= 0x100)
150 {
151 4x *dest++ = dig[v >> 8];
152 4x v &= 0x0ff;
153 4x *dest++ = dig[v >> 4];
154 4x v &= 0x0f;
155 4x *dest++ = dig[v];
156 }
157
2/2
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 36 times.
37x else if (v >= 0x10)
158 {
159 1x *dest++ = dig[v >> 4];
160 1x v &= 0x0f;
161 1x *dest++ = dig[v];
162 }
163 else
164 {
165 36x *dest++ = dig[v];
166 }
167 66x return dest;
168 };
169
170 29x auto const dest0 = dest;
171 // find longest run of zeroes
172 29x std::size_t best_len = 0;
173 29x int best_pos = -1;
174 29x auto it = addr_.data();
175 29x auto const v4 = is_v4_mapped();
176
2/2
✓ Branch 4 → 5 taken 3 times.
✓ Branch 4 → 8 taken 26 times.
58x auto const end = v4 ? (it + addr_.size() - 4) : it + addr_.size();
177
178
2/2
✓ Branch 19 → 12 taken 91 times.
✓ Branch 19 → 20 taken 29 times.
120x while (it != end)
179 {
180 91x auto n = count_zeroes(it, end);
181
2/2
✓ Branch 13 → 14 taken 66 times.
✓ Branch 13 → 15 taken 25 times.
91x if (n == 0)
182 {
183 66x it += 2;
184 66x continue;
185 }
186
1/2
✓ Branch 15 → 16 taken 25 times.
✗ Branch 15 → 18 not taken.
25x if (n > best_len)
187 {
188 25x best_pos = static_cast<int>(it - addr_.data());
189 25x best_len = n;
190 }
191 25x it += n;
192 }
193
194 29x it = addr_.data();
195
2/2
✓ Branch 21 → 22 taken 13 times.
✓ Branch 21 → 24 taken 16 times.
29x if (best_pos != 0)
196 {
197 13x unsigned short v = static_cast<unsigned short>(it[0] * 256U + it[1]);
198 13x dest = print_hex(dest, v);
199 13x it += 2;
200 }
201 else
202 {
203 16x *dest++ = ':';
204 16x it += best_len;
205
2/2
✓ Branch 24 → 25 taken 2 times.
✓ Branch 24 → 26 taken 14 times.
16x if (it == end)
206 2x *dest++ = ':';
207 }
208
209
2/2
✓ Branch 34 → 27 taken 62 times.
✓ Branch 34 → 35 taken 29 times.
91x while (it != end)
210 {
211 62x *dest++ = ':';
212
2/2
✓ Branch 28 → 29 taken 9 times.
✓ Branch 28 → 32 taken 53 times.
62x if (it - addr_.data() == best_pos)
213 {
214 9x it += best_len;
215
2/2
✓ Branch 29 → 30 taken 1 time.
✓ Branch 29 → 31 taken 8 times.
9x if (it == end)
216 1x *dest++ = ':';
217 9x continue;
218 }
219 53x unsigned short v = static_cast<unsigned short>(it[0] * 256U + it[1]);
220 53x dest = print_hex(dest, v);
221 53x it += 2;
222 }
223
224
2/2
✓ Branch 35 → 36 taken 3 times.
✓ Branch 35 → 46 taken 26 times.
29x if (v4)
225 {
226 ipv4_address::bytes_type bytes;
227 3x bytes[0] = it[0];
228 3x bytes[1] = it[1];
229 3x bytes[2] = it[2];
230 3x bytes[3] = it[3];
231 3x ipv4_address a(bytes);
232 3x *dest++ = ':';
233 char buf[ipv4_address::max_str_len];
234 3x auto sv = a.to_buffer(buf, sizeof(buf));
235 3x std::memcpy(dest, sv.data(), sv.size());
236 3x dest += sv.size();
237 }
238
239
2/2
✓ Branch 46 → 47 taken 6 times.
✓ Branch 46 → 53 taken 23 times.
29x if (scope_id_ != 0)
240 {
241 6x *dest++ = '%';
242 char digits[10];
243 6x int n = 0;
244 6x std::uint32_t v = scope_id_;
245 do
246 {
247 33x digits[n++] = static_cast<char>('0' + v % 10);
248 33x v /= 10;
249 }
250
2/2
✓ Branch 48 → 48 taken 27 times.
✓ Branch 48 → 49 taken 6 times.
33x while (v != 0);
251
2/2
✓ Branch 51 → 50 taken 33 times.
✓ Branch 51 → 52 taken 6 times.
39x while (n != 0)
252 33x *dest++ = digits[--n];
253 }
254
255 29x return static_cast<std::size_t>(dest - dest0);
256 }
257
258 namespace {
259
260 // Convert hex character to value (0-15), or -1 if not hex
261 inline int
262 799x hexdig_value(char c) noexcept
263 {
264
4/4
✓ Branch 2 → 3 taken 775 times.
✓ Branch 2 → 5 taken 24 times.
✓ Branch 3 → 4 taken 460 times.
✓ Branch 3 → 5 taken 315 times.
799x if (c >= '0' && c <= '9')
265 460x return c - '0';
266
4/4
✓ Branch 5 → 6 taken 171 times.
✓ Branch 5 → 8 taken 168 times.
✓ Branch 6 → 7 taken 131 times.
✓ Branch 6 → 8 taken 40 times.
339x if (c >= 'a' && c <= 'f')
267 131x return c - 'a' + 10;
268
4/4
✓ Branch 8 → 9 taken 52 times.
✓ Branch 8 → 11 taken 156 times.
✓ Branch 9 → 10 taken 8 times.
✓ Branch 9 → 11 taken 44 times.
208x if (c >= 'A' && c <= 'F')
269 8x return c - 'A' + 10;
270 200x return -1;
271 }
272
273 // Parse h16 (1-4 hex digits) returning 16-bit value
274 // Returns true on success, advances `it`
275 bool
276 287x parse_h16(
277 char const*& it,
278 char const* end,
279 unsigned char& hi,
280 unsigned char& lo) noexcept
281 {
282 − if (it == end) // LCOV_EXCL_LINE callers pre-check end-of-input
283 − return false; // LCOV_EXCL_LINE callers pre-check end-of-input
284
285 287x int d = hexdig_value(*it);
286
2/2
✓ Branch 5 → 6 taken 20 times.
✓ Branch 5 → 7 taken 267 times.
287x if (d < 0)
287 20x return false;
288
289 267x unsigned v = static_cast<unsigned>(d);
290 267x ++it;
291
292
4/4
✓ Branch 12 → 13 taken 398 times.
✓ Branch 12 → 14 taken 45 times.
✓ Branch 13 → 8 taken 333 times.
✓ Branch 13 → 14 taken 65 times.
443x for (int i = 0; i < 3 && it != end; ++i)
293 {
294 333x d = hexdig_value(*it);
295
2/2
✓ Branch 9 → 10 taken 157 times.
✓ Branch 9 → 11 taken 176 times.
333x if (d < 0)
296 157x break;
297 176x v = (v << 4) | static_cast<unsigned>(d);
298 176x ++it;
299 }
300
301 267x hi = static_cast<unsigned char>((v >> 8) & 0xff);
302 267x lo = static_cast<unsigned char>(v & 0xff);
303 267x return true;
304 }
305
306 // Check if a hex word could be 0..255 if interpreted as decimal
307 bool
308 17x maybe_octet(unsigned char const* p) noexcept
309 {
310 17x unsigned short word = static_cast<unsigned short>(p[0]) * 256 +
311 17x static_cast<unsigned short>(p[1]);
312
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 16 times.
17x if (word > 0x255)
313 1x return false;
314
2/2
✓ Branch 4 → 5 taken 1 time.
✓ Branch 4 → 6 taken 15 times.
16x if (((word >> 4) & 0xf) > 9)
315 1x return false;
316
2/2
✓ Branch 6 → 7 taken 1 time.
✓ Branch 6 → 8 taken 14 times.
15x if ((word & 0xf) > 9)
317 1x return false;
318 14x return true;
319 }
320
321 } // namespace
322
323 static std::error_code
324 137x parse_ipv6_impl(std::string_view s, ipv6_address& addr) noexcept
325 {
326 137x auto it = s.data();
327 137x auto const end = it + s.size();
328
329 137x int n = 8; // words needed
330 137x int b = -1; // value of n when '::' seen
331 137x bool c = false; // need colon
332 137x auto prev = it;
333 137x ipv6_address::bytes_type bytes{};
334 unsigned char hi, lo;
335
336 for (;;)
337 {
338
2/2
✓ Branch 5 → 6 taken 70 times.
✓ Branch 5 → 9 taken 419 times.
489x if (it == end)
339 {
340
2/2
✓ Branch 6 → 7 taken 64 times.
✓ Branch 6 → 8 taken 6 times.
70x if (b != -1)
341 {
342 // end in "::"
343 64x break;
344 }
345 // not enough words
346 6x return std::make_error_code(std::errc::invalid_argument);
347 }
348
349
2/2
✓ Branch 9 → 10 taken 219 times.
✓ Branch 9 → 28 taken 200 times.
419x if (*it == ':')
350 {
351 219x ++it;
352
2/2
✓ Branch 10 → 11 taken 7 times.
✓ Branch 10 → 12 taken 212 times.
219x if (it == end)
353 {
354 // expected ':'
355 7x return std::make_error_code(std::errc::invalid_argument);
356 }
357
2/2
✓ Branch 12 → 13 taken 95 times.
✓ Branch 12 → 18 taken 117 times.
212x if (*it == ':')
358 {
359
2/2
✓ Branch 13 → 14 taken 94 times.
✓ Branch 13 → 17 taken 1 time.
95x if (b == -1)
360 {
361 // first "::"
362 94x ++it;
363 94x --n;
364 94x b = n;
365
2/2
✓ Branch 14 → 15 taken 1 time.
✓ Branch 14 → 16 taken 93 times.
94x if (n == 0)
366 1x break;
367 93x c = false;
368 93x continue;
369 }
370 // extra "::" found
371 1x return std::make_error_code(std::errc::invalid_argument);
372 }
373
2/2
✓ Branch 18 → 19 taken 114 times.
✓ Branch 18 → 27 taken 3 times.
117x if (c)
374 {
375 114x prev = it;
376
2/2
✓ Branch 20 → 21 taken 2 times.
✓ Branch 20 → 22 taken 112 times.
114x if (!parse_h16(it, end, hi, lo))
377 2x return std::make_error_code(std::errc::invalid_argument);
378 112x bytes[2 * (8 - n) + 0] = hi;
379 112x bytes[2 * (8 - n) + 1] = lo;
380 112x --n;
381
2/2
✓ Branch 24 → 25 taken 7 times.
✓ Branch 24 → 26 taken 105 times.
112x if (n == 0)
382 7x break;
383 105x continue;
384 }
385 // expected h16
386 3x return std::make_error_code(std::errc::invalid_argument);
387 }
388
389
2/2
✓ Branch 28 → 29 taken 21 times.
✓ Branch 28 → 67 taken 179 times.
200x if (*it == '.')
390 {
391
4/4
✓ Branch 29 → 30 taken 5 times.
✓ Branch 29 → 32 taken 16 times.
✓ Branch 30 → 31 taken 4 times.
✓ Branch 30 → 32 taken 1 time.
21x if (b == -1 && n > 1)
392 {
393 // not enough h16
394 14x return std::make_error_code(std::errc::invalid_argument);
395 }
396
2/2
✓ Branch 34 → 35 taken 3 times.
✓ Branch 34 → 36 taken 14 times.
17x if (!maybe_octet(&bytes[std::size_t(2) * std::size_t(7 - n)]))
397 {
398 // invalid octet
399 3x return std::make_error_code(std::errc::invalid_argument);
400 }
401 // rewind the h16 and parse it as IPv4
402 14x it = prev;
403 14x auto [v4ec, v4] = make_ipv4_address(
404 14x std::string_view(it, static_cast<std::size_t>(end - it)));
405
2/2
✓ Branch 41 → 42 taken 7 times.
✓ Branch 41 → 43 taken 7 times.
14x if (v4ec)
406 7x return v4ec;
407 // Must consume exactly the IPv4 address portion
408 // Re-parse to find where it ends
409 7x auto v4_it = it;
410
2/2
✓ Branch 45 → 46 taken 74 times.
✓ Branch 45 → 49 taken 7 times.
81x while (v4_it != end &&
411
4/6
✓ Branch 46 → 44 taken 21 times.
✓ Branch 46 → 47 taken 53 times.
✓ Branch 47 → 48 taken 53 times.
✗ Branch 47 → 49 not taken.
✓ Branch 48 → 44 taken 53 times.
✗ Branch 48 → 49 not taken.
74x (*v4_it == '.' || (*v4_it >= '0' && *v4_it <= '9')))
412 74x ++v4_it;
413 // Verify it parsed correctly by re-parsing the exact substring
414 7x auto [ckec, v4_check] = make_ipv4_address(
415 7x std::string_view(it, static_cast<std::size_t>(v4_it - it)));
416 − if (ckec) // LCOV_EXCL_LINE prefix of a parsed tail cannot fail
417 − return ckec; // LCOV_EXCL_LINE prefix of a parsed tail cannot fail
418 7x it = v4_it;
419 7x auto const b4 = v4_check.to_bytes();
420 7x bytes[2 * (7 - n) + 0] = b4[0];
421 7x bytes[2 * (7 - n) + 1] = b4[1];
422 7x bytes[2 * (7 - n) + 2] = b4[2];
423 7x bytes[2 * (7 - n) + 3] = b4[3];
424 7x --n;
425 7x break;
426 }
427
428 179x auto d = hexdig_value(*it);
429
4/4
✓ Branch 68 → 69 taken 85 times.
✓ Branch 68 → 71 taken 94 times.
✓ Branch 69 → 70 taken 3 times.
✓ Branch 69 → 71 taken 82 times.
179x if (b != -1 && d < 0)
430 {
431 // ends in "::"
432 3x break;
433 }
434
435
2/2
✓ Branch 71 → 72 taken 173 times.
✓ Branch 71 → 80 taken 3 times.
176x if (!c)
436 {
437 173x prev = it;
438
2/2
✓ Branch 73 → 74 taken 18 times.
✓ Branch 73 → 75 taken 155 times.
173x if (!parse_h16(it, end, hi, lo))
439 18x return std::make_error_code(std::errc::invalid_argument);
440 155x bytes[2 * (8 - n) + 0] = hi;
441 155x bytes[2 * (8 - n) + 1] = lo;
442 155x --n;
443
2/2
✓ Branch 77 → 78 taken 1 time.
✓ Branch 77 → 79 taken 154 times.
155x if (n == 0)
444 1x break;
445 154x c = true;
446 154x continue;
447 }
448
449 // ':' divides a word
450 3x return std::make_error_code(std::errc::invalid_argument);
451 352x }
452
453 // Must have consumed entire string
454
2/2
✓ Branch 82 → 83 taken 6 times.
✓ Branch 82 → 84 taken 77 times.
83x if (it != end)
455 6x return std::make_error_code(std::errc::invalid_argument);
456
457
2/2
✓ Branch 84 → 85 taken 3 times.
✓ Branch 84 → 87 taken 74 times.
77x if (b == -1)
458 {
459 3x addr = ipv6_address{bytes};
460 3x return {};
461 }
462
463
2/2
✓ Branch 87 → 88 taken 5 times.
✓ Branch 87 → 90 taken 69 times.
74x if (b == n)
464 {
465 // "::" last
466 5x auto const i = 2 * (7 - n);
467 5x std::memset(&bytes[i], 0, 16 - i);
468 }
469
2/2
✓ Branch 90 → 91 taken 22 times.
✓ Branch 90 → 95 taken 47 times.
69x else if (b == 7)
470 {
471 // "::" first
472 22x auto const i = 2 * (b - n);
473 22x std::memmove(&bytes[16 - i], &bytes[2], i);
474 22x std::memset(&bytes[0], 0, 16 - i);
475 }
476 else
477 {
478 // "::" in middle
479 47x auto const i0 = 2 * (7 - b);
480 47x auto const i1 = 2 * (b - n);
481 47x std::memmove(&bytes[16 - i1], &bytes[i0 + 2], i1);
482 47x std::memset(&bytes[i0], 0, 16 - (i0 + i1));
483 }
484
485 74x addr = ipv6_address{bytes};
486 74x return {};
487 }
488
489 namespace {
490
491 // Strict decimal: full consumption, no sign, must fit uint32
492 bool
493 19x parse_zone_numeric(std::string_view s, std::uint32_t& out) noexcept
494 {
495
2/2
✓ Branch 3 → 4 taken 1 time.
✓ Branch 3 → 5 taken 18 times.
19x if (s.empty())
496 1x return false;
497 18x std::uint64_t v = 0;
498
2/2
✓ Branch 13 → 7 taken 36 times.
✓ Branch 13 → 14 taken 13 times.
49x for (char c : s)
499 {
500
4/4
✓ Branch 7 → 8 taken 34 times.
✓ Branch 7 → 9 taken 2 times.
✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 10 taken 32 times.
36x if (c < '0' || c > '9')
501 4x return false;
502 32x v = v * 10 + static_cast<unsigned>(c - '0');
503
2/2
✓ Branch 10 → 11 taken 1 time.
✓ Branch 10 → 12 taken 31 times.
32x if (v > 0xffffffffull)
504 1x return false;
505 }
506 13x out = static_cast<std::uint32_t>(v);
507 13x return true;
508 }
509
510 // Numeric everywhere; interface names via the system where the
511 // platform names interfaces (Windows zones are numeric). An unknown
512 // name is an error, never a silent zone 0.
513 bool
514 19x parse_zone(std::string_view s, std::uint32_t& out) noexcept
515 {
516
2/2
✓ Branch 3 → 4 taken 13 times.
✓ Branch 3 → 5 taken 6 times.
19x if (parse_zone_numeric(s, out))
517 13x return true;
518 #if BOOST_COROSIO_POSIX
519 char name[IF_NAMESIZE];
520 if (s.empty() || s.size() >= sizeof(name))
521 return false;
522 std::memcpy(name, s.data(), s.size());
523 name[s.size()] = '\0';
524 auto idx = if_nametoindex(name);
525 if (idx == 0)
526 return false;
527 out = static_cast<std::uint32_t>(idx);
528 return true;
529 #else
530 6x return false;
531 #endif
532 }
533
534 } // namespace
535
536 capy::io_result<ipv6_address>
537 143x make_ipv6_address(std::string_view s) noexcept
538 {
539 143x std::uint32_t scope_id = 0;
540
2/2
✓ Branch 3 → 4 taken 19 times.
✓ Branch 3 → 12 taken 124 times.
143x if (auto pos = s.find('%'); pos != std::string_view::npos)
541 {
542
2/2
✓ Branch 6 → 7 taken 6 times.
✓ Branch 6 → 11 taken 13 times.
19x if (!parse_zone(s.substr(pos + 1), scope_id))
543 return {
544 6x std::make_error_code(std::errc::invalid_argument),
545 6x ipv6_address{}};
546 13x s = s.substr(0, pos);
547 }
548 137x ipv6_address addr;
549
2/2
✓ Branch 14 → 15 taken 60 times.
✓ Branch 14 → 18 taken 77 times.
137x if (auto ec = parse_ipv6_impl(s, addr))
550 60x return {ec, ipv6_address{}};
551 77x return {std::error_code{}, ipv6_address(addr.to_bytes(), scope_id)};
552 }
553
554 } // namespace boost::corosio
555