Skip to content

Commit 91ec55b

Browse files
committed
Rewrite IPAddress without union
- Using a union allowed undefined behavior for array-like INPUT and OUTPUT arguments through a 32-bit integer (and vice versa). C++ standard says (https://timsong-cpp.github.io/cppwp/n4659/class.union#1): "At most one of the non-static data members of an object of union type can be active at any time, that is, the value of at most one of the non-static data members can be stored in a union at any time. [ Note: One special guarantee is made in order to simplify the use of unions: If a standard-layout union contains several standard-layout structs that share a common initial sequence, and if a non-static data member of an object of this standard-layout union type is active and is one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of the standard-layout struct members; see [class.mem].  — end note ]" and (https://timsong-cpp.github.io/cppwp/n4659/class.mem#22): "Two standard-layout unions are layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in any order) have layout-compatible types." you can't hope that compilers don't seem to do undefined behavior at the moment
1 parent 6d9ebea commit 91ec55b

File tree

2 files changed

+32
-33
lines changed

2 files changed

+32
-33
lines changed

cores/esp32/IPAddress.cpp

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,62 +20,62 @@
2020
#include <Arduino.h>
2121
#include <IPAddress.h>
2222
#include <Print.h>
23+
#include <algorithm>
2324

24-
IPAddress::IPAddress()
25-
{
26-
_address.dword = 0;
27-
}
25+
// IPAddress::IPAddress()
26+
// {
27+
// _address.dword = 0;
28+
// }
2829

2930
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)
30-
{
31-
_address.bytes[0] = first_octet;
32-
_address.bytes[1] = second_octet;
33-
_address.bytes[2] = third_octet;
34-
_address.bytes[3] = fourth_octet;
35-
}
31+
: _address({first_octet,
32+
second_octet,
33+
third_octet,
34+
fourth_octet})
35+
{}
3636

3737
IPAddress::IPAddress(uint32_t address)
3838
{
39-
_address.dword = address;
39+
uint32_t& addressRef = reinterpret_cast<uint32_t&>(_address.front());
40+
addressRef = address;
4041
}
4142

42-
IPAddress::IPAddress(const uint8_t *address)
43-
{
44-
memcpy(_address.bytes, address, sizeof(_address.bytes));
45-
}
43+
IPAddress::IPAddress(const uint8_t *address) : _address({address[0], address[1], address[2], address[3]})
44+
{}
4645

4746
IPAddress& IPAddress::operator=(const uint8_t *address)
4847
{
49-
memcpy(_address.bytes, address, sizeof(_address.bytes));
48+
std::copy(address, address + _address.size(), _address.begin());
5049
return *this;
5150
}
5251

5352
IPAddress& IPAddress::operator=(uint32_t address)
5453
{
55-
_address.dword = address;
54+
uint32_t& addressRef = reinterpret_cast<uint32_t&>(_address.front());
55+
addressRef = address;
5656
return *this;
5757
}
5858

5959
bool IPAddress::operator==(const uint8_t* addr) const
6060
{
61-
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
61+
return std::equal(_address.begin(), _address.end(), addr);
6262
}
6363

6464
size_t IPAddress::printTo(Print& p) const
6565
{
6666
size_t n = 0;
6767
for(int i = 0; i < 3; i++) {
68-
n += p.print(_address.bytes[i], DEC);
68+
n += p.print(_address[i], DEC);
6969
n += p.print('.');
7070
}
71-
n += p.print(_address.bytes[3], DEC);
71+
n += p.print(_address[3], DEC);
7272
return n;
7373
}
7474

7575
String IPAddress::toString() const
7676
{
7777
char szRet[16];
78-
sprintf(szRet,"%u.%u.%u.%u", _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3]);
78+
sprintf(szRet,"%u.%u.%u.%u", _address[0], _address[1], _address[2], _address[3]);
7979
return String(szRet);
8080
}
8181

@@ -103,7 +103,7 @@ bool IPAddress::fromString(const char *address)
103103
// Too much dots (there must be 3 dots)
104104
return false;
105105
}
106-
_address.bytes[dots++] = acc;
106+
_address[dots++] = acc;
107107
acc = 0;
108108
}
109109
else
@@ -117,7 +117,7 @@ bool IPAddress::fromString(const char *address)
117117
// Too few dots (there must be 3 dots)
118118
return false;
119119
}
120-
_address.bytes[3] = acc;
120+
_address[3] = acc;
121121
return true;
122122
}
123123

cores/esp32/IPAddress.h

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,29 +23,27 @@
2323
#include <stdint.h>
2424
#include <WString.h>
2525
#include <Printable.h>
26+
#include <array>
2627

2728
// A class to make it easier to handle and pass around IP addresses
2829

2930
class IPAddress: public Printable
3031
{
3132
private:
32-
union {
33-
uint8_t bytes[4]; // IPv4 address
34-
uint32_t dword;
35-
} _address;
33+
alignas(alignof(uint32_t)) std::array<uint8_t,4> _address{}; // IPv4 address
3634

3735
// Access the raw byte array containing the address. Because this returns a pointer
3836
// to the internal structure rather than a copy of the address this function should only
3937
// be used when you know that the usage of the returned uint8_t* will be transient and not
4038
// stored.
4139
uint8_t* raw_address()
4240
{
43-
return _address.bytes;
41+
return _address.data();
4442
}
4543

4644
public:
4745
// Constructors
48-
IPAddress();
46+
IPAddress() = default;
4947
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
5048
IPAddress(uint32_t address);
5149
IPAddress(const uint8_t *address);
@@ -54,26 +52,27 @@ class IPAddress: public Printable
5452
bool fromString(const char *address);
5553
bool fromString(const String &address) { return fromString(address.c_str()); }
5654

55+
5756
// Overloaded cast operator to allow IPAddress objects to be used where a pointer
5857
// to a four-byte uint8_t array is expected
5958
operator uint32_t() const
6059
{
61-
return _address.dword;
60+
return reinterpret_cast<const uint32_t&>(_address.front());
6261
}
6362
bool operator==(const IPAddress& addr) const
6463
{
65-
return _address.dword == addr._address.dword;
64+
return _address == addr._address;
6665
}
6766
bool operator==(const uint8_t* addr) const;
6867

6968
// Overloaded index operator to allow getting and setting individual octets of the address
7069
uint8_t operator[](int index) const
7170
{
72-
return _address.bytes[index];
71+
return _address[index];
7372
}
7473
uint8_t& operator[](int index)
7574
{
76-
return _address.bytes[index];
75+
return _address[index];
7776
}
7877

7978
// Overloaded copy operators to allow initialisation of IPAddress objects from other types

0 commit comments

Comments
 (0)