src/corosio/src/ipv4_address.cpp

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