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 = static_library(
|
||||||
'net',
|
'net',
|
||||||
net_sources,
|
net_sources,
|
||||||
|
'FormatAddress.cxx',
|
||||||
'ToString.cxx',
|
'ToString.cxx',
|
||||||
'HostParser.cxx',
|
'HostParser.cxx',
|
||||||
'Resolver.cxx',
|
'Resolver.cxx',
|
||||||
|
|
Loading…
Reference in New Issue