Skip to content

Added sized overloads for buffer construction of IPv6Address and MacAddress. #1847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Jul 12, 2025
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1f48aaa
Added sized overloads of copyTo and constructor for IPAddress and Mac…
Dimi1010 Jun 7, 2025
5b4a78a
Updated mac test.
Dimi1010 Jun 7, 2025
7d48ee0
Header fix.
Dimi1010 Jun 7, 2025
b2f2949
Doc improvement.
Dimi1010 Jun 7, 2025
6a7748d
Fixed docs.
Dimi1010 Jun 7, 2025
170415e
Fixed docs.
Dimi1010 Jun 7, 2025
0aaf158
Doc fix.
Dimi1010 Jun 7, 2025
e43e0bc
Merge remote-tracking branch 'upstream/dev' into refactor/sized-copy-to
Dimi1010 Jun 21, 2025
856a191
Deprecated allocating API.
Dimi1010 Jun 21, 2025
8316826
Lint
Dimi1010 Jun 21, 2025
2e69735
Updated sized MacAddress ctor. Added tests.
Dimi1010 Jun 21, 2025
9036156
Updated raw array to smart ptr array.
Dimi1010 Jun 21, 2025
5537578
Added unit tests for copy to.
Dimi1010 Jun 21, 2025
665d195
Added safety checks on IPv4 and IPv6 buffer constructors.
Dimi1010 Jun 21, 2025
708b7f8
Fixed test
Dimi1010 Jun 21, 2025
a6cf5a4
Added unit tests for IPv6Address::copyTo
Dimi1010 Jun 21, 2025
e1c4703
Lint
Dimi1010 Jun 21, 2025
019b376
Returned allocating `copyTo` under the name `copyToNewBuffer`.
Dimi1010 Jun 27, 2025
4143274
Simplified tests.
Dimi1010 Jun 27, 2025
870d33e
Added unit tests for copyToNewBuffer.
Dimi1010 Jun 27, 2025
b0eea08
Lint
Dimi1010 Jun 27, 2025
9399e5f
Typo fix
Dimi1010 Jun 27, 2025
fb3f8f3
Moved larger constructors to cpp files.
Dimi1010 Jul 6, 2025
3075097
Merge remote-tracking branch 'upstream/dev' into refactor/sized-copy-to
Dimi1010 Jul 6, 2025
3803685
Removed aliases
Dimi1010 Jul 12, 2025
f04b721
Merge branch 'dev' into refactor/sized-copy-to
Dimi1010 Jul 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 55 additions & 4 deletions Common++/header/IpAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <array>
#include <memory>

#include "DeprecationUtils.h"

/// @file

/// @namespace pcpp
Expand Down Expand Up @@ -39,8 +41,21 @@ namespace pcpp

/// A constructor that creates an instance of the class out of 4-byte array.
/// @param[in] bytes The address as 4-byte array in network byte order
IPv4Address(const uint8_t bytes[4])
/// @remarks This constructor assumes that the provided array is exactly 4 bytes long.
/// Prefer using the constructor with size parameter if the array length is not guaranteed to be 4 bytes.
IPv4Address(const uint8_t bytes[4]) : IPv4Address(bytes, 4)
{}

/// A constructor that creates an instance of the class out of a 4-byte array.
/// @param[in] bytes The address as 4-byte array in network byte order
/// @param[in] size The size of the array in bytes
/// @throws std::out_of_range If the provided size is smaller than 4 bytes.
IPv4Address(const uint8_t bytes[4], size_t size)
{
if (size < 4)
{
throw std::out_of_range("Buffer size is smaller than IPv4 address size");
}
memcpy(m_Bytes.data(), bytes, 4 * sizeof(uint8_t));
}

Expand Down Expand Up @@ -160,9 +175,22 @@ namespace pcpp

/// A constructor that creates an instance of the class out of 16-byte array.
/// @param[in] bytes The address as 16-byte array in network byte order
IPv6Address(const uint8_t bytes[16])
/// remarks This constructor assumes that the provided array is exactly 16 bytes long.
/// Prefer using the constructor with size parameter if the array length is not guaranteed to be 16 bytes.
IPv6Address(const uint8_t bytes[16]) : IPv6Address(bytes, 16)
{}

/// @brief A constructor that creates an instance of the class out of a 16-byte array.
/// @param bytes The address as 16-byte array in network byte order
/// @param size The size of the array in bytes
/// @throws std::out_of_range If the provided size is smaller than 16 bytes.
IPv6Address(const uint8_t bytes[16], size_t size)
{
memcpy(m_Bytes.data(), bytes, 16 * sizeof(uint8_t));
if (size < 16)
{
throw std::out_of_range("Buffer size is smaller than IPv6 address size");
}
std::memcpy(m_Bytes.data(), bytes, 16 * sizeof(uint8_t));
}

