src/corosio/src/ipv4_address.cpp

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