thash.c 23.1 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
    }                                       \
                                            \
    DO_FREE_HASH_NODE(_n);                  \
H
Haojun Liao 已提交
38 39
  } 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
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
Haojun Liao 已提交
65
  taosWUnLockLatch(lock);
66 67 68 69 70
}

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

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

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

    pNode = pNode->next;
  }

  return pNode;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

206 207 208 209
int32_t taosHashGetSize(const SHashObj *pHashObj) {
  if (!pHashObj) {
    return 0;
  }
D
dapan1121 已提交
210
  return (int32_t)atomic_load_32(&pHashObj->size);
211 212 213 214 215
}

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

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

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

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

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

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

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

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

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

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

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

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

    // enable resize
H
Haojun Liao 已提交
273
    __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
D
dapan1121 已提交
274
    atomic_add_fetch_32(&pHashObj->size, 1);
H
Haojun Liao 已提交
275 276

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

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

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

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

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

300 301 302 303 304 305 306 307 308
//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
H
Haojun Liao 已提交
309
  __rd_lock((void*) &pHashObj->lock, pHashObj->type);
310 311 312 313 314 315

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

  // no data, return directly
  if (atomic_load_32(&pe->num) == 0) {
H
Haojun Liao 已提交
316
    __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
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
    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); 
    }
346
    memcpy((char *)(*d), GET_HASH_NODE_DATA(pNode), pNode->dataLen);
347 348
    // just make runtime happy 
    if ((*sz) - pNode->dataLen > 0) {
349
      memset((char *)(*d) + pNode->dataLen, 0, (*sz) - pNode->dataLen);
350 351 352 353 354 355 356 357 358
    } 

    data = GET_HASH_NODE_DATA(pNode);
  }

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

H
Haojun Liao 已提交
359
  __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
360 361
  return data;
}
H
Haojun Liao 已提交
362

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

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

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

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

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

  char *data = NULL;

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

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

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

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

D
dapan1121 已提交
405
    if (acquire) {
D
dapan1121 已提交
406
      atomic_add_fetch_16(&pNode->count, 1);
D
dapan1121 已提交
407 408
    }

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

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

H
Haojun Liao 已提交
416
  __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
417
  return data;
418 419
}

D
dapan1121 已提交
420 421 422 423 424 425 426 427 428
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 已提交
429
int32_t taosHashRemove(SHashObj *pHashObj, const void *key, size_t keyLen/*, void *data, size_t dsize*/) {
430
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj)) {
H
Haojun Liao 已提交
431 432 433
    return -1;
  }

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

H
Haojun Liao 已提交
436
  // disable the resize process
H
Haojun Liao 已提交
437
  __rd_lock((void*) &pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
438

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

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

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

H
Haojun Liao 已提交
451
    __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
452
    return -1;
H
Haojun Liao 已提交
453 454
  }

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

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

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

H
Haojun Liao 已提交
467

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

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

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

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

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

H
Haojun Liao 已提交
492
  __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
493

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

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

  // disable the resize process
H
Haojun Liao 已提交
503
  __rd_lock((void*) &pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
504

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

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

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

H
Haojun Liao 已提交
523 524
        pEntry->next = pNode->next;

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

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

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

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

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

H
Haojun Liao 已提交
555 556 557 558 559
          FREE_HASH_NODE(pHashObj, pNext);
        } else {
          pNode = pNext;
        }
      }
H
Haojun Liao 已提交
560 561 562 563 564 565 566
    }

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

H
Haojun Liao 已提交
567
  __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
568 569 570
  return 0;
}

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

  SHashNode *pNode, *pNext;

H
Haojun Liao 已提交
578
  __wr_lock((void*) &pHashObj->lock, pHashObj->type);
579

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

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

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

594
      pNode = pNext;
595 596
    }

597 598
    pEntry->num = 0;
    pEntry->next = NULL;
599 600
  }

D
dapan1121 已提交
601
  atomic_store_32(&pHashObj->size, 0);
H
Haojun Liao 已提交
602
  __wr_unlock((void*) &pHashObj->lock, pHashObj->type);
603 604 605 606 607 608 609
}

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

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

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

  taosArrayDestroy(pHashObj->pMemBlock);
621

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

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

632
  int32_t num = 0;
H
hjxilinx 已提交
633 634

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

641 642
  return num;
}
H
hjLiao 已提交
643 644

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

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

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

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

H
Haojun Liao 已提交
667 668 669
  pHashObj->hashList = pNewEntryList;

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

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

  taosArrayPush(pHashObj->pMemBlock, &p);

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

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

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

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

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

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

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

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

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

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

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

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

H
hjLiao 已提交
742
    }
H
Haojun Liao 已提交
743

H
hjLiao 已提交
744 745
  }

H
Haojun Liao 已提交
746 747
  int64_t et = taosGetTimestampUs();

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

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

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

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

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

H
hjLiao 已提交
770 771 772
  return pNewNode;
}

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

H
Haojun Liao 已提交
776 777
  pNode->next = pEntry->next;
  pEntry->next = pNode;
H
Haojun Liao 已提交
778 779

  pEntry->num += 1;
H
hjLiao 已提交
780 781
}

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

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

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

  return 0;
W
wpan 已提交
800 801
}

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


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

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
839 840 841 842 843 844 845 846 847
    pOld->count--;
    if (pOld->count <=0) {
      if (prevNode) {
        prevNode->next = pOld->next;
      } else {
        pe->next = pOld->next;
      }
   
      pe->num--;
D
dapan1121 已提交
848
      atomic_sub_fetch_32(&pHashObj->size, 1);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
      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
H
Haojun Liao 已提交
865
  __rd_lock((void*) &pHashObj->lock, pHashObj->type);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
866 867 868 869 870 871 872 873 874 875 876

  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 已提交
877
    }
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
878
  }
H
Haojun Liao 已提交
879

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

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

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

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

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

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

H
Haojun Liao 已提交
912
  __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
913 914
  return data;

H
hjLiao 已提交
915
}
H
Haojun Liao 已提交
916

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
920
  // only add the read lock to disable the resize process
H
Haojun Liao 已提交
921
  __rd_lock((void*) &pHashObj->lock, pHashObj->type);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
922 923 924 925 926 927 928 929 930

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

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

H
Haojun Liao 已提交
931
  __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
932
}
D
dapan1121 已提交
933 934 935 936 937 938

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