2 * Copyright 2000, International Business Machines Corporation and others.
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
10 #include <afsconfig.h>
11 #include <afs/param.h>
17 #include <netinet/in.h>
20 #include <sys/types.h>
23 #include <afs/bubasics.h>
24 #include "budb_errs.h"
26 #include "budb_internal.h"
27 #include "error_macros.h"
30 int sizeFunctions
[HT_MAX_FUNCTION
+ 1];
31 int nHTBuckets
= NhtBucketS
; /* testing: we need small HT blocks */
33 int ht_minHBlocks(struct memoryHashTable
*mht
);
35 /* ht_TableSize - return the size of table necessary to represent a hashtable
36 * of given length in memory. It basically rounds the length up by the number
37 * of buckets per block. */
40 ht_TableSize(int length
)
45 n
= (length
+ nHTBuckets
- 1) / nHTBuckets
;
46 return n
* sizeof(struct memoryHTBlock
*);
49 /* ht_ResetT - resets the in-memory representation of a hashtable block array.
50 * It also resets the global variable nHTBuckets. */
53 ht_ResetT(struct memoryHTBlock
***blocksP
, int *sizeP
, int length
)
55 struct memoryHTBlock
**b
= *blocksP
;
60 nHTBuckets
= ntohl(db
.h
.nHTBuckets
);
62 n
= *sizeP
/ sizeof(b
[0]);
63 newsize
= ht_TableSize(length
);
64 if (*sizeP
!= newsize
) {
65 /* free all blocks in the old array */
66 for (i
= 0; i
< n
; i
++)
73 /* invalidate the blocks of the array */
74 for (i
= 0; i
< n
; i
++)
82 * reinitialize a memory hash table.
83 * Calls ht_ResetT to invalidate the two block arrays.
87 ht_Reset(struct memoryHashTable
*mht
)
89 struct hashTable
*ht
= NULL
;
91 if (!(mht
&& (ht
= mht
->ht
)))
92 db_panic("some ht called with bad mht");
93 mht
->threadOffset
= ntohl(ht
->threadOffset
);
94 mht
->length
= ntohl(ht
->length
);
95 mht
->oldLength
= ntohl(ht
->oldLength
);
96 mht
->progress
= ntohl(ht
->progress
);
97 ht_ResetT(&mht
->blocks
, &mht
->size
, mht
->length
);
98 ht_ResetT(&mht
->oldBlocks
, &mht
->oldSize
, mht
->oldLength
);
101 /* InitDBhash - When server starts, do hash table initialization.
102 test - initialization parameters: bit 4 is small ht. */
107 sizeFunctions
[0] = 0;
109 sizeFunctions
[HT_dumpIden_FUNCTION
] = sizeof(struct dump
);
110 sizeFunctions
[HT_dumpName_FUNCTION
] = sizeof(struct dump
);
111 sizeFunctions
[HT_volName_FUNCTION
] = sizeof(struct volInfo
);
112 sizeFunctions
[HT_tapeName_FUNCTION
] = sizeof(struct tape
);
114 db
.volName
.ht
= &db
.h
.volName
;
115 db
.tapeName
.ht
= &db
.h
.tapeName
;
116 db
.dumpName
.ht
= &db
.h
.dumpName
;
117 db
.dumpIden
.ht
= &db
.h
.dumpIden
;
121 /* ht_DBInit - When rebuilding database, this sets up the hash tables. */
126 db
.h
.nHTBuckets
= htonl(nHTBuckets
);
129 struct volInfo
*s
= 0;
130 db
.h
.volName
.threadOffset
=
131 htonl((char *)&s
->nameHashChain
- (char *)s
);
132 db
.h
.volName
.functionType
= htonl(HT_volName_FUNCTION
);
136 db
.h
.tapeName
.threadOffset
=
137 htonl((char *)&s
->nameHashChain
- (char *)s
);
138 db
.h
.tapeName
.functionType
= htonl(HT_tapeName_FUNCTION
);
142 db
.h
.dumpName
.threadOffset
=
143 htonl((char *)&s
->nameHashChain
- (char *)s
);
144 db
.h
.dumpName
.functionType
= htonl(HT_dumpName_FUNCTION
);
146 db
.h
.dumpIden
.threadOffset
=
147 htonl((char *)&s
->idHashChain
- (char *)s
);
148 db
.h
.dumpIden
.functionType
= htonl(HT_dumpIden_FUNCTION
);
150 ht_Reset(&db
.volName
);
151 ht_Reset(&db
.tapeName
);
152 ht_Reset(&db
.dumpName
);
153 ht_Reset(&db
.dumpIden
);
157 ht_AllocTable(struct ubik_trans
*ut
, struct memoryHashTable
*mht
)
159 struct hashTable
*ht
= NULL
;
162 int nb
, mnb
; /* number of blocks for hashTable */
164 struct memoryHTBlock
**b
;
167 if (!(mht
&& (ht
= mht
->ht
)))
168 db_panic("some ht called with bad mht");
169 if (ht
->length
|| mht
->blocks
)
170 db_panic("previous table still allocated");
172 len
= ntohl(ht
->entries
) * 2; /* allow room to grow */
173 nb
= (len
+ nHTBuckets
- 1) / nHTBuckets
;
174 mnb
= ht_minHBlocks(mht
);
176 nb
= mnb
; /* use minimum */
177 len
= nb
* nHTBuckets
; /* new hash table length */
179 mht
->size
= nb
* sizeof(struct memoryHTBlock
*);
180 b
= mht
->blocks
= (struct memoryHTBlock
**)malloc(mht
->size
);
181 memset(b
, 0, mht
->size
);
183 for (i
= 0; i
< nb
; i
++) {
184 b
[i
] = (struct memoryHTBlock
*)malloc(sizeof(struct memoryHTBlock
));
185 code
= AllocBlock(ut
, (struct block
*)&b
[i
]->b
, &b
[i
]->a
);
190 b
[i
]->b
.h
.type
= hashTable_BLOCK
;
192 /* thread the blocks */
194 b
[i
- 1]->b
.h
.next
= htonl(b
[i
]->a
);
196 for (i
= 0; i
< nb
; i
++) {
198 dbwrite(ut
, b
[i
]->a
, (char *)&b
[i
]->b
,
199 sizeof(struct htBlock
) + (nHTBuckets
-
200 NhtBucketS
) * sizeof(dbadr
));
204 if ((code
= set_word_addr(ut
, 0, &db
.h
, &ht
->table
, htonl(b
[0]->a
))))
208 if ((code
= set_word_addr(ut
, 0, &db
.h
, plen
, htonl(len
))))
215 ht_FreeTable(struct ubik_trans
*ut
, struct memoryHashTable
*mht
)
217 struct hashTable
*ht
= NULL
;
219 struct blockHeader bh
;
221 afs_int32
*plen
, *pprog
;
223 if (!(mht
&& (ht
= mht
->ht
)))
224 db_panic("some ht called with bad mht");
225 if (ht
->oldLength
== 0)
226 db_panic("no table to free");
228 ht_ResetT(&mht
->oldBlocks
, &mht
->oldSize
, 0);
230 for (a
= ntohl(ht
->oldTable
); a
; a
= na
) {
231 if (dbread(ut
, a
, (char *)&bh
, sizeof(bh
))) {
232 Log("ht_FreeTable: dbread failed\n");
236 if ((code
= FreeBlock(ut
, &bh
, a
)))
239 plen
= &ht
->oldLength
;
240 pprog
= &ht
->progress
;
241 if (set_word_addr(ut
, 0, &db
.h
, &ht
->oldTable
, 0)
242 || set_word_addr(ut
, 0, &db
.h
, plen
, 0)
243 || set_word_addr(ut
, 0, &db
.h
, pprog
, 0))
245 mht
->oldLength
= mht
->progress
= 0;
250 ht_GetTableBlock(struct ubik_trans
*ut
, struct memoryHashTable
*mht
,
251 afs_uint32 hash
, int old
, struct memoryHTBlock
**blockP
,
254 struct hashTable
*ht
= NULL
;
255 struct memoryHTBlock
**b
;
257 struct memoryHTBlock
***blocksP
;
265 || ((ht
= mht
->ht
) == 0)
267 db_panic("some ht called with bad mht");
273 if ((length
= mht
->oldLength
) == 0)
274 return 0; /* no entries */
276 if (hi
< mht
->progress
)
277 return 0; /* no such entry */
278 blocksP
= &mht
->oldBlocks
;
279 sizeP
= &mht
->oldSize
;
281 if ((length
= mht
->length
) == 0)
282 return 0; /* no entries */
284 blocksP
= &mht
->blocks
;
288 bi
= hi
/ nHTBuckets
; /* block index */
289 *boP
= hi
- bi
* nHTBuckets
; /* block offset ptr */
292 *sizeP
= ht_TableSize(length
);
293 *blocksP
= (struct memoryHTBlock
**)malloc(*sizeP
);
294 memset(*blocksP
, 0, *sizeP
);
296 n
= *sizeP
/ sizeof(struct memoryHTBlock
*);
298 db_panic("table size inconsistent");
301 /* find an allocated block or the beginning of the block array */
302 for (i
= bi
; (i
> 0) && (b
[i
] == 0); i
--);
306 if (i
== 0) { /* the first block is found from the hashTable */
307 ta
= ntohl(old
? ht
->oldTable
: ht
->table
);
309 db_panic("non-zero length, but no table");
311 /* else ta is set from last time around loop */
313 (struct memoryHTBlock
*)malloc(sizeof(struct memoryHTBlock
));
319 if (dbread(ut
, b
[i
]->a
, (char *)&b
[i
]->b
, sizeof(struct htBlock
)))
326 /* printf("ht_GetTableBlock: hash %d block %d offset %d\n",
327 * hash, *blockP, *boP); */
331 ta
= ntohl(b
[i
++]->b
.h
.next
); /* get next ptr from current block */
336 * Decide when to push the current hash table to the old hash table.
337 * The entries in the old hash table are VALID, and are slowly hashed
338 * into the current table.
342 ht_MaybeAdjust(struct ubik_trans
*ut
, struct memoryHashTable
*mht
)
344 struct hashTable
*ht
= mht
->ht
;
345 int numberEntries
= ntohl(ht
->entries
);
347 /* old hash table must be empty */
348 if (mht
->oldLength
!= 0)
352 * It costs a lot to grow and shrink the hash table. Therefore, we will not
353 * shrink the hash table (only grow it). If the table is more than 2 entries per
354 * chain (average) we need to grow: push the entries to the old hash table.
357 * || ((mht->length > nHTBuckets) && (numberEntries*8 < mht->length))
360 /* Only grow a hash table if the number of entries is twice the
361 * number of hash length and is less than 20,450 (20 hash blocks). This
362 * means that the volname hash table will not grow (its initial
363 * hashtable size contains 30,600 buckets). Earlier revisions of
364 * the buserver have the initial size at 510 and 5,100 buckets -
365 * in which case we do want to grow it). We don't grow anything larger
366 * than 20,450 entries because it's expensive to re-hash everything.
368 if ((numberEntries
> mht
->length
* 2) && (numberEntries
< 20450)) { /* push current hash table to old hash table */
369 ht
->oldLength
= ht
->length
;
370 ht
->oldTable
= ht
->table
;
375 (ut
, ((char *)ht
- (char *)&db
.h
), (char *)ht
, sizeof(*ht
)))
379 LogDebug(2, "ht_MaybeAdjust: push ht to old\n");
385 ht_LookupBucket(struct ubik_trans
*ut
, struct memoryHashTable
*mht
,
386 afs_uint32 hash
, int old
)
388 struct memoryHTBlock
*block
;
392 if ((old
? mht
->oldLength
: mht
->length
) == 0)
394 code
= ht_GetTableBlock(ut
, mht
, hash
, old
, &block
, &bo
);
395 if (code
|| (block
== 0))
397 return ntohl(block
->b
.bucket
[bo
]);
400 /* This function is not too bad, for small hash tables, but suffers, I think,
401 * from insufficient mixing of the hash information. */
404 Old2StringHashFunction(unsigned char *str
)
406 afs_uint32 hash
= 1000003; /* big prime to make "" hash nicely */
408 hash
= (hash
<< 1) + (hash
>> 31) + *str
++;
412 /* This was actually a coding error, and produces dreadful results. The
413 * problem is that the hash needs to be mixed up not the incoming character. */
416 Old3StringHashFunction(unsigned char *str
)
418 afs_uint32 hash
= 1000003; /* big prime to make "" hash nicely */
420 hash
+= (*str
++) * 0x072a51a4;
424 /* This function is pretty good. Its main problem is that the low two bits of
425 * the hash multiplier are zero which tends to shift information too far left.
426 * It behaves especially badly for hash tables whose size is a power of two. */
429 Old4StringHashFunction(unsigned char *str
)
431 afs_uint32 hash
= 1000003; /* big prime to make "" hash nicely */
433 hash
= (*str
++) + hash
* 0x072a51a4;
437 /* While this is good for a hash table with 500 buckets it is nearly as bad as
438 * #3 with a hash table as big as 8200. */
441 Old5StringHashFunction(unsigned char *str
)
443 afs_uint32 hash
= 1000003; /* big prime to make "" hash nicely */
449 /* This was an attempt to produce a hash function with the smallest and
450 * simplest mixing multiplier. This is only a little worse than the real one,
451 * and the difference seems to be smaller with larger hash tables. It behaves
452 * better than the random hash function. */
455 Old6StringHashFunction(unsigned char *str
)
457 afs_uint32 hash
= 1000003; /* big prime to make "" hash nicely */
459 hash
= hash
* 0x81 + (*str
++);
463 /* This actually seems to be little better then the real one. Having the same
464 * number of bits but only 5 bits apart seems to produce worse results but
465 * having the bits spanning the same range farther apart also doesn't do as
466 * well. All these differences are fairly small, however. */
469 Old7StringHashFunction(unsigned char *str
)
471 afs_uint32 hash
= 1000003; /* big prime to make "" hash nicely */
473 hash
= hash
* 0x42108421 + (*str
++);
477 /* This function tries to provide some non-linearity by providing some feedback
478 * from higher-order bits in the word. It also uses shifts instead of
479 * multiplies, which may be faster on some architectures. */
482 Old8StringHashFunction(unsigned char *str
)
484 afs_uint32 hash
= 1000003; /* big prime to make "" hash nicely */
487 hash
+ (hash
<< 7) + (hash
<< 14) + (hash
<< 21) + (hash
<< 28) +
488 (hash
>> 17) + *str
++;
492 /* This is the result of the above search for good hash functions. It seems
493 * that the choice of multipliers is somewhat arbitrary but has several
494 * constraints. It shouldn't have too many or too few one bits and should be
495 * odd. It behaves beeter than the random hash function. */
498 StringHashFunction(unsigned char *str
)
500 afs_uint32 hash
= 1000003; /* big prime to make "" hash nicely */
501 /* The multiplicative constant should be odd and have a goodly number of
504 hash
= (*str
++) + hash
* 0x10204081;
509 IdHashFunction(afs_uint32 id
)
518 /* The minimum hash table blocks to allocate. Each block contains 510
519 * buckets. They hash table grows when the number of entries reaches
520 * twice the number of buckets.
523 ht_minHBlocks(struct memoryHashTable
*mht
)
527 switch (ntohl(mht
->ht
->functionType
)) {
528 case HT_dumpIden_FUNCTION
:
529 case HT_dumpName_FUNCTION
: /* hash table able to handle (befor it grows) ... */
530 retval
= 2; /* 1,020 dump entries */
533 case HT_tapeName_FUNCTION
:
534 retval
= 4; /* 2,040 tape entries */
537 case HT_volName_FUNCTION
:
538 retval
= 60; /* 61,200 volInfo entries (with different names) */
542 db_panic("Illegal hash function type");
543 retval
= -1; /* not reached */
549 ht_HashEntry(struct memoryHashTable
*mht
,
550 char *e
) /* entry's address (in b) */
552 int type
= ntohl(mht
->ht
->functionType
);
556 case HT_dumpIden_FUNCTION
:
557 retval
= IdHashFunction(ntohl(((struct dump
*)e
)->id
));
558 LogDebug(5, "HashEntry: dumpid returns %d\n", retval
);
561 case HT_dumpName_FUNCTION
:
562 retval
= StringHashFunction((unsigned char *)((struct dump
*)e
)->dumpName
);
563 LogDebug(5, "HashEntry: dumpname returns %d\n", retval
);
566 case HT_tapeName_FUNCTION
:
567 retval
= StringHashFunction((unsigned char *)((struct tape
*)e
)->name
);
568 LogDebug(5, "HashEntry: tapename returns %d\n", retval
);
571 case HT_volName_FUNCTION
:
572 retval
= StringHashFunction((unsigned char *)((struct volInfo
*)e
)->name
);
573 LogDebug(5, "HashEntry: volname returns %d\n", retval
);
577 db_panic("illegal hash function");
578 retval
= -1; /* not reached */
586 * returns a ptr to the memory hash table for the specified hash
590 struct memoryHashTable
*
591 ht_GetType(int type
, int *e_sizeP
)
593 struct memoryHashTable
*mht
;
595 if ((type
<= 0) || (type
> HT_MAX_FUNCTION
))
599 *e_sizeP
= sizeFunctions
[type
];
601 case HT_dumpIden_FUNCTION
:
605 case HT_dumpName_FUNCTION
:
609 case HT_tapeName_FUNCTION
:
613 case HT_volName_FUNCTION
:
620 if (ntohl(mht
->ht
->functionType
) != type
)
621 db_panic("ht types don't match");
626 ht_KeyMatch(int type
, char *key
, char *e
)
629 case HT_dumpIden_FUNCTION
:
630 return *(dumpId
*) key
== ntohl(((struct dump
*)e
)->id
);
631 case HT_dumpName_FUNCTION
:
632 return strcmp(key
, ((struct dump
*)e
)->dumpName
) == 0;
633 case HT_tapeName_FUNCTION
:
634 return strcmp(key
, ((struct tape
*)e
)->name
) == 0;
635 case HT_volName_FUNCTION
:
636 return strcmp(key
, ((struct volInfo
*)e
)->name
) == 0;
639 db_panic("illegal hash function");
647 * ut - ubik transaction
648 * mht - memory hash table ptr
649 * key - hash and lookup key
651 * eaP - dbaddr of entry found or zero if failed
652 * e - contents of located entry
656 ht_LookupEntry(struct ubik_trans
*ut
,
657 struct memoryHashTable
*mht
,
658 void *key
, /* pointer to lookup key to match */
659 dbadr
*eaP
, /* db addr of entry found or zero */
660 void *e
) /* contents of located entry */
662 struct hashTable
*ht
= NULL
;
669 if (!key
|| !eaP
|| !e
)
670 db_panic("null ptrs passed to LookupEntry");
671 if (!(mht
&& (ht
= mht
->ht
)))
672 db_panic("some ht called with bad mht");
674 *eaP
= 0; /* initialize not-found indicator */
676 type
= ntohl(ht
->functionType
);
677 e_size
= sizeFunctions
[type
];
678 if (type
== HT_dumpIden_FUNCTION
)
679 hash
= IdHashFunction(*(dumpId
*) key
);
681 hash
= StringHashFunction(key
);
683 for (old
= 0;; old
++) {
684 a
= ht_LookupBucket(ut
, mht
, hash
, old
);
686 if (dbread(ut
, a
, e
, e_size
))
688 if (ht_KeyMatch(type
, key
, e
)) {
692 a
= ntohl(*(dbadr
*) ((char *)e
+ mht
->threadOffset
));
701 * opQuota - max # of items to move
703 * opQuota - adjusted to reflect # of moves
707 ht_HashInList(struct ubik_trans
*ut
, struct memoryHashTable
*mht
,
708 int *opQuota
, struct memoryHTBlock
*block
, int blockOffset
)
710 struct hashTable
*ht
= mht
->ht
;
714 char e
[sizeof(struct block
)]; /* unnecessarily conservative */
715 int e_size
= sizeFunctions
[ntohl(ht
->functionType
)];
717 if (mht
->length
== 0) {
718 if ((code
= ht_AllocTable(ut
, mht
))) {
719 Log("ht_HashInList: ht_AllocTable failed\n");
724 listA
= ntohl(block
->b
.bucket
[blockOffset
]);
727 Log("ht_HashInList: expecting non-zero bucket\n");
731 for (ea
= listA
; ea
; ea
= next_ea
) { /*f */
733 LogDebug(3, "ht_HashInList: move entry at %d, type %d\n", ea
,
734 ntohl(mht
->ht
->functionType
));
736 if (dbread(ut
, ea
, e
, e_size
))
739 /* LogNetDump((struct dump *) e); */
741 /* get the address of the next item on the list */
742 next_ea
= ntohl(*(dbadr
*) (e
+ mht
->threadOffset
));
744 /* write the link into the bucket */
746 set_word_addr(ut
, block
->a
, &block
->b
,
747 &block
->b
.bucket
[blockOffset
], htonl(next_ea
));
749 Log("ht_HashInList: bucket update failed\n");
754 struct memoryHTBlock
*block
;
758 /* get the hash value */
759 hash
= ht_HashEntry(mht
, e
) % mht
->length
;
760 LogDebug(4, "ht_HashInList: moved to %d\n", hash
);
762 /* get the new hash table block */
763 code
= ht_GetTableBlock(ut
, mht
, hash
, 0 /*old */ , &block
, &bo
);
765 Log("ht_HashInList: ht_GetTableBlock failed\n");
769 Log("ht_HashInList: ht_GetTableBlock returned 0\n");
770 return BUDB_INTERNALERROR
;
773 /* Chain entry at front of bucket;
774 * first threadOffset of entry = bucket
775 * then bucket = addr of entry
778 (ut
, ea
, e
, mht
->threadOffset
, block
->b
.bucket
[bo
])
779 || set_word_addr(ut
, block
->a
, &block
->b
,
780 &block
->b
.bucket
[bo
], htonl(ea
)))
784 if (--(*opQuota
) == 0)
792 * The hash table is needs to be re-sized. Move entries from the old
797 ht_MoveEntries(struct ubik_trans
*ut
, struct memoryHashTable
*mht
)
799 struct memoryHTBlock
*block
;
806 if (mht
->oldLength
== 0)
809 LogDebug(3, "ht_MoveEntries:\n");
810 /* we assume here that the hash function will map numbers smaller than the
811 * size of the hash table straight through to hash table indexes.
813 hash
= mht
->progress
;
815 /* get hash table block ? */
816 code
= ht_GetTableBlock(ut
, mht
, hash
, 1 /*old */ , &block
, &bo
);
821 return BUDB_INTERNALERROR
;
823 count
= 10; /* max. # entries to move */
826 if (block
->b
.bucket
[bo
]) {
827 code
= ht_HashInList(ut
, mht
, &count
, block
, bo
);
829 Log("ht_MoveEntries: ht_HashInList failed\n");
834 if (block
->b
.bucket
[bo
] == 0) {
835 /* this bucket is now empty */
839 /* don't exceed the quota of items to be moved */
843 } while (++bo
< nHTBuckets
);
845 if (mht
->progress
>= mht
->oldLength
)
846 return (ht_FreeTable(ut
, mht
));
848 pprog
= &mht
->ht
->progress
;
849 if (set_word_addr(ut
, 0, &db
.h
, pprog
, htonl(mht
->progress
))) {
850 Log("ht_MoveEntries: progress set failed\n");
859 ht_MoveEntries(struct ubik_trans
*ut
, struct memoryHashTable
*mht
)
863 struct memoryHTBlock
*block
;
866 if (mht
->oldLength
== 0)
869 LogDebug(3, "ht_MoveEntries:\n");
870 /* we assume here that the hash function will map numbers smaller than the
871 * size of the hash table straight through to hash table indexes.
873 hash
= mht
->progress
;
875 /* get hash table block ? */
876 code
= ht_GetTableBlock(ut
, mht
, hash
, 1 /*old */ , &block
, &bo
);
881 return BUDB_INTERNALERROR
;
885 if (block
->b
.bucket
[bo
]) {
886 code
= ht_HashInList(ut
, mht
, ntohl(block
->b
.bucket
[bo
]));
888 Log("ht_MoveEntries: ht_HashInList failed\n");
892 set_word_addr(ut
, block
->a
, &block
->b
, &block
->b
.bucket
[bo
],
895 Log("ht_MoveEntries: clear old entry failed\n");
900 } while (++bo
< nHTBuckets
);
902 if (mht
->progress
>= mht
->oldLength
)
903 return (ht_FreeTable(ut
, mht
));
905 if (set_word_addr(ut
, 0, &db
.h
, &mht
->ht
->progress
, htonl(mht
->progress
))) {
906 Log("ht_MoveEntries: progress set failed\n");
914 ht_HashIn(struct ubik_trans
*ut
,
915 struct memoryHashTable
*mht
,
916 dbadr ea
, /* block db address */
917 void *e
) /* entry's address (in b) */
919 struct hashTable
*ht
= NULL
;
921 struct memoryHTBlock
*block
;
926 if (!(mht
&& (ht
= mht
->ht
)))
927 db_panic("some ht called with bad mht");
929 if ((code
= ht_MaybeAdjust(ut
, mht
)))
931 if (mht
->length
== 0)
932 if ((code
= ht_AllocTable(ut
, mht
)))
935 hash
= ht_HashEntry(mht
, e
);
936 code
= ht_GetTableBlock(ut
, mht
, hash
, 0 /*old */ , &block
, &bo
);
940 return BUDB_INTERNALERROR
;
942 code
= set_word_offset(ut
, ea
, e
, mht
->threadOffset
, block
->b
.bucket
[bo
]);
945 LogDebug(5, "Hashin: set %d to %d\n", mht
->threadOffset
,
946 block
->b
.bucket
[bo
]);
949 set_word_addr(ut
, block
->a
, &block
->b
, &block
->b
.bucket
[bo
],
953 LogDebug(5, "Hashin: set %"AFS_PTR_FMT
" to %d\n",
954 &block
->b
.bucket
[bo
], htonl(ea
));
956 pentries
= &ht
->entries
;
958 set_word_addr(ut
, 0, &db
.h
, pentries
,
959 htonl(ntohl(ht
->entries
) + 1));
963 return ht_MoveEntries(ut
, mht
);
966 /* RemoveFromList - generic procedure to delete an entry from a list given its
967 * head and thread offset. Only a single long is modified by this routine.
968 * The head pointer is modified, in place, using set_word_addr if the entry is
969 * at the head of the list, otherwise only the thread of the previous entry is
970 * modified. The entry pointer is only used to calculate the thread offset,
971 * but is not otherwise used. */
974 RemoveFromList(struct ubik_trans
*ut
,
975 dbadr ea
, /* db addr of head structure */
976 void *e
, /* head structure */
977 dbadr
*head
, /* address of head pointer */
978 dbadr ta
, /* db addr of strucure to be removed */
979 void *t
, /* structure being removed */
980 dbadr
*thread
) /* pointer to thread pointer */
983 int threadOffset
= ((char *)thread
- (char *)t
);
984 dbadr next_a
; /* db addr of next element in list */
985 dbadr loop_a
; /* db addr of current list element */
988 return -1; /* empty list: not found */
989 next_a
= ntohl(*head
); /* start at head of list */
990 if (next_a
== ta
) { /* remove from head of list */
991 code
= set_word_addr(ut
, ea
, e
, head
, *thread
);
997 dbread(ut
, loop_a
+ threadOffset
, (char *)&next_a
, sizeof(dbadr
));
1001 return -1; /* end of list: not found */
1002 } while (ta
!= (next_a
= ntohl(next_a
)));
1003 code
= dbwrite(ut
, loop_a
+ threadOffset
, (char *)thread
, sizeof(dbadr
));
1008 ht_HashOutT(struct ubik_trans
*ut
, struct memoryHashTable
*mht
,
1009 afs_uint32 hash
, dbadr ea
, char *e
, int old
)
1011 struct memoryHTBlock
*block
;
1014 afs_int32
*pentries
;
1016 if ((old
? mht
->oldLength
: mht
->length
) == 0)
1018 code
= ht_GetTableBlock(ut
, mht
, hash
, old
, &block
, &bo
);
1021 if ((block
== 0) || (block
->b
.bucket
[bo
] == 0))
1025 RemoveFromList(ut
, block
->a
, (char *)&block
->b
, &block
->b
.bucket
[bo
],
1026 ea
, e
, (dbadr
*) (e
+ mht
->threadOffset
));
1031 unthread_ea
= *(afs_int32
*) ((char *)e
+ mht
->threadOffset
);
1032 if (block
->b
.bucket
[bo
] == net_ea
) {
1034 (ut
, block
->a
, &block
->b
, &block
->b
.bucket
[bo
], unthread_ea
))
1038 loop_a
= ntohl(block
->b
.bucket
[bo
]);
1041 (ut
, loop_a
+ mht
->threadOffset
, (char *)&next_loop_a
,
1044 if (next_loop_a
== 0)
1045 return -1; /* not found */
1046 if (net_ea
== next_loop_a
) {
1048 (ut
, loop_a
+ mht
->threadOffset
, (char *)&unthread_ea
,
1053 loop_a
= ntohl(next_loop_a
);
1057 pentries
= &mht
->ht
->entries
;
1059 (ut
, 0, &db
.h
, pentries
, htonl(ntohl(mht
->ht
->entries
) - 1)))
1065 ht_HashOut(struct ubik_trans
*ut
, struct memoryHashTable
*mht
, dbadr ea
,
1072 db_panic("some ht called with bad mht");
1073 hash
= ht_HashEntry(mht
, e
);
1074 if (mht
->oldLength
) {
1075 code
= ht_HashOutT(ut
, mht
, hash
, ea
, e
, 1 /*old */ );
1078 else if (code
!= -1)
1081 if (mht
->length
== 0) /* not found */
1082 ERROR(BUDB_INTERNALERROR
);
1083 code
= ht_HashOutT(ut
, mht
, hash
, ea
, e
, 0 /*old */ );
1089 code
= ht_MoveEntries(ut
, mht
);
1092 code
= ht_MaybeAdjust(ut
, mht
);
1100 /* generic hash table traversal routines */
1104 scanHashTableBlock(struct ubik_trans
*ut
,
1105 struct memoryHashTable
*mhtPtr
,
1106 struct htBlock
*htBlockPtr
,
1108 afs_int32 length
, /* size of whole hash table */
1109 int index
, /* base index of this block */
1110 int (*selectFn
) (dbadr
, void *, void *),
1111 int (*operationFn
) (dbadr
, void *, void *),
1114 int type
; /* hash table type */
1115 int entrySize
; /* hashed entry size */
1117 char entry
[sizeof(struct block
)];
1118 dbadr entryAddr
, nextEntryAddr
;
1122 type
= ntohl(mhtPtr
->ht
->functionType
);
1123 entrySize
= sizeFunctions
[type
];
1125 /* step through this hash table block, being careful to stop
1126 * before the end of the overall hash table
1129 for (i
= 0; (i
< nHTBuckets
) && (index
< length
); i
++, index
++) { /*f */
1131 nextEntryAddr
= ntohl(htBlockPtr
->bucket
[i
]);
1133 /* if this is the old hash table, all entries below the progress mark
1134 * should have been moved to the new hash table
1136 if (old
&& (index
< mhtPtr
->progress
) && nextEntryAddr
)
1137 return BUDB_INTERNALERROR
;
1139 /* now walk down the chain of each bucket */
1140 while (nextEntryAddr
) { /*w */
1142 entryAddr
= nextEntryAddr
;
1143 if (dbread(ut
, entryAddr
, &entry
[0], entrySize
))
1144 return (BUDB_INTERNALERROR
);
1146 if ((*selectFn
) (entryAddr
, &entry
[0], rockPtr
)) {
1147 (*operationFn
) (entryAddr
, &entry
[0], rockPtr
);
1151 ntohl(*((dbadr
*) (entry
+ mhtPtr
->threadOffset
)));
1160 scanHashTable(struct ubik_trans
*ut
, struct memoryHashTable
*mhtPtr
,
1161 int (*selectFn
) (dbadr
, void *, void *),
1162 int (*operationFn
) (dbadr
, void *, void *),
1165 struct htBlock hashTableBlock
;
1166 dbadr tableAddr
; /* disk addr of hash block */
1167 int tableLength
; /* # entries */
1168 int blockLength
; /* # blocks */
1174 extern int nHTBuckets
; /* # buckets in a hash table */
1176 for (old
= 0; old
<= 1; old
++) { /*fo */
1178 /* check the old hash table */
1179 tableLength
= mhtPtr
->oldLength
;
1180 if (tableLength
== 0)
1181 continue; /* nothing to do */
1183 tableAddr
= ntohl(mhtPtr
->ht
->oldTable
);
1185 /* check current hash table */
1186 tableLength
= mhtPtr
->length
;
1187 if (tableLength
== 0)
1188 continue; /* nothing to do */
1190 tableAddr
= ntohl(mhtPtr
->ht
->table
);
1193 blockLength
= (tableLength
- 1) / nHTBuckets
;
1196 /* follow the hash chain */
1197 for (i
= 0; i
<= blockLength
; i
++) { /*fi */
1198 /* chain too short */
1200 ERROR(BUDB_DATABASEINCONSISTENT
);
1203 dbread(ut
, tableAddr
, &hashTableBlock
,
1204 sizeof(hashTableBlock
));
1209 scanHashTableBlock(ut
, mhtPtr
, &hashTableBlock
, old
,
1210 tableLength
, hashIndex
, selectFn
,
1211 operationFn
, rockPtr
);
1215 hashIndex
+= nHTBuckets
;
1216 tableAddr
= ntohl(hashTableBlock
.h
.next
);