/// A constructor that creates an instance of the class out of a 16-byte standard array.
Expand Down Expand Up @@ -224,16 +252,39 @@ namespace pcpp
/// Allocates a byte array and copies address value into it. Array deallocation is user responsibility
/// @param[in] arr A pointer to where array will be allocated
/// @param[out] length Returns the length in bytes of the array that was allocated
/// @deprecated Use copyToNewBuffer instead as it returns a unique pointer to the allocated array.
PCPP_DEPRECATED("Use copyToNewBuffer instead.")
void copyTo(uint8_t** arr, size_t& length) const;

/// Gets a pointer to an already allocated byte array and copies the address value to it.
/// This method assumes array allocated size is at least 16 (the size of an IPv6 address)
/// @param[in] arr A pointer to the array which address will be copied to
/// @remarks This method assumes that the provided array is at least 16 bytes long.
/// Prefer using the copyTo(uint8_t* buffer, size_t size) method if the array length is not guaranteed to be 16
/// bytes.
void copyTo(uint8_t* arr) const
{
memcpy(arr, m_Bytes.data(), m_Bytes.size() * sizeof(uint8_t));
return copyTo(arr, 16);
}

/// @brief Copies the address value to a user-provided buffer.
/// @param buffer[in] A pointer to the buffer where the address will be copied
/// @param size[in] The size of the buffer in bytes
/// @throws std::out_of_range If the provided size is smaller than 16 bytes.
void copyTo(uint8_t* buffer, size_t size) const
{
if (size < m_Bytes.size())
{
throw std::out_of_range("Buffer size is smaller than IPv6 address size");
}
memcpy(buffer, m_Bytes.data(), m_Bytes.size() * sizeof(uint8_t));
}

/// @brief Allocates a byte array and copies address value into it.
/// @param[out] size Returns the size in bytes of the allocated array. Usually 16.
/// @return A unique pointer to the allocated byte array containing the address value
std::unique_ptr<uint8_t[]> copyToNewBuffer(size_t& size) const;
Copy link
Collaborator Author

@Dimi1010 Dimi1010 Jun 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debating if its better to return unique_ptr with size as an out param, or return pair<unique_ptr<...>, size_t>

PS: Kinda starting to lean to pair as it will keep the buffer and size logically grouped? 🤔

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can consider to use std::array here as well?

Copy link
Collaborator Author

@Dimi1010 Dimi1010 Jun 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is toByteArray that returns an std::array type.

This one mostly covers existing functionality that was in copyTo that allocates heap buffer. Which I suppose can be cheaper if it will be changing ownership around a lot?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am okay with having std::unique_ptr API, but I wonder if people need it. IMO, most of the time, people may only need the reference to the bytes directly from the IpAddress object. In rare cases, they can just copy the bytes. Then, we can keep the interface as simple as possible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is true. I can't find a lot of usages where a new buffer is required. Most of the time it is copied to an already existing buffer, if the copyTo api is used.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ended up deprecating the allocating copyTo API altogether. It is not commonly used and it is clunky IMO.
The use case can be handled by the new size_t copyTo(uint8_t* buffer, size_t size) function that returns the written bytes or the required size. It also supports querying the function.

Example usage:

MacAddress mac;
size_t addrLen = mac.copyTo(nullptr, 0); // Query the function for a required buffer.
uint8_t* addr = new uint8_t[addrLen];
if(mac.copyTo(addr, addrLen) != addrLen) { /* Error check */ }
// Use addr...


/// Checks whether the address matches a network.
/// @param network An IPv6Network network
/// @return True if the address matches the network or false otherwise
Expand Down
53 changes: 49 additions & 4 deletions Common++/header/MacAddress.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include <cstdint>
#include <string>
#include <array>
#include <memory>

#include "DeprecationUtils.h"

/// @file

Expand All @@ -28,8 +31,21 @@ namespace pcpp
/// The byte array length should be 6 (as MAC address is 6-byte long), and the remaining bytes are ignored.
/// If the byte array is invalid, the constructor throws an exception.
/// @param[in] addr A pointer to the byte array containing 6 bytes representing the MAC address
explicit MacAddress(const uint8_t addr[6])
/// @remarks This constructor assumes that the provided array is exactly 6 bytes long.
/// Prefer using the constructor with size parameter if the array length is not guaranteed to be 6 bytes.
explicit MacAddress(const uint8_t addr[6]) : MacAddress(addr, 6)
{}

