src/corosio/src/endpoint.cpp

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