thash.c 22.8 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 "taos.h"
20
#include "tdef.h"
21

22
#define EXT_SIZE 1024 
23

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

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

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

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

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

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

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

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

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

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

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

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

    pNode = pNode->next;
  }

  return pNode;
}

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

H
hjLiao 已提交
99 100 101 102 103 104 105 106 107
/**
 * @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);
108

H
hjLiao 已提交
109 110 111 112 113 114 115 116 117 118
/**
 * 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) 已提交
119
static FORCE_INLINE SHashNode *doUpdateHashNode(SHashObj *pHashObj, SHashEntry* pe, SHashNode* prev, SHashNode *pNode, SHashNode *pNewNode) {
H
Haojun Liao 已提交
120
  assert(pNode->keyLen == pNewNode->keyLen);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
121 122

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
129 130 131 132 133 134 135 136 137
  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 已提交
138 139
  return pNewNode;
}
140

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

149 150 151 152 153 154 155 156
/**
 * 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);

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

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

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

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

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

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

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

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

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

208 209 210 211 212 213 214 215 216 217
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;
}
218

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

D
dapan1121 已提交
294
    return pHashObj->enableUpdate ? 0 : -2;
H
Haojun Liao 已提交
295
  }
296 297
}

H
hjLiao 已提交
298
void *taosHashGet(SHashObj *pHashObj, const void *key, size_t keyLen) {
H
Haojun Liao 已提交
299
  return taosHashGetClone(pHashObj, key, keyLen, NULL);
H
Haojun Liao 已提交
300
}
H
Haojun Liao 已提交
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 347
//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); 
    }
348
    memcpy((char *)(*d), GET_HASH_NODE_DATA(pNode), pNode->dataLen);
349 350
    // just make runtime happy 
    if ((*sz) - pNode->dataLen > 0) {
351
      memset((char *)(*d) + pNode->dataLen, 0, (*sz) - pNode->dataLen);
352 353 354 355 356 357 358 359 360 361 362 363
    } 

    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 已提交
364

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

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

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

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

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

  char *data = NULL;

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

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

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

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

D
dapan1121 已提交
407 408 409 410
    if (acquire) {
      pNode->count++;
    }

411
    data = GET_HASH_NODE_DATA(pNode);
H
Haojun Liao 已提交
412 413 414 415 416 417 418 419
  }

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

  __rd_unlock(&pHashObj->lock, pHashObj->type);
  return data;
420 421
}

D
dapan1121 已提交
422 423 424 425 426 427 428 429 430
void* taosHashGetClone(SHashObj *pHashObj, const void *key, size_t keyLen, void* d) {
  return taosHashGetCloneImpl(pHashObj, key, keyLen, d, false);
}

void* taosHashAcquire(SHashObj *pHashObj, const void *key, size_t keyLen) {
  return taosHashGetCloneImpl(pHashObj, key, keyLen, NULL, true);
}


H
Haojun Liao 已提交
431
int32_t taosHashRemove(SHashObj *pHashObj, const void *key, size_t keyLen/*, void *data, size_t dsize*/) {
432
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj)) {
H
Haojun Liao 已提交
433 434 435
    return -1;
  }

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

H
Haojun Liao 已提交
438
  // disable the resize process
H
Haojun Liao 已提交
439
  __rd_lock(&pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
440

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

444 445 446 447 448
  if (pHashObj->type == HASH_ENTRY_LOCK) {
    taosWLockLatch(&pe->latch);
  }

  // double check after locked
H
Haojun Liao 已提交
449
  if (pe->num == 0) {
H
Haojun Liao 已提交
450
    assert(pe->next == NULL);
451
    taosWUnLockLatch(&pe->latch);
H
Haojun Liao 已提交
452

H
Haojun Liao 已提交
453
    __rd_unlock(&pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
454
    return -1;
H
Haojun Liao 已提交
455 456
  }

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
457
  int code = -1;
H
Haojun Liao 已提交
458
  SHashNode *pNode = pe->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
459
  SHashNode *prevNode = NULL;
H
Haojun Liao 已提交
460

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
465 466 467
    prevNode = pNode;
    pNode = pNode->next;
  }
H
Haojun Liao 已提交
468

H
Haojun Liao 已提交
469

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
473
    pNode->count--;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
474
    pNode->removed = 1;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
475 476 477 478 479 480
    if (pNode->count <= 0) {
      if (prevNode) {
        prevNode->next = pNode->next;
      } else {
        pe->next = pNode->next;
      }
H
Haojun Liao 已提交
481 482

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
484 485 486 487
      pe->num--;
      atomic_sub_fetch_64(&pHashObj->size, 1);
      FREE_HASH_NODE(pHashObj, pNode);
    }
H
Haojun Liao 已提交
488
  }
