[PATCH v2 4/5] resolv: Optimize inet_ntop
DJ Delorie
dj@redhat.com
Tue Jun 24 04:00:07 GMT 2025
Adhemerval Zanella <adhemerval.zanella@linaro.org> writes:
> The benchtests/inet_ntop_ipv4 and benchtests/inet_ntop_ipv6 profile
> shows that most of time is spent in costly sprint operations:
LGTM
Reviewed-by: DJ Delorie <dj@redhat.com>
> A new implementation is used instead:
>
> * The printf usage is replaced with an expanded function that prints
> either an IPv4 octet or an IPv6 quartet;
>
> * The strcpy is replaced with a memcpy (since ABIs usually tends to
> optimize the latter);
>
> * For IPv6, the '::' shorthanding is done in-place instead of using
> a temporary buffer.
>
> * An temporary buffer is used iff the size if larger than
> INET_ADDRSTRLEN/INET6_ADDRSTRLEN.
>
> * Inline is used for both inet_ntop4 and inet_ntop6,
>
> The code is significand rewrote, so I take this requires a new license.
Confirmed this is a 95% rewrite. I won't argue that the remaining 5%
means "derived work; preserve license" but I'll note it's not a 100%
rewrite.
> +static inline char *
> +put_uint8 (uint8_t word, char *tp)
> {
> + int s = 1;
> + if (word >= 10)
> {
> + if (word >= 100)
> + {
> + tp[2] = '0' + word % 10;
> + word /= 10;
> + s += 1;
> + }
> +
> + tp[1] = '0' + word % 10;
> + word /= 10;
> + s += 1;
> }
> + *tp = '0' + word % 10;
> + return tp + s;
> }
This is a bit devious and deserving of a comment; I almost complained
about the math before I realized what it was building the string right
to left...
> +static inline char *
> +put_uint16 (uint16_t word, char *tp)
> {
> + if (word >= 0x1000)
> + *tp++ = _itoa_lower_digits[(word >> 12) & 0xf];
> + if (word >= 0x100)
> + *tp++ = _itoa_lower_digits[(word >> 8) & 0xf];
> + if (word >= 0x10)
> + *tp++ = _itoa_lower_digits[(word >> 4) & 0xf];
> + *tp++ = _itoa_lower_digits[word & 0xf];
> + return tp;
> +}
Ok.
> +static __always_inline char *
> +inet_ntop4_format (const uint8_t *src, char *dst)
> +{
> + dst = put_uint8 (src[0], dst);
> + *(dst++) = '.';
> + dst = put_uint8 (src[1], dst);
> + *(dst++) = '.';
> + dst = put_uint8 (src[2], dst);
> + *(dst++) = '.';
> + dst = put_uint8 (src[3], dst);
> + *dst++ = '\0';
> + return dst;
> +}
Ok.
> +static __always_inline const char *
> +inet_ntop4 (const uint8_t *src, char *dst, socklen_t size)
> +{
> + if (size >= INET_ADDRSTRLEN)
> + {
> + inet_ntop4_format (src, dst);
> + return dst;
> + }
> +
> + char tmp[INET_ADDRSTRLEN];
> + char *tp = inet_ntop4_format (src, tmp);
> + socklen_t tmp_s = tp - tmp;
> + if (tmp_s > size)
> {
> __set_errno (ENOSPC);
> + return NULL;
> }
> + return memcpy (dst, tmp, tmp_s);
> }
Ok.
> +struct best_t
> {
> + int base;
> + int len;
> +};
>
> +static inline uint16_t
> +in6_addr_addr16 (const struct in6_addr *src, int idx)
> +{
> + const struct { uint16_t x; } __attribute__((__packed__)) *pptr =
> + (typeof(pptr))(&src->s6_addr16[idx]);
> + return ntohs (pptr->x);
> +}
Ok.
> +static __always_inline char *
> +inet_ntop6_format (const struct in6_addr *src, struct best_t best, char *dst)
> +{
> + char *tp = dst;
> + for (int i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++)
> {
> + /* Are we inside the best run of 0x00's? */
> + if (best.base != -1 && i >= best.base && i < (best.base + best.len))
> + {
> + if (i == best.base)
> + *tp++ = ':';
> + continue;
> + }
> + /* Are we following an initial run of 0x00s or any real hex? */
> + if (i != 0)
> + *tp++ = ':';
> + /* Is this address an encapsulated IPv4? */
> + if (i == 6 && best.base == 0
> + && (best.len == 6 || (best.len == 5
> + && in6_addr_addr16 (src, 5) == 0xffff)))
> + {
> + if (!inet_ntop4 (src->s6_addr + 12, tp,
> + INET6_ADDRSTRLEN - (tp - dst)))
> + return NULL;
> + tp += strlen (tp);
> + break;
> + }
> + tp = put_uint16 (in6_addr_addr16 (src, i), tp);
> + }
> + /* Was it a trailing run of 0x00's? */
> + if (best.base != -1 && (best.base + best.len) == (NS_IN6ADDRSZ / NS_INT16SZ))
> + *tp++ = ':';
> + *tp++ = '\0';
> +
> + return tp;
> +}
Ok.
> +static inline const char *
> +inet_ntop6 (const struct in6_addr *src, char *dst, socklen_t size)
> +{
> + struct best_t best = { -1, 0 }, cur = { -1, 0 };
> +
> + /* ind the longest run of 0x00's in src[] for :: shorthanding. */
> + for (int i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++)
> + {
> + if (in6_addr_addr16 (src, i) == 0)
> {
> if (cur.base == -1)
> cur.base = i, cur.len = 1;
> @@ -155,45 +174,37 @@ inet_ntop6 (const u_char *src, char *dst, socklen_t size)
> if (best.base != -1 && best.len < 2)
> best.base = -1;
Ok.
> + if (size >= INET6_ADDRSTRLEN)
> {
> + inet_ntop6_format (src, best, dst);
> + return dst;
> }
>
> + char tmp[INET6_ADDRSTRLEN];
> + char *tp = inet_ntop6_format (src, best, tmp);
> +
> + socklen_t tmp_s = tp - tmp;
> + if (tmp_s > size)
> {
> __set_errno (ENOSPC);
> return (NULL);
> }
> + return memcpy (dst, tmp, tmp_s);
> }
Ok.
> +const char *
> +__inet_ntop (int af, const void *src, char *dst, socklen_t size)
> +{
> + switch (af)
> + {
> + case AF_INET:
> + return (inet_ntop4 (src, dst, size));
> + case AF_INET6:
> + return (inet_ntop6 (src, dst, size));
> + default:
> + __set_errno (EAFNOSUPPORT);
> + return (NULL);
> + }
> +}
> +libc_hidden_def (__inet_ntop)
> +weak_alias (__inet_ntop, inet_ntop)
Ok.
More information about the Libc-alpha
mailing list