thash.c 24.4 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
log  
Shengliang Guan 已提交
18
#include "tlog.h"
19
#include "taos.h"
20
#include "tdef.h"
21

22 23 24
// the add ref count operation may trigger the warning if the reference count is greater than the MAX_WARNING_REF_COUNT
#define MAX_WARNING_REF_COUNT 10000
#define EXT_SIZE              1024
H
Haojun Liao 已提交
25
#define HASH_NEED_RESIZE(_h) ((_h)->size >= (_h)->capacity * HASH_DEFAULT_LOAD_FACTOR)
H
Haojun Liao 已提交
26

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

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

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

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

H
Haojun Liao 已提交
55 56
static FORCE_INLINE void __rd_unlock(void *lock, int32_t type) {
  if (type == HASH_NO_LOCK) {
H
hjxilinx 已提交
57 58
    return;
  }
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
Haojun Liao 已提交
66
  taosWUnLockLatch(lock);
67 68 69
}

static FORCE_INLINE int32_t taosHashCapacity(int32_t length) {
dengyihao's avatar
dengyihao 已提交
70
  int32_t len = TMIN(length, HASH_MAX_CAPACITY);
71

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
  if (pNode->count <= 0) {
    pNewNode->next = pNode->next;
    DO_FREE_HASH_NODE(pNode);
  } else {
    pNewNode->next = pNode;    
    pe->num++;
D
dapan1121 已提交
134
    atomic_add_fetch_32(&pHashObj->size, 1);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
135 136
  }
  
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
int32_t taosHashGetSize(const SHashObj *pHashObj) {
  if (!pHashObj) {
    return 0;
  }
D
dapan1121 已提交
211
  return (int32_t)atomic_load_32(&pHashObj->size);
212 213 214 215 216
}

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

D
dapan1121 已提交
218
int32_t taosHashPutImpl(SHashObj *pHashObj, const void *key, size_t keyLen, void *data, size_t size, bool *newAdded) {
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((void*) &pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
228
    taosHashTableResize(pHashObj);
H
Haojun Liao 已提交
229
    __wr_unlock((void*) &pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
230 231
  }

H
Haojun Liao 已提交
232
  __rd_lock((void*) &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((void*) &pHashObj->lock, pHashObj->type);
D
dapan1121 已提交
275
    atomic_add_fetch_32(&pHashObj->size, 1);
H
Haojun Liao 已提交
276

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

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

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

D
dapan1121 已提交
297 298 299 300
    if (newAdded) {
      *newAdded = false;
    }

D
dapan1121 已提交
301
    return pHashObj->enableUpdate ? 0 : -2;
H
Haojun Liao 已提交
302
  }
303 304
}

D
dapan1121 已提交
305 306 307 308 309 310 311 312 313
int32_t taosHashPut(SHashObj *pHashObj, const void *key, size_t keyLen, void *data, size_t size) {
  return taosHashPutImpl(pHashObj, key, keyLen, data, size, NULL);
}

int32_t taosHashPutExt(SHashObj *pHashObj, const void *key, size_t keyLen, void *data, size_t size, bool *newAdded) {
  return taosHashPutImpl(pHashObj, key, keyLen, data, size, newAdded);
}


H
hjLiao 已提交
314
void *taosHashGet(SHashObj *pHashObj, const void *key, size_t keyLen) {
H
Haojun Liao 已提交
315
  return taosHashGetClone(pHashObj, key, keyLen, NULL);
H
Haojun Liao 已提交
316
}
H
Haojun Liao 已提交
317

318 319 320 321 322 323 324 325 326
//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 已提交
327
  __rd_lock((void*) &pHashObj->lock, pHashObj->type);
328 329 330 331 332 333

  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 已提交
334
    __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
    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); 
    }
364
    memcpy((char *)(*d), GET_HASH_NODE_DATA(pNode), pNode->dataLen);
