src/corosio/src/ipv4_address.cpp

100.0% Lines (105/105) 100.0% List of functions (16/16) 91.7% Branches (55/60)
ipv4_address.cpp
f(x) Functions (16)
Function Calls Lines Branches Blocks
boost::corosio::ipv4_address::ipv4_address(unsigned int) :19 4740x 100.0% 100.0% boost::corosio::ipv4_address::ipv4_address(std::array<unsigned char, 4ull> const&) :21 9450x 100.0% 100.0% boost::corosio::ipv4_address::ipv4_address(std::basic_string_view<char, std::char_traits<char> >) :29 17x 100.0% 100.0% 100.0% boost::corosio::ipv4_address::to_bytes() const :38 4769x 100.0% 100.0% boost::corosio::ipv4_address::to_uint() const :49 36x 100.0% 100.0% boost::corosio::ipv4_address::to_string[abi:cxx11]() const :55 20x 100.0% 100.0% 72.7% boost::corosio::ipv4_address::to_buffer(char*, unsigned long long) const :63 5x 100.0% 100.0% 88.9% boost::corosio::ipv4_address::is_loopback() const :72 5x 100.0% 100.0% boost::corosio::ipv4_address::is_unspecified() const :78 4x 100.0% 100.0% boost::corosio::ipv4_address::is_multicast() const :84 4x 100.0% 100.0% boost::corosio::operator<<(std::ostream&, boost::corosio::ipv4_address const&) :90 1x 100.0% 100.0% 100.0% boost::corosio::ipv4_address::print_impl(char*) const :98 24x 100.0% 100.0% boost::corosio::ipv4_address::print_impl(char*) const::{lambda(char*&, unsigned char)#1}::operator()(char*&, unsigned char) const :101 96x 100.0% 100.0% 100.0% boost::corosio::(anonymous namespace)::parse_dec_octet(char const*&, char const*, unsigned char&) :131 298x 100.0% 88.2% 100.0% boost::corosio::parse_ipv4_impl(std::basic_string_view<char, std::char_traits<char> >, boost::corosio::ipv4_address&) :179 99x 100.0% 91.7% 100.0% boost::corosio::make_ipv4_address(std::basic_string_view<char, std::char_traits<char> >) :212 99x 100.0% 100.0% 100.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/ipv4_address.hpp>
11
12 #include <boost/corosio/detail/except.hpp>
13
14 #include <ostream>
15 #include <stdexcept>
16
17 namespace boost::corosio {
18
19 4740x ipv4_address::ipv4_address(uint_type u) noexcept : addr_(u) {}
20
21 9450x ipv4_address::ipv4_address(bytes_type const& bytes) noexcept
22 {
23 9450x addr_ = (static_cast<std::uint32_t>(bytes[0]) << 24) |
24 9450x (static_cast<std::uint32_t>(bytes[1]) << 16) |
25 9450x (static_cast<std::uint32_t>(bytes[2]) << 8) |
26 9450x (static_cast<std::uint32_t>(bytes[3]));
27 9450x }
28
29 17x ipv4_address::ipv4_address(std::string_view s)
30 {
31 17x auto [ec, addr] = make_ipv4_address(s);
32
2/2
✓ Branch 6 → 7 taken 4 times.
✓ Branch 6 → 8 taken 13 times.
17x if (ec)
33 4x detail::throw_system_error(ec, "invalid IPv4 address");
34 13x *this = addr;
35 13x }
36
37 auto
38 4769x ipv4_address::to_bytes() const noexcept -> bytes_type
39 {
40 bytes_type bytes;
41 4769x bytes[0] = static_cast<unsigned char>((addr_ >> 24) & 0xff);
42 4769x bytes[1] = static_cast<unsigned char>((addr_ >> 16) & 0xff);
43 4769x bytes[2] = static_cast<unsigned char>((addr_ >> 8) & 0xff);
44 4769x bytes[3] = static_cast<unsigned char>(addr_ & 0xff);
45 4769x return bytes;
46 }
47
48 auto
49 36x ipv4_address::to_uint() const noexcept -> uint_type
50 {
51 36x return addr_;
52 }
53
54 std::string
55 20x ipv4_address::to_string() const
56 {
57 char buf[max_str_len];
58 20x auto n = print_impl(buf);
59
1/1
✓ Branch 5 → 6 taken 20 times.
40x return std::string(buf, n);
60 }
61
62 std::string_view
63 5x ipv4_address::to_buffer(char* dest, std::size_t dest_size) const
64 {
65
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 6 taken 4 times.
5x if (dest_size < max_str_len)
66
1/1
✓ Branch 4 → 5 taken 1 time.
1x throw std::length_error("buffer too small for IPv4 address");
67 4x auto n = print_impl(dest);
68 4x return std::string_view(dest, n);
69 }
70
71 bool
72 5x ipv4_address::is_loopback() const noexcept
73 {
74 5x return (addr_ & 0xFF000000) == 0x7F000000;
75 }
76
77 bool
78 4x ipv4_address::is_unspecified() const noexcept
79 {
80 4x return addr_ == 0;
81 }
82
83 bool
84 4x ipv4_address::is_multicast() const noexcept
85 {
86 4x return (addr_ & 0xF0000000) == 0xE0000000;
87 }
88
89 std::ostream&
90 1x operator<<(std::ostream& os, ipv4_address const& addr)
91 {
92 char buf[ipv4_address::max_str_len];
93
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 3 → 4 taken 1 time.
1x os << addr.to_buffer(buf, sizeof(buf));
94 1x return os;
95 }
96
97 std::size_t
98 24x ipv4_address::print_impl(char* dest) const noexcept
99 {
100 24x auto const start = dest;
101 96x auto const write = [](char*& dest, unsigned char v) {
102
2/2
✓ Branch 2 → 3 taken 32 times.
✓ Branch 2 → 4 taken 64 times.
96x if (v >= 100)
103 {
104 32x *dest++ = '0' + v / 100;
105 32x v %= 100;
106 32x *dest++ = '0' + v / 10;
107 32x v %= 10;
108 }
109
2/2
✓ Branch 4 → 5 taken 5 times.
✓ Branch 4 → 6 taken 59 times.
64x else if (v >= 10)
110 {
111 5x *dest++ = '0' + v / 10;
112 5x v %= 10;
113 }
114 96x *dest++ = '0' + v;
115 96x };
116 24x write(dest, static_cast<unsigned char>((addr_ >> 24) & 0xff));
117 24x *dest++ = '.';
118 24x write(dest, static_cast<unsigned char>((addr_ >> 16) & 0xff));
119 24x *dest++ = '.';
120 24x write(dest, static_cast<unsigned char>((addr_ >> 8) & 0xff));
121 24x *dest++ = '.';
122 24x write(dest, static_cast<unsigned char>(addr_ & 0xff));
123 24x return static_cast<std::size_t>(dest - start);
124 }
125
126 namespace {
127
128 // Parse a decimal octet (0-255), no leading zeros except "0"
129 // Returns true on success, advances `it`
130 bool
131 298x parse_dec_octet(char const*& it, char const* end, unsigned char& octet) noexcept
132 {
133
2/2
✓ Branch 2 → 3 taken 2 times.
✓ Branch 2 → 4 taken 296 times.
298x if (it == end)
134 2x return false;
135
136 296x char c = *it;
137
4/4
✓ Branch 4 → 5 taken 292 times.
✓ Branch 4 → 6 taken 4 times.
✓ Branch 5 → 6 taken 16 times.
✓ Branch 5 → 7 taken 276 times.
296x if (c < '0' || c > '9')
138 20x return false;
139
140 276x unsigned v = static_cast<unsigned>(c - '0');
141 276x ++it;
142
143
2/2
✓ Branch 7 → 8 taken 41 times.
✓ Branch 7 → 13 taken 235 times.
276x if (v == 0)
144 {
145 // "0" is valid, but "00", "01", etc. are not
146
5/6
✓ Branch 8 → 9 taken 40 times.
✓ Branch 8 → 12 taken 1 time.
✓ Branch 9 → 10 taken 3 times.
✓ Branch 9 → 12 taken 37 times.
✓ Branch 10 → 11 taken 3 times.
✗ Branch 10 → 12 not taken.
41x if (it != end && *it >= '0' && *it <= '9')
147 3x return false;
148 38x octet = 0;
149 38x return true;
150 }
151
152 // First digit was 1-9, can have more
153
6/6
✓ Branch 13 → 14 taken 180 times.
✓ Branch 13 → 23 taken 55 times.
✓ Branch 14 → 15 taken 68 times.
✓ Branch 14 → 23 taken 112 times.
✓ Branch 15 → 16 taken 65 times.
✓ Branch 15 → 23 taken 3 times.
235x if (it != end && *it >= '0' && *it <= '9')
154 {
155 65x v = v * 10 + static_cast<unsigned>(*it - '0');
156 65x ++it;
157
158
4/6
✓ Branch 16 → 17 taken 65 times.
✗ Branch 16 → 23 not taken.
✓ Branch 17 → 18 taken 62 times.
✓ Branch 17 → 23 taken 3 times.
✓ Branch 18 → 19 taken 62 times.
✗ Branch 18 → 23 not taken.
65x if (it != end && *it >= '0' && *it <= '9')
159 {
160 62x v = v * 10 + static_cast<unsigned>(*it - '0');
161 62x ++it;
162
163 // Can't have more than 3 digits
164
5/6
✓ Branch 19 → 20 taken 59 times.
✓ Branch 19 → 23 taken 3 times.
✓ Branch 20 → 21 taken 1 time.
✓ Branch 20 → 23 taken 58 times.
✓ Branch 21 → 22 taken 1 time.
✗ Branch 21 → 23 not taken.
62x if (it != end && *it >= '0' && *it <= '9')
165 1x return false;
166 }
167 }
168
169
2/2
✓ Branch 23 → 24 taken 9 times.
✓ Branch 23 → 25 taken 225 times.
234x if (v > 255)
170 9x return false;
171
172 225x octet = static_cast<unsigned char>(v);
173 225x return true;
174 }
175
176 } // namespace
177
178 static std::error_code
179 99x parse_ipv4_impl(std::string_view s, ipv4_address& addr) noexcept
180 {
181 99x auto it = s.data();
182 99x auto const end = it + s.size();
183
184 unsigned char octets[4];
185
186 // Parse first octet
187
2/2
✓ Branch 5 → 6 taken 27 times.
✓ Branch 5 → 7 taken 72 times.
99x if (!parse_dec_octet(it, end, octets[0]))
188 27x return std::make_error_code(std::errc::invalid_argument);
189
190 // Parse remaining octets
191
2/2
✓ Branch 15 → 8 taken 206 times.
✓ Branch 15 → 16 taken 57 times.
263x for (int i = 1; i < 4; ++i)
192 {
193
3/4
✓ Branch 8 → 9 taken 199 times.
✓ Branch 8 → 10 taken 7 times.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 11 taken 199 times.
206x if (it == end || *it != '.')
194 7x return std::make_error_code(std::errc::invalid_argument);
195 199x ++it; // skip '.'
196
197
2/2
✓ Branch 12 → 13 taken 8 times.
✓ Branch 12 → 14 taken 191 times.
199x if (!parse_dec_octet(it, end, octets[i]))
198 8x return std::make_error_code(std::errc::invalid_argument);
199 }
200
201 // Must have consumed entire string
202
2/2
✓ Branch 16 → 17 taken 7 times.
✓ Branch 16 → 18 taken 50 times.
57x if (it != end)
203 7x return std::make_error_code(std::errc::invalid_argument);
204
205 50x addr = ipv4_address(
206 50x ipv4_address::bytes_type{{octets[0], octets[1], octets[2], octets[3]}});
207
208 50x return {};
209 }
210
211 capy::io_result<ipv4_address>
212 99x make_ipv4_address(std::string_view s) noexcept
213 {
214 99x ipv4_address addr;
215
2/2
✓ Branch 4 → 5 taken 49 times.
✓ Branch 4 → 8 taken 50 times.
99x if (auto ec = parse_ipv4_impl(s, addr))
216 49x return {ec, ipv4_address{}};
217 50x return {std::error_code{}, addr};
218 }
219
220 } // namespace boost::corosio
221