Implement twalk(3), add unit tests.

I've also added insque(3) and remque(3) (from NetBSD because the OpenBSD
ones are currently broken for non-circular lists).

I've not added the three hash table functions that should be in this header
because they operate on a single global hash table and thus aren't likely
to be useful.

Bug: https://code.google.com/p/android/issues/detail?id=73719

(cherry picked from commit 3e424d0a24)

Change-Id: I5882a6b48c80fea8ac6b9c27e7b9de10b202b4ff
This commit is contained in:
Elliott Hughes 2014-07-23 16:02:26 -07:00
parent 5ade7e3f6b
commit b902641d73
14 changed files with 534 additions and 213 deletions

View File

@ -287,20 +287,18 @@ libc_upstream_netbsd_src_files := \
upstream-netbsd/lib/libc/stdlib/div.c \
upstream-netbsd/lib/libc/stdlib/drand48.c \
upstream-netbsd/lib/libc/stdlib/erand48.c \
upstream-netbsd/lib/libc/stdlib/insque.c \
upstream-netbsd/lib/libc/stdlib/jrand48.c \
upstream-netbsd/lib/libc/stdlib/ldiv.c \
upstream-netbsd/lib/libc/stdlib/lldiv.c \
upstream-netbsd/lib/libc/stdlib/lrand48.c \
upstream-netbsd/lib/libc/stdlib/lsearch.c \
upstream-netbsd/lib/libc/stdlib/mrand48.c \
upstream-netbsd/lib/libc/stdlib/nrand48.c \
upstream-netbsd/lib/libc/stdlib/_rand48.c \
upstream-netbsd/lib/libc/stdlib/rand_r.c \
upstream-netbsd/lib/libc/stdlib/remque.c \
upstream-netbsd/lib/libc/stdlib/seed48.c \
upstream-netbsd/lib/libc/stdlib/srand48.c \
upstream-netbsd/lib/libc/stdlib/tdelete.c \
upstream-netbsd/lib/libc/stdlib/tfind.c \
upstream-netbsd/lib/libc/stdlib/tsearch.c \
upstream-netbsd/lib/libc/string/memccpy.c \
upstream-netbsd/lib/libc/string/strcasestr.c \
upstream-netbsd/lib/libc/string/strcoll.c \
@ -469,6 +467,7 @@ libc_upstream_openbsd_src_files := \
upstream-openbsd/lib/libc/stdlib/atoll.c \
upstream-openbsd/lib/libc/stdlib/exit.c \
upstream-openbsd/lib/libc/stdlib/getenv.c \
upstream-openbsd/lib/libc/stdlib/lsearch.c \
upstream-openbsd/lib/libc/stdlib/setenv.c \
upstream-openbsd/lib/libc/stdlib/strtoimax.c \
upstream-openbsd/lib/libc/stdlib/strtol.c \
@ -477,6 +476,8 @@ libc_upstream_openbsd_src_files := \
upstream-openbsd/lib/libc/stdlib/strtoull.c \
upstream-openbsd/lib/libc/stdlib/strtoumax.c \
upstream-openbsd/lib/libc/stdlib/system.c \
upstream-openbsd/lib/libc/stdlib/tfind.c \
upstream-openbsd/lib/libc/stdlib/tsearch.c \
upstream-openbsd/lib/libc/string/strcasecmp.c \
upstream-openbsd/lib/libc/string/strcspn.c \
upstream-openbsd/lib/libc/string/strdup.c \

View File

@ -55,6 +55,34 @@
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------
Copyright (c) 1993 John Brezak
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------
====================================================

View File

