[6.0] src/dict.{h,c}

This commit is contained in:
tporadowski
2021-01-26 23:16:18 +01:00
parent 7c238d573e
commit d3ef6a36e4
2 changed files with 45 additions and 11 deletions
+44 -11
View File
@@ -142,10 +142,10 @@ int _dictInit(dict *d, dictType *type,
* but with the invariant of a USED/BUCKETS ratio near to <= 1 */
int dictResize(dict *d)
{
int minimal;
PORT_ULONG minimal;
if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;
minimal = (int)d->ht[0].used; WIN_PORT_FIX /* cast (int) */
minimal = d->ht[0].used;
if (minimal < DICT_HT_INITIAL_SIZE)
minimal = DICT_HT_INITIAL_SIZE;
return dictExpand(d, minimal);
@@ -169,7 +169,7 @@ int dictExpand(dict *d, PORT_ULONG size)
n.size = realsize;
n.sizemask = realsize-1;
n.table = zcalloc(realsize*sizeof(dictEntry*));
n.used = (size_t) 0; WIN_PORT_FIX /* cast (size_t) */
n.used = 0;
/* Is this the first initialization? If so it's not really a rehashing
* we just set the first hash table so that it can accept keys. */
@@ -254,7 +254,9 @@ PORT_LONGLONG timeInMilliseconds(void) {
#endif
}
/* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */
/* Rehash in ms+"delta" milliseconds. The value of "delta" is larger
* than 0, and is smaller than 1 in most cases. The exact upper bound
* depends on the running time of dictRehash(d,100).*/
int dictRehashMilliseconds(dict *d, int ms) {
PORT_LONGLONG start = timeInMilliseconds();
int rehashes = 0;
@@ -495,7 +497,7 @@ dictEntry *dictFind(dict *d, const void *key)
dictEntry *he;
uint64_t h, idx, table;
if (d->ht[0].used + d->ht[1].used == 0) return NULL; /* dict is empty */
if (dictSize(d) == 0) return NULL; /* dict is empty */
if (dictIsRehashing(d)) _dictRehashStep(d);
h = dictHashKey(d, key);
for (table = 0; table <= 1; table++) {
@@ -705,9 +707,9 @@ unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count) {
}
tables = dictIsRehashing(d) ? 2 : 1;
maxsizemask = (unsigned int) d->ht[0].sizemask; WIN_PORT_FIX /* cast (unsigned int) */
maxsizemask = (PORT_ULONG) d->ht[0].sizemask; WIN_PORT_FIX /* cast (PORT_ULONG) */
if (tables > 1 && maxsizemask < d->ht[1].sizemask)
maxsizemask = (unsigned int) d->ht[1].sizemask; WIN_PORT_FIX /* cast (unsigned int) */
maxsizemask = (PORT_ULONG) d->ht[1].sizemask; WIN_PORT_FIX /* cast (PORT_ULONG) */
/* Pick a random point inside the larger table. */
PORT_ULONG i = random() & maxsizemask;
@@ -753,13 +755,37 @@ unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count) {
}
i = (i+1) & maxsizemask;
}
return (unsigned int)stored; WIN_PORT_FIX /* cast (unsigned int) */
return stored;
}
/* This is like dictGetRandomKey() from the POV of the API, but will do more
* work to ensure a better distribution of the returned element.
*
* This function improves the distribution because the dictGetRandomKey()
* problem is that it selects a random bucket, then it selects a random
* element from the chain in the bucket. However elements being in different
* chain lengths will have different probabilities of being reported. With
* this function instead what we do is to consider a "linear" range of the table
* that may be constituted of N buckets with chains of different lengths
* appearing one after the other. Then we report a random element in the range.
* In this way we smooth away the problem of different chain lengths. */
#define GETFAIR_NUM_ENTRIES 15
dictEntry *dictGetFairRandomKey(dict *d) {
dictEntry *entries[GETFAIR_NUM_ENTRIES];
unsigned int count = dictGetSomeKeys(d,entries,GETFAIR_NUM_ENTRIES);
/* Note that dictGetSomeKeys() may return zero elements in an unlucky
* run() even if there are actually elements inside the hash table. So
* when we get zero, we call the true dictGetRandomKey() that will always
* yeld the element if the hash table has at least one. */
if (count == 0) return dictGetRandomKey(d);
unsigned int idx = rand() % count;
return entries[idx];
}
/* Function to reverse bits. Algorithm from:
* http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel */
static PORT_ULONG rev(PORT_ULONG v) {
PORT_ULONG s = 8 * sizeof(v); // bit size; must be power of 2
PORT_ULONG s = CHAR_BIT * sizeof(v); // bit size; must be power of 2
PORT_ULONG mask = ~0;
while ((s >>= 1) > 0) {
mask ^= (mask << s);
@@ -864,6 +890,10 @@ PORT_ULONG dictScan(dict *d,
if (dictSize(d) == 0) return 0;
/* Having a safe iterator means no rehashing can happen, see _dictRehashStep.
* This is needed in case the scan callback tries to do dictFind or alike. */
d->iterators++;
if (!dictIsRehashing(d)) {
t0 = &(d->ht[0]);
m0 = (PORT_ULONG)t0->sizemask; WIN_PORT_FIX /* cast (PORT_ULONG) */
@@ -930,6 +960,9 @@ PORT_ULONG dictScan(dict *d,
} while (v & (m0 ^ m1));
}
/* undo the ++ at the top */
d->iterators--;
return v;
}
@@ -1030,7 +1063,7 @@ dictEntry **dictFindEntryRefByPtrAndHash(dict *d, const void *oldptr, uint64_t h
dictEntry *he, **heref;
PORT_ULONG idx, table;
if (d->ht[0].used + d->ht[1].used == 0) return NULL; /* dict is empty */
if (dictSize(d) == 0) return NULL; /* dict is empty */
for (table = 0; table <= 1; table++) {
idx = hash & d->ht[table].sizemask;
heref = &d->ht[table].table[idx];
@@ -1105,7 +1138,7 @@ size_t _dictGetStatsHt(char *buf, size_t bufsize, dictht *ht, int tableid) {
i, clvector[i], ((float)clvector[i]/ht->size)*100);
}
/* Unlike snprintf(), teturn the number of characters actually written. */
/* Unlike snprintf(), return the number of characters actually written. */
if (bufsize) buf[bufsize-1] = '\0';
return strlen(buf);
}
+1
View File
@@ -169,6 +169,7 @@ dictIterator *dictGetSafeIterator(dict *d);
dictEntry *dictNext(dictIterator *iter);
void dictReleaseIterator(dictIterator *iter);
dictEntry *dictGetRandomKey(dict *d);
dictEntry *dictGetFairRandomKey(dict *d);
unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count);
void dictGetStats(char *buf, size_t bufsize, dict *d);
uint64_t dictGenHashFunction(const void *key, int len);