[libc-commits] [libc] [libc] Add inet_ntop (PR #204143)

via libc-commits libc-commits at lists.llvm.org
Tue Jun 16 06:17:39 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libc

Author: Pavel Labath (labath)

<details>
<summary>Changes</summary>

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

---

Patch is 25.69 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/204143.diff


14 Files Affected:

- (modified) libc/config/linux/aarch64/entrypoints.txt (+1) 
- (modified) libc/config/linux/riscv/entrypoints.txt (+1) 
- (modified) libc/config/linux/x86_64/entrypoints.txt (+1) 
- (modified) libc/hdr/CMakeLists.txt (+9) 
- (added) libc/hdr/inet-address-macros.h (+28) 
- (modified) libc/include/arpa/inet.yaml (+10) 
- (modified) libc/src/__support/net/CMakeLists.txt (+6) 
- (modified) libc/src/__support/net/address.cpp (+163-1) 
- (modified) libc/src/__support/net/address.h (+13-1) 
- (modified) libc/src/arpa/inet/CMakeLists.txt (+21) 
- (added) libc/src/arpa/inet/inet_ntop.cpp (+53) 
- (added) libc/src/arpa/inet/inet_ntop.h (+27) 
- (modified) libc/test/src/arpa/inet/CMakeLists.txt (+17) 
- (added) libc/test/src/arpa/inet/inet_ntop_test.cpp (+255) 


``````````diff
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index f86a3be3538c1..798a824f053d7 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -1016,6 +1016,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
 
diff --git a/libc/config/linux/riscv/entrypoints.txt b/libc/config/linux/riscv/entrypoints.txt
index fe95f4ce24448..ef673337a1101 100644
--- a/libc/config/linux/riscv/entrypoints.txt
+++ b/libc/config/linux/riscv/entrypoints.txt
@@ -1149,6 +1149,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
 
diff --git a/libc/config/linux/x86_64/entrypoints.txt b/libc/config/linux/x86_64/entrypoints.txt
index 0fac360949dd2..8f8044bce6a5d 100644
--- a/libc/config/linux/x86_64/entrypoints.txt
+++ b/libc/config/linux/x86_64/entrypoints.txt
@@ -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
 
diff --git a/libc/hdr/CMakeLists.txt b/libc/hdr/CMakeLists.txt
index 0bc0202022680..a1382039d867e 100644
--- a/libc/hdr/CMakeLists.txt
+++ b/libc/hdr/CMakeLists.txt
@@ -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
diff --git a/libc/hdr/inet-address-macros.h b/libc/hdr/inet-address-macros.h
new file mode 100644
index 0000000000000..32e6134f86fe7
--- /dev/null
+++ b/libc/hdr/inet-address-macros.h
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <arpa/inet.h>
+// Or #include <netinet/in.h>. Both files should define these.
+
+#endif // LLVM_LIBC_FULL_BUILD
+
+#endif // LLVM_LIBC_HDR_INET_ADDRESS_MACROS_H
diff --git a/libc/include/arpa/inet.yaml b/libc/include/arpa/inet.yaml
index 38e975c3dfbfd..533c0f920024a 100644
--- a/libc/include/arpa/inet.yaml
+++ b/libc/include/arpa/inet.yaml
@@ -11,6 +11,7 @@ macros:
 types:
   - type_name: in_addr_t
   - type_name: struct_in_addr
+  - type_name: socklen_t
 enums: []
 objects: []
 functions:
@@ -27,3 +28,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
diff --git a/libc/src/__support/net/CMakeLists.txt b/libc/src/__support/net/CMakeLists.txt
index 84e9f1bebf230..e243ed5c8a2d4 100644
--- a/libc/src/__support/net/CMakeLists.txt
+++ b/libc/src/__support/net/CMakeLists.txt
@@ -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
 )