365 366
    // just make runtime happy 
    if ((*sz) - pNode->dataLen > 0) {
367
      memset((char *)(*d) + pNode->dataLen, 0, (*sz) - pNode->dataLen);
368 369 370 371 372 373 374 375 376
    } 

    data = GET_HASH_NODE_DATA(pNode);
  }

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

H
Haojun Liao 已提交
377
  __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
378 379
  return data;
}
H
Haojun Liao 已提交
380

D
dapan1121 已提交
381
void* taosHashGetCloneImpl(SHashObj *pHashObj, const void *key, size_t keyLen, void* d, bool acquire) {
382
  if (taosHashTableEmpty(pHashObj) || keyLen == 0 || key == NULL) {
H
Haojun Liao 已提交
383 384 385
    return NULL;
  }

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

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

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

H
Haojun Liao 已提交
394 395
  // no data, return directly
  if (atomic_load_32(&pe->num) == 0) {
H
Haojun Liao 已提交
396
    __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
397 398
    return NULL;
  }
H
Haojun Liao 已提交
399 400 401 402 403 404 405 406

  char *data = NULL;

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

H
Haojun Liao 已提交
407 408 409 410 411 412
  if (pe->num > 0) {
    assert(pe->next != NULL);
  } else {
    assert(pe->next == NULL);
  }

413
  SHashNode *pNode = doSearchInEntryList(pHashObj, pe, key, keyLen, hashVal);
H
Haojun Liao 已提交
414
  if (pNode != NULL) {
H
Haojun Liao 已提交
415 416
    if (pHashObj->callbackFp != NULL) {
      pHashObj->callbackFp(GET_HASH_NODE_DATA(pNode));
H
Haojun Liao 已提交
417
    }
H
Haojun Liao 已提交
418

H
Haojun Liao 已提交
419
    if (d != NULL) {
420
      memcpy(d, GET_HASH_NODE_DATA(pNode), pNode->dataLen);
H
Haojun Liao 已提交
421
    }
422

D
dapan1121 已提交
423
    if (acquire) {
D
dapan1121 已提交
424
      atomic_add_fetch_16(&pNode->count, 1);
D
dapan1121 已提交
425 426
    }

427
    data = GET_HASH_NODE_DATA(pNode);
H
Haojun Liao 已提交
428 429 430 431 432 433
  }

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

H
Haojun Liao 已提交
434
  __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
435
  return data;
436 437
}

D
dapan1121 已提交
438 439 440 441 442 443 444 445 446
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 已提交
447
int32_t taosHashRemove(SHashObj *pHashObj, const void *key, size_t keyLen/*, void *data, size_t dsize*/) {
448
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj)) {
H
Haojun Liao 已提交
449 450 451
    return -1;
  }

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

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

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

460 461 462 463 464
  if (pHashObj->type == HASH_ENTRY_LOCK) {
    taosWLockLatch(&pe->latch);
  }

  // double check after locked
H
Haojun Liao 已提交
465
  if (pe->num == 0) {
H
Haojun Liao 已提交
466
    assert(pe->next == NULL);
467
    taosWUnLockLatch(&pe->latch);
H
Haojun Liao 已提交
468

H
Haojun Liao 已提交
469
    __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
470
    return -1;
H
Haojun Liao 已提交
471 472
  }

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
473
  int code = -1;
H
Haojun Liao 已提交
474
  SHashNode *pNode = pe->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
475
  SHashNode *prevNode = NULL;
H
Haojun Liao 已提交
476

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
481 482 483
    prevNode = pNode;
    pNode = pNode->next;
  }
H
Haojun Liao 已提交
484

H
Haojun Liao 已提交
485

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
489
    pNode->count--;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
490
    pNode->removed = 1;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
491 492 493 494 495 496
    if (pNode->count <= 0) {
      if (prevNode) {
        prevNode->next = pNode->next;
      } else {
        pe->next = pNode->next;
      }
H
Haojun Liao 已提交
497 498

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
500
      pe->num--;
D
dapan1121 已提交
501
      atomic_sub_fetch_32(&pHashObj->size, 1);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
502 503
      FREE_HASH_NODE(pHashObj, pNode);
    }
