[libc] Add inet_ntop (#204143)
The function converts from IPv4/6 addresses to their string forms. The complication comes from the v6 part due to address compression and v4-mapping. The traditional implementation of this is to sprintf the address into a temporary buffer. We cannot do that here due to the ban on calling other entry points. Using the lower-level constructs is possible, but a straight forward application of IntegerToString does not result in particularly impressive performance: 50%-100% slower than what's in this patch (and slower than glibc). It also doesn't make the code much smaller as the majority of it is dedicated to v6 compression. The IPv6 implementation uses a temporary buffer, but it also has a fast-path which skips the buffer if its size is guaranteed to be sufficient. The IPv4 implementation also has a fast path, but the fallback here is to compute the precise length of the string instead (the buffer option was slower and IPv4 makes it easier to compute the precise length). The v6 compression code is unrolled to make it easier to optimize. While it is possible to write a single printing loop that handles all cases, its branchiness does not make it amenable for optimization. The resulting function is faster than its glibc counterpart, though the exact ratio varies depending on the input: - random IPv4: 15% faster - random IPv6: 6% faster - "::": 50% faster
This commit is contained in:
parent
38a4eef071
commit
4db63ee847
14 changed files with 647 additions and 2 deletions
|
|
@ -1029,6 +1029,7 @@ if(LLVM_LIBC_FULL_BUILD)
|
|||
libc.src.arpa.inet.htons
|
||||
libc.src.arpa.inet.inet_addr
|
||||
libc.src.arpa.inet.inet_aton
|
||||
libc.src.arpa.inet.inet_ntop
|
||||
libc.src.arpa.inet.ntohl
|
||||
libc.src.arpa.inet.ntohs
|
||||
|
||||
|
|
|
|||
|
|
@ -1163,6 +1163,7 @@ if(LLVM_LIBC_FULL_BUILD)
|
|||
libc.src.arpa.inet.htons
|
||||
libc.src.arpa.inet.inet_addr
|
||||
libc.src.arpa.inet.inet_aton
|
||||
libc.src.arpa.inet.inet_ntop
|
||||
libc.src.arpa.inet.ntohl
|
||||
libc.src.arpa.inet.ntohs
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ set(TARGET_LIBC_ENTRYPOINTS
|
|||
libc.src.arpa.inet.htons
|
||||
libc.src.arpa.inet.inet_addr
|
||||
libc.src.arpa.inet.inet_aton
|
||||
libc.src.arpa.inet.inet_ntop
|
||||
libc.src.arpa.inet.ntohl
|
||||
libc.src.arpa.inet.ntohs
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,15 @@ add_proxy_header_library(
|
|||
libc.include.fenv
|
||||
)
|
||||
|
||||
add_proxy_header_library(
|
||||
inet_address_macros
|
||||
HDRS
|
||||
inet-address-macros.h
|
||||
FULL_BUILD_DEPENDS
|
||||
libc.include.arpa_inet
|
||||
libc.include.llvm-libc-macros.inet_address_macros
|
||||
)
|
||||
|
||||
add_proxy_header_library(
|
||||
netinet_in_macros
|
||||
HDRS
|
||||
|
|
|
|||
27
libc/hdr/inet-address-macros.h
Normal file
27
libc/hdr/inet-address-macros.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// Definitions internet address macros.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIBC_HDR_INET_ADDRESS_MACROS_H
|
||||
#define LLVM_LIBC_HDR_INET_ADDRESS_MACROS_H
|
||||
|
||||
#ifdef LIBC_FULL_BUILD
|
||||
|
||||
#include "include/llvm-libc-macros/inet-address-macros.h"
|
||||
|
||||
#else // Overlay mode
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#endif // LIBC_FULL_BUILD
|
||||
|
||||
#endif // LLVM_LIBC_HDR_INET_ADDRESS_MACROS_H
|
||||
|
|
@ -22,6 +22,7 @@ types:
|
|||
- type_name: in_addr_t
|
||||
- type_name: socklen_t
|
||||
- type_name: struct_in_addr
|
||||
- type_name: socklen_t
|
||||
enums: []
|
||||
objects: []
|
||||
functions:
|
||||
|
|
@ -38,3 +39,12 @@ functions:
|
|||
arguments:
|
||||
- type: const char *
|
||||
- type: struct in_addr *
|
||||
- name: inet_ntop
|
||||
standards:
|
||||
- posix
|
||||
return_type: const char *
|
||||
arguments:
|
||||
- type: int
|
||||
- type: const void *__restrict
|
||||
- type: char *__restrict
|
||||
- type: socklen_t
|
||||
|
|
|
|||
|
|
@ -6,8 +6,14 @@ add_object_library(
|
|||
address.cpp
|
||||
DEPENDS
|
||||
libc.hdr.types.in_addr_t
|
||||
libc.hdr.types.struct_in_addr
|
||||
libc.hdr.types.struct_in6_addr
|
||||
libc.hdr.inet_address_macros
|
||||
libc.src.__support.common
|
||||
libc.src.__support.ctype_utils
|
||||
libc.src.__support.CPP.optional
|
||||
libc.src.__support.CPP.span
|
||||
libc.src.__support.libc_assert
|
||||
libc.src.__support.str_to_integer
|
||||
libc.src.string.memory_utils.inline_memcpy
|
||||
)
|
||||
|
|
|
|||
|
|
@ -7,15 +7,22 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// This file implements helper functions for parsing network addresses.
|
||||
/// This file implements helper functions for parsing and formatting network
|
||||
/// addresses.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "src/__support/net/address.h"
|
||||
#include "hdr/inet-address-macros.h"
|
||||
#include "hdr/types/in_addr_t.h"
|
||||
#include "hdr/types/struct_in6_addr.h"
|
||||
#include "hdr/types/struct_in_addr.h"
|
||||
#include "src/__support/common.h"
|
||||
#include "src/__support/ctype_utils.h"
|
||||
#include "src/__support/endian_internal.h"
|
||||
#include "src/__support/libc_assert.h"
|
||||
#include "src/__support/str_to_integer.h"
|
||||
#include "src/string/memory_utils/inline_memcpy.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
namespace net {
|
||||
|
|
@ -61,5 +68,160 @@ cpp::optional<in_addr_t> inet_addr(const char *cp) {
|
|||
return Endian::to_big_endian(result);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
size_t ipv4_num_bytes(cpp::span<const uint8_t> src) {
|
||||
size_t result = 8; // four digits, three dots and '\0'
|
||||
for (uint8_t val : src)
|
||||
result += (val >= 10) + (val >= 100);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t ipv4_to_str_unchecked(cpp::span<const uint8_t> src,
|
||||
cpp::span<char> dst) {
|
||||
size_t pos = 0;
|
||||
for (unsigned i = 0; i < 4; ++i) {
|
||||
uint8_t val = src[i];
|
||||
if (val >= 100) {
|
||||
uint8_t cent = val / 100;
|
||||
uint8_t rem = val % 100;
|
||||
dst[pos++] = internal::int_to_b36_char(cent);
|
||||
dst[pos++] = internal::int_to_b36_char(rem / 10);
|
||||
dst[pos++] = internal::int_to_b36_char(rem % 10);
|
||||
} else if (val >= 10) {
|
||||
dst[pos++] = internal::int_to_b36_char(val / 10);
|
||||
dst[pos++] = internal::int_to_b36_char(val % 10);
|
||||
} else {
|
||||
dst[pos++] = internal::int_to_b36_char(val);
|
||||
}
|
||||
dst[pos++] = i < 3 ? '.' : '\0';
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
size_t ipv6_to_str_unchecked(const struct in6_addr &src, cpp::span<char> dst) {
|
||||
// Find the longest run of zeroes to compress to "::"
|
||||
struct Run {
|
||||
unsigned start = 0;
|
||||
unsigned len = 0;
|
||||
};
|
||||
Run best, current;
|
||||
for (unsigned i = 0; i < 8; ++i) {
|
||||
uint16_t val = src.s6_addr16[i];
|
||||
if (val == 0) {
|
||||
++current.len;
|
||||
} else {
|
||||
// In case of ties, the first sequence wins.
|
||||
if (current.len > best.len)
|
||||
best = current;
|
||||
current = {i + 1, 0};
|
||||
}
|
||||
}
|
||||
if (current.len > best.len)
|
||||
best = current;
|
||||
|
||||
bool is_mapped =
|
||||
best.start == 0 &&
|
||||
(best.len == 6 || (best.len == 5 && src.s6_addr16[5] == 0xffff));
|
||||
unsigned num_words = is_mapped ? 6 : 8;
|
||||
|
||||
size_t pos = 0;
|
||||
auto append_word = [&](unsigned i) {
|
||||
uint16_t word = Endian::from_big_endian(src.s6_addr16[i]);
|
||||
// This isn't using int_to_b36_char because it's large intermediate
|
||||
// representation prevents append_word from being inlined.
|
||||
static constexpr char DIGITS[] = "0123456789abcdef";
|
||||
if (word >= 0x1000) {
|
||||
dst[pos] = DIGITS[word >> 12];
|
||||
dst[pos + 1] = DIGITS[(word >> 8) & 0xf];
|
||||
dst[pos + 2] = DIGITS[(word >> 4) & 0xf];
|
||||
dst[pos + 3] = DIGITS[word & 0xf];
|
||||
pos += 4;
|
||||
} else if (word >= 0x100) {
|
||||
dst[pos] = DIGITS[word >> 8];
|
||||
dst[pos + 1] = DIGITS[(word >> 4) & 0xf];
|
||||
dst[pos + 2] = DIGITS[word & 0xf];
|
||||
pos += 3;
|
||||
} else if (word >= 0x10) {
|
||||
dst[pos] = DIGITS[(word >> 4) & 0xf];
|
||||
dst[pos + 1] = DIGITS[word & 0xf];
|
||||
pos += 2;
|
||||
} else {
|
||||
dst[pos] = DIGITS[word];
|
||||
pos += 1;
|
||||
}
|
||||
};
|
||||
|
||||
if (best.len < 2) {
|
||||
// No compression
|
||||
for (unsigned i = 0; i < 7; ++i) {
|
||||
append_word(i);
|
||||
dst[pos++] = ':';
|
||||
}
|
||||
append_word(7);
|
||||
dst[pos++] = '\0';
|
||||
return pos;
|
||||
}
|
||||
|
||||
// Left part
|
||||
for (unsigned i = 0; i < best.start; ++i) {
|
||||
append_word(i);
|
||||
dst[pos++] = ':';
|
||||
}
|
||||
// Compressed part
|
||||
if (best.start == 0)
|
||||
dst[pos++] = ':';
|
||||
dst[pos++] = ':';
|
||||
|
||||
// Right part (if it exists)
|
||||
if (best.start + best.len < num_words) {
|
||||
unsigned end = num_words - 1;
|
||||
for (unsigned i = best.start + best.len; i < end; ++i) {
|
||||
append_word(i);
|
||||
dst[pos++] = ':';
|
||||
}
|
||||
append_word(end);
|
||||
if (num_words == 6)
|
||||
dst[pos++] = ':';
|
||||
}
|
||||
|
||||
if (is_mapped) {
|
||||
cpp::span<const uint8_t> ipv4_part(src.s6_addr + 12, 4);
|
||||
pos += ipv4_to_str_unchecked(ipv4_part, dst.subspan(pos));
|
||||
} else {
|
||||
dst[pos++] = '\0';
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool ipv4_to_str(const struct in_addr &src, cpp::span<char> dst) {
|
||||
cpp::span<const uint8_t> addr(reinterpret_cast<const uint8_t *>(&src), 4);
|
||||
|
||||
if (dst.size() < INET_ADDRSTRLEN) {
|
||||
if (dst.size() < ipv4_num_bytes(addr))
|
||||
return false;
|
||||
}
|
||||
|
||||
ipv4_to_str_unchecked(addr, dst);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ipv6_to_str(const struct in6_addr &src, cpp::span<char> dst) {
|
||||
if (dst.size() >= INET6_ADDRSTRLEN) {
|
||||
ipv6_to_str_unchecked(src, dst);
|
||||
return true;
|
||||
}
|
||||
char buf[INET6_ADDRSTRLEN];
|
||||
size_t len = ipv6_to_str_unchecked(src, buf);
|
||||
LIBC_ASSERT(len < INET6_ADDRSTRLEN);
|
||||
if (len > dst.size())
|
||||
return false;
|
||||
inline_memcpy(dst.data(), buf, len);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@
|
|||
#ifndef LLVM_LIBC_SRC___SUPPORT_NET_ADDRESS_H
|
||||
#define LLVM_LIBC_SRC___SUPPORT_NET_ADDRESS_H
|
||||
|
||||
#include "hdr/types/in_addr_t.h"
|
||||
#include "hdr/types/struct_in6_addr.h"
|
||||
#include "hdr/types/struct_in_addr.h"
|
||||
#include "src/__support/CPP/optional.h"
|
||||
#include "src/__support/CPP/span.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
|
@ -24,6 +26,16 @@ namespace net {
|
|||
|
||||
cpp::optional<in_addr_t> inet_addr(const char *cp);
|
||||
|
||||
/// Writes a string representation (including the terminating \0) of the
|
||||
/// provided address into the destination buffer. In case of error, returns
|
||||
/// false and does not modify the buffer.
|
||||
[[nodiscard]] bool ipv4_to_str(const struct in_addr &src, cpp::span<char> dst);
|
||||
|
||||
/// Writes a string representation (including the terminating \0) of the
|
||||
/// provided address into the destination buffer. In case of error, returns
|
||||
/// false and does not modify the buffer.
|
||||
[[nodiscard]] bool ipv6_to_str(const struct in6_addr &src, cpp::span<char> dst);
|
||||
|
||||
} // namespace net
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,28 @@ add_entrypoint_object(
|
|||
libc.src.__support.net.address
|
||||
)
|
||||
|
||||
add_entrypoint_object(
|
||||
inet_ntop
|
||||
SRCS
|
||||
inet_ntop.cpp
|
||||
HDRS
|
||||
inet_ntop.h
|
||||
DEPENDS
|
||||
libc.include.arpa_inet
|
||||
libc.hdr.errno_macros
|
||||
libc.hdr.sys_socket_macros
|
||||
libc.hdr.types.struct_in_addr
|
||||
libc.hdr.types.struct_in6_addr
|
||||
libc.hdr.types.socklen_t
|
||||
libc.src.__support.common
|
||||
libc.src.__support.CPP.span
|
||||
libc.src.__support.libc_errno
|
||||
libc.src.__support.macros.config
|
||||
libc.src.__support.macros.null_check
|
||||
libc.src.__support.net.address
|
||||
)
|
||||
|
||||
|
||||
add_entrypoint_object(
|
||||
ntohl
|
||||
SRCS
|
||||
|
|
|
|||
53
libc/src/arpa/inet/inet_ntop.cpp
Normal file
53
libc/src/arpa/inet/inet_ntop.cpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// Implementation of inet_ntop function.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "src/arpa/inet/inet_ntop.h"
|
||||
#include "hdr/errno_macros.h"
|
||||
#include "hdr/sys_socket_macros.h"
|
||||
#include "hdr/types/struct_in6_addr.h"
|
||||
#include "hdr/types/struct_in_addr.h"
|
||||
#include "src/__support/CPP/span.h"
|
||||
#include "src/__support/common.h"
|
||||
#include "src/__support/libc_errno.h"
|
||||
#include "src/__support/macros/null_check.h"
|
||||
#include "src/__support/net/address.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
||||
LLVM_LIBC_FUNCTION(const char *, inet_ntop,
|
||||
(int af, const void *__restrict src, char *__restrict dst,
|
||||
socklen_t size)) {
|
||||
LIBC_CRASH_ON_NULLPTR(src);
|
||||
LIBC_CRASH_ON_NULLPTR(dst);
|
||||
|
||||
bool success;
|
||||
if (af == AF_INET) {
|
||||
success = net::ipv4_to_str(*reinterpret_cast<const struct in_addr *>(src),
|
||||
cpp::span<char>(dst, size));
|
||||
} else if (af == AF_INET6) {
|
||||
success = net::ipv6_to_str(*reinterpret_cast<const struct in6_addr *>(src),
|
||||
cpp::span<char>(dst, size));
|
||||
} else {
|
||||
libc_errno = EAFNOSUPPORT;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
libc_errno = ENOSPC;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
27
libc/src/arpa/inet/inet_ntop.h
Normal file
27
libc/src/arpa/inet/inet_ntop.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// Implementation header of inet_ntop.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_LIBC_SRC_ARPA_INET_INET_NTOP_H
|
||||
#define LLVM_LIBC_SRC_ARPA_INET_INET_NTOP_H
|
||||
|
||||
#include "hdr/types/socklen_t.h"
|
||||
#include "src/__support/macros/config.h"
|
||||
|
||||
namespace LIBC_NAMESPACE_DECL {
|
||||
|
||||
const char *inet_ntop(int af, const void *__restrict src, char *__restrict dst,
|
||||
socklen_t size);
|
||||
|
||||
} // namespace LIBC_NAMESPACE_DECL
|
||||
|
||||
#endif // LLVM_LIBC_SRC_ARPA_INET_INET_NTOP_H
|
||||
|
|
@ -44,6 +44,23 @@ add_libc_unittest(
|
|||
libc.src.arpa.inet.inet_aton
|
||||
)
|
||||
|
||||
add_libc_unittest(
|
||||
inet_ntop
|
||||
SUITE
|
||||
libc_arpa_inet_unittests
|
||||
SRCS
|
||||
inet_ntop_test.cpp
|
||||
DEPENDS
|
||||
libc.src.arpa.inet.inet_ntop
|
||||
libc.hdr.errno_macros
|
||||
libc.hdr.sys_socket_macros
|
||||
libc.hdr.types.struct_in_addr
|
||||
libc.hdr.types.struct_in6_addr
|
||||
libc.src.__support.common
|
||||
libc.test.UnitTest.ErrnoCheckingTest
|
||||
)
|
||||
|
||||
|
||||
add_libc_unittest(
|
||||
ntohl
|
||||
SUITE
|
||||
|
|
|
|||
297
libc/test/src/arpa/inet/inet_ntop_test.cpp
Normal file
297
libc/test/src/arpa/inet/inet_ntop_test.cpp
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
///
|
||||
/// \file
|
||||
/// Unittests for inet_ntop.
|
||||
///
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "hdr/errno_macros.h"
|
||||
#include "hdr/sys_socket_macros.h"
|
||||
#include "hdr/types/struct_in6_addr.h"
|
||||
#include "hdr/types/struct_in_addr.h"
|
||||
#include "src/__support/endian_internal.h"
|
||||
#include "src/arpa/inet/inet_ntop.h"
|
||||
#include "test/UnitTest/ErrnoCheckingTest.h"
|
||||
#include "test/UnitTest/Test.h"
|
||||
|
||||
using LlvmLibcInetNtopTest = LIBC_NAMESPACE::testing::ErrnoCheckingTest;
|
||||
|
||||
TEST_F(LlvmLibcInetNtopTest, InvalidFamily) {
|
||||
char buf[64];
|
||||
struct in_addr addr = {0};
|
||||
ASSERT_EQ(static_cast<const char *>(nullptr),
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET + AF_INET6 + 1, &addr, buf,
|
||||
sizeof(buf)));
|
||||
ASSERT_ERRNO_EQ(EAFNOSUPPORT);
|
||||
}
|
||||
|
||||
static void *ipv4(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
|
||||
static struct in_addr addr;
|
||||
addr.s_addr = LIBC_NAMESPACE::Endian::to_big_endian(
|
||||
static_cast<uint32_t>(a) << 24 | static_cast<uint32_t>(b) << 16 |
|
||||
static_cast<uint32_t>(c) << 8 | static_cast<uint32_t>(d));
|
||||
return &addr;
|
||||
}
|
||||
|
||||
static void *ipv6(uint16_t a, uint16_t b, uint16_t c, uint16_t d, uint16_t e,
|
||||
uint16_t f, uint16_t g, uint16_t h) {
|
||||
static struct in6_addr addr;
|
||||
addr.s6_addr16[0] = LIBC_NAMESPACE::Endian::to_big_endian(a);
|
||||
addr.s6_addr16[1] = LIBC_NAMESPACE::Endian::to_big_endian(b);
|
||||
addr.s6_addr16[2] = LIBC_NAMESPACE::Endian::to_big_endian(c);
|
||||
addr.s6_addr16[3] = LIBC_NAMESPACE::Endian::to_big_endian(d);
|
||||
addr.s6_addr16[4] = LIBC_NAMESPACE::Endian::to_big_endian(e);
|
||||
addr.s6_addr16[5] = LIBC_NAMESPACE::Endian::to_big_endian(f);
|
||||
addr.s6_addr16[6] = LIBC_NAMESPACE::Endian::to_big_endian(g);
|
||||
addr.s6_addr16[7] = LIBC_NAMESPACE::Endian::to_big_endian(h);
|
||||
return &addr;
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcInetNtopTest, IPv4Tests) {
|
||||
char buf[16];
|
||||
|
||||
EXPECT_STREQ("127.0.0.1", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET, ipv4(127, 0, 0, 1), buf, sizeof(buf)));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
// Test buffer too small (needs 10 bytes including null terminator)
|
||||
EXPECT_EQ(static_cast<const char *>(nullptr),
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(127, 0, 0, 1), buf, 9));
|
||||
ASSERT_ERRNO_EQ(ENOSPC);
|
||||
|
||||
EXPECT_STREQ("127.0.0.1",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(127, 0, 0, 1), buf, 10));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("255.255.255.255",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(255, 255, 255, 255), buf,
|
||||
sizeof(buf)));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
// Test buffer too small (needs 16 bytes)
|
||||
EXPECT_EQ(
|
||||
static_cast<const char *>(nullptr),
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(255, 255, 255, 255), buf, 15));
|
||||
ASSERT_ERRNO_EQ(ENOSPC);
|
||||
|
||||
// Boundary conditions for the number of digits.
|
||||
EXPECT_STREQ("0.0.0.0",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(0, 0, 0, 0), buf, 10));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("0.0.0.1",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(0, 0, 0, 1), buf, 10));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("0.0.0.9",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(0, 0, 0, 9), buf, 10));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("0.0.0.10",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(0, 0, 0, 10), buf, 11));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("0.0.0.11",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(0, 0, 0, 11), buf, 11));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("0.0.0.99",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(0, 0, 0, 99), buf, 11));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("0.0.0.100",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(0, 0, 0, 100), buf, 12));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
}
|
||||
|
||||
TEST_F(LlvmLibcInetNtopTest, IPv6Tests) {
|
||||
char buf[64];
|
||||
|
||||
// No compression
|
||||
EXPECT_STREQ("1:2:3:4:5:6:7:8",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET6, ipv6(1, 2, 3, 4, 5, 6, 7, 8),
|
||||
buf, sizeof(buf)));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
// Compression, fully compressed.
|
||||
EXPECT_STREQ(static_cast<const char *>(nullptr),
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET6, ipv6(0, 0, 0, 0, 0, 0, 0, 0),
|
||||
buf, 2));
|
||||
ASSERT_ERRNO_EQ(ENOSPC);
|
||||
|
||||
EXPECT_STREQ("::", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0, 0, 0, 0, 0, 0, 0, 0), buf, 3));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
// Leading block of zeroes.
|
||||
EXPECT_STREQ("::1", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0, 0, 0, 0, 0, 0, 0, 1), buf, 4));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_EQ(static_cast<const char *>(nullptr),
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET6, ipv6(0, 0, 0, 0, 0, 0, 0, 1),
|
||||
buf, 3));
|
||||
ASSERT_ERRNO_EQ(ENOSPC);
|
||||
|
||||
EXPECT_STREQ("::1:2:3", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0, 0, 0, 0, 0, 1, 2, 3), buf, 8));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
// Trailing block of zeroes.
|
||||
EXPECT_STREQ("1::", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(1, 0, 0, 0, 0, 0, 0, 0), buf, 4));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("1:2::", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(1, 2, 0, 0, 0, 0, 0, 0), buf, 6));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
// Middle of string.
|
||||
EXPECT_STREQ("2001:db8::1",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET6,
|
||||
ipv6(0x2001, 0x0db8, 0, 0, 0, 0, 0, 1),
|
||||
buf, sizeof(buf)));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("2001:db8:0:1::1",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET6,
|
||||
ipv6(0x2001, 0x0db8, 0, 1, 0, 0, 0, 1),
|
||||
buf, sizeof(buf)));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
// Longer block wins.
|
||||
EXPECT_STREQ("2001:0:0:1::1", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0x2001, 0, 0, 1, 0, 0, 0, 1),
|
||||
buf, sizeof(buf)));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("0:0:1:1::",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET6, ipv6(0, 0, 1, 1, 0, 0, 0, 0),
|
||||
buf, sizeof(buf)));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
// In case of ties, the first block wins.
|
||||
EXPECT_STREQ("::1:0:0:1:0:0",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET6, ipv6(0, 0, 1, 0, 0, 1, 0, 0),
|
||||
buf, sizeof(buf)));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
// A single zero is not compressed, no matter it's position.
|
||||
EXPECT_STREQ("0:1:1:1:1:1:1:1",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET6, ipv6(0, 1, 1, 1, 1, 1, 1, 1),
|
||||
buf, sizeof(buf)));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("1:0:1:1:1:1:1:1",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET6, ipv6(1, 0, 1, 1, 1, 1, 1, 1),
|
||||
buf, sizeof(buf)));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("1:1:1:1:1:1:1:0",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET6, ipv6(1, 1, 1, 1, 1, 1, 1, 0),
|
||||
buf, sizeof(buf)));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
// V4 mapped addresses.
|
||||
EXPECT_STREQ("::ffff:192.168.0.1",
|
||||
LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0, 0, 0, 0, 0, 0xffff, 0xc0a8, 1), buf, 19));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("::192.168.0.1", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0, 0, 0, 0, 0, 0, 0xc0a8, 1),
|
||||
buf, sizeof(buf)));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
// If only the last component is set, we don't v4-map.
|
||||
EXPECT_STREQ("::dead", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0, 0, 0, 0, 0, 0, 0, 0xdead), buf,
|
||||
sizeof(buf)));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
// But we do for the penultimate.
|
||||
EXPECT_STREQ("::10.13.0.0", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0, 0, 0, 0, 0, 0, 0x0a0d, 0),
|
||||
buf, sizeof(buf)));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
// Edge cases for numbers of digits.
|
||||
EXPECT_STREQ("0:1::", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0, 1, 0, 0, 0, 0, 0, 0), buf, 6));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("1:1::", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(1, 1, 0, 0, 0, 0, 0, 0), buf, 6));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("2:1::", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(2, 1, 0, 0, 0, 0, 0, 0), buf, 6));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("4:1::", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(4, 1, 0, 0, 0, 0, 0, 0), buf, 6));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("8:1::", LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(8, 1, 0, 0, 0, 0, 0, 0), buf, 6));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("10:1::",
|
||||
LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0x10, 1, 0, 0, 0, 0, 0, 0), buf, 7));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("ff:1::",
|
||||
LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0xff, 1, 0, 0, 0, 0, 0, 0), buf, 7));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("100:1::",
|
||||
LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0x100, 1, 0, 0, 0, 0, 0, 0), buf, 8));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("fff:1::",
|
||||
LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0xfff, 1, 0, 0, 0, 0, 0, 0), buf, 8));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_STREQ("1000:1::",
|
||||
LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0x1000, 1, 0, 0, 0, 0, 0, 0), buf, 9));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
// Maximum length address.
|
||||
EXPECT_STREQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET6,
|
||||
ipv6(0xffff, 0xffff, 0xffff, 0xffff,
|
||||
0xffff, 0xffff, 0xffff, 0xffff),
|
||||
buf, 40));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_EQ(static_cast<const char *>(nullptr),
|
||||
LIBC_NAMESPACE::inet_ntop(AF_INET6,
|
||||
ipv6(0xffff, 0xffff, 0xffff, 0xffff,
|
||||
0xffff, 0xffff, 0xffff, 0xffff),
|
||||
buf, 39));
|
||||
ASSERT_ERRNO_EQ(ENOSPC);
|
||||
|
||||
// IPv4-mapped max length
|
||||
EXPECT_STREQ(
|
||||
"::ffff:255.255.255.255",
|
||||
LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff), buf, 23));
|
||||
ASSERT_ERRNO_SUCCESS();
|
||||
|
||||
EXPECT_EQ(
|
||||
static_cast<const char *>(nullptr),
|
||||
LIBC_NAMESPACE::inet_ntop(
|
||||
AF_INET6, ipv6(0, 0, 0, 0, 0, 0xffff, 0xffff, 0xffff), buf, 22));
|
||||
ASSERT_ERRNO_EQ(ENOSPC);
|
||||
}
|
||||
Loading…
Reference in a new issue