src/corosio/src/endpoint.cpp

92.6% Lines (75/81) 100.0% List of functions (3/3) 80.0% Branches (52/65)
f(x) Functions (3)
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/endpoint.hpp>
11
12 #include <charconv>
13
14 namespace boost::corosio {
15
16 endpoint_format
17 52x detect_endpoint_format(std::string_view s) noexcept
18 {
19
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
52x if (s.empty())
20 return endpoint_format::ipv4_no_port;
21
22 // Bracketed IPv6
23
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 32 times.
52x if (s[0] == '[')
24 20x return endpoint_format::ipv6_bracketed;
25
26 // Count colons
27 32x std::size_t colon_count = 0;
28
2/2
✓ Branch 0 taken 325 times.
✓ Branch 1 taken 32 times.
357x for (char c : s)
29 {
30
2/2
✓ Branch 0 taken 293 times.
✓ Branch 1 taken 32 times.
325x if (c == ':')
31 32x ++colon_count;
32 }
33
34
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 9 times.
32x if (colon_count == 0)
35 9x return endpoint_format::ipv4_no_port;
36
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 6 times.
23x if (colon_count == 1)
37 17x return endpoint_format::ipv4_with_port;
38 6x return endpoint_format::ipv6_no_port;
39 52x }
40
41 namespace {
42
43 // Parse port number from string
44 // Returns true on success
45 bool
46 26x parse_port(std::string_view s, std::uint16_t& port) noexcept
47 {
48
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 22 times.
26x if (s.empty())
49 4x return false;
50
51 // No leading zeros allowed (except "0" itself)
52
4/4
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 2 times.
22x if (s.size() > 1 && s[0] == '0')
53 2x return false;
54
55 20x unsigned long val = 0;
56
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20x auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), val);
57
3/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
20x if (ec != std::errc{} || ptr != s.data() + s.size())
58 6x return false;
59
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 10 times.
14x if (val > 65535)
60 4x return false;
61
62 10x port = static_cast<std::uint16_t>(val);
63 10x return true;
64 26x }
65
66 } // namespace
67
68 std::error_code
69 48x parse_endpoint(std::string_view s, endpoint& ep) noexcept
70 {
71
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 46 times.
48x if (s.empty())
72 2x return std::make_error_code(std::errc::invalid_argument);
73
74 46x auto fmt = detect_endpoint_format(s);
75
76
4/5
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 18 times.
46x switch (fmt)
77 {
78 case endpoint_format::ipv4_no_port:
79 {
80 8x ipv4_address addr;
81 8x auto ec = parse_ipv4_address(s, addr);
82
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8x if (ec)
83 6x return ec;
84 2x ep = endpoint(addr, 0);
85 2x return {};
86 }
87
88 case endpoint_format::ipv4_with_port:
89 {
90 // Find the colon separating address and port
91 16x auto colon_pos = s.rfind(':');
92
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16x if (colon_pos == std::string_view::npos)
93 return std::make_error_code(std::errc::invalid_argument);
94
95
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16x auto addr_str = s.substr(0, colon_pos);
96
1/2
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
16x auto port_str = s.substr(colon_pos + 1);
97
98 16x ipv4_address addr;
99 16x auto ec = parse_ipv4_address(addr_str, addr);
100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16x if (ec)
101 return ec;
102
103 std::uint16_t port;
104
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 10 times.
16x if (!parse_port(port_str, port))
105 10x return std::make_error_code(std::errc::invalid_argument);
106
107 6x ep = endpoint(addr, port);
108 6x return {};
109 }
110
111 case endpoint_format::ipv6_no_port:
112 {
113 4x ipv6_address addr;
114 4x auto ec = parse_ipv6_address(s, addr);
115
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4x if (ec)
116 return ec;
117 4x ep = endpoint(addr, 0);
118 4x return {};
119 }
120
121 case endpoint_format::ipv6_bracketed:
122 {
123 // Must start with '[' and contain ']'
124
3/4
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
18x if (s.size() < 2 || s[0] != '[')
125 2x return std::make_error_code(std::errc::invalid_argument);
126
127 16x auto close_bracket = s.find(']');
128
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 2 times.
16x if (close_bracket == std::string_view::npos)
129 2x return std::make_error_code(std::errc::invalid_argument);
130
131
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14x auto addr_str = s.substr(1, close_bracket - 1);
132
133 14x ipv6_address addr;
134 14x auto ec = parse_ipv6_address(addr_str, addr);
135
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 12 times.
14x if (ec)
136 2x return ec;
137
138 12x std::uint16_t port = 0;
139
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 10 times.
12x if (close_bracket + 1 < s.size())
140 {
141 // There's something after ']'
142
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10x if (s[close_bracket + 1] != ':')
143 return std::make_error_code(std::errc::invalid_argument);
144
145
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10x auto port_str = s.substr(close_bracket + 2);
146
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
10x if (!parse_port(port_str, port))
147 6x return std::make_error_code(std::errc::invalid_argument);
148 4x }
149
150 6x ep = endpoint(addr, port);
151 6x return {};
152 }
153
154 default:
155 return std::make_error_code(std::errc::invalid_argument);
156 }
157 48x }
158
159 } // namespace boost::corosio
160