libs/corosio/src/corosio/src/endpoint.cpp

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