diff --git a/libc/src/__support/net/address.cpp b/libc/src/__support/net/address.cpp
index 1cb0924b07a99..0ef1b5810cf95 100644
--- a/libc/src/__support/net/address.cpp
+++ b/libc/src/__support/net/address.cpp
@@ -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 (unsigned i = 0; i < 4; ++i)
+    result += (src[i] >= 10) + (src[i] >= 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;
+      val -= cent * 100;
+      uint8_t dec = val / 10;
+      dst[pos++] = '0' + cent;
+      dst[pos++] = '0' + dec;
+      dst[pos++] = '0' + val % 10;
+    } else if (val >= 10) {
+      uint8_t dec = val / 10;
+      dst[pos++] = '0' + dec;
+      dst[pos++] = '0' + val % 10;
+    } else {
+      dst[pos++] = '0' + 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;
+
+  char *pos = dst.data();
+  auto append_word = [&](unsigned i) {
+    uint16_t word = Endian::from_big_endian(src.s6_addr16[i]);
+    static constexpr char DIGITS[] = "0123456789abcdef";
+    if (word >= 0x1000) {
+      pos[0] = DIGITS[word >> 12];
+      pos[1] = DIGITS[(word >> 8) & 0xf];
+      pos[2] = DIGITS[(word >> 4) & 0xf];
+      pos[3] = DIGITS[word & 0xf];
+      pos += 4;
+    } else if (word >= 0x100) {
+      pos[0] = DIGITS[word >> 8];
+      pos[1] = DIGITS[(word >> 4) & 0xf];
+      pos[2] = DIGITS[word & 0xf];
+      pos += 3;
+    } else if (word >= 0x10) {
+      pos[0] = DIGITS[(word >> 4) & 0xf];
+      pos[1] = DIGITS[word & 0xf];
+      pos += 2;
+    } else {
+      pos[0] = DIGITS[word];
+      pos += 1;
+    }
+  };
+
+  if (best.len < 2) {
+    // No compression
+    for (unsigned i = 0; i < 7; ++i) {
+      append_word(i);
+      *pos++ = ':';
+    }
+    append_word(7);
+    *pos++ = '\0';
+    return static_cast<size_t>(pos - dst.data());
+  }
+
+  // Left part
+  for (unsigned i = 0; i < best.start; ++i) {
+    append_word(i);
+    *pos++ = ':';
+  }
+  // Compressed part
+  if (best.start == 0)
+    *pos++ = ':';
+  *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);
+      *pos++ = ':';
+    }
+    append_word(end);
+    if (num_words == 6)
+      *pos++ = ':';
+  }
+
+  if (is_mapped) {
+    cpp::span<const uint8_t> ipv4_part(src.s6_addr + 12, 4);
+    pos += ipv4_to_str_unchecked(ipv4_part, cpp::span<char>(pos, dst.end()));
+  } else {
+    *pos++ = '\0';
+  }
+
+  return static_cast<size_t>(pos - dst.data());
+}
+
+} // 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
diff --git a/libc/src/__support/net/address.h b/libc/src/__support/net/address.h
index 925b8d379f703..082cf7a187899 100644
--- a/libc/src/__support/net/address.h
+++ b/libc/src/__support/net/address.h
@@ -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
 
diff --git a/libc/src/arpa/inet/CMakeLists.txt b/libc/src/arpa/inet/CMakeLists.txt
index da041fc314b43..875a5f002a921 100644
--- a/libc/src/arpa/inet/CMakeLists.txt
+++ b/libc/src/arpa/inet/CMakeLists.txt
@@ -51,6 +51,27 @@ 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.null_check
+    libc.src.__support.net.address
+)
+
+
 add_entrypoint_object(
   ntohl
   SRCS
diff --git a/libc/src/arpa/inet/inet_ntop.cpp b/libc/src/arpa/inet/inet_ntop.cpp
new file mode 100644
index 0000000000000..b0120657d56f3
--- /dev/null
+++ b/libc/src/arpa/inet/inet_ntop.cpp
@@ -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(*static_cast<const struct in_addr *>(src),
+                               cpp::span<char>(dst, size));
+  } else if (af == AF_INET6) {
+    success = net::ipv6_to_str(*static_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
diff --git a/libc/src/arpa/inet/inet_ntop.h b/libc/src/arpa/inet/inet_ntop.h
new file mode 100644
index 0000000000000..83a527c011aa1
--- /dev/null
+++ b/libc/src/arpa/inet/inet_ntop.h
@@ -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
diff --git a/libc/test/src/arpa/inet/CMakeLists.txt b/libc/test/src/arpa/inet/CMakeLists.txt
index 1d400c4374f70..5a6da519b2782 100644
--- a/libc/test/src/arpa/inet/CMakeLists.txt
+++ b/libc/test/src/arpa/inet/CMakeLists.txt
@@ -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
diff --git a/libc/test/src/arpa/inet/inet_ntop_test.cpp b/libc/test/src/arpa/inet/inet_ntop_test.cpp
new file mode 100644
index 0000000000000..fa46442847757
--- /dev/null
+++ b/libc/test/src/arpa/inet/inet_ntop_test.cpp
@@ -0,0 +1,255 @@
+//===----------------------------------------------------------------------===//
+//
+// 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)));
+
+  // 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));
+
+  EXPECT_STREQ("255.255.255.255",
+               LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(255, 255, 255, 255), buf,
+                                         sizeof(buf)));
+
+  // 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));
+
+  EXPECT_STREQ("0.0.0.1",
+               LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(0, 0, 0, 1), buf, 10));
+
+  EXPECT_STREQ("0.0.0.9",
+               LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(0, 0, 0, 9), buf, 10));
+
+  EXPECT_STREQ("0.0.0.10",
+               LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(0, 0, 0, 10), buf, 11));
+
+  EXPECT_STREQ("0.0.0.11",
+               LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(0, 0, 0, 11), buf, 11));
+
+  EXPECT_STREQ("0.0.0.99",
+               LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(0, 0, 0, 99), buf, 11));
+
+  EXPECT_STREQ("0.0.0.100",
+               LIBC_NAMESPACE::inet_ntop(AF_INET, ipv4(0, 0, 0, 100), buf, 12));
+}
+
+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)));
+
+  // 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));
+
+  // Leading block of zeroes.
+  EXPECT_STREQ("::1", LIBC_N...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/204143


More information about the libc-commits mailing list