H
Haojun Liao 已提交
504
  }
505

H
Haojun Liao 已提交
506
  if (pHashObj->type == HASH_ENTRY_LOCK) {
H
Haojun Liao 已提交
507
    taosWUnLockLatch(&pe->latch);
H
Haojun Liao 已提交
508 509
  }

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
512
  return code;
H
Haojun Liao 已提交
513 514
}

H
Haojun Liao 已提交
515
int32_t taosHashCondTraverse(SHashObj *pHashObj, bool (*fp)(void *, void *), void *param) {
516
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj)) {
H
Haojun Liao 已提交
517 518 519 520
    return 0;
  }

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

S
Shengliang Guan 已提交
523
  int32_t numOfEntries = (int32_t)pHashObj->capacity;
H
Haojun Liao 已提交
524 525
  for (int32_t i = 0; i < numOfEntries; ++i) {
    SHashEntry *pEntry = pHashObj->hashList[i];
H
Haojun Liao 已提交
526
    if (pEntry->num == 0) {
H
Haojun Liao 已提交
527 528 529 530 531 532 533
      continue;
    }

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

H
Haojun Liao 已提交
534
    // todo remove the first node
H
Haojun Liao 已提交
535 536
    SHashNode *pNode = NULL;
    while((pNode = pEntry->next) != NULL) {
H
Haojun Liao 已提交
537
      if (fp && (!fp(param, GET_HASH_NODE_DATA(pNode)))) {
H
Haojun Liao 已提交
538
        pEntry->num -= 1;
D
dapan1121 已提交
539
        atomic_sub_fetch_32(&pHashObj->size, 1);
540

H
Haojun Liao 已提交
541 542
        pEntry->next = pNode->next;

H
Haojun Liao 已提交
543 544 545 546 547 548
        if (pEntry->num == 0) {
          assert(pEntry->next == NULL);
        } else {
          assert(pEntry->next != NULL);
        }

H
Haojun Liao 已提交
549 550 551
        FREE_HASH_NODE(pHashObj, pNode);
      } else {
        break;
H
Haojun Liao 已提交
552
      }
H
Haojun Liao 已提交
553 554 555 556 557 558
    }

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

H
Haojun Liao 已提交
560 561
      while ((pNext = pNode->next) != NULL) {
        // not qualified, remove it
H
Haojun Liao 已提交
562
        if (fp && (!fp(param, GET_HASH_NODE_DATA(pNext)))) {
H
Haojun Liao 已提交
563 564
          pNode->next = pNext->next;
          pEntry->num -= 1;
D
dapan1121 已提交
565
          atomic_sub_fetch_32(&pHashObj->size, 1);
H
Haojun Liao 已提交
566

H
Haojun Liao 已提交
567 568 569 570 571 572
          if (pEntry->num == 0) {
            assert(pEntry->next == NULL);
          } else {
            assert(pEntry->next != NULL);
          }

H
Haojun Liao 已提交
573 574 575 576 577
          FREE_HASH_NODE(pHashObj, pNext);
        } else {
          pNode = pNext;
        }
      }
H
Haojun Liao 已提交
578 579 580 581 582 583 584
    }

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

H
Haojun Liao 已提交
585
  __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
586 587 588
  return 0;
}

