thash.c 22.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * Copyright (c) 2019 TAOS Data, Inc. <jhtao@taosdata.com>
 *
 * This program is free software: you can use, redistribute, and/or modify
 * it under the terms of the GNU Affero General Public License, version 3
 * or later ("AGPL"), as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "os.h"
H
Haojun Liao 已提交
17
#include "thash.h"
S
Shengliang Guan 已提交
18
#include "ulog.h"
19
#include "tdef.h"
20

21
#define EXT_SIZE 1024 
22

H
Haojun Liao 已提交
23
#define HASH_NEED_RESIZE(_h) ((_h)->size >= (_h)->capacity * HASH_DEFAULT_LOAD_FACTOR)
H
Haojun Liao 已提交
24

H
Haojun Liao 已提交
25 26
#define DO_FREE_HASH_NODE(_n) \
  do {                        \
H
Haojun Liao 已提交
27
    tfree(_n);                \
H
Haojun Liao 已提交
28 29
  } while (0)

H
Haojun Liao 已提交
30 31 32
#define FREE_HASH_NODE(_h, _n)  \
  do {                          \
    if ((_h)->freeFp) {         \
H
Haojun Liao 已提交
33
      (_h)->freeFp(GET_HASH_NODE_DATA(_n)); \
H
Haojun Liao 已提交
34 35 36 37 38
    }                           \
                                \
    DO_FREE_HASH_NODE(_n);      \
  } while (0);

H
Haojun Liao 已提交
39 40
static FORCE_INLINE void __wr_lock(void *lock, int32_t type) {
  if (type == HASH_NO_LOCK) {
H
hjxilinx 已提交
41 42
    return;
  }
H
Haojun Liao 已提交
43
  taosWLockLatch(lock);
44 45
}

H
Haojun Liao 已提交
46 47
static FORCE_INLINE void __rd_lock(void *lock, int32_t type) {
  if (type == HASH_NO_LOCK) {
H
hjxilinx 已提交
48 49
    return;
  }
H
Haojun Liao 已提交
50
  taosRLockLatch(lock);
51 52
}

H
Haojun Liao 已提交
53 54
static FORCE_INLINE void __rd_unlock(void *lock, int32_t type) {
  if (type == HASH_NO_LOCK) {
H
hjxilinx 已提交
55 56
    return;
  }
H
hjxilinx 已提交
57

H
Haojun Liao 已提交
58
  taosRUnLockLatch(lock);
59 60
}

H
Haojun Liao 已提交
61 62
static FORCE_INLINE void __wr_unlock(void *lock, int32_t type) {
  if (type == HASH_NO_LOCK) {
H
hjxilinx 已提交
63 64
    return;
  }
H
hjxilinx 已提交
65

H
Haojun Liao 已提交
66
  taosWUnLockLatch(lock);
67 68 69 70 71
}

static FORCE_INLINE int32_t taosHashCapacity(int32_t length) {
  int32_t len = MIN(length, HASH_MAX_CAPACITY);

S
Shengliang Guan 已提交
72
  int32_t i = 4;
73
  while (i < len) i = (i << 1u);
74 75 76
  return i;
}

