Skip to content

Commit a749083

Browse files
committed
Introduce fly::net namespace with IPv4 address class
The existing socket infrastructure is pretty bad. Inherently only supports IPv4 and is just awkward to use. The fly::net namespace will replace the socket library. Its first class is a compile-time-friendly IPv4 address class.
1 parent 1126a17 commit a749083

9 files changed

+546
-0
lines changed

build/win/libfly/libfly.vcxproj

+2
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@
179179
<ClInclude Include="..\..\..\fly\logger\logger.hpp" />
180180
<ClInclude Include="..\..\..\fly\logger\logger_config.hpp" />
181181
<ClInclude Include="..\..\..\fly\logger\styler.hpp" />
182+
<ClInclude Include="..\..\..\fly\net\ipv4_address.hpp" />
182183
<ClInclude Include="..\..\..\fly\parser\ini_parser.hpp" />
183184
<ClInclude Include="..\..\..\fly\parser\json_parser.hpp" />
184185
<ClInclude Include="..\..\..\fly\parser\parser.hpp" />
@@ -248,6 +249,7 @@
248249
<ClCompile Include="..\..\..\fly\logger\logger.cpp" />
249250
<ClCompile Include="..\..\..\fly\logger\logger_config.cpp" />
250251
<ClCompile Include="..\..\..\fly\logger\styler.cpp" />
252+
<ClCompile Include="..\..\..\fly\net\ipv4_address.cpp" />
251253
<ClCompile Include="..\..\..\fly\parser\ini_parser.cpp" />
252254
<ClCompile Include="..\..\..\fly\parser\json_parser.cpp" />
253255
<ClCompile Include="..\..\..\fly\parser\parser.cpp" />

build/win/libfly/libfly.vcxproj.filters

+9
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
<Filter Include="logger\detail\win">
2323
<UniqueIdentifier>{fb24379c-bda2-4c85-8a32-63b8e689821c}</UniqueIdentifier>
2424
</Filter>
25+
<Filter Include="net">
26+
<UniqueIdentifier>{03e02b28-3bca-4b51-ac6e-e59c2879f83e}</UniqueIdentifier>
27+
</Filter>
2528
<Filter Include="parser">
2629
<UniqueIdentifier>{e39571b7-e6c1-48ea-917e-08c233c64755}</UniqueIdentifier>
2730
</Filter>
@@ -142,6 +145,9 @@
142145
<ClInclude Include="..\..\..\fly\logger\detail\win\styler_proxy_impl.hpp">
143146
<Filter>logger\detail\win</Filter>
144147
</ClInclude>
148+
<ClInclude Include="..\..\..\fly\net\ipv4_address.hpp">
149+
<Filter>net</Filter>
150+
</ClInclude>
145151
<ClInclude Include="..\..\..\fly\parser\ini_parser.hpp">
146152
<Filter>parser</Filter>
147153
</ClInclude>
@@ -345,6 +351,9 @@
345351
<ClCompile Include="..\..\..\fly\logger\detail\win\styler_proxy_impl.cpp">
346352
<Filter>logger\detail\win</Filter>
347353
</ClCompile>
354+
<ClCompile Include="..\..\..\fly\net\ipv4_address.cpp">
355+
<Filter>net</Filter>
356+
</ClCompile>
348357
<ClCompile Include="..\..\..\fly\parser\ini_parser.cpp">
349358
<Filter>parser</Filter>
350359
</ClCompile>

build/win/libfly_unit_tests/libfly_unit_tests.vcxproj

+1
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@
223223
<ClCompile Include="..\..\..\test\logger\file_logger.cpp" />
224224
<ClCompile Include="..\..\..\test\logger\logger.cpp" />
225225
<ClCompile Include="..\..\..\test\logger\styler.cpp" />
226+
<ClCompile Include="..\..\..\test\net\ipv4_address.cpp" />
226227
<ClCompile Include="..\..\..\test\parser\ini_parser.cpp" />
227228
<ClCompile Include="..\..\..\test\parser\json_parser.cpp" />
228229
<ClCompile Include="..\..\..\test\parser\parser.cpp" />

build/win/libfly_unit_tests/libfly_unit_tests.vcxproj.filters

+6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
<Filter Include="logger">
1111
<UniqueIdentifier>{7ecf1fbd-e9f8-430f-aa13-4ddb9e46dbd8}</UniqueIdentifier>
1212
</Filter>
13+
<Filter Include="net">
14+
<UniqueIdentifier>{55d9c271-cf93-407f-ae69-03717f0c64f0}</UniqueIdentifier>
15+
</Filter>
1316
<Filter Include="parser">
1417
<UniqueIdentifier>{c4c87e06-0ba0-4d4f-902e-35c28da6e07e}</UniqueIdentifier>
1518
</Filter>
@@ -74,6 +77,9 @@
7477
<ClCompile Include="..\..\..\test\logger\styler.cpp">
7578
<Filter>logger</Filter>
7679
</ClCompile>
80+
<ClCompile Include="..\..\..\test\net\ipv4_address.cpp">
81+
<Filter>net</Filter>
82+
</ClCompile>
7783
<ClCompile Include="..\..\..\test\parser\ini_parser.cpp">
7884
<Filter>parser</Filter>
7985
</ClCompile>