489

H
Haojun Liao 已提交
490
  if (pHashObj->type == HASH_ENTRY_LOCK) {
H
Haojun Liao 已提交
491
    taosWUnLockLatch(&pe->latch);
H
Haojun Liao 已提交
492 493
  }

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
496
  return code;
H
Haojun Liao 已提交
497 498
}

H
Haojun Liao 已提交
499
int32_t taosHashCondTraverse(SHashObj *pHashObj, bool (*fp)(void *, void *), void *param) {
500
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj)) {
H
Haojun Liao 已提交
501 502 503 504 505 506
    return 0;
  }

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

S
Shengliang Guan 已提交
507
  int32_t numOfEntries = (int32_t)pHashObj->capacity;
H
Haojun Liao 已提交
508 509
  for (int32_t i = 0; i < numOfEntries; ++i) {
    SHashEntry *pEntry = pHashObj->hashList[i];
H
Haojun Liao 已提交
510
    if (pEntry->num == 0) {
H
Haojun Liao 已提交
511 512 513 514 515 516 517
      continue;
    }

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

H
Haojun Liao 已提交
518
    // todo remove the first node
H
Haojun Liao 已提交
519 520
    SHashNode *pNode = NULL;
    while((pNode = pEntry->next) != NULL) {
H
Haojun Liao 已提交
521
      if (fp && (!fp(param, GET_HASH_NODE_DATA(pNode)))) {
H
Haojun Liao 已提交
522
        pEntry->num -= 1;
523 524
        atomic_sub_fetch_64(&pHashObj->size, 1);

H
Haojun Liao 已提交
525 526
        pEntry->next = pNode->next;

H
Haojun Liao 已提交
527 528 529 530 531 532
        if (pEntry->num == 0) {
          assert(pEntry->next == NULL);
        } else {
          assert(pEntry->next != NULL);
        }

H
Haojun Liao 已提交
533 534 535
        FREE_HASH_NODE(pHashObj, pNode);
      } else {
        break;
H
Haojun Liao 已提交
536
      }
H
Haojun Liao 已提交
537 538 539 540 541 542
    }

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

H
Haojun Liao 已提交
544 545
      while ((pNext = pNode->next) != NULL) {
        // not qualified, remove it
H
Haojun Liao 已提交
546
        if (fp && (!fp(param, GET_HASH_NODE_DATA(pNext)))) {
H
Haojun Liao 已提交
547 548
          pNode->next = pNext->next;
          pEntry->num -= 1;
549
          atomic_sub_fetch_64(&pHashObj->size, 1);
H
Haojun Liao 已提交
550

H
Haojun Liao 已提交
551 552 553 554 555 556
          if (pEntry->num == 0) {
            assert(pEntry->next == NULL);
          } else {
            assert(pEntry->next != NULL);
          }

H
Haojun Liao 已提交
557 558 559 560 561
          FREE_HASH_NODE(pHashObj, pNext);
        } else {
          pNode = pNext;
        }
      }
H
Haojun Liao 已提交
562 563 564 565 566 567 568 569 570 571 572
    }

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

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

573
void taosHashClear(SHashObj *pHashObj) {
H
Haojun Liao 已提交
574 575 576
  if (pHashObj == NULL) {
    return;
  }
577 578 579

  SHashNode *pNode, *pNext;

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

582 583 584 585 586 587
  for (int32_t i = 0; i < pHashObj->capacity; ++i) {
    SHashEntry *pEntry = pHashObj->hashList[i];
    if (pEntry->num == 0) {
      assert(pEntry->next == 0);
      continue;
    }
588

589 590
    pNode = pEntry->next;
    assert(pNode != NULL);
H
Haojun Liao 已提交
591

592 593 594
    while (pNode) {
      pNext = pNode->next;
      FREE_HASH_NODE(pHashObj, pNode);
H
hjxilinx 已提交
595

596
      pNode = pNext;
597 598
    }

599 600
    pEntry->num = 0;
    pEntry->next = NULL;
601 602
  }

603
  pHashObj->size = 0;
H
Haojun Liao 已提交
604
  __wr_unlock(&pHashObj->lock, pHashObj->type);
605 606 607 608 609 610 611
}

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

612
  taosHashClear(pHashObj);
613
  tfree(pHashObj->hashList);
H
Haojun Liao 已提交
614 615 616

  // destroy mem block
  size_t memBlock = taosArrayGetSize(pHashObj->pMemBlock);