77
static FORCE_INLINE SHashNode *doSearchInEntryList(SHashObj *pHashObj, SHashEntry *pe, const void *key, size_t keyLen, uint32_t hashVal) {
H
Haojun Liao 已提交
78
  SHashNode *pNode = pe->next;
H
Haojun Liao 已提交
79
  while (pNode) {
80
    if ((pNode->keyLen == keyLen) && ((*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0) && pNode->removed == 0) {
H
Haojun Liao 已提交
81 82 83 84 85 86 87 88 89 90
      assert(pNode->hashVal == hashVal);
      break;
    }

    pNode = pNode->next;
  }

  return pNode;
}

91
/**
H
hjLiao 已提交
92
 * Resize the hash list if the threshold is reached
93
 *
H
hjxilinx 已提交
94
 * @param pHashObj
95
 */
H
hjLiao 已提交
96
static void taosHashTableResize(SHashObj *pHashObj);
97

H
hjLiao 已提交
98 99 100 101 102 103 104 105 106
/**
 * @param key      key of object for hash, usually a null-terminated string
 * @param keyLen   length of key
 * @param pData    actually data. Requires a consecutive memory block, no pointer is allowed in pData.
 *                 Pointer copy causes memory access error.
 * @param dsize    size of data
 * @return         SHashNode
 */
static SHashNode *doCreateHashNode(const void *key, size_t keyLen, const void *pData, size_t dsize, uint32_t hashVal);
107

H
hjLiao 已提交
108 109 110 111 112 113 114 115 116 117
/**
 * Update the hash node
 *
 * @param pNode   hash node
 * @param key     key for generate hash value
 * @param keyLen  key length
 * @param pData   actual data
 * @param dsize   size of actual data
 * @return        hash node
 */
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
118
static FORCE_INLINE SHashNode *doUpdateHashNode(SHashObj *pHashObj, SHashEntry* pe, SHashNode* prev, SHashNode *pNode, SHashNode *pNewNode) {
H
Haojun Liao 已提交
119
  assert(pNode->keyLen == pNewNode->keyLen);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
120 121

  pNode->count--;
H
Haojun Liao 已提交
122 123
  if (prev != NULL) {
    prev->next = pNewNode;
124 125
  } else {
    pe->next = pNewNode;
H
Haojun Liao 已提交
126
  }
H
Haojun Liao 已提交
127

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
128 129 130 131 132 133 134 135 136
  if (pNode->count <= 0) {
    pNewNode->next = pNode->next;
    DO_FREE_HASH_NODE(pNode);
  } else {
    pNewNode->next = pNode;    
    pe->num++;
    atomic_add_fetch_64(&pHashObj->size, 1);
  }
  
H
Haojun Liao 已提交
137 138
  return pNewNode;
}
139

H
hjLiao 已提交
140 141 142 143 144 145
/**
 * insert the hash node at the front of the linked list
 *
 * @param pHashObj
 * @param pNode
 */
H
Haojun Liao 已提交
146
static void pushfrontNodeInEntryList(SHashEntry *pEntry, SHashNode *pNode);
147

148 149 150 151 152 153 154 155
/**
 * Check whether the hash table is empty or not.
 *
 * @param pHashObj the hash table object
 * @return if the hash table is empty or not
 */
static FORCE_INLINE bool taosHashTableEmpty(const SHashObj *pHashObj);

156
/**
H
hjLiao 已提交
157 158
 * Get the next element in hash table for iterator
 * @param pIter
159 160
 * @return
 */
H
hjLiao 已提交
161

H
Haojun Liao 已提交
162
SHashObj *taosHashInit(size_t capacity, _hash_fn_t fn, bool update, SHashLockTypeE type) {
163 164 165
  assert(fn != NULL);
  if (capacity == 0) {
    capacity = 4;
166 167
  }

H
hjxilinx 已提交
168 169
  SHashObj *pHashObj = (SHashObj *)calloc(1, sizeof(SHashObj));
  if (pHashObj == NULL) {
S
slguan 已提交
170
    uError("failed to allocate memory, reason:%s", strerror(errno));
171 172 173 174
    return NULL;
  }

  // the max slots is not defined by user
S
Shengliang Guan 已提交
175
  pHashObj->capacity = taosHashCapacity((int32_t)capacity);
H
hjxilinx 已提交
176
  assert((pHashObj->capacity & (pHashObj->capacity - 1)) == 0);
177 178
  pHashObj->equalFp = memcmp;
  pHashObj->hashFp  = fn;
H
Haojun Liao 已提交
179
  pHashObj->type = type;
H
Haojun Liao 已提交
180
  pHashObj->enableUpdate = update;
181

H
Haojun Liao 已提交
182
  pHashObj->hashList = (SHashEntry **)calloc(pHashObj->capacity, sizeof(void *));
H
hjxilinx 已提交
183 184
  if (pHashObj->hashList == NULL) {
    free(pHashObj);
S
slguan 已提交
185
    uError("failed to allocate memory, reason:%s", strerror(errno));
186
    return NULL;
H
Haojun Liao 已提交
187
  } else {
H
Haojun Liao 已提交
188
    pHashObj->pMemBlock = taosArrayInit(8, sizeof(void *));
H
Haojun Liao 已提交
189

H
Haojun Liao 已提交
190 191
    void *p = calloc(pHashObj->capacity, sizeof(SHashEntry));
    for (int32_t i = 0; i < pHashObj->capacity; ++i) {
S
Shengliang Guan 已提交
192
      pHashObj->hashList[i] = (void *)((char *)p + i * sizeof(SHashEntry));
H
Haojun Liao 已提交
193 194 195
    }

    taosArrayPush(pHashObj->pMemBlock, &p);
196
  }
H
hjxilinx 已提交
197

H
hjxilinx 已提交
198
  return pHashObj;
199 200
}

201 202 203 204 205 206
void taosHashSetEqualFp(SHashObj *pHashObj, _equal_fn_t fp) {
  if (pHashObj != NULL && fp != NULL) {
    pHashObj->equalFp = fp;
  } 
} 

207 208 209 210 211 212 213 214 215 216
int32_t taosHashGetSize(const SHashObj *pHashObj) {
  if (!pHashObj) {
    return 0;
  }
  return (int32_t)atomic_load_64(&pHashObj->size);
}

static FORCE_INLINE bool taosHashTableEmpty(const SHashObj *pHashObj) {
  return taosHashGetSize(pHashObj) == 0;
}
217

H
hjLiao 已提交
218
int32_t taosHashPut(SHashObj *pHashObj, const void *key, size_t keyLen, void *data, size_t size) {
S
Shengliang Guan 已提交
219
  uint32_t   hashVal = (*pHashObj->hashFp)(key, (uint32_t)keyLen);
H
Haojun Liao 已提交
220 221 222 223
  SHashNode *pNewNode = doCreateHashNode(key, keyLen, data, size, hashVal);
  if (pNewNode == NULL) {
    return -1;
  }
224

H
Haojun Liao 已提交
225 226
  // need the resize process, write lock applied
  if (HASH_NEED_RESIZE(pHashObj)) {
H
Haojun Liao 已提交
227
    __wr_lock(&pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
228
    taosHashTableResize(pHashObj);
H
Haojun Liao 已提交
229
    __wr_unlock(&pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
230 231
  }

H
Haojun Liao 已提交
232
  __rd_lock(&pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
233

H
Haojun Liao 已提交
234
  int32_t     slot = HASH_INDEX(hashVal, pHashObj->capacity);
H
Haojun Liao 已提交
235
  SHashEntry *pe = pHashObj->hashList[slot];
236

H
Haojun Liao 已提交
237
  if (pHashObj->type == HASH_ENTRY_LOCK) {
H
Haojun Liao 已提交
238 239
    taosWLockLatch(&pe->latch);
  }
240

H
Haojun Liao 已提交
241 242 243 244 245 246 247
  SHashNode *pNode = pe->next;
  if (pe->num > 0) {
    assert(pNode != NULL);
  } else {
    assert(pNode == NULL);
  }

H
Haojun Liao 已提交
248
  SHashNode* prev = NULL;
H
Haojun Liao 已提交
249
  while (pNode) {
250
    if ((pNode->keyLen == keyLen) && ((*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0) && pNode->removed == 0) {
H
Haojun Liao 已提交
251 252
      assert(pNode->hashVal == hashVal);
      break;
253 254
    }

H
Haojun Liao 已提交
255
    prev = pNode;
H
Haojun Liao 已提交
256 257 258 259 260
    pNode = pNode->next;
  }

  if (pNode == NULL) {
    // no data in hash table with the specified key, add it into hash table
H
Haojun Liao 已提交
261
    pushfrontNodeInEntryList(pe, pNewNode);
H
Haojun Liao 已提交
262

H
Haojun Liao 已提交
263 264 265 266 267 268
    if (pe->num == 0) {
      assert(pe->next == NULL);
    } else {
      assert(pe->next != NULL);
    }

H
Haojun Liao 已提交
269
    if (pHashObj->type == HASH_ENTRY_LOCK) {
H
Haojun Liao 已提交
270 271 272 273
      taosWUnLockLatch(&pe->latch);
    }

    // enable resize
H
Haojun Liao 已提交
274
    __rd_unlock(&pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
275
    atomic_add_fetch_64(&pHashObj->size, 1);
H
Haojun Liao 已提交
276 277

    return 0;
278
  } else {
H
Haojun Liao 已提交
279 280
    // not support the update operation, return error
    if (pHashObj->enableUpdate) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
281
      doUpdateHashNode(pHashObj, pe, prev, pNode, pNewNode);
282 283
    } else {
      DO_FREE_HASH_NODE(pNewNode);
H
Haojun Liao 已提交
284 285
    }

H
Haojun Liao 已提交
286
    if (pHashObj->type == HASH_ENTRY_LOCK) {
H
Haojun Liao 已提交
287 288 289 290
      taosWUnLockLatch(&pe->latch);
    }

    // enable resize
H
Haojun Liao 已提交
291
    __rd_unlock(&pHashObj->lock, pHashObj->type);
292

H
Haojun Liao 已提交
293
    return pHashObj->enableUpdate ? 0 : -1;
H
Haojun Liao 已提交
294
  }
295 296
}

H
hjLiao 已提交
297
void *taosHashGet(SHashObj *pHashObj, const void *key, size_t keyLen) {
H
Haojun Liao 已提交
298
  return taosHashGetClone(pHashObj, key, keyLen, NULL);
H
Haojun Liao 已提交
299
}
H
Haojun Liao 已提交
300

301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
//TODO(yihaoDeng), merge with taosHashGetClone
void* taosHashGetCloneExt(SHashObj *pHashObj, const void *key, size_t keyLen, void (*fp)(void *), void** d, size_t *sz) {
  if (taosHashTableEmpty(pHashObj) || keyLen == 0 || key == NULL) {
    return NULL;
  }

  uint32_t hashVal = (*pHashObj->hashFp)(key, (uint32_t)keyLen);

  // only add the read lock to disable the resize process
  __rd_lock(&pHashObj->lock, pHashObj->type);

  int32_t     slot = HASH_INDEX(hashVal, pHashObj->capacity);
  SHashEntry *pe = pHashObj->hashList[slot];

  // no data, return directly
  if (atomic_load_32(&pe->num) == 0) {
    __rd_unlock(&pHashObj->lock, pHashObj->type);
    return NULL;
  }

  char *data = NULL;

  // lock entry
  if (pHashObj->type == HASH_ENTRY_LOCK) {
    taosRLockLatch(&pe->latch);
  }

  if (pe->num > 0) {
    assert(pe->next != NULL);
  } else {
    assert(pe->next == NULL);
  }

  SHashNode *pNode = doSearchInEntryList(pHashObj, pe, key, keyLen, hashVal);
  if (pNode != NULL) {
    if (fp != NULL) {
      fp(GET_HASH_NODE_DATA(pNode));
    }
     
    if (*d == NULL) {
      *sz =  pNode->dataLen + EXT_SIZE;
      *d  =  calloc(1, *sz);   
    } else if (*sz < pNode->dataLen){
      *sz = pNode->dataLen + EXT_SIZE;
      *d  = realloc(*d, *sz); 
    }
347
    memcpy((char *)(*d), GET_HASH_NODE_DATA(pNode), pNode->dataLen);
348 349
    // just make runtime happy 
    if ((*sz) - pNode->dataLen > 0) {
350
      memset((char *)(*d) + pNode->dataLen, 0, (*sz) - pNode->dataLen);
351 352 353 354 355 356 357 358 359 360 361 362
    } 

    data = GET_HASH_NODE_DATA(pNode);
  }

  if (pHashObj->type == HASH_ENTRY_LOCK) {
    taosRUnLockLatch(&pe->latch);
  }

  __rd_unlock(&pHashObj->lock, pHashObj->type);
  return data;
}
H
Haojun Liao 已提交
363

H
Haojun Liao 已提交
364
void* taosHashGetClone(SHashObj *pHashObj, const void *key, size_t keyLen, void* d) {
365
  if (taosHashTableEmpty(pHashObj) || keyLen == 0 || key == NULL) {
H
Haojun Liao 已提交
366 367 368
    return NULL;
  }

S
Shengliang Guan 已提交
369
  uint32_t hashVal = (*pHashObj->hashFp)(key, (uint32_t)keyLen);
370

H
Haojun Liao 已提交
371
  // only add the read lock to disable the resize process
H
Haojun Liao 已提交
372
  __rd_lock(&pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
373

H
Haojun Liao 已提交
374 375
  int32_t     slot = HASH_INDEX(hashVal, pHashObj->capacity);
  SHashEntry *pe = pHashObj->hashList[slot];
376

H
Haojun Liao 已提交
377 378 379
  // no data, return directly
  if (atomic_load_32(&pe->num) == 0) {
    __rd_unlock(&pHashObj->lock, pHashObj->type);
380 381
    return NULL;
  }
H
Haojun Liao 已提交
382 383 384 385 386 387 388 389

  char *data = NULL;

  // lock entry
  if (pHashObj->type == HASH_ENTRY_LOCK) {
    taosRLockLatch(&pe->latch);
  }

H
Haojun Liao 已提交
390 391 392 393 394 395
  if (pe->num > 0) {
    assert(pe->next != NULL);
  } else {
    assert(pe->next == NULL);
  }

396
  SHashNode *pNode = doSearchInEntryList(pHashObj, pe, key, keyLen, hashVal);
H
Haojun Liao 已提交
397
  if (pNode != NULL) {
H
Haojun Liao 已提交
398 399
    if (pHashObj->callbackFp != NULL) {
      pHashObj->callbackFp(GET_HASH_NODE_DATA(pNode));
H
Haojun Liao 已提交
400
    }
H
Haojun Liao 已提交
401

H
Haojun Liao 已提交
402
    if (d != NULL) {
403
      memcpy(d, GET_HASH_NODE_DATA(pNode), pNode->dataLen);
H
Haojun Liao 已提交
404
    }
405 406

    data = GET_HASH_NODE_DATA(pNode);
H
Haojun Liao 已提交
407 408 409 410 411 412 413 414
  }

  if (pHashObj->type == HASH_ENTRY_LOCK) {
    taosRUnLockLatch(&pe->latch);
  }

  __rd_unlock(&pHashObj->lock, pHashObj->type);
  return data;
415 416
}

H
Haojun Liao 已提交
417
int32_t taosHashRemove(SHashObj *pHashObj, const void *key, size_t keyLen/*, void *data, size_t dsize*/) {
418
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj)) {
H
Haojun Liao 已提交
419 420 421
    return -1;
  }

S
Shengliang Guan 已提交
422
  uint32_t hashVal = (*pHashObj->hashFp)(key, (uint32_t)keyLen);
H
Haojun Liao 已提交
423

H
Haojun Liao 已提交
424
  // disable the resize process
H
Haojun Liao 已提交
425
  __rd_lock(&pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
426

H
Haojun Liao 已提交
427
  int32_t     slot = HASH_INDEX(hashVal, pHashObj->capacity);
H
Haojun Liao 已提交
428 429
  SHashEntry *pe = pHashObj->hashList[slot];

430 431 432 433 434
  if (pHashObj->type == HASH_ENTRY_LOCK) {
    taosWLockLatch(&pe->latch);
  }

  // double check after locked
H
Haojun Liao 已提交
435
  if (pe->num == 0) {
H
Haojun Liao 已提交
436
    assert(pe->next == NULL);
437
    taosWUnLockLatch(&pe->latch);
H
Haojun Liao 已提交
438

H
Haojun Liao 已提交
439
    __rd_unlock(&pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
440
    return -1;
H
Haojun Liao 已提交
441 442
  }

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
443
  int code = -1;
H
Haojun Liao 已提交
444
  SHashNode *pNode = pe->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
445
  SHashNode *prevNode = NULL;
H
Haojun Liao 已提交
446

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
447
  while (pNode) {
H
Haojun Liao 已提交
448
    if ((pNode->keyLen == keyLen) && ((*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0) && pNode->removed == 0)
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
449
      break;
H
Haojun Liao 已提交
450

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
451 452 453
    prevNode = pNode;
    pNode = pNode->next;
  }
H
Haojun Liao 已提交
454

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
455 456
  if (pNode) {
    code = 0;  // it is found
H
Haojun Liao 已提交
457

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
458
    pNode->count--;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
459
    pNode->removed = 1;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
460 461 462 463 464 465
    if (pNode->count <= 0) {
      if (prevNode) {
        prevNode->next = pNode->next;
      } else {
        pe->next = pNode->next;
      }
H
Haojun Liao 已提交
466 467

//      if (data) memcpy(data, GET_HASH_NODE_DATA(pNode), dsize);
H
Haojun Liao 已提交
468

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
469 470 471 472
      pe->num--;
      atomic_sub_fetch_64(&pHashObj->size, 1);
      FREE_HASH_NODE(pHashObj, pNode);
    }
H
Haojun Liao 已提交
473
  }
474

H
Haojun Liao 已提交
475
  if (pHashObj->type == HASH_ENTRY_LOCK) {
H
Haojun Liao 已提交
476
    taosWUnLockLatch(&pe->latch);
H
Haojun Liao 已提交
477 478
  }

H
Haojun Liao 已提交
479
  __rd_unlock(&pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
480

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
481
  return code;
H
Haojun Liao 已提交
482 483
}

H
Haojun Liao 已提交
484
int32_t taosHashCondTraverse(SHashObj *pHashObj, bool (*fp)(void *, void *), void *param) {
485
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj)) {
H
Haojun Liao 已提交
486 487 488 489 490 491
    return 0;
  }

  // disable the resize process
  __rd_lock(&pHashObj->lock, pHashObj->type);

S
Shengliang Guan 已提交
492
  int32_t numOfEntries = (int32_t)pHashObj->capacity;
H
Haojun Liao 已提交
493 494
  for (int32_t i = 0; i < numOfEntries; ++i) {
    SHashEntry *pEntry = pHashObj->hashList[i];
H
Haojun Liao 已提交
495
    if (pEntry->num == 0) {
H
Haojun Liao 已提交
496 497 498 499 500 501 502
      continue;
    }

    if (pHashObj->type == HASH_ENTRY_LOCK) {
      taosWLockLatch(&pEntry->latch);
    }

H
Haojun Liao 已提交
503
    // todo remove the first node
H
Haojun Liao 已提交
504 505
    SHashNode *pNode = NULL;
    while((pNode = pEntry->next) != NULL) {
H
Haojun Liao 已提交
506
      if (fp && (!fp(param, GET_HASH_NODE_DATA(pNode)))) {
H
Haojun Liao 已提交
507
        pEntry->num -= 1;
508 509
        atomic_sub_fetch_64(&pHashObj->size, 1);

H
Haojun Liao 已提交
510 511
        pEntry->next = pNode->next;

H
Haojun Liao 已提交
512 513 514 515 516 517
        if (pEntry->num == 0) {
          assert(pEntry->next == NULL);
        } else {
          assert(pEntry->next != NULL);
        }

H
Haojun Liao 已提交
518 519 520
        FREE_HASH_NODE(pHashObj, pNode);
      } else {
        break;
H
Haojun Liao 已提交
521
      }
H
Haojun Liao 已提交
522 523 524 525 526 527
    }

    // handle the following node
    if (pNode != NULL) {
      assert(pNode == pEntry->next);
      SHashNode *pNext = NULL;
H
Haojun Liao 已提交
528

H
Haojun Liao 已提交
529 530
      while ((pNext = pNode->next) != NULL) {
        // not qualified, remove it
H
Haojun Liao 已提交
531
        if (fp && (!fp(param, GET_HASH_NODE_DATA(pNext)))) {
H
Haojun Liao 已提交
532 533
          pNode->next = pNext->next;
          pEntry->num -= 1;
534
          atomic_sub_fetch_64(&pHashObj->size, 1);
H
Haojun Liao 已提交
535

H
Haojun Liao 已提交
536 537 538 539 540 541
          if (pEntry->num == 0) {
            assert(pEntry->next == NULL);
          } else {
            assert(pEntry->next != NULL);
          }

H
Haojun Liao 已提交
542 543 544 545 546
          FREE_HASH_NODE(pHashObj, pNext);
        } else {
          pNode = pNext;
        }
      }
H
Haojun Liao 已提交
547 548 549 550 551 552 553 554 555 556 557
    }

    if (pHashObj->type == HASH_ENTRY_LOCK) {
      taosWUnLockLatch(&pEntry->latch);
    }
  }

  __rd_unlock(&pHashObj->lock, pHashObj->type);
  return 0;
}

558
void taosHashClear(SHashObj *pHashObj) {
H
Haojun Liao 已提交
559 560 561
  if (pHashObj == NULL) {
    return;
  }
562 563 564

  SHashNode *pNode, *pNext;

H
Haojun Liao 已提交
565
  __wr_lock(&pHashObj->lock, pHashObj->type);
566

567 568 569 570 571 572
  for (int32_t i = 0; i < pHashObj->capacity; ++i) {
    SHashEntry *pEntry = pHashObj->hashList[i];
    if (pEntry->num == 0) {
      assert(pEntry->next == 0);
      continue;
    }
573

574 575
    pNode = pEntry->next;
    assert(pNode != NULL);
H
Haojun Liao 已提交
576

577 578 579
    while (pNode) {
      pNext = pNode->next;
      FREE_HASH_NODE(pHashObj, pNode);
H
hjxilinx 已提交
580

581
      pNode = pNext;
582 583
    }

584 585
    pEntry->num = 0;
    pEntry->next = NULL;
586 587
  }

588
  pHashObj->size = 0;
H
Haojun Liao 已提交
589
  __wr_unlock(&pHashObj->lock, pHashObj->type);
590 591 592 593 594 595 596
}

void taosHashCleanup(SHashObj *pHashObj) {
  if (pHashObj == NULL) {
    return;
  }

597
  taosHashClear(pHashObj);
598
  tfree(pHashObj->hashList);
H
Haojun Liao 已提交
599 600 601

  // destroy mem block
  size_t memBlock = taosArrayGetSize(pHashObj->pMemBlock);
H
Haojun Liao 已提交
602 603
  for (int32_t i = 0; i < memBlock; ++i) {
    void *p = taosArrayGetP(pHashObj->pMemBlock, i);
S
TD-1848  
Shengliang Guan 已提交
604
    tfree(p);
H
Haojun Liao 已提交
605 606 607
  }

  taosArrayDestroy(pHashObj->pMemBlock);
608

H
hjxilinx 已提交
609 610
  memset(pHashObj, 0, sizeof(SHashObj));
  free(pHashObj);
611 612 613
}

// for profile only
H
hjxilinx 已提交
614
int32_t taosHashGetMaxOverflowLinkLength(const SHashObj *pHashObj) {
615
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj)) {
616 617
    return 0;
  }
H
hjxilinx 已提交
618

619
  int32_t num = 0;
H
hjxilinx 已提交
620 621

  for (int32_t i = 0; i < pHashObj->size; ++i) {
H
Haojun Liao 已提交
622 623 624
    SHashEntry *pEntry = pHashObj->hashList[i];
    if (num < pEntry->num) {
      num = pEntry->num;
625 626
    }
  }
H
hjxilinx 已提交
627

628 629
  return num;
}
H
hjLiao 已提交
630 631

void taosHashTableResize(SHashObj *pHashObj) {
H
Haojun Liao 已提交
632
  if (!HASH_NEED_RESIZE(pHashObj)) {
H
hjLiao 已提交
633 634
    return;
  }
H
Haojun Liao 已提交
635

H
hjLiao 已提交
636 637 638
  // double the original capacity
  SHashNode *pNode = NULL;
  SHashNode *pNext = NULL;
H
Haojun Liao 已提交
639

S
Shengliang Guan 已提交
640
  int32_t newSize = (int32_t)(pHashObj->capacity << 1u);
H
hjLiao 已提交
641
  if (newSize > HASH_MAX_CAPACITY) {
H
Haojun Liao 已提交
642 643
    //    uDebug("current capacity:%d, maximum capacity:%d, no resize applied due to limitation is reached",
    //           pHashObj->capacity, HASH_MAX_CAPACITY);
H
hjLiao 已提交
644 645 646
    return;
  }

H
Haojun Liao 已提交
647
  int64_t st = taosGetTimestampUs();
648
  void *pNewEntryList = realloc(pHashObj->hashList, sizeof(void *) * newSize);
H
Haojun Liao 已提交
649 650
  if (pNewEntryList == NULL) {  // todo handle error
    //    uDebug("cache resize failed due to out of memory, capacity remain:%d", pHashObj->capacity);
H
hjLiao 已提交
651 652
    return;
  }
H
Haojun Liao 已提交
653

H
Haojun Liao 已提交
654 655 656
  pHashObj->hashList = pNewEntryList;

  size_t inc = newSize - pHashObj->capacity;
H
Haojun Liao 已提交
657
  void * p = calloc(inc, sizeof(SHashEntry));
H
Haojun Liao 已提交
658

H
Haojun Liao 已提交
659
  for (int32_t i = 0; i < inc; ++i) {
S
Shengliang Guan 已提交
660
    pHashObj->hashList[i + pHashObj->capacity] = (void *)((char *)p + i * sizeof(SHashEntry));
H
Haojun Liao 已提交
661 662 663 664
  }

  taosArrayPush(pHashObj->pMemBlock, &p);

H
hjLiao 已提交
665 666
  pHashObj->capacity = newSize;
  for (int32_t i = 0; i < pHashObj->capacity; ++i) {
H
Haojun Liao 已提交
667
    SHashEntry *pe = pHashObj->hashList[i];
H
Haojun Liao 已提交
668 669 670 671 672 673 674

    if (pe->num == 0) {
      assert(pe->next == NULL);
    } else {
      assert(pe->next != NULL);
    }

H
Haojun Liao 已提交
675
    if (pe->num == 0) {
H
Haojun Liao 已提交
676
      assert(pe->next == NULL);
H
Haojun Liao 已提交
677
      continue;
H
hjLiao 已提交
678
    }
H
Haojun Liao 已提交
679

H
Haojun Liao 已提交
680
    while ((pNode = pe->next) != NULL) {
H
hjLiao 已提交
681
      int32_t j = HASH_INDEX(pNode->hashVal, pHashObj->capacity);
H
Haojun Liao 已提交
682 683 684
      if (j != i) {
        pe->num -= 1;
        pe->next = pNode->next;
H
Haojun Liao 已提交
685

H
Haojun Liao 已提交
686 687 688 689 690 691
        if (pe->num == 0) {
          assert(pe->next == NULL);
        } else {
          assert(pe->next != NULL);
        }

H
Haojun Liao 已提交
692
        SHashEntry *pNewEntry = pHashObj->hashList[j];
H
Haojun Liao 已提交
693 694 695 696 697
        pushfrontNodeInEntryList(pNewEntry, pNode);
      } else {
        break;
      }
    }
H
Haojun Liao 已提交
698

H
Haojun Liao 已提交
699 700 701 702
    if (pNode != NULL) {
      while ((pNext = pNode->next) != NULL) {
        int32_t j = HASH_INDEX(pNext->hashVal, pHashObj->capacity);
        if (j != i) {
H
Haojun Liao 已提交
703 704
          pe->num -= 1;

H
Haojun Liao 已提交
705 706 707 708 709
          pNode->next = pNext->next;
          pNext->next = NULL;

          // added into new slot
          SHashEntry *pNewEntry = pHashObj->hashList[j];
H
Haojun Liao 已提交
710 711 712 713 714 715 716

          if (pNewEntry->num == 0) {
            assert(pNewEntry->next == NULL);
          } else {
            assert(pNewEntry->next != NULL);
          }

H
Haojun Liao 已提交
717 718 719 720
          pushfrontNodeInEntryList(pNewEntry, pNext);
        } else {
          pNode = pNext;
        }
H
hjLiao 已提交
721
      }
H
Haojun Liao 已提交
722 723 724 725 726 727 728

      if (pe->num == 0) {
        assert(pe->next == NULL);
      } else {
        assert(pe->next != NULL);
      }

H
hjLiao 已提交
729
    }
H
Haojun Liao 已提交
730

H
hjLiao 已提交
731 732
  }

H
Haojun Liao 已提交
733 734
  int64_t et = taosGetTimestampUs();

S
TD-1530  
Shengliang Guan 已提交
735
  uDebug("hash table resize completed, new capacity:%d, load factor:%f, elapsed time:%fms", (int32_t)pHashObj->capacity,
H
Haojun Liao 已提交
736
           ((double)pHashObj->size) / pHashObj->capacity, (et - st) / 1000.0);
H
hjLiao 已提交
737 738 739
}

SHashNode *doCreateHashNode(const void *key, size_t keyLen, const void *pData, size_t dsize, uint32_t hashVal) {
740
  SHashNode *pNewNode = malloc(sizeof(SHashNode) + keyLen + dsize);
H
Haojun Liao 已提交
741

H
hjLiao 已提交
742 743 744 745
  if (pNewNode == NULL) {
    uError("failed to allocate memory, reason:%s", strerror(errno));
    return NULL;
  }
H
Haojun Liao 已提交
746

747
  pNewNode->keyLen  = (uint32_t)keyLen;
H
hjLiao 已提交
748
  pNewNode->hashVal = hashVal;
H
Haojun Liao 已提交
749
  pNewNode->dataLen = (uint32_t) dsize;
750 751 752
  pNewNode->count   = 1;
  pNewNode->removed = 0;
  pNewNode->next    = NULL;
H
Haojun Liao 已提交
753 754 755 756

  memcpy(GET_HASH_NODE_DATA(pNewNode), pData, dsize);
  memcpy(GET_HASH_NODE_KEY(pNewNode), key, keyLen);

H
hjLiao 已提交
757 758 759
  return pNewNode;
}

H
Haojun Liao 已提交
760
void pushfrontNodeInEntryList(SHashEntry *pEntry, SHashNode *pNode) {
H
Haojun Liao 已提交
761
  assert(pNode != NULL && pEntry != NULL);
H
Haojun Liao 已提交
762

H
Haojun Liao 已提交
763 764
  pNode->next = pEntry->next;
  pEntry->next = pNode;
H
Haojun Liao 已提交
765 766

  pEntry->num += 1;
H
hjLiao 已提交
767 768
}

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
769 770 771 772
size_t taosHashGetMemSize(const SHashObj *pHashObj) {
  if (pHashObj == NULL) {
    return 0;
  }
H
Haojun Liao 已提交
773

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
774 775
  return (pHashObj->capacity * (sizeof(SHashEntry) + POINTER_BYTES)) + sizeof(SHashNode) * taosHashGetSize(pHashObj) + sizeof(SHashObj);
}
H
Haojun Liao 已提交
776

W
wpan 已提交
777
FORCE_INLINE void *taosHashGetDataKey(SHashObj *pHashObj, void *data) {
W
wpan 已提交
778 779
  SHashNode * node = GET_HASH_PNODE(data);
  return GET_HASH_NODE_KEY(node);
W
wpan 已提交
780 781
}

W
wpan 已提交
782 783 784 785 786 787
FORCE_INLINE uint32_t taosHashGetDataKeyLen(SHashObj *pHashObj, void *data) {
  SHashNode * node = GET_HASH_PNODE(data);
  return node->keyLen;
}


陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
788 789
// release the pNode, return next pNode, and lock the current entry
static void *taosHashReleaseNode(SHashObj *pHashObj, void *p, int *slot) {
H
Haojun Liao 已提交
790

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
  SHashNode *pOld = (SHashNode *)GET_HASH_PNODE(p);
  SHashNode *prevNode = NULL;

  *slot = HASH_INDEX(pOld->hashVal, pHashObj->capacity);
  SHashEntry *pe = pHashObj->hashList[*slot];

  // lock entry
  if (pHashObj->type == HASH_ENTRY_LOCK) {
    taosWLockLatch(&pe->latch);
  }

  SHashNode *pNode = pe->next;

  while (pNode) {
    if (pNode == pOld) 
      break;

    prevNode = pNode;
    pNode = pNode->next;
  }

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
812
  if (pNode) { 
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
813
    pNode = pNode->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
814 815 816 817 818
    while (pNode) {
      if (pNode->removed == 0) break;
      pNode = pNode->next;
    }

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
    pOld->count--;
    if (pOld->count <=0) {
      if (prevNode) {
        prevNode->next = pOld->next;
      } else {
        pe->next = pOld->next;
      }
   
      pe->num--;
      atomic_sub_fetch_64(&pHashObj->size, 1);
      FREE_HASH_NODE(pHashObj, pOld);
    } 
  } else {
    uError("pNode:%p data:%p is not there!!!", pNode, p);
  }

  return pNode;
}

void *taosHashIterate(SHashObj *pHashObj, void *p) {
  if (pHashObj == NULL) return NULL; 

  int  slot = 0;
  char *data = NULL;

  // only add the read lock to disable the resize process
  __rd_lock(&pHashObj->lock, pHashObj->type);

  SHashNode *pNode = NULL;
  if (p) {
    pNode = taosHashReleaseNode(pHashObj, p, &slot);
    if (pNode == NULL) {
      SHashEntry *pe = pHashObj->hashList[slot];
      if (pHashObj->type == HASH_ENTRY_LOCK) {
        taosWUnLockLatch(&pe->latch);
      } 

      slot = slot + 1;
H
Haojun Liao 已提交
857
    }
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
858
  }
H
Haojun Liao 已提交
859

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
860 861 862
  if (pNode == NULL) {
    for (; slot < pHashObj->capacity; ++slot) {
      SHashEntry *pe = pHashObj->hashList[slot];
H
Haojun Liao 已提交
863

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
864 865 866 867 868 869
      // lock entry
      if (pHashObj->type == HASH_ENTRY_LOCK) {
        taosWLockLatch(&pe->latch);
      }

      pNode = pe->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
870 871 872 873 874
      while (pNode) {
        if (pNode->removed == 0) break;
        pNode = pNode->next;
      }

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
875 876 877 878 879
      if (pNode) break;

      if (pHashObj->type == HASH_ENTRY_LOCK) {
        taosWUnLockLatch(&pe->latch);
      } 
H
Haojun Liao 已提交
880
    }
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
881
  }
H
Haojun Liao 已提交
882

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
883 884 885 886 887 888 889
  if (pNode) {
    SHashEntry *pe = pHashObj->hashList[slot];
    pNode->count++;
    data = GET_HASH_NODE_DATA(pNode);
    if (pHashObj->type == HASH_ENTRY_LOCK) {
      taosWUnLockLatch(&pe->latch);
    } 
H
hjLiao 已提交
890
  }
H
Haojun Liao 已提交
891

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
892 893 894
  __rd_unlock(&pHashObj->lock, pHashObj->type);
  return data;

H
hjLiao 已提交
895
}
H
Haojun Liao 已提交
896

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
897 898
void taosHashCancelIterate(SHashObj *pHashObj, void *p) {
  if (pHashObj == NULL || p == NULL) return;
H
Haojun Liao 已提交
899

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
900 901 902 903 904 905 906 907 908 909 910 911
  // only add the read lock to disable the resize process
  __rd_lock(&pHashObj->lock, pHashObj->type);

  int slot;
  taosHashReleaseNode(pHashObj, p, &slot);

  SHashEntry *pe = pHashObj->hashList[slot];
  if (pHashObj->type == HASH_ENTRY_LOCK) {
    taosWUnLockLatch(&pe->latch);
  } 

  __rd_unlock(&pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
912
}