Skip to content

Commit 77f7375

Browse files
committed
rewrite(IPAddress): fix implementation
fix implementation and add discussion questions (safe using of IPAddress).
1 parent d85523f commit 77f7375

File tree

2 files changed

+44
-6
lines changed

2 files changed

+44
-6
lines changed

api/IPAddress.cpp

+29-4
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,34 @@ IPAddress::IPAddress(uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4, uint8_t o5,
3939
// IPv4 only
4040
IPAddress::IPAddress(uint32_t address)
4141
{
42-
uint32_t& addressRef = reinterpret_cast<uint32_t&>(_address[IPADDRESS_V4_BYTES_INDEX]);
43-
addressRef = address;
42+
memcpy(&_address[IPADDRESS_V4_BYTES_INDEX], &address, 4); // This method guarantees a defined behavior. Any pointer conversions to write to ADDRESS storage (as a multibyte integer) are undefined behavior when the lifetime of the multibyte type has not previously started.
43+
44+
// C++ standard draft [basic.life#7](https://eel.is/c++draft/basic.life#7)
45+
// Before the lifetime of an object has started but after the storage which the object
46+
// will occupy has been allocated or, after the lifetime of an object has ended and
47+
// before the storage which the object occupied is reused or released, any pointer that
48+
// represents the address of the storage location where the object will be or was
49+
// located may be used but only in limited ways. For an object under construction or
50+
// destruction, see [class.cdtor]. Otherwise, such a pointer refers to allocated storage
51+
// ([basic.stc.dynamic.allocation]), and using the pointer as if the pointer were of
52+
// type void* is well-defined. Indirection through such a pointer is permitted but the
53+
// resulting lvalue may only be used in limited ways, as described below.
54+
// The program has undefined behavior if
55+
// --the pointer is used as the operand of a delete-expression,
56+
// --the pointer is used as the operand of a static_cast ([expr.static.cast]), except
57+
// when the conversion is to pointer to cv void, or to pointer to cv void and subsequently
58+
// to pointer to cv char, cv unsigned char, or cv std::byte ([cstddef.syn]), or
59+
60+
// C++ standard draft [basic.life#8](https://eel.is/c++draft/basic.life#8)
61+
// Similarly, before the lifetime of an object has started but after the storage which
62+
// the object will occupy has been allocated or, after the lifetime of an object has
63+
// ended and before the storage which the object occupied is reused or released, any
64+
// glvalue that refers to the original object may be used but only in limited ways.
65+
// For an object under construction or destruction, see [class.cdtor]. Otherwise, such
66+
// a glvalue refers to allocated storage ([basic.stc.dynamic.allocation]), and using
67+
// the properties of the glvalue that do not depend on its value is well-defined.
68+
// The program has undefined behavior if
69+
// -- the glvalue is used to access the object, or
4470

4571
// NOTE on conversion/comparison and uint32_t:
4672
// These conversions are host platform dependent.
@@ -244,8 +270,7 @@ IPAddress& IPAddress::operator=(uint32_t address)
244270
// See note on conversion/comparison and uint32_t
245271
_type = IPv4;
246272
memset(_address, 0, sizeof(_address));
247-
uint32_t& addressRef = reinterpret_cast<uint32_t&>(_address[IPADDRESS_V4_BYTES_INDEX]);
248-
addressRef = address;
273+
memcpy(&_address[IPADDRESS_V4_BYTES_INDEX], &address, 4);
249274
return *this;
250275
}
251276

api/IPAddress.h

+15-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
#pragma once
2121

22+
#include <string.h>
2223
#include <stdint.h>
2324
#include "Printable.h"
2425
#include "String.h"
@@ -41,7 +42,10 @@ enum IPType {
4142

4243
class IPAddress : public Printable {
4344
private:
44-
alignas(alignof(uint32_t)) uint8_t _address[16]{};
45+
alignas(alignof(uint32_t)) uint8_t _address[16]{}; // If the implementation does not require
46+
// storage as a multibyte integer, you can
47+
// remove the storage field alignment.
48+
// Address (as uint32) is accessed by copying.
4549
IPType _type{IPv4};
4650

4751
// Access the raw byte array containing the address. Because this returns a pointer
@@ -59,6 +63,7 @@ class IPAddress : public Printable {
5963
IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
6064
IPAddress(uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4, uint8_t o5, uint8_t o6, uint8_t o7, uint8_t o8, uint8_t o9, uint8_t o10, uint8_t o11, uint8_t o12, uint8_t o13, uint8_t o14, uint8_t o15, uint8_t o16);
6165
// IPv4; see implementation note
66+
// NOTE: address MUST BE BigEndian.
6267
IPAddress(uint32_t address);
6368
// Default IPv4
6469
IPAddress(const uint8_t *address);
@@ -71,7 +76,15 @@ class IPAddress : public Printable {
7176

7277
// Overloaded cast operator to allow IPAddress objects to be used where a uint32_t is expected
7378
// NOTE: IPv4 only; see implementation note
74-
operator uint32_t() const { return _type == IPv4 ? *reinterpret_cast<const uint32_t*>(&_address[IPADDRESS_V4_BYTES_INDEX]) : 0; };
79+
// NOTE: Data of the returned integer in the native endiannes, but relevant ordering is a BigEndian.
80+
// The user is responsible for ensuring that the value is converted to BigEndian.
81+
operator uint32_t() const {
82+
uint32_t ret;
83+
memcpy(&ret, &_address[IPADDRESS_V4_BYTES_INDEX], 4);
84+
// NOTE: maybe use the placement-new for starting of the integer type lifetime in the storage when constructing an IPAddress?
85+
// FIXME: need endiannes checking? how do this with the arduino-api?
86+
return _type == IPv4 ? ret : 0;
87+
};
7588

7689
bool operator==(const IPAddress& addr) const;
7790
bool operator!=(const IPAddress& addr) const { return !(*this == addr); };

0 commit comments

Comments
 (0)