H
Haojun Liao 已提交
617 618
  for (int32_t i = 0; i < memBlock; ++i) {
    void *p = taosArrayGetP(pHashObj->pMemBlock, i);
S
TD-1848  
Shengliang Guan 已提交
619
    tfree(p);
H
Haojun Liao 已提交
620 621 622
  }

  taosArrayDestroy(pHashObj->pMemBlock);
623

H
hjxilinx 已提交
624 625
  memset(pHashObj, 0, sizeof(SHashObj));
  free(pHashObj);
626 627 628
}

// for profile only
H
hjxilinx 已提交
629
int32_t taosHashGetMaxOverflowLinkLength(const SHashObj *pHashObj) {
630
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj)) {
631 632
    return 0;
  }
H
hjxilinx 已提交
633

634
  int32_t num = 0;
H
hjxilinx 已提交
635 636

  for (int32_t i = 0; i < pHashObj->size; ++i) {
H
Haojun Liao 已提交
637 638 639
    SHashEntry *pEntry = pHashObj->hashList[i];
    if (num < pEntry->num) {
      num = pEntry->num;
640 641
    }
  }
H
hjxilinx 已提交
642

643 644
  return num;
}
H
hjLiao 已提交
645 646

void taosHashTableResize(SHashObj *pHashObj) {
H
Haojun Liao 已提交
647
  if (!HASH_NEED_RESIZE(pHashObj)) {
H
hjLiao 已提交
648 649
    return;
  }
H
Haojun Liao 已提交
650

H
hjLiao 已提交
651 652 653
  // double the original capacity
  SHashNode *pNode = NULL;
  SHashNode *pNext = NULL;
H
Haojun Liao 已提交
654

S
Shengliang Guan 已提交
655
  int32_t newSize = (int32_t)(pHashObj->capacity << 1u);
H
hjLiao 已提交
656
  if (newSize > HASH_MAX_CAPACITY) {
H
Haojun Liao 已提交
657 658
    //    uDebug("current capacity:%d, maximum capacity:%d, no resize applied due to limitation is reached",
    //           pHashObj->capacity, HASH_MAX_CAPACITY);
H
hjLiao 已提交
659 660 661
    return;
  }

H
Haojun Liao 已提交
662
  int64_t st = taosGetTimestampUs();
663
  void *pNewEntryList = realloc(pHashObj->hashList, sizeof(void *) * newSize);
H
Haojun Liao 已提交
664 665
  if (pNewEntryList == NULL) {  // todo handle error
    //    uDebug("cache resize failed due to out of memory, capacity remain:%d", pHashObj->capacity);
H
hjLiao 已提交
666 667
    return;
  }
H
Haojun Liao 已提交
668

H
Haojun Liao 已提交
669 670 671
  pHashObj->hashList = pNewEntryList;

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

H
Haojun Liao 已提交
674
  for (int32_t i = 0; i < inc; ++i) {
S
Shengliang Guan 已提交
675
    pHashObj->hashList[i + pHashObj->capacity] = (void *)((char *)p + i * sizeof(SHashEntry));
H
Haojun Liao 已提交
676 677 678 679
  }

  taosArrayPush(pHashObj->pMemBlock, &p);

H
hjLiao 已提交
680 681
  pHashObj->capacity = newSize;
  for (int32_t i = 0; i < pHashObj->capacity; ++i) {
H
Haojun Liao 已提交
682
    SHashEntry *pe = pHashObj->hashList[i];
H
Haojun Liao 已提交
683 684 685 686 687 688 689

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

H
Haojun Liao 已提交
690
    if (pe->num == 0) {
H
Haojun Liao 已提交
691
      assert(pe->next == NULL);
H
Haojun Liao 已提交
692
      continue;
H
hjLiao 已提交
693
    }
H
Haojun Liao 已提交
694

H
Haojun Liao 已提交
695
    while ((pNode = pe->next) != NULL) {
H
hjLiao 已提交
696
      int32_t j = HASH_INDEX(pNode->hashVal, pHashObj->capacity);
H
Haojun Liao 已提交
697 698 699
      if (j != i) {
        pe->num -= 1;
        pe->next = pNode->next;
H
Haojun Liao 已提交
700

H
Haojun Liao 已提交
701 702 703 704 705 706
        if (pe->num == 0) {
          assert(pe->next == NULL);
        } else {
          assert(pe->next != NULL);
        }

H
Haojun Liao 已提交
707
        SHashEntry *pNewEntry = pHashObj->hashList[j];
H
Haojun Liao 已提交
708 709 710 711 712
        pushfrontNodeInEntryList(pNewEntry, pNode);
      } else {
        break;
      }
    }
H
Haojun Liao 已提交
713

H
Haojun Liao 已提交
714 715 716 717
    if (pNode != NULL) {
      while ((pNext = pNode->next) != NULL) {
        int32_t j = HASH_INDEX(pNext->hashVal, pHashObj->capacity);
        if (j != i) {
H
Haojun Liao 已提交
718 719
          pe->num -= 1;

H
Haojun Liao 已提交
720 721 722 723 724
          pNode->next = pNext->next;
          pNext->next = NULL;

          // added into new slot
          SHashEntry *pNewEntry = pHashObj->hashList[j];
H
Haojun Liao 已提交
725 726 727 728 729 730 731

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

H
Haojun Liao 已提交
732 733 734 735
          pushfrontNodeInEntryList(pNewEntry, pNext);
        } else {
          pNode = pNext;
        }
H
hjLiao 已提交
736
      }
H
Haojun Liao 已提交
737 738 739 740 741 742 743

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

H
hjLiao 已提交
744
    }
H
Haojun Liao 已提交
745

H
hjLiao 已提交
746 747
  }

H
Haojun Liao 已提交
748 749
  int64_t et = taosGetTimestampUs();

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

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

H
hjLiao 已提交
757 758 759 760
  if (pNewNode == NULL) {
    uError("failed to allocate memory, reason:%s", strerror(errno));
    return NULL;
  }
H
Haojun Liao 已提交
761

762
  pNewNode->keyLen  = (uint32_t)keyLen;
H
hjLiao 已提交
763
  pNewNode->hashVal = hashVal;
H
Haojun Liao 已提交
764
  pNewNode->dataLen = (uint32_t) dsize;
765 766 767
  pNewNode->count   = 1;
  pNewNode->removed = 0;
  pNewNode->next    = NULL;
H
Haojun Liao 已提交
768 769 770 771

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

H
hjLiao 已提交
772 773 774
  return pNewNode;
}