@ -19,7 +19,7 @@
#include <stdlib.h>
// Destroy a tree and free all allocated resources.
// This is a GNU extension, not available from NetBSD.
// This is a GNU extension, not available from BSD.
void tdestroy(void* root, void (*destroy_func)(void*)) {
node_t* root_node = (node_t*) root;
if (root_node == NULL) {

View File

@ -29,6 +29,9 @@ typedef struct node {
__BEGIN_DECLS
void insque(void*, void*);
void remque(void*);
void* lfind(const void*, const void*, size_t*, size_t, int (*)(const void*, const void*));
void* lsearch(const void*, void*, size_t*, size_t, int (*)(const void*, const void*));

View File

@ -0,0 +1,58 @@
/*
* Copyright (c) 1993 John Brezak
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
__RCSID("$NetBSD: insque.c,v 1.3 2012/06/25 22:32:45 abs Exp $");
#endif /* LIBC_SCCS and not lint */
#include <assert.h>
#include <search.h>
struct qelem {
struct qelem *q_forw;
struct qelem *q_back;
};
void
insque(void *entry, void *pred)
{
struct qelem *e = (struct qelem *) entry;
struct qelem *p = (struct qelem *) pred;
_DIAGASSERT(e != 0);
e->q_back = p;
if (p) {
e->q_forw = p->q_forw;
if (p->q_forw)
p->q_forw->q_back = e;
p->q_forw = e;
} else
e->q_forw = 0;
}

View File

@ -0,0 +1,53 @@
/*
* Copyright (c) 1993 John Brezak
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
__RCSID("$NetBSD: remque.c,v 1.3 2012/06/25 22:32:45 abs Exp $");
#endif /* LIBC_SCCS and not lint */
#include <assert.h>
#include <search.h>
struct qelem {
struct qelem *q_forw;
struct qelem *q_back;
};
void
remque(void *element)
{
struct qelem *e = (struct qelem *) element;
_DIAGASSERT(e != 0);
if (e->q_forw)
e->q_forw->q_back = e->q_back;
if (e->q_back)
e->q_back->q_forw = e->q_forw;
}

View File

@ -1,67 +0,0 @@
/* $NetBSD: tdelete.c,v 1.6 2012/06/25 22:32:45 abs Exp $ */
/*
* Tree search generalized from Knuth (6.2.2) Algorithm T just like
* the AT&T man page says.
*
* The node_t structure is for internal use only, lint doesn't grok it.
*
* Written by reading the System V Interface Definition, not the code.
*
* Totally public domain.
*/
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
__RCSID("$NetBSD: tdelete.c,v 1.6 2012/06/25 22:32:45 abs Exp $");
#endif /* LIBC_SCCS and not lint */
#include <assert.h>
#define _SEARCH_PRIVATE
#include <search.h>
#include <stdlib.h>
/* find a node with key "vkey" in tree "vrootp" */
void *
tdelete(const void *vkey, void **vrootp,
int (*compar)(const void *, const void *))
{
node_t **rootp = (node_t **)vrootp;
node_t *p, *q, *r;
int cmp;
_DIAGASSERT(vkey != NULL);
_DIAGASSERT(compar != NULL);
if (rootp == NULL || (p = *rootp) == NULL)
return NULL;
while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0) {
p = *rootp;
rootp = (cmp < 0) ?
&(*rootp)->llink : /* follow llink branch */
&(*rootp)->rlink; /* follow rlink branch */
if (*rootp == NULL)
return NULL; /* key not found */
}
r = (*rootp)->rlink; /* D1: */
if ((q = (*rootp)->llink) == NULL) /* Left NULL? */
q = r;
else if (r != NULL) { /* Right link is NULL? */
if (r->llink == NULL) { /* D2: Find successor */
r->llink = q;
q = r;
} else { /* D3: Find NULL link */
for (q = r->llink; q->llink != NULL; q = r->llink)
r = q;
r->llink = q->rlink;
q->llink = (*rootp)->llink;
q->rlink = (*rootp)->rlink;
}
}
if (p != *rootp)
free(*rootp); /* D4: Free node */
*rootp = q; /* link parent to new node */
return p;
}

View File

@ -1,47 +0,0 @@
/* $NetBSD: tfind.c,v 1.7 2012/06/25 22:32:45 abs Exp $ */
/*
* Tree search generalized from Knuth (6.2.2) Algorithm T just like
* the AT&T man page says.
*
* The node_t structure is for internal use only, lint doesn't grok it.
*
* Written by reading the System V Interface Definition, not the code.
*
* Totally public domain.
*/
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
__RCSID("$NetBSD: tfind.c,v 1.7 2012/06/25 22:32:45 abs Exp $");
#endif /* LIBC_SCCS and not lint */
#include <assert.h>
#define _SEARCH_PRIVATE
#include <stdlib.h>
#include <search.h>
/* find a node by key "vkey" in tree "vrootp", or return 0 */
void *
tfind(const void *vkey, void * const *vrootp,
int (*compar)(const void *, const void *))
{
node_t * const *rootp = (node_t * const*)vrootp;
_DIAGASSERT(vkey != NULL);
_DIAGASSERT(compar != NULL);
if (rootp == NULL)
return NULL;
while (*rootp != NULL) { /* T1: */
int r;
if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
return *rootp; /* key found */
rootp = (r < 0) ?
&(*rootp)->llink : /* T3: follow left branch */
&(*rootp)->rlink; /* T4: follow right branch */
}
return NULL;
}

View File

@ -1,56 +0,0 @@
/* $NetBSD: tsearch.c,v 1.7 2012/06/25 22:32:45 abs Exp $ */
/*
* Tree search generalized from Knuth (6.2.2) Algorithm T just like
* the AT&T man page says.
*
* The node_t structure is for internal use only, lint doesn't grok it.
*
* Written by reading the System V Interface Definition, not the code.
*
* Totally public domain.
*/
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
__RCSID("$NetBSD: tsearch.c,v 1.7 2012/06/25 22:32:45 abs Exp $");
#endif /* LIBC_SCCS and not lint */
#include <assert.h>
#define _SEARCH_PRIVATE
#include <search.h>
#include <stdlib.h>
/* find or insert datum into search tree */
void *
tsearch(const void *vkey, void **vrootp,
int (*compar)(const void *, const void *))
{
node_t *q;
node_t **rootp = (node_t **)vrootp;
_DIAGASSERT(vkey != NULL);
_DIAGASSERT(compar != NULL);
if (rootp == NULL)
return NULL;
while (*rootp != NULL) { /* Knuth's T1: */
int r;
if ((r = (*compar)(vkey, (*rootp)->key)) == 0) /* T2: */
return *rootp; /* we found it! */
rootp = (r < 0) ?
&(*rootp)->llink : /* T3: follow left branch */
&(*rootp)->rlink; /* T4: follow right branch */
}
q = malloc(sizeof(node_t)); /* T5: key not found */
if (q != 0) { /* make new node */
*rootp = q; /* link new node to old */
q->key = __UNCONST(vkey); /* initialize new node */
q->llink = q->rlink = NULL;
}
return q;
}

View File

@ -1,3 +1,5 @@
/* $OpenBSD: lsearch.c,v 1.5 2014/07/18 04:16:09 matthew Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
@ -30,64 +32,39 @@
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#if defined(LIBC_SCCS) && !defined(lint)
#if 0
static char sccsid[] = "@(#)lsearch.c 8.1 (Berkeley) 6/4/93";
#else
__RCSID("$NetBSD: lsearch.c,v 1.7 2012/06/25 22:32:45 abs Exp $");
#endif
#endif /* LIBC_SCCS and not lint */
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <search.h>
typedef int (*cmp_fn_t)(const void *, const void *);
static void *linear_base(const void *, void *, size_t *, size_t,
cmp_fn_t, int);
static void *linear_base(const void *, const void *, size_t *, size_t,
cmp_fn_t, int);
void *
lsearch(const void *key, void *base, size_t *nelp, size_t width,
cmp_fn_t compar)
cmp_fn_t compar)
{
_DIAGASSERT(key != NULL);
_DIAGASSERT(base != NULL);
_DIAGASSERT(compar != NULL);
return(linear_base(key, base, nelp, width, compar, 1));
}
void *
lfind(const void *key, const void *base, size_t *nelp, size_t width,
cmp_fn_t compar)
cmp_fn_t compar)
{
_DIAGASSERT(key != NULL);
_DIAGASSERT(base != NULL);
_DIAGASSERT(compar != NULL);
return(linear_base(key, __UNCONST(base), nelp, width, compar, 0));
return(linear_base(key, base, nelp, width, compar, 0));
}
static void *
linear_base(const void *key, void *base, size_t *nelp, size_t width,
linear_base(const void *key, const void *base, size_t *nelp, size_t width,
cmp_fn_t compar, int add_flag)
{
char *element, *end;
const char *element, *end;
_DIAGASSERT(key != NULL);
_DIAGASSERT(base != NULL);
_DIAGASSERT(compar != NULL);
end = (char *)base + *nelp * width;
for (element = (char *)base; element < end; element += width)
if (!compar(element, key)) /* key found */
return element;
end = (const char *)base + *nelp * width;
for (element = base; element < end; element += width)
if (!compar(key, element)) /* key found */
return((void *)element);
if (!add_flag) /* key not found */
return(NULL);
@ -102,6 +79,6 @@ linear_base(const void *key, void *base, size_t *nelp, size_t width,
* manual.
*/
++*nelp;
memcpy(end, key, width);
return end;
memcpy((void *)end, key, width);
return((void *)end);
}

View File

@ -0,0 +1,41 @@
/* $OpenBSD: tfind.c,v 1.6 2014/03/16 18:38:30 guenther Exp $ */
/*
* Tree search generalized from Knuth (6.2.2) Algorithm T just like
* the AT&T man page says.
*
* The node_t structure is for internal use only
*
* Written by reading the System V Interface Definition, not the code.
*
* Totally public domain.
*/
/*LINTLIBRARY*/
#include <search.h>
typedef struct node_t
{
char *key;
struct node_t *llink, *rlink;
} node;
/* find a node, or return 0 */
void *
tfind(const void *vkey, void * const *vrootp,
int (*compar)(const void *, const void *))
{
char *key = (char *)vkey;
node **rootp = (node **)vrootp;
if (rootp == (struct node_t **)0)
return ((struct node_t *)0);
while (*rootp != (struct node_t *)0) { /* T1: */
int r;
if ((r = (*compar)(key, (*rootp)->key)) == 0) /* T2: */
return (*rootp); /* key found */
rootp = (r < 0) ?
&(*rootp)->llink : /* T3: follow left branch */
&(*rootp)->rlink; /* T4: follow right branch */
}
return (node *)0;
}

View File

@ -0,0 +1,119 @@
/* $OpenBSD: tsearch.c,v 1.8 2014/03/16 18:38:30 guenther Exp $ */
/*
* Tree search generalized from Knuth (6.2.2) Algorithm T just like
* the AT&T man page says.
*
* The node_t structure is for internal use only
*
* Written by reading the System V Interface Definition, not the code.
*
* Totally public domain.
*/
/*LINTLIBRARY*/
#include <search.h>
#include <stdlib.h>
typedef struct node_t {
char *key;
struct node_t *left, *right;
} node;
/* find or insert datum into search tree */
void *
tsearch(const void *vkey, void **vrootp,
int (*compar)(const void *, const void *))
{
node *q;
char *key = (char *)vkey;
node **rootp = (node **)vrootp;
if (rootp == (struct node_t **)0)
return ((void *)0);
while (*rootp != (struct node_t *)0) { /* Knuth's T1: */
int r;
if ((r = (*compar)(key, (*rootp)->key)) == 0) /* T2: */
return ((void *)*rootp); /* we found it! */
rootp = (r < 0) ?
&(*rootp)->left : /* T3: follow left branch */
&(*rootp)->right; /* T4: follow right branch */
}
q = (node *) malloc(sizeof(node)); /* T5: key not found */
if (q != (struct node_t *)0) { /* make new node */
*rootp = q; /* link new node to old */
q->key = key; /* initialize new node */
q->left = q->right = (struct node_t *)0;
}
return ((void *)q);
}
/* delete node with given key */
void *
tdelete(const void *vkey, void **vrootp,
int (*compar)(const void *, const void *))
{
node **rootp = (node **)vrootp;
char *key = (char *)vkey;
node *p = (node *)1;
node *q;
node *r;
int cmp;
if (rootp == (struct node_t **)0 || *rootp == (struct node_t *)0)
return ((struct node_t *)0);
while ((cmp = (*compar)(key, (*rootp)->key)) != 0) {
p = *rootp;
rootp = (cmp < 0) ?
&(*rootp)->left : /* follow left branch */
&(*rootp)->right; /* follow right branch */
if (*rootp == (struct node_t *)0)
return ((void *)0); /* key not found */
}
r = (*rootp)->right; /* D1: */
if ((q = (*rootp)->left) == (struct node_t *)0) /* Left (struct node_t *)0? */
q = r;
else if (r != (struct node_t *)0) { /* Right link is null? */
if (r->left == (struct node_t *)0) { /* D2: Find successor */
r->left = q;
q = r;
} else { /* D3: Find (struct node_t *)0 link */
for (q = r->left; q->left != (struct node_t *)0; q = r->left)
r = q;
r->left = q->right;
q->left = (*rootp)->left;
q->right = (*rootp)->right;
}
}
free((struct node_t *) *rootp); /* D4: Free node */
*rootp = q; /* link parent to new node */
return(p);
}
/* Walk the nodes of a tree */
static void
trecurse(node *root, void (*action)(const void *, VISIT, int), int level)
{
if (root->left == (struct node_t *)0 && root->right == (struct node_t *)0)
(*action)(root, leaf, level);
else {
(*action)(root, preorder, level);
if (root->left != (struct node_t *)0)
trecurse(root->left, action, level + 1);
(*action)(root, postorder, level);
if (root->right != (struct node_t *)0)
trecurse(root->right, action, level + 1);
(*action)(root, endorder, level);
}
}
/* Walk the nodes of a tree */
void
twalk(const void *vroot, void (*action)(const void *, VISIT, int))
{
node *root = (node *)vroot;
if (root != (node *)0 && action != (void (*)(const void *, VISIT, int))0)
trecurse(root, action, 0);
}

View File

@ -85,6 +85,7 @@ libBionicStandardTests_src_files := \
pthread_test.cpp \
regex_test.cpp \
sched_test.cpp \
search_test.cpp \
signal_test.cpp \
stack_protector_test.cpp \
stack_unwinding_test.cpp \

210
tests/search_test.cpp Normal file
View File

@ -0,0 +1,210 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <search.h>
static int int_cmp(const void* lhs, const void* rhs) {
return *reinterpret_cast<const int*>(rhs) - *reinterpret_cast<const int*>(lhs);
}
TEST(search, lfind_lsearch) {
int xs[10];
memset(xs, 0, sizeof(xs));
size_t x_size = 0;
int needle;
// lfind(3) can't find '2' in the empty table.
needle = 2;
ASSERT_EQ(nullptr, lfind(&needle, xs, &x_size, sizeof(xs[0]), int_cmp));
ASSERT_EQ(0U, x_size);
// lsearch(3) will add it.
ASSERT_EQ(&xs[0], lsearch(&needle, xs, &x_size, sizeof(xs[0]), int_cmp));
ASSERT_EQ(2, xs[0]);
ASSERT_EQ(1U, x_size);
// And then lfind(3) can find it.
ASSERT_EQ(&xs[0], lfind(&needle, xs, &x_size, sizeof(xs[0]), int_cmp));
ASSERT_EQ(1U, x_size);
// Inserting a duplicate does nothing (but returns the existing element).
ASSERT_EQ(&xs[0], lsearch(&needle, xs, &x_size, sizeof(xs[0]), int_cmp));
ASSERT_EQ(1U, x_size);
}
struct node {
node(const char* s) : s(strdup(s)) {}
char* s;
};
static int node_cmp(const void* lhs, const void* rhs) {
return strcmp(reinterpret_cast<const node*>(lhs)->s, reinterpret_cast<const node*>(rhs)->s);
}
static std::vector<std::string> g_nodes;
static void node_walk(const void* p, VISIT order, int) {
const node* n = *reinterpret_cast<const node* const*>(p);
if (order == postorder || order == leaf) {
g_nodes.push_back(n->s);
}
}
static size_t g_free_calls;
static void node_free(void* p) {
node* n = reinterpret_cast<node*>(p);
free(n->s);
++g_free_calls;
}
TEST(search, tfind_tsearch_twalk_tdestroy) {
void* root = nullptr;
node n1("z");
node n2("a");
node n3("m");
// tfind(3) can't find anything in the empty tree.
ASSERT_EQ(nullptr, tfind(&n1, &root, node_cmp));
ASSERT_EQ(nullptr, tfind(&n2, &root, node_cmp));
ASSERT_EQ(nullptr, tfind(&n3, &root, node_cmp));
// tsearch(3) inserts and returns a pointer to a new node.
void* i1 = tsearch(&n1, &root, node_cmp);
ASSERT_NE(nullptr, i1);
// ...which tfind(3) will then return.
ASSERT_EQ(i1, tfind(&n1, &root, node_cmp));
ASSERT_EQ(nullptr, tfind(&n2, &root, node_cmp));
ASSERT_EQ(nullptr, tfind(&n3, &root, node_cmp));
// Add the other nodes.
ASSERT_NE(nullptr, tsearch(&n2, &root, node_cmp));
ASSERT_NE(nullptr, tsearch(&n3, &root, node_cmp));
// Use twalk(3) to iterate over the nodes.
g_nodes.clear();
twalk(root, node_walk);
ASSERT_EQ(3U, g_nodes.size());
ASSERT_EQ("a", g_nodes[0]);
ASSERT_EQ("m", g_nodes[1]);
ASSERT_EQ("z", g_nodes[2]);
// tdestroy(3) removes nodes under a node, calling our callback to destroy each one.
g_free_calls = 0;
tdestroy(root, node_free);
ASSERT_EQ(3U, g_free_calls);
}
struct pod_node {
pod_node(int i) : i(i) {}
int i;
};
static int pod_node_cmp(const void* lhs, const void* rhs) {
return reinterpret_cast<const pod_node*>(rhs)->i - reinterpret_cast<const pod_node*>(lhs)->i;
}
TEST(search, tdelete) {
void* root = nullptr;
pod_node n1(123);
ASSERT_NE(nullptr, tsearch(&n1, &root, pod_node_cmp));
// tdelete(3) leaks n1.
pod_node not_there(456);
ASSERT_EQ(nullptr, tdelete(&not_there, &root, pod_node_cmp));
ASSERT_NE(nullptr, tdelete(&n1, &root, pod_node_cmp));
}
struct q_node {
q_node(int i) : i(i) {}
q_node* next;
q_node* prev;
int i;
};
TEST(search, insque_remque) {
q_node zero(0);
q_node one(1);
q_node two(2);
// Linear (not circular).
insque(&zero, NULL);
insque(&one, &zero);
insque(&two, &one);
int expected = 0;
for (q_node* q = &zero; q != NULL; q = q->next) {
ASSERT_EQ(expected, q->i);
++expected;
}
ASSERT_EQ(3, expected);
for (q_node* q = &two; q != NULL; q = q->prev) {
--expected;
ASSERT_EQ(expected, q->i);
}
ASSERT_EQ(0, expected);
q_node* head = &zero;
remque(&one);
ASSERT_EQ(0, head->i);
ASSERT_EQ(2, head->next->i);
ASSERT_EQ(nullptr, head->next->next);
remque(&two);
ASSERT_EQ(0, head->i);
ASSERT_EQ(nullptr, head->next);
remque(&zero);
// Circular.
zero.next = &zero;
zero.prev = &zero;
insque(&one, &zero);
insque(&two, &one);
ASSERT_EQ(0, head->i);
ASSERT_EQ(1, head->next->i);
ASSERT_EQ(2, head->next->next->i);
ASSERT_EQ(0, head->next->next->next->i);
ASSERT_EQ(1, head->next->next->next->next->i);
ASSERT_EQ(2, head->next->next->next->next->next->i);
remque(&one);
ASSERT_EQ(0, head->i);
ASSERT_EQ(2, head->next->i);
ASSERT_EQ(0, head->next->next->i);
ASSERT_EQ(2, head->next->next->next->i);
remque(&two);
ASSERT_EQ(0, head->i);
ASSERT_EQ(0, head->next->i);
remque(&zero);
}