src/corosio/src/ipv6_address.cpp

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