/// @brief A constructor that creates an instance of the class out of a byte array.
/// @param[in] addr The address as a byte array in network byte order
/// @param[in] size The size of the array in bytes
/// @throws std::out_of_range If the provided size is smaller than 6 bytes.
explicit MacAddress(const uint8_t addr[6], size_t size)
{
if (size < 6)
{
throw std::out_of_range("Buffer size is smaller than MAC address size");
}
std::copy(addr, addr + 6, m_Address.begin());
}

Expand Down Expand Up @@ -127,18 +143,47 @@ namespace pcpp
/// Allocates a byte array of length 6 and copies address value into it. Array deallocation is user
/// responsibility
/// @param[in] arr A pointer to where array will be allocated
/// @deprecated Use copyToNewBuffer instead as it returns a unique pointer to the allocated array.
PCPP_DEPRECATED("Use copyToNewBuffer instead as it returns a unique pointer to the allocated array.")
void copyTo(uint8_t** arr) const
{
*arr = new uint8_t[m_Address.size()];
std::copy(m_Address.begin(), m_Address.end(), *arr);
size_t size;
*arr = copyToNewBuffer(size).release();
}

/// Gets a pointer to an already allocated byte array and copies the address value to it.
/// This method assumes array allocated size is at least 6 (the size of a MAC address)
/// @param[in] arr A pointer to the array which address will be copied to
/// @remarks This method assumes that the provided array is at least 6 bytes long.
/// Prefer using the copyTo(uint8_t* buffer, size_t size) method if the array length is not guaranteed to be 6
/// bytes.
void copyTo(uint8_t arr[6]) const
{
std::copy(m_Address.begin(), m_Address.end(), arr);
return copyTo(arr, 6);
}

/// @brief Copies the address value to a user-provided buffer.
/// @param[in] buffer A pointer to the buffer where the address will be copied
/// @param[in] size The size of the buffer in bytes
/// @throws std::out_of_range If the provided size is smaller than 6 bytes.
void copyTo(uint8_t* buffer, size_t size) const
{
if (size < m_Address.size())
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto: do we need to support copying to an array smaller than 6 bytes?

{
throw std::out_of_range("Buffer size is smaller than MAC address size");
}
std::copy(m_Address.begin(), m_Address.end(), buffer);
}

/// @brief Allocates a byte array and copies address value into it.
/// @param size[out] The size of the allocated array in bytes. Usually 6.
/// @return A unique pointer to the allocated byte array containing the MAC address.
std::unique_ptr<uint8_t[]> copyToNewBuffer(size_t& size) const
{
size = m_Address.size();
auto arr = std::make_unique<uint8_t[]>(size);
std::copy(m_Address.begin(), m_Address.end(), arr.get());
return arr;
}

/// A static value representing a zero value of MAC address, meaning address of value "00:00:00:00:00:00"
Expand Down
13 changes: 9 additions & 4 deletions Common++/src/IpAddress.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,15 @@ namespace pcpp

void IPv6Address::copyTo(uint8_t** arr, size_t& length) const
{
const size_t addrLen = m_Bytes.size() * sizeof(uint8_t);
length = addrLen;
*arr = new uint8_t[addrLen];
memcpy(*arr, m_Bytes.data(), addrLen);
*arr = copyToNewBuffer(length).release();
}

std::unique_ptr<uint8_t[]> IPv6Address::copyToNewBuffer(size_t& size) const
{
size = m_Bytes.size() * sizeof(uint8_t);
auto arr = std::make_unique<uint8_t[]>(size);
memcpy(arr.get(), m_Bytes.data(), size);
return arr;
}

bool IPv6Address::matchNetwork(const IPv6Network& network) const
Expand Down
6 changes: 3 additions & 3 deletions Tests/Pcap++Test/Tests/IpMacTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -270,15 +270,15 @@ PTF_TEST_CASE(TestMacAddress)
oss << macAddr1;
PTF_ASSERT_EQUAL(oss.str(), "11:02:33:04:55:06");

uint8_t* arrToCopyTo = nullptr;
macAddr3.copyTo(&arrToCopyTo);
size_t arrLen = 0;
auto arrToCopyTo = macAddr3.copyToNewBuffer(arrLen);
PTF_ASSERT_EQUAL(arrLen, 6);
PTF_ASSERT_EQUAL(arrToCopyTo[0], 0x11, hex);
PTF_ASSERT_EQUAL(arrToCopyTo[1], 0x02, hex);
PTF_ASSERT_EQUAL(arrToCopyTo[2], 0x33, hex);
PTF_ASSERT_EQUAL(arrToCopyTo[3], 0x04, hex);
PTF_ASSERT_EQUAL(arrToCopyTo[4], 0x55, hex);
PTF_ASSERT_EQUAL(arrToCopyTo[5], 0x06, hex);
delete[] arrToCopyTo;

uint8_t macBytes[6];
macAddr3.copyTo(macBytes);
Expand Down
Loading