net/FormatAddress: new library to replace ToString.cxx
This library writes to a caller-specified buffer instead of allocating a std::string which can be faster by avoiding heap allocations.
This commit is contained in:
parent
fcddab84c6
commit
d3ef4ab234
|
@ -0,0 +1,141 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
// Copyright CM4all GmbH
|
||||
// author: Max Kellermann <mk@cm4all.com>
|
||||
|
||||
#include "FormatAddress.hxx"
|
||||
#include "Features.hxx"
|
||||
#include "SocketAddress.hxx"
|
||||
#include "IPv4Address.hxx"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#ifdef HAVE_TCP
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UN
|
||||
#include <sys/un.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UN
|
||||
|
||||
static bool
|
||||
LocalToString(std::span<char> buffer, std::string_view raw) noexcept
|
||||
{
|
||||
if (raw.empty())
|
||||
return false;
|
||||
|
||||
if (raw.size() >= buffer.size())
|
||||
/* truncate to the buffer size */
|
||||
raw = raw.substr(0, buffer.size() - 1);
|
||||
|
||||
if (raw.front() != '\0' && raw.back() == '\0')
|
||||
/* don't convert the null terminator of a non-abstract socket
|
||||
to a '@' */
|
||||
raw.remove_suffix(1);
|
||||
|
||||
*std::copy(raw.begin(), raw.end(), buffer.begin()) = '\0';
|
||||
|
||||
/* replace all null bytes with '@'; this also handles abstract
|
||||
addresses (Linux specific) */
|
||||
const auto result = buffer.first(raw.size());
|
||||
std::replace(result.begin(), result.end(), '\0', '@');
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool
|
||||
ToString(std::span<char> buffer, SocketAddress address) noexcept
|
||||
{
|
||||
if (address.IsNull() || address.GetSize() == 0)
|
||||
return false;
|
||||
|
||||
#ifdef HAVE_UN
|
||||
if (address.GetFamily() == AF_LOCAL)
|
||||
/* return path of local socket */
|
||||
return LocalToString(buffer, address.GetLocalRaw());
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_IPV6) && defined(IN6_IS_ADDR_V4MAPPED)
|
||||
IPv4Address ipv4_buffer;
|
||||
if (address.IsV4Mapped())
|
||||
address = ipv4_buffer = address.UnmapV4();
|
||||
#endif
|
||||
|
||||
char serv[NI_MAXSERV];
|
||||
int ret = getnameinfo(address.GetAddress(), address.GetSize(),
|
||||
buffer.data(), buffer.size(),
|
||||
serv, sizeof(serv),
|
||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
if (ret != 0)
|
||||
return false;
|
||||
|
||||
if (serv[0] != 0 && (serv[0] != '0' || serv[1] != 0)) {
|
||||
#ifdef HAVE_IPV6
|
||||
if (address.GetFamily() == AF_INET6) {
|
||||
/* enclose IPv6 address in square brackets */
|
||||
|
||||
std::size_t length = strlen(buffer.data());
|
||||
if (length + 4 >= buffer.size())
|
||||
/* no more room */
|
||||
return false;
|
||||
|
||||
memmove(buffer.data() + 1, buffer.data(), length);
|
||||
buffer[0] = '[';
|
||||
buffer[++length] = ']';
|
||||
buffer[++length] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (strlen(buffer.data()) + 1 + strlen(serv) >= buffer.size())
|
||||
/* no more room */
|
||||
return false;
|
||||
|
||||
strcat(buffer.data(), ":");
|
||||
strcat(buffer.data(), serv);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *
|
||||
ToString(std::span<char> buffer, SocketAddress address,
|
||||
const char *fallback) noexcept
|
||||
{
|
||||
return ToString(buffer, address)
|
||||
? buffer.data()
|
||||
: fallback;
|
||||
}
|
||||
|
||||
bool
|
||||
HostToString(std::span<char> buffer, SocketAddress address) noexcept
|
||||
{
|
||||
if (address.IsNull() || address.GetSize() == 0)
|
||||
return false;
|
||||
|
||||
#ifdef HAVE_UN
|
||||
if (address.GetFamily() == AF_LOCAL)
|
||||
/* return path of local socket */
|
||||
return LocalToString(buffer, address.GetLocalRaw());
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_IPV6) && defined(IN6_IS_ADDR_V4MAPPED)
|
||||
IPv4Address ipv4_buffer;
|
||||
if (address.IsV4Mapped())
|
||||
address = ipv4_buffer = address.UnmapV4();
|
||||
#endif
|
||||
|
||||
return getnameinfo(address.GetAddress(), address.GetSize(),
|
||||
buffer.data(), buffer.size(),
|
||||
nullptr, 0,
|
||||
NI_NUMERICHOST | NI_NUMERICSERV) == 0;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
// Copyright CM4all GmbH
|
||||
// author: Max Kellermann <mk@cm4all.com>
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <span>
|
||||
|
||||
class SocketAddress;
|
||||
|
||||
/**
|
||||
* Generates the string representation of a #SocketAddress into the
|
||||
* specified buffer.
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool
|
||||
ToString(std::span<char> buffer, SocketAddress address) noexcept;
|
||||
|
||||
/**
|
||||
* Like ToString() above, but return the string pointer (or on error:
|
||||
* return the given fallback pointer).
|
||||
*/
|
||||
const char *
|
||||
ToString(std::span<char> buffer, SocketAddress address,
|
||||
const char *fallback) noexcept;
|
||||
|
||||
/**
|
||||
* Generates the string representation of a #SocketAddress into the
|
||||
* specified buffer, without the port number.
|
||||
*
|
||||
* @return true on success
|
||||
*/
|
||||
bool
|
||||
HostToString(std::span<char> buffer, SocketAddress address) noexcept;
|
|
@ -41,6 +41,7 @@ endif
|
|||
net = static_library(
|
||||
'net',
|
||||
net_sources,
|
||||
'FormatAddress.cxx',
|
||||
'ToString.cxx',
|
||||
'HostParser.cxx',
|
||||
'Resolver.cxx',
|
||||
|
|
Loading…
Reference in New Issue