589
void taosHashClear(SHashObj *pHashObj) {
H
Haojun Liao 已提交
590 591 592
  if (pHashObj == NULL) {
    return;
  }
593 594 595

  SHashNode *pNode, *pNext;

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

598 599 600 601 602 603
  for (int32_t i = 0; i < pHashObj->capacity; ++i) {
    SHashEntry *pEntry = pHashObj->hashList[i];
    if (pEntry->num == 0) {
      assert(pEntry->next == 0);
      continue;
    }
604

605 606
    pNode = pEntry->next;
    assert(pNode != NULL);
H
Haojun Liao 已提交
607

608 609 610
    while (pNode) {
      pNext = pNode->next;
      FREE_HASH_NODE(pHashObj, pNode);
H
hjxilinx 已提交
611

612
      pNode = pNext;
613 614
    }

615 616
    pEntry->num = 0;
    pEntry->next = NULL;
617 618
  }

D
dapan1121 已提交
619
  atomic_store_32(&pHashObj->size, 0);
H
Haojun Liao 已提交
620
  __wr_unlock((void*) &pHashObj->lock, pHashObj->type);
621 622 623 624 625 626 627
}

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

628
  taosHashClear(pHashObj);
629
  tfree(pHashObj->hashList);
H
Haojun Liao 已提交
630 631 632

  // destroy mem block
  size_t memBlock = taosArrayGetSize(pHashObj->pMemBlock);
H
Haojun Liao 已提交
633 634
  for (int32_t i = 0; i < memBlock; ++i) {
    void *p = taosArrayGetP(pHashObj->pMemBlock, i);
S
TD-1848  
Shengliang Guan 已提交
635
    tfree(p);
H
Haojun Liao 已提交
636 637 638
  }

  taosArrayDestroy(pHashObj->pMemBlock);
639

H
hjxilinx 已提交
640 641
  memset(pHashObj, 0, sizeof(SHashObj));
  free(pHashObj);
642 643 644
}

// for profile only
H
hjxilinx 已提交
645
int32_t taosHashGetMaxOverflowLinkLength(const SHashObj *pHashObj) {
646
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj)) {
647 648
    return 0;
  }
H
hjxilinx 已提交
649

650
  int32_t num = 0;
H
hjxilinx 已提交
651 652

  for (int32_t i = 0; i < pHashObj->size; ++i) {
H
Haojun Liao 已提交
653 654 655
    SHashEntry *pEntry = pHashObj->hashList[i];
    if (num < pEntry->num) {
      num = pEntry->num;
656 657
    }
  }
H
hjxilinx 已提交
658

659 660
  return num;
}
H
hjLiao 已提交
661 662

void taosHashTableResize(SHashObj *pHashObj) {
H
Haojun Liao 已提交
663
  if (!HASH_NEED_RESIZE(pHashObj)) {
H
hjLiao 已提交
664 665
    return;
  }
H
Haojun Liao 已提交
666

H
hjLiao 已提交
667 668 669
  // double the original capacity
  SHashNode *pNode = NULL;
  SHashNode *pNext = NULL;
H
Haojun Liao 已提交
670

S
Shengliang Guan 已提交
671
  int32_t newSize = (int32_t)(pHashObj->capacity << 1u);
H
hjLiao 已提交
672
  if (newSize > HASH_MAX_CAPACITY) {
H
Haojun Liao 已提交
673 674
    //    uDebug("current capacity:%d, maximum capacity:%d, no resize applied due to limitation is reached",
    //           pHashObj->capacity, HASH_MAX_CAPACITY);
H
hjLiao 已提交
675 676 677
    return;
  }

H
Haojun Liao 已提交
678
  int64_t st = taosGetTimestampUs();
679
  void *pNewEntryList = realloc(pHashObj->hashList, sizeof(void *) * newSize);
H
Haojun Liao 已提交
680 681
  if (pNewEntryList == NULL) {  // todo handle error
    //    uDebug("cache resize failed due to out of memory, capacity remain:%d", pHashObj->capacity);
H
hjLiao 已提交
682 683
    return;
  }
H
Haojun Liao 已提交
684

H
Haojun Liao 已提交
685 686 687
  pHashObj->hashList = pNewEntryList;

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

H
Haojun Liao 已提交
690
  for (int32_t i = 0; i < inc; ++i) {
S
Shengliang Guan 已提交
691
    pHashObj->hashList[i + pHashObj->capacity] = (void *)((char *)p + i * sizeof(SHashEntry));
H
Haojun Liao 已提交
692 693 694 695
  }

  taosArrayPush(pHashObj->pMemBlock, &p);

H
hjLiao 已提交
696 697
  pHashObj->capacity = newSize;
  for (int32_t i = 0; i < pHashObj->capacity; ++i) {
H
Haojun Liao 已提交
698
    SHashEntry *pe = pHashObj->hashList[i];
H
Haojun Liao 已提交
699 700 701 702 703 704 705

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

H
Haojun Liao 已提交
706
    if (pe->num == 0) {
H
Haojun Liao 已提交
707
      assert(pe->next == NULL);
H
Haojun Liao 已提交
708
      continue;
H
hjLiao 已提交
709
    }
H
Haojun Liao 已提交
710

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

H
Haojun Liao 已提交
717 718 719 720 721 722
        if (pe->num == 0) {
          assert(pe->next == NULL);
        } else {
          assert(pe->next != NULL);
        }

H
Haojun Liao 已提交
723
        SHashEntry *pNewEntry = pHashObj->hashList[j];
H
Haojun Liao 已提交
724 725 726 727 728
        pushfrontNodeInEntryList(pNewEntry, pNode);
      } else {
        break;
      }
    }