fly/files.mk

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ SRC_DIRS_$(d) := \
22
$(d)/coders \
33
$(d)/config \
44
$(d)/logger \
5+
$(d)/net \
56
$(d)/parser \
67
$(d)/path \
78
$(d)/socket \

fly/net/ipv4_address.cpp

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include "fly/net/ipv4_address.hpp"
2+
3+
#include "fly/types/string/string.hpp"
4+
5+
namespace fly::net {
6+
7+
//==================================================================================================
8+
std::string IPv4Address::to_string() const
9+
{
10+
return fly::String::format(
11+
"{}.{}.{}.{}",
12+
m_address & 0xff,
13+
(m_address >> 8) & 0xff,
14+
(m_address >> 16) & 0xff,
15+
(m_address >> 24) & 0xff);
16+
}
17+
18+
//==================================================================================================
19+
std::ostream &operator<<(std::ostream &stream, const IPv4Address &address)
20+
{
21+
stream << address.to_string();
22+
return stream;
23+
}
24+
25+
} // namespace fly::net

fly/net/ipv4_address.hpp

+224
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
#pragma once
2+
3+
#include "fly/types/numeric/endian.hpp"
4+
#include "fly/types/string/string_lexer.hpp"
5+
6+
#include <array>
7+
#include <compare>
8+
#include <cstdint>
9+
#include <limits>
10+
#include <optional>
11+
#include <ostream>
12+
#include <string>
13+
14+
namespace fly::net {
15+
16+
/**
17+
* Class to store an IPv4 address in network order, and to provide convenient access to its data as
18+
* required by various network APIs.
19+
*
20+
* @author Timothy Flynn ([email protected])
21+
* @version February 13, 2021
22+
*/
23+
class IPv4Address
24+
{
25+
public:
26+
using address_type = std::array<std::uint8_t, 4>;
27+
using int_type = std::uint32_t;
28+
29+
/**
30+
* Default constructor. Initializes the IPv4 address to 0.0.0.0.
31+
*/
32+
IPv4Address() = default;
33+
34+
/**
35+
* Constructor. Create an IPv4 address from a 4-part array of octets in decimal format. The
36+
* array should be ordered such that index 0 is the first octet and index 3 is the fourth octet.
37+
*
38+
* @param address The 4-part array of octets to initialize the IPv4 address from.
39+
*/
40+
explicit constexpr IPv4Address(const address_type &address) noexcept;
41+
42+
/**
43+
* Constructor. Create an IPv4 address from a network-order 32-bit value.
44+
*
45+
* @param address The network-order address to initialize the IPv4 address from.
46+
*/
47+
explicit constexpr IPv4Address(int_type address) noexcept;
48+
49+
IPv4Address(const IPv4Address &) = default;
50+
IPv4Address(IPv4Address &&) = default;
51+
52+
IPv4Address &operator=(const IPv4Address &) = default;
53+
IPv4Address &operator=(IPv4Address &&) = default;
54+
55+
/**
56+
* @return An IPv4 address representing INADDR_ANY.
57+
*/
58+
static constexpr IPv4Address in_addr_any();
59+
60+
/**
61+
* @return An IPv4 address representing INADDR_BROADCAST.
62+
*/
63+
static constexpr IPv4Address in_addr_broadcast();
64+
65+
/**
66+
* @return An IPv4 address representing INADDR_LOOPBACK.
67+
*/
68+
static constexpr IPv4Address in_addr_loopback();
69+
70+
/**
71+
* Construct an IPv4 address from a string in dot-decimal notation.
72+
*
73+
* The provided string must betwen one and four octets, inclusive. If the string contains less
74+
* than four octets, the last octet is treated as an integer of as many bytes as are required to
75+
* fill out the address to four octets. Thus, the string "127.65530" is converted to the IPv4
76+
* address 127.0.255.250.
77+
*
78+
* @param address The string in dot-decimal notation to initialize the IPv4 address from.
79+
*
80+
* @return If successful, the constructed IPv4 address. Otherwise, an uninitialized value.
81+
*/
82+
static constexpr std::optional<IPv4Address> from_string(std::string_view address);
83+
84+
/**
85+
* Convert the IPv4 address to a four octet string in dot-decimal notation.
86+
*
87+
* @return The IPv4 address in dot-decimal notation.
88+
*/
89+
std::string to_string() const;
90+
91+
/**
92+
* @return The IPv4 address as an integer in network order.
93+
*/
94+
constexpr int_type network_order() const;
95+
96+
/**
97+
* @return The IPv4 address as an integer in host order.
98+
*/
99+
constexpr int_type host_order() const;
100+
101+
/**
102+
* Three-way-comparison operator. Defaulted to perform the comparison on the network-order IPv4
103+
* address.
104+
*/
105+
auto operator<=>(const IPv4Address &) const = default;
106+
107+
/**
108+
* Stream an IPv4 address as a four octet string in dot-decimal notation.
109+
*
110+
* @param stream A reference to the output stream.
111+
* @param address The IPv4 address to stream.
112+
*
113+
* @return A reference to the output stream.
114+
*/
115+
friend std::ostream &operator<<(std::ostream &stream, const IPv4Address &address);
116+
117+
private:
118+
int_type m_address {0};
119+
};
120+
121+
//==================================================================================================
122+
constexpr IPv4Address::IPv4Address(const address_type &address) noexcept
123+
{
124+
m_address |= static_cast<int_type>(address[0]);
125+
m_address |= static_cast<int_type>(address[1] << 8);
126+
m_address |= static_cast<int_type>(address[2] << 16);
127+
m_address |= static_cast<int_type>(address[3] << 24);
128+
}
129+
130+
//==================================================================================================
131+
constexpr IPv4Address::IPv4Address(int_type address) noexcept : m_address(address)
132+
{
133+
}
134+
135+
//==================================================================================================
136+
constexpr IPv4Address IPv4Address::in_addr_any()
137+
{
138+
return IPv4Address(0x00'00'00'00);
139+
}
140+
141+
//==================================================================================================
142+
constexpr IPv4Address IPv4Address::in_addr_broadcast()
143+
{
144+
return IPv4Address(0xff'ff'ff'ff);
145+
}
146+
147+
//==================================================================================================
148+
constexpr IPv4Address IPv4Address::in_addr_loopback()
149+
{
150+
return IPv4Address(0x01'00'00'7f);
151+
}
152+
153+
//==================================================================================================
154+
constexpr std::optional<IPv4Address> IPv4Address::from_string(std::string_view address)
155+
{
156+
constexpr const auto s_max32 = static_cast<std::uint64_t>(std::numeric_limits<int_type>::max());
157+
constexpr const auto s_decimal = '.';
158+
159+
fly::BasicStringLexer<std::string> lexer(std::move(address));
160+
161+
std::array<int_type, 4> parts {};
162+
std::size_t index = 0;
163+
164+
do
165+
{
166+
if (const auto segment = lexer.consume_number(); segment && (*segment <= s_max32))
167+
{
168+
parts[index++] = static_cast<int_type>(*segment);
169+
}
170+
else
171+
{
172+
return std::nullopt;
173+
}
174+
} while ((index < parts.size()) && lexer.consume_if(s_decimal));
175+
176+
std::optional<int_type> host_address;
177+
178+
if (index == 1)
179+
{
180+
host_address = parts[0];
181+
}
182+
else if (index == 2)
183+
{
184+
if ((parts[0] <= 0xff) && (parts[1] <= 0xff'ff'ff))
185+
{
186+
host_address = (parts[0] << 24) | parts[1];
187+
}
188+
}
189+
else if (index == 3)
190+
{
191+
if ((parts[0] <= 0xff) && (parts[1] <= 0xff) && (parts[2] <= 0xff'ff))
192+
{
193+
host_address = (parts[0] << 24) | (parts[1] << 16) | parts[2];
194+
}
195+
}
196+
else if (index == 4)
197+
{
198+
if ((parts[0] <= 0xff) && (parts[1] <= 0xff) && (parts[2] <= 0xff) && (parts[3] <= 0xff))
199+
{
200+
host_address = (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3];
201+
}
202+
}
203+
204+
if (!host_address || lexer.peek())
205+
{
206+
return std::nullopt;
207+
}
208+
209+
return IPv4Address(fly::endian_swap_if_non_native<std::endian::big>(*host_address));
210+
}
211+
212+
//==================================================================================================
213+
constexpr auto IPv4Address::network_order() const -> int_type
214+
{
215+
return m_address;
216+
}
217+
218+
//==================================================================================================
219+
constexpr auto IPv4Address::host_order() const -> int_type
220+
{
221+
return fly::endian_swap_if_non_native<std::endian::big>(m_address);
222+
}
223+
224+
} // namespace fly::net

test/files.mk

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ SRC_DIRS_$(d) += \
44
$(d)/coders \
55
$(d)/config \
66
$(d)/logger \
7+
$(d)/net \
78
$(d)/parser \
89
$(d)/path \
910
$(d)/socket \

0 commit comments

Comments
 (0)