From c90a6442747381d460f853595f2361718c4ae371 Mon Sep 17 00:00:00 2001 From: millert <> Date: Thu, 24 Jun 2004 04:43:33 +0000 Subject: [PATCH] Working hcreate(3) et al from NetBSD (cgd) via ray at cyth dot net. Now passes the regress tests. --- src/lib/libc/stdlib/Makefile.inc | 11 +- src/lib/libc/stdlib/hcreate.3 | 195 ++++++++++++++++++++++++++++++ src/lib/libc/stdlib/hcreate.c | 200 +++++++++++++++++++++++++++++++ 3 files changed, 401 insertions(+), 5 deletions(-) create mode 100644 src/lib/libc/stdlib/hcreate.3 create mode 100644 src/lib/libc/stdlib/hcreate.c diff --git a/src/lib/libc/stdlib/Makefile.inc b/src/lib/libc/stdlib/Makefile.inc index 80d3c221..1d5ba5c9 100644 --- a/src/lib/libc/stdlib/Makefile.inc +++ b/src/lib/libc/stdlib/Makefile.inc @@ -5,10 +5,10 @@ SRCS+= a64l.c abort.c atexit.c atoi.c atof.c atol.c atoll.c bsearch.c \ calloc.c cfree.c exit.c ecvt.c gcvt.c getenv.c getopt_long.c \ - getsubopt.c heapsort.c l64a.c llabs.c lsearch.c malloc.c merge.c \ - multibyte.c putenv.c qsort.c radixsort.c rand.c random.c realpath.c \ - setenv.c strtod.c strtol.c strtoll.c strtonum.c strtoul.c strtoull.c \ - system.c \ + getsubopt.c hcreate.c heapsort.c l64a.c llabs.c lsearch.c malloc.c \ + merge.c multibyte.c putenv.c qsort.c radixsort.c rand.c random.c \ + realpath.c setenv.c strtod.c strtol.c strtoll.c strtonum.c strtoul.c \ + strtoull.c system.c \ tfind.c tsearch.c _rand48.c drand48.c erand48.c jrand48.c lcong48.c \ lrand48.c mrand48.c nrand48.c seed48.c srand48.c qabs.c qdiv.c _Exit.c @@ -41,7 +41,7 @@ SRCS+= insque.c remque.c MAN+= a64l.3 abort.3 abs.3 alloca.3 atexit.3 atof.3 atoi.3 atol.3 atoll.3 \ bsearch.3 div.3 ecvt.3 exit.3 getenv.3 getopt.3 getopt_long.3 \ - getsubopt.3 insque.3 labs.3 ldiv.3 lsearch.3 malloc.3 qabs.3 \ + getsubopt.3 hcreate.3 insque.3 labs.3 ldiv.3 lsearch.3 malloc.3 qabs.3 \ qdiv.3 qsort.3 radixsort.3 rand48.3 rand.3 random.3 realpath.3 \ strtod.3 strtonum.3 strtol.3 strtoul.3 system.3 tsearch.3 @@ -49,6 +49,7 @@ MLINKS+=exit.3 _Exit.3 MLINKS+=ecvt.3 fcvt.3 ecvt.3 gcvt.3 MLINKS+=getenv.3 setenv.3 getenv.3 unsetenv.3 getenv.3 putenv.3 MLINKS+=getopt_long.3 getopt_long_only.3 +MLINKS+=hcreate.3 hdestroy.3 hcreate.3 hsearch.3 MLINKS+=insque.3 remque.3 MLINKS+=labs.3 llabs.3 MLINKS+=lsearch.3 lfind.3 diff --git a/src/lib/libc/stdlib/hcreate.3 b/src/lib/libc/stdlib/hcreate.3 new file mode 100644 index 00000000..d1d4e5c1 --- /dev/null +++ b/src/lib/libc/stdlib/hcreate.3 @@ -0,0 +1,195 @@ +.\" $OpenBSD: hcreate.3,v 1.1 2004/06/24 04:43:33 millert Exp $ +.\" $NetBSD: hcreate.3,v 1.6 2003/04/16 13:34:46 wiz Exp $ +.\" +.\" Copyright (c) 1999 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Klaus Klein. +.\" +.\" 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the NetBSD +.\" Foundation, Inc. and its contributors. +.\" 4. Neither the name of The NetBSD Foundation nor the names of its +.\" contributors may be used to endorse or promote products derived +.\" from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``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 FOUNDATION OR CONTRIBUTORS +.\" 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. +.\" +.Dd February 13, 2001 +.Dt HCREATE 3 +.Os +.Sh NAME +.Nm hcreate , +.Nm hdestroy , +.Nm hsearch +.Nd manage hash search table +.Sh SYNOPSIS +.In search.h +.Ft int +.Fn hcreate "size_t nel" +.Ft void +.Fn hdestroy "void" +.Ft ENTRY * +.Fn hsearch "ENTRY item" "ACTION action" +.Sh DESCRIPTION +The +.Fn hcreate , +.Fn hdestroy +and +.Fn hsearch +functions manage hash search tables. +.Pp +The +.Fn hcreate +function allocates and initializes the table. +The +.Fa nel +argument specifies an estimate of the maximum number of entries to be held +by the table. +Unless further memory allocation fails, supplying an insufficient +.Fa nel +value will not result in functional harm, although a performance degradation +may occur. +Initialization using the +.Fn hcreate +function is mandatory prior to any access operations using +.Fn hsearch . +.Pp +The +.Fn hdestroy +function destroys a table previously created using +.Fn hcreate . +After a call to +.Fn hdestroy , +the data can no longer be accessed. +.Pp +The +.Fn hsearch +function is used to search to the hash table. +It returns a pointer into the +hash table indicating the address of an item. +The +.Fa item +argument is of type +.Dv ENTRY , +a structural type which contains the following members: +.Bl -tag -compact -offset indent -width voidX*dataXX +.It Fa char *key +comparison key. +.It Fa void *data +pointer to data associated with +.Fa key . +.El +.Pp +The key comparison function used by +.Fn hsearch +is +.Xr strcmp 3 . +.Pp +The +.Fa action +argument is of type +.Dv ACTION , +an enumeration type which defines the following values: +.Bl -tag -compact -offset indent -width ENTERXX +.It Dv ENTER +Insert +.Fa item +into the hash table. +If an existing item with the same key is found, it is not replaced. +Note that the +.Fa key +and +.Fa data +elements of +.Fa item +are used directly by the new table entry. +The storage for the +key must not be modified during the lifetime of the hash table. +.It Dv FIND +Search the hash table without inserting +.Fa item . +.El +.Sh RETURN VALUES +If successful, the +.Fn hcreate +function returns a non-zero value. +Otherwise, a value of 0 is returned and +.Va errno +is set to indicate the error. +.Pp +The +.Fn hdestroy +functions +returns no value. +.Pp +If successful, the +.Fn hsearch +function returns a pointer to hash table entry matching +the provided key. +If the action is +.Dv FIND +and the item was not found, or if the action is +.Dv ENTER +and the insertion failed, +.Dv NULL +is returned and +.Va errno +is set to indicate the error. +If the action is +.Dv ENTER +and an entry already existed in the table matching the given +key, the existing entry is returned and is not replaced. +.Sh ERRORS +The +.Fn hcreate +and +.Fn hsearch +functions will fail if: +.Bl -tag -width Er +.It Bq Er ENOMEM +Insufficient memory is available. +.El +.Sh SEE ALSO +.Xr bsearch 3 , +.Xr lsearch 3 , +.Xr malloc 3 , +.Xr strcmp 3 +.Sh STANDARDS +The +.Fn hcreate , +.Fn hdestroy +and +.Fn hsearch +functions conform to +.St -xpg4.2 . +.Sh HISTORY +The +.Fn hcreate , +.Fn hdestroy +and +.Fn hsearch +functions first appeared in +.At V . +.Sh BUGS +The interface permits the use of only one hash table at a time. diff --git a/src/lib/libc/stdlib/hcreate.c b/src/lib/libc/stdlib/hcreate.c new file mode 100644 index 00000000..14e6fa41 --- /dev/null +++ b/src/lib/libc/stdlib/hcreate.c @@ -0,0 +1,200 @@ +/* $OpenBSD: hcreate.c,v 1.1 2004/06/24 04:43:33 millert Exp $ */ +/* $NetBSD: hcreate.c,v 1.5 2004/04/23 02:48:12 simonb Exp $ */ + +/* + * Copyright (c) 2001 Christopher G. Demetriou + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the + * NetBSD Project. See http://www.NetBSD.org/ for + * information about NetBSD. + * 4. The name of the author may not 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. + * + * <> + */ + +/* + * hcreate() / hsearch() / hdestroy() + * + * SysV/XPG4 hash table functions. + * + * Implementation done based on NetBSD manual page and Solaris manual page, + * plus my own personal experience about how they're supposed to work. + * + * I tried to look at Knuth (as cited by the Solaris manual page), but + * nobody had a copy in the office, so... + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 2001 Christopher G. Demetriou. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static const char rcsid[] = "$OpenBSD: hcreate.c,v 1.1 2004/06/24 04:43:33 millert Exp $"; +#endif /* not lint */ + +#include "namespace.h" +#include +#include +#include +#include +#include +#include +#include + +#ifndef _DIAGASSERT +#define _DIAGASSERT +#endif + +/* + * DO NOT MAKE THIS STRUCTURE LARGER THAN 32 BYTES (4 ptrs on 64-bit + * ptr machine) without adjusting MAX_BUCKETS_LG2 below. + */ +struct internal_entry { + SLIST_ENTRY(internal_entry) link; + ENTRY ent; +}; +SLIST_HEAD(internal_head, internal_entry); + +#define MIN_BUCKETS_LG2 4 +#define MIN_BUCKETS (1 << MIN_BUCKETS_LG2) + +/* + * max * sizeof internal_entry must fit into size_t. + * assumes internal_entry is <= 32 (2^5) bytes. + */ +#define MAX_BUCKETS_LG2 (sizeof (size_t) * 8 - 1 - 5) +#define MAX_BUCKETS ((size_t)1 << MAX_BUCKETS_LG2) + +/* Default hash function, from db/hash/hash_func.c */ +extern u_int32_t (*__default_hash)(const void *, size_t); + +static struct internal_head *htable; +static size_t htablesize; + +int +hcreate(size_t nel) +{ + size_t idx; + unsigned int p2; + + /* Make sure this isn't called when a table already exists. */ + _DIAGASSERT(htable == NULL); + if (htable != NULL) { + errno = EINVAL; + return 0; + } + + /* If nel is too small, make it min sized. */ + if (nel < MIN_BUCKETS) + nel = MIN_BUCKETS; + + /* If it's too large, cap it. */ + if (nel > MAX_BUCKETS) + nel = MAX_BUCKETS; + + /* If it's is not a power of two in size, round up. */ + if ((nel & (nel - 1)) != 0) { + for (p2 = 0; nel != 0; p2++) + nel >>= 1; + _DIAGASSERT(p2 <= MAX_BUCKETS_LG2); + nel = 1 << p2; + } + + /* Allocate the table. */ + htablesize = nel; + htable = malloc(htablesize * sizeof htable[0]); + if (htable == NULL) { + errno = ENOMEM; + return 0; + } + + /* Initialize it. */ + for (idx = 0; idx < htablesize; idx++) + SLIST_INIT(&htable[idx]); + + return 1; +} + +void +hdestroy(void) +{ + struct internal_entry *ie; + size_t idx; + + _DIAGASSERT(htable != NULL); + if (htable == NULL) + return; + + for (idx = 0; idx < htablesize; idx++) { + while (!SLIST_EMPTY(&htable[idx])) { + ie = SLIST_FIRST(&htable[idx]); + SLIST_REMOVE_HEAD(&htable[idx], link); + free(ie->ent.key); + free(ie); + } + } + free(htable); + htable = NULL; +} + +ENTRY * +hsearch(ENTRY item, ACTION action) +{ + struct internal_head *head; + struct internal_entry *ie; + uint32_t hashval; + size_t len; + + _DIAGASSERT(htable != NULL); + _DIAGASSERT(item.key != NULL); + _DIAGASSERT(action == ENTER || action == FIND); + + len = strlen(item.key); + hashval = (*__default_hash)(item.key, len); + + head = &htable[hashval & (htablesize - 1)]; + ie = SLIST_FIRST(head); + while (ie != NULL) { + if (strcmp(ie->ent.key, item.key) == 0) + break; + ie = SLIST_NEXT(ie, link); + } + + if (ie != NULL) + return &ie->ent; + else if (action == FIND) + return NULL; + + ie = malloc(sizeof *ie); + if (ie == NULL) + return NULL; + ie->ent.key = item.key; + ie->ent.data = item.data; + + SLIST_INSERT_HEAD(head, ie, link); + return &ie->ent; +}