H
Haojun Liao 已提交
729

H
Haojun Liao 已提交
730 731 732 733
    if (pNode != NULL) {
      while ((pNext = pNode->next) != NULL) {
        int32_t j = HASH_INDEX(pNext->hashVal, pHashObj->capacity);
        if (j != i) {
H
Haojun Liao 已提交
734 735
          pe->num -= 1;

H
Haojun Liao 已提交
736 737 738 739 740
          pNode->next = pNext->next;
          pNext->next = NULL;

          // added into new slot
          SHashEntry *pNewEntry = pHashObj->hashList[j];
H
Haojun Liao 已提交
741 742 743 744 745 746 747

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

H
Haojun Liao 已提交
748 749 750 751
          pushfrontNodeInEntryList(pNewEntry, pNext);
        } else {
          pNode = pNext;
        }
H
hjLiao 已提交
752
      }
H
Haojun Liao 已提交
753 754 755 756 757 758 759

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

H
hjLiao 已提交
760
    }
H
Haojun Liao 已提交
761

H
hjLiao 已提交
762 763
  }

H
Haojun Liao 已提交
764 765
  int64_t et = taosGetTimestampUs();

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

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

H
hjLiao 已提交
773 774 775 776
  if (pNewNode == NULL) {
    uError("failed to allocate memory, reason:%s", strerror(errno));
    return NULL;
  }
H
Haojun Liao 已提交
777

778
  pNewNode->keyLen  = (uint32_t)keyLen;
H
hjLiao 已提交
779
  pNewNode->hashVal = hashVal;
H
Haojun Liao 已提交
780
  pNewNode->dataLen = (uint32_t) dsize;
781 782 783
  pNewNode->count   = 1;
  pNewNode->removed = 0;
  pNewNode->next    = NULL;
H
Haojun Liao 已提交
784 785 786 787

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

H
hjLiao 已提交
788 789 790
  return pNewNode;
}

H
Haojun Liao 已提交
791
void pushfrontNodeInEntryList(SHashEntry *pEntry, SHashNode *pNode) {
H
Haojun Liao 已提交
792
  assert(pNode != NULL && pEntry != NULL);
H
Haojun Liao 已提交
793

H
Haojun Liao 已提交
794 795
  pNode->next = pEntry->next;
  pEntry->next = pNode;
H
Haojun Liao 已提交
796 797

  pEntry->num += 1;
H
hjLiao 已提交
798 799
}

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
800 801 802 803
size_t taosHashGetMemSize(const SHashObj *pHashObj) {
  if (pHashObj == NULL) {
    return 0;
  }
H
Haojun Liao 已提交
804

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

D
dapan1121 已提交
808 809 810 811 812
FORCE_INLINE int32_t taosHashGetKey(void *data, void** key, size_t* keyLen) {
  if (NULL == data || NULL == key) {
    return -1;
  }
  
W
wpan 已提交
813
  SHashNode * node = GET_HASH_PNODE(data);
D
dapan1121 已提交
814
  *key = GET_HASH_NODE_KEY(node);
L
Liu Jicong 已提交
815 816 817
  if (keyLen) {
    *keyLen = node->keyLen;
  }
D
dapan1121 已提交
818 819

  return 0;
W
wpan 已提交
820 821
}

L
Liu Jicong 已提交
822 823 824 825 826
FORCE_INLINE int32_t taosHashGetDataLen(void *data) {
  SHashNode * node = GET_HASH_PNODE(data);
  return node->keyLen;
}

W
wpan 已提交
827 828 829 830 831 832
FORCE_INLINE uint32_t taosHashGetDataKeyLen(SHashObj *pHashObj, void *data) {
  SHashNode * node = GET_HASH_PNODE(data);
  return node->keyLen;
}


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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
  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) 已提交
857
  if (pNode) { 
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
858
    pNode = pNode->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
859 860 861 862 863
    while (pNode) {
      if (pNode->removed == 0) break;
      pNode = pNode->next;
    }

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
864 865 866 867 868 869 870 871 872
    pOld->count--;
    if (pOld->count <=0) {
      if (prevNode) {
        prevNode->next = pOld->next;
      } else {
        pe->next = pOld->next;
      }
   
      pe->num--;
D
dapan1121 已提交
873
      atomic_sub_fetch_32(&pHashObj->size, 1);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
      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 已提交
890
  __rd_lock((void*) &pHashObj->lock, pHashObj->type);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
891 892 893 894 895 896 897 898 899 900 901

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

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
909 910 911 912 913 914
      // lock entry
      if (pHashObj->type == HASH_ENTRY_LOCK) {
        taosWLockLatch(&pe->latch);
      }

      pNode = pe->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
915 916 917 918 919
      while (pNode) {
        if (pNode->removed == 0) break;
        pNode = pNode->next;
      }

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
920 921 922 923 924
      if (pNode) break;

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
928 929
  if (pNode) {
    SHashEntry *pe = pHashObj->hashList[slot];
930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947

    uint16_t prevRef = atomic_load_16(&pNode->count);
    uint16_t afterRef = atomic_add_fetch_16(&pNode->count, 1);

    // the reference count value is overflow, which will cause the delete node operation immediately.
    if (prevRef > afterRef) {
      uError("hash entry ref count overflow, prev ref:%d, current ref:%d", prevRef, afterRef);
      // restore the value
      atomic_sub_fetch_16(&pNode->count, 1);
      data = NULL;
    } else {
      data = GET_HASH_NODE_DATA(pNode);
    }

    if (afterRef >= MAX_WARNING_REF_COUNT) {
      uWarn("hash entry ref count is abnormally high: %d", afterRef);
    }

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
948 949 950
    if (pHashObj->type == HASH_ENTRY_LOCK) {
      taosWUnLockLatch(&pe->latch);
    } 
H
hjLiao 已提交
951
  }
H
Haojun Liao 已提交
952

H
Haojun Liao 已提交
953
  __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
954
  return data;
H
hjLiao 已提交
955
}
H
Haojun Liao 已提交
956

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
960
  // only add the read lock to disable the resize process
H
Haojun Liao 已提交
961
  __rd_lock((void*) &pHashObj->lock, pHashObj->type);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
962 963 964 965 966 967 968 969 970

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

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

H
Haojun Liao 已提交
971
  __rd_unlock((void*) &pHashObj->lock, pHashObj->type);
H
Haojun Liao 已提交
972
}
D
dapan1121 已提交
973 974 975 976 977 978

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