src/corosio/src/endpoint.cpp

96.4% Lines (80/0/83) 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(std::errc::invalid_argument);
93
94 18x auto addr_str = s.substr(0, colon_pos);
95 18x auto port_str = s.substr(colon_pos + 1);
96
97 18x auto [ec, addr] = make_ipv4_address(addr_str);
98 18x if (ec)
99 1x return ec;
100
101 std::uint16_t port;
102 17x if (!parse_port(port_str, port))
103 10x return std::make_error_code(std::errc::invalid_argument);
104
105 7x ep = endpoint(addr, port);
106 7x return {};
107 }
108
109 5x case endpoint_format::ipv6_no_port:
110 {
111 5x auto [ec, addr] = make_ipv6_address(s);
112 5x if (ec)
113 1x return ec;
114 4x ep = endpoint(addr, 0);
115 4x return {};
116 }
117
118 18x case endpoint_format::ipv6_bracketed:
119 {
120 // Must start with '[' and contain ']'
121 18x if (s.size() < 2 || s[0] != '[')
122 2x return std::make_error_code(std::errc::invalid_argument);
123
124 16x auto close_bracket = s.find(']');
125 16x if (close_bracket == std::string_view::npos)
126 2x return std::make_error_code(std::errc::invalid_argument);
127
128 14x auto addr_str = s.substr(1, close_bracket - 1);
129
130 14x auto [ec, addr] = make_ipv6_address(addr_str);
131 14x if (ec)
132 2x return ec;
133
134 12x std::uint16_t port = 0;
135 12x if (close_bracket + 1 < s.size())
136 {
137 // There's something after ']'
138 10x if (s[close_bracket + 1] != ':')
139 6x return std::make_error_code(std::errc::invalid_argument);
140
141 10x auto port_str = s.substr(close_bracket + 2);
142 10x if (!parse_port(port_str, port))
143 6x return std::make_error_code(std::errc::invalid_argument);
144 }
145
146 6x ep = endpoint(addr, port);
147 6x return {};
148 }
149
150 default:
151 return std::make_error_code(std::errc::invalid_argument);
152 }
153 }
154
155 capy::io_result<endpoint>
156 52x make_endpoint(std::string_view s) noexcept
157 {
158 52x endpoint ep;
159 52x if (auto ec = parse_endpoint_impl(s, ep))
160 33x return {ec, endpoint{}};
161 19x return {std::error_code{}, ep};
162 }
163
164 } // namespace boost::corosio
165