H
Haojun Liao 已提交
775
void pushfrontNodeInEntryList(SHashEntry *pEntry, SHashNode *pNode) {
H
Haojun Liao 已提交
776
  assert(pNode != NULL && pEntry != NULL);
H
Haojun Liao 已提交
777

H
Haojun Liao 已提交
778 779
  pNode->next = pEntry->next;
  pEntry->next = pNode;
H
Haojun Liao 已提交
780 781

  pEntry->num += 1;
H
hjLiao 已提交
782 783
}

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
784 785 786 787
size_t taosHashGetMemSize(const SHashObj *pHashObj) {
  if (pHashObj == NULL) {
    return 0;
  }
H
Haojun Liao 已提交
788

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

D
dapan1121 已提交
792 793 794 795 796
FORCE_INLINE int32_t taosHashGetKey(void *data, void** key, size_t* keyLen) {
  if (NULL == data || NULL == key) {
    return -1;
  }
  
W
wpan 已提交
797
  SHashNode * node = GET_HASH_PNODE(data);
D
dapan1121 已提交
798 799 800 801
  *key = GET_HASH_NODE_KEY(node);
  *keyLen = node->keyLen;

  return 0;
W
wpan 已提交
802 803
}

W
wpan 已提交
804 805 806 807 808 809
FORCE_INLINE uint32_t taosHashGetDataKeyLen(SHashObj *pHashObj, void *data) {
  SHashNode * node = GET_HASH_PNODE(data);
  return node->keyLen;
}


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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
  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) 已提交
834
  if (pNode) { 
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
835
    pNode = pNode->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
836 837 838 839 840
    while (pNode) {
      if (pNode->removed == 0) break;
      pNode = pNode->next;
    }

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878
    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 已提交
879
    }
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
880
  }
H
Haojun Liao 已提交
881

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
886 887 888 889 890 891
      // lock entry
      if (pHashObj->type == HASH_ENTRY_LOCK) {
        taosWLockLatch(&pe->latch);
      }

      pNode = pe->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
892 893 894 895 896
      while (pNode) {
        if (pNode->removed == 0) break;
        pNode = pNode->next;
      }

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
897 898 899 900 901
      if (pNode) break;

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
905 906 907 908 909 910 911
  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 已提交
912
  }
H
Haojun Liao 已提交
913

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
914 915 916
  __rd_unlock(&pHashObj->lock, pHashObj->type);
  return data;

H
hjLiao 已提交
917
}
H
Haojun Liao 已提交
918

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
922 923 924 925 926 927 928 929 930 931 932 933
  // 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 已提交
934
}
D
dapan1121 已提交
935 936 937 938 939 940

void taosHashRelease(SHashObj *pHashObj, void *p) {
  taosHashCancelIterate(pHashObj, p);
}