libc: add glibc-compatible tdestroy(3)

The function clears the whole tree.

Relnotes:	yes
Reviewed by:	alc, emaste
Discussed with:	dougm
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
Differential revision:	https://reviews.freebsd.org/D54365
This commit is contained in:
Konstantin Belousov 2025-12-26 01:38:25 +02:00
parent 04a664d876
commit b8c99e7d91
4 changed files with 71 additions and 0 deletions

View file

@ -77,6 +77,7 @@ void twalk(const posix_tnode *, void (*)(const posix_tnode *, VISIT, int));
int hcreate_r(size_t, struct hsearch_data *);
void hdestroy_r(struct hsearch_data *);
int hsearch_r(ENTRY, ACTION, ENTRY **, struct hsearch_data *);
void tdestroy(void *, void (*)(void *));
#endif
__END_DECLS

View file

@ -67,6 +67,7 @@ MISRCS+= \
strtouq.c \
system.c \
tdelete.c \
tdestroy.c \
tfind.c \
tsearch.c \
twalk.c

View file

@ -134,6 +134,7 @@ FBSD_1.8 {
FBSD_1.9 {
memalignment;
recallocarray;
tdestroy;
};
FBSDprivate_1.0 {

View file

@ -0,0 +1,68 @@
/*
* Copyright 2025 The FreeBSD Foundation
*
* SPDX-License-Identifier: BSD-2-Clause
*
* This software was developed by Konstantin Belousov <kib@FreeBSD.org>
* under sponsorship from the FreeBSD Foundation.
*/
#define _SEARCH_PRIVATE
#include <search.h>
#include <stdlib.h>
static void
nul_node_free(void *node __unused)
{
}
/* Find the leftmost node. */
static posix_tnode *
tdestroy_find_leftmost(posix_tnode *tn)
{
while (tn->llink != NULL)
tn = tn->llink;
return (tn);
}
/*
* This algorithm for non-recursive non-allocating destruction of the tree
* is described in
* https://codegolf.stackexchange.com/questions/478/free-a-binary-tree/489#489P
* and in https://devblogs.microsoft.com/oldnewthing/20251107-00/?p=111774.
*/
void
tdestroy(void *rootp, void (*node_free)(void *))
{
posix_tnode *tn, *tn_leftmost, *xtn;
tn = rootp;
if (tn == NULL)
return;
if (node_free == NULL)
node_free = nul_node_free;
tn_leftmost = tn;
while (tn != NULL) {
/*
* Make the right subtree the left subtree of the
* leftmost node, and recalculate the leftmost.
*/
tn_leftmost = tdestroy_find_leftmost(tn_leftmost);
if (tn->rlink != NULL) {
tn_leftmost->llink = tn->rlink;
tn_leftmost = tn_leftmost->llink;
}
/*
* At this point, all children of tn have been
* arranged to be reachable via tn->left. We can
* safely delete the current node and advance to its
* left child as the new root.
*/
xtn = tn->llink;
node_free(tn->key);
free(tn);
tn = xtn;
}
}