Home | History | Annotate | Download | only in libnisdb
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  *	db_index_entry.cc
     24  *
     25  *	Copyright (c) 1988-2000 by Sun Microsystems, Inc.
     26  *	All Rights Reserved.
     27  */
     28 
     29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     30 
     31 #include <stdio.h>
     32 
     33 #include "db_headers.h"
     34 #include "db_index_entry.h"
     35 #include "nisdb_mt.h"
     36 
     37 /* Constructor:  create an entry using given string and location info. */
     38 db_index_entry::db_index_entry(char* name, int nlen, entryp ep)
     39 {
     40 	if ((key = new item(name, nlen)) == NULL)
     41 		FATAL("db_index_entry::db_index_entry: cannot allocate space",
     42 			DB_MEMORY_LIMIT);
     43 	location = ep;
     44 	next_result = next = NULL;
     45 	/* what about hashval ? */
     46 }
     47 
     48 /*
     49  * Constructor:  create an entry using the given info.
     50  * A copy of the item is made.  New entry is added to head of list of 'n'.
     51 */
     52 db_index_entry::db_index_entry(unsigned long hval, item* k,
     53 				entryp ep, db_index_entry_p rest)
     54 {
     55 	if ((key = new item(k)) == NULL)
     56 		FATAL(
     57 		"db_index_entry::db_index_entry: cannot allocate space (2)",
     58 		DB_MEMORY_LIMIT);
     59 	location = ep;
     60 	next = rest;
     61 	next_result = NULL;
     62 	hashval = hval;
     63 }
     64 
     65 /*
     66  * Join two lists (entry as identified by its 'location' occurs on both list,
     67  * then it is included in the list returned).
     68  * Returns pointer to resulting list; size of list
     69  * returned in 'newsize'.  List is chained using the 'nextresult' pointer.
     70  */
     71 db_index_entry_p
     72 db_index_entry::join(long /* size1 */, long /* size2 */,
     73 	db_index_entry_p list2, long * newsize)
     74 {
     75 	db_index_entry_p mergedlist = NULL, // records that occur on both lists
     76 		mergedtail = NULL, 	// tail pointer of mergedlist
     77 		current,		// current pointer of this list
     78 		other,			// current pointer of updated list2
     79 		otherprev,		// previous pointer of updated list2
     80 		otherstart = list2;	// head of updated list2
     81 	int count = 0;
     82 
     83 	/*
     84 	 * algorithm is straightforward:
     85 	 * traverse this list,
     86 	 * for each item, traverse list2,
     87 	 * if item on list1 matches item on list2,
     88 	 * add to merged list and delete it from list2.
     89 	 */
     90 
     91 	for (current = this; (current != NULL) && (otherstart != NULL);
     92 					current = current->next_result) {
     93 		/* find 'current' in 'other' list */
     94 		otherprev = NULL;
     95 		for (other = otherstart;
     96 			other != NULL;
     97 			other = other->next_result) {
     98 			if (current->location == other->location)
     99 				break;
    100 			else
    101 				otherprev = other;
    102 		}
    103 		if (other != NULL) { /* found */
    104 			/* delete 'other' from future consideration */
    105 			if (otherprev == NULL) {
    106 				/* new head */
    107 				otherstart = otherstart->next_result;
    108 			} else {
    109 				/* bypass 'other' */
    110 				otherprev->next_result = other->next_result;
    111 			}
    112 			/* add 'current' to list of items found so far */
    113 			if (mergedlist == NULL)
    114 				mergedlist = current;	/* first one found */
    115 			else
    116 				mergedtail->next_result = current; /* append */
    117 			mergedtail = current; /* point to last entry found */
    118 			++count;
    119 		}
    120 	}
    121 	if (mergedtail) mergedtail->next_result = NULL;  /* set end to null */
    122 	*newsize = count;
    123 	return (mergedlist);
    124 }
    125 
    126 /* Relocate bucket starting with this entry to new hashtable 'new_tab'. */
    127 void
    128 db_index_entry::relocate(db_index_entry_p *new_tab, unsigned long hashsize)
    129 {
    130 	db_index_entry_p np, next_np, *hp;
    131 
    132 	for (np = this; np != NULL; np = next_np) {
    133 		next_np = np->next;
    134 		hp = &new_tab[np->hashval % hashsize];
    135 		np->next = *hp;
    136 		*hp = np;
    137 	}
    138 }
    139 
    140 /* Return the next entry in the bucket starting with this entry
    141 	    with the same hashvalue, key and location as this entry. */
    142 db_index_entry_p
    143 db_index_entry::getnext(bool_t casein, unsigned long hval, item *i, entryp l)
    144 {
    145 	db_index_entry_p np;
    146 
    147 	for (np = this; np != NULL; np = np->next) {
    148 		if ((np->hashval == hval) &&
    149 	(np->key->equal(i, casein)) && l == location) {
    150 			break;
    151 		}
    152 	}
    153 
    154 	if (np != NULL)
    155 		return (np->next);
    156 	else
    157 		return (NULL);
    158 }
    159 
    160 /*
    161  * Return pointer to index entry with same hash value, same key,
    162  * and same record number as those supplied.  Returns NULL if not found.
    163  */
    164 db_index_entry_p
    165 db_index_entry::lookup(bool_t casein, unsigned long hval,
    166 			item *i, entryp recnum)
    167 {
    168 	db_index_entry_p np;
    169 
    170 	for (np = this; np != NULL; np = np->next) {
    171 		if (np->hashval == hval && np->key->equal(i, casein) &&
    172 			np->location == recnum) {
    173 			break;
    174 		}
    175 	}
    176 	if (np) np->next_result = NULL;  /* should only be 1 */
    177 	return (np);
    178 }
    179 
    180 /*
    181  * Returns pointer to a list of index entries with the same hash value and
    182  * key as those given.  Returns in 'how_many' the number of entries in the
    183  * list returned.  The list is linked by the 'next_result' field of the
    184  * index entries.  These may be changed after the next call to 'lookup'
    185  * or 'join'.
    186  */
    187 db_index_entry_p
    188 db_index_entry::lookup(bool_t casein, unsigned long hval,
    189 			item *i, long * how_many)
    190 {
    191 	db_index_entry_p fst, prev, curr;
    192 	long count = 0;
    193 
    194 	for (fst = this; fst != NULL; fst = fst->next) {
    195 		if ((fst->hashval == hval) && (fst->key->equal(i, casein))) {
    196 			++count;
    197 			break;
    198 		}
    199 	}
    200 	/*
    201 	 * gather all the ones with the same key; assume that all entries
    202 	 * with same key are located contiguously.
    203 	 */
    204 	if (fst != NULL) {
    205 		prev = fst;
    206 		for (curr = fst->next; curr != NULL; curr = curr->next) {
    207 			if ((curr->hashval == hval) &&
    208 				(curr->key->equal(i, casein))) {
    209 	prev->addresult(curr);
    210 	prev = curr;
    211 	++count;
    212 			}
    213 			else
    214 	break;
    215 		}
    216 		prev->addresult(NULL); /* terminate the list -CM */
    217 	}
    218 	*how_many = count;
    219 	return (fst);
    220 }
    221 
    222 /*
    223  * Remove entry with the specified hashvalue, key, and record number.
    224  * Returns 'TRUE' if successful, FALSE otherwise.
    225  * If the entry being removed is at the head of the list, then
    226  * the head is updated to reflect the removal. The storage for the index
    227  * entry is freed. The record pointed to by 'recnum' must be removed
    228  * through another means.  All that is updated in this operation is the
    229  * index.
    230  */
    231 bool_t
    232 db_index_entry::remove(db_index_entry_p *head, bool_t casein,
    233 			unsigned long hval, item *i, entryp recnum)
    234 {
    235 	db_index_entry_p np, dp;
    236 
    237 	/* Search for it in the bucket */
    238 	for (dp = np = this; np != NULL; np = np->next) {
    239 		if (np->hashval == hval && np->key->equal(i, casein) &&
    240 			np->location == recnum) {
    241 			break;
    242 		} else {
    243 			dp = np;
    244 		}
    245 	}
    246 
    247 	if (np == NULL) return FALSE;	// cannot delete if it is not there
    248 
    249 	if (dp == np) {
    250 		*head = np->next;	// deleting head of bucket
    251 	} else {
    252 		dp->next = np->next;	// deleting interior link
    253 		}
    254 	delete np;
    255 
    256 	return (TRUE);
    257 }
    258 
    259 /* Replace the 'location' field of the index entry with the given one. */
    260 /*
    261 void
    262 db_index_entry::replace(entryp ep)
    263 {
    264 	location = ep;
    265 }
    266 */
    267 
    268 /*
    269  * Create and add an entry with the given hashvalue, key value, and record
    270  * location, to the bucket pointed to by 'hashvalue'.
    271  * If an entry with the same hashvalue and key value is found,
    272  * the entry is added after the first entry with this property.  Otherwise,
    273  * the entry is added to the head of the bucket.  This way, entries
    274  * with the same hashvalue and key are not scattered throughout the bucket
    275  * but they occur together. Copy is made of given key.
    276  */
    277 bool_t
    278 db_index_entry::add(db_index_entry **head, bool_t casein,
    279 			unsigned long hval, item *i, entryp recnum)
    280 
    281 {
    282 	db_index_entry_p curr, prev, rp, save;
    283 
    284 	/* Search for it in the bucket */
    285 	for (prev = curr = this; curr != NULL; curr = curr->next) {
    286 		if (curr->hashval == hval && curr->key->equal(i, casein)) {
    287 			break;
    288 		} else {
    289 			prev = curr;
    290 		}
    291 	}
    292 
    293 
    294 
    295 	if (curr == NULL) {
    296 		/* none with same hashvalue/key found. Add to head of list. */
    297 		save = *head;
    298 		*head = new db_index_entry(hval, i, recnum, * head);
    299 		if (*head == NULL) {
    300 			*head = save;	// restore previous state
    301 			FATAL3(
    302 			"db_index_entry::add: cannot allocate space for head",
    303 			DB_MEMORY_LIMIT, FALSE);
    304 		}
    305 	} else {
    306 		/* Found same hashvalue/key.  Add entry after that one. */
    307 		save = prev->next;
    308 		prev->next = new db_index_entry(hval, i, recnum, prev->next);
    309 		if (prev->next == NULL) {
    310 			prev->next = save; // restore previous state
    311 			FATAL3(
    312 			"db_index_entry::add: cannot allocate space for entry",
    313 			DB_MEMORY_LIMIT, FALSE);
    314 		}
    315 	}
    316 
    317 	return (TRUE);
    318 }
    319 
    320 /* Print this entry to stdout. */
    321 void
    322 db_index_entry::print()
    323 {
    324 	if (key != NULL) {
    325 			key->print();
    326 			printf("\t");
    327 		}
    328 	printf(": %d\n", location);
    329 }
    330 
    331 /* Print bucket starting with this entry. */
    332 void
    333 db_index_entry::print_all()
    334 {
    335 	db_index_entry *np;
    336 	for (np = this; np != NULL; np = np->next) {
    337 		np->print();
    338 		}
    339 }
    340 
    341 /* Print result list starting with this entry. */
    342 void
    343 db_index_entry::print_results()
    344 {
    345 	db_index_entry *np;
    346 	for (np = this; np != NULL; np = np->next_result) {
    347 		np->print();
    348 		}
    349 }
    350