thash.c 23.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 * 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/>.
 */

S
hash  
Shengliang Guan 已提交
16
#define _DEFAULT_SOURCE
H
Haojun Liao 已提交
17
#include "thash.h"
H
Haojun Liao 已提交
18 19
#include "taoserror.h"
#include "os.h"
S
hash  
Shengliang Guan 已提交
20
#include "tlog.h"
21

22
// the add ref count operation may trigger the warning if the reference count is greater than the MAX_WARNING_REF_COUNT
H
Haojun Liao 已提交
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
#define MAX_WARNING_REF_COUNT    10000
#define EXT_SIZE                 1024
#define HASH_MAX_CAPACITY        (1024 * 1024 * 16)
#define HASH_DEFAULT_LOAD_FACTOR (0.75)
#define HASH_INDEX(v, c)         ((v) & ((c)-1))

#define HASH_NEED_RESIZE(_h) ((_h)->size >= (_h)->capacity * HASH_DEFAULT_LOAD_FACTOR)

#define GET_HASH_NODE_KEY(_n)  ((char*)(_n) + sizeof(SHashNode) + (_n)->dataLen)
#define GET_HASH_NODE_DATA(_n) ((char*)(_n) + sizeof(SHashNode))
#define GET_HASH_PNODE(_n)     ((SHashNode *)((char*)(_n) - sizeof(SHashNode)))

#define FREE_HASH_NODE(_n) \
  do {                     \
    tfree(_n);             \
H
Haojun Liao 已提交
38 39
  } while (0);

H
Haojun Liao 已提交
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
typedef struct SHashEntry {
  int32_t    num;      // number of elements in current entry
  SRWLatch   latch;    // entry latch
  SHashNode *next;
} SHashEntry;

typedef struct SHashObj {
  SHashEntry     **hashList;
  size_t           capacity;     // number of slots
  size_t           size;         // number of elements in hash table
  _hash_fn_t       hashFp;       // hash function
  _equal_fn_t      equalFp;      // equal function
  _hash_free_fn_t  freeFp;       // hash node free callback function
  SRWLatch         lock;         // read-write spin lock
  SHashLockTypeE   type;         // lock type
  bool             enableUpdate; // enable update
  SArray          *pMemBlock;    // memory block allocated for SHashEntry
  _hash_before_fn_t callbackFp;  // function invoked before return the value to caller
} SHashObj;

/*
 * Function definition
 */
static FORCE_INLINE void taosHashWLock(SHashObj *pHashObj) {
  if (pHashObj->type == HASH_NO_LOCK) {
    return;
  }
  taosWLockLatch(&pHashObj->lock);
}

static FORCE_INLINE void taosHashWUnlock(SHashObj *pHashObj) {
  if (pHashObj->type == HASH_NO_LOCK) {
H
hjxilinx 已提交
72 73
    return;
  }
H
Haojun Liao 已提交
74 75

  taosWUnLockLatch(&pHashObj->lock);
76 77
}

H
Haojun Liao 已提交
78 79
static FORCE_INLINE void taosHashRLock(SHashObj *pHashObj) {
  if (pHashObj->type == HASH_NO_LOCK) {
H
hjxilinx 已提交
80 81
    return;
  }
H
Haojun Liao 已提交
82 83

  taosRLockLatch(&pHashObj->lock);
84 85
}

H
Haojun Liao 已提交
86 87
static FORCE_INLINE void taosHashRUnlock(SHashObj *pHashObj) {
  if (pHashObj->type == HASH_NO_LOCK) {
H
hjxilinx 已提交
88 89
    return;
  }
H
Haojun Liao 已提交
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114

  taosRUnLockLatch(&pHashObj->lock);
}

static FORCE_INLINE void taosHashEntryWLock(const SHashObj *pHashObj, SHashEntry* pe) {
  if (pHashObj->type == HASH_NO_LOCK) {
    return;
  }
  taosWLockLatch(&pe->latch);
}

static FORCE_INLINE void taosHashEntryWUnlock(const SHashObj *pHashObj, SHashEntry* pe) {
  if (pHashObj->type == HASH_NO_LOCK) {
    return;
  }

  taosWUnLockLatch(&pe->latch);
}

static FORCE_INLINE void taosHashEntryRLock(const SHashObj *pHashObj, SHashEntry* pe) {
  if (pHashObj->type == HASH_NO_LOCK) {
    return;
  }

  taosRLockLatch(&pe->latch);
115 116
}

H
Haojun Liao 已提交
117 118
static FORCE_INLINE void taosHashEntryRUnlock(const SHashObj *pHashObj, SHashEntry* pe) {
  if (pHashObj->type == HASH_NO_LOCK) {
H
hjxilinx 已提交
119 120
    return;
  }
H
Haojun Liao 已提交
121 122

  taosRUnLockLatch(&pe->latch);
123 124 125
}

static FORCE_INLINE int32_t taosHashCapacity(int32_t length) {
H
Haojun Liao 已提交
126
  int32_t len = MIN(length, HASH_MAX_CAPACITY);
127

S
Shengliang Guan 已提交
128
  int32_t i = 4;
129
  while (i < len) i = (i << 1u);
130 131 132
  return i;
}

H
Haojun Liao 已提交
133 134
static FORCE_INLINE SHashNode *
doSearchInEntryList(SHashObj *pHashObj, SHashEntry *pe, const void *key, size_t keyLen, uint32_t hashVal) {
H
Haojun Liao 已提交
135
  SHashNode *pNode = pe->next;
H
Haojun Liao 已提交
136
  while (pNode) {
H
Haojun Liao 已提交
137 138
    if ((pNode->keyLen == keyLen) &&
        ((*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0) &&
S
hash  
Shengliang Guan 已提交
139
        pNode->removed == 0) {
H
Haojun Liao 已提交
140 141 142 143 144 145 146 147 148 149
      assert(pNode->hashVal == hashVal);
      break;
    }

    pNode = pNode->next;
  }

  return pNode;
}

150
/**
H
Haojun Liao 已提交
151
 * resize the hash list if the threshold is reached
152
 *
H
hjxilinx 已提交
153
 * @param pHashObj
154
 */
H
hjLiao 已提交
155
static void taosHashTableResize(SHashObj *pHashObj);
156

H
hjLiao 已提交
157
/**
H
Haojun Liao 已提交
158 159
 * allocate and initialize a hash node
 *
H
hjLiao 已提交
160 161
 * @param key      key of object for hash, usually a null-terminated string
 * @param keyLen   length of key
H
Haojun Liao 已提交
162
 * @param pData    data to be stored in hash node
H
hjLiao 已提交
163 164 165 166
 * @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);
167

H
hjLiao 已提交
168
/**
H
Haojun Liao 已提交
169
 * update the hash node
H
hjLiao 已提交
170
 *
H
Haojun Liao 已提交
171 172 173 174 175
 * @param pHashObj   hash table object
 * @param pe         hash table entry to operate on
 * @param prev       previous node
 * @param pNode      the old node with requested key
 * @param pNewNode   the new node with requested key
H
hjLiao 已提交
176
 */
H
Haojun Liao 已提交
177
static FORCE_INLINE void doUpdateHashNode(SHashObj *pHashObj, SHashEntry* pe, SHashNode* prev, SHashNode *pNode, SHashNode *pNewNode) {
H
Haojun Liao 已提交
178
  assert(pNode->keyLen == pNewNode->keyLen);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
179

H
Haojun Liao 已提交
180
  atomic_sub_fetch_32(&pNode->refCount, 1);
H
Haojun Liao 已提交
181 182
  if (prev != NULL) {
    prev->next = pNewNode;
183 184
  } else {
    pe->next = pNewNode;
H
Haojun Liao 已提交
185
  }
H
Haojun Liao 已提交
186

H
Haojun Liao 已提交
187
  if (pNode->refCount <= 0) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
188
    pNewNode->next = pNode->next;
H
Haojun Liao 已提交
189
    FREE_HASH_NODE(pNode);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
190
  } else {
S
hash  
Shengliang Guan 已提交
191
    pNewNode->next = pNode;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
192
    pe->num++;
H
Haojun Liao 已提交
193
    atomic_add_fetch_64(&pHashObj->size, 1);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
194
  }
H
Haojun Liao 已提交
195
}
196

H
hjLiao 已提交
197 198 199
/**
 * insert the hash node at the front of the linked list
 *
H
Haojun Liao 已提交
200 201
 * @param pHashObj   hash table object
 * @param pNode      the old node with requested key
H
hjLiao 已提交
202
 */
H
Haojun Liao 已提交
203
static void pushfrontNodeInEntryList(SHashEntry *pEntry, SHashNode *pNode);
204

205 206 207 208 209 210 211 212
/**
 * 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);

213
/**
H
Haojun Liao 已提交
214 215 216 217 218 219 220
 * initialize a hash table
 *
 * @param capacity   initial capacity of the hash table
 * @param fn         hash function
 * @param update     whether the hash table allows in place update
 * @param type       whether the hash table has per entry lock
 * @return           hash table object
221
 */
H
Haojun Liao 已提交
222
SHashObj *taosHashInit(size_t capacity, _hash_fn_t fn, bool update, SHashLockTypeE type) {
H
Haojun Liao 已提交
223 224 225 226 227
  if (fn == NULL) {
    assert(0);
    return NULL;
  }

228 229
  if (capacity == 0) {
    capacity = 4;
230 231
  }

H
hjxilinx 已提交
232 233
  SHashObj *pHashObj = (SHashObj *)calloc(1, sizeof(SHashObj));
  if (pHashObj == NULL) {
H
Haojun Liao 已提交
234
    terrno = TSDB_CODE_OUT_OF_MEMORY;
235 236 237 238
    return NULL;
  }

  // the max slots is not defined by user
S
Shengliang Guan 已提交
239
  pHashObj->capacity = taosHashCapacity((int32_t)capacity);
H
Haojun Liao 已提交
240

241
  pHashObj->equalFp = memcmp;
H
Haojun Liao 已提交
242
  pHashObj->hashFp  = fn;
H
Haojun Liao 已提交
243
  pHashObj->type = type;
H
Haojun Liao 已提交
244
  pHashObj->enableUpdate = update;
245

H
Haojun Liao 已提交
246 247
  ASSERT((pHashObj->capacity & (pHashObj->capacity - 1)) == 0);

H
Haojun Liao 已提交
248
  pHashObj->hashList = (SHashEntry **)calloc(pHashObj->capacity, sizeof(void *));
H
hjxilinx 已提交
249 250
  if (pHashObj->hashList == NULL) {
    free(pHashObj);
H
Haojun Liao 已提交
251
    terrno = TSDB_CODE_OUT_OF_MEMORY;
252
    return NULL;
H
Haojun Liao 已提交
253
  }
H
Haojun Liao 已提交
254

H
Haojun Liao 已提交
255 256 257 258 259 260 261
  pHashObj->pMemBlock = taosArrayInit(8, sizeof(void *));
  if (pHashObj->pMemBlock == NULL) {
    free(pHashObj->hashList);
    free(pHashObj);
    terrno = TSDB_CODE_OUT_OF_MEMORY;
    return NULL;
  }
H
Haojun Liao 已提交
262

H
Haojun Liao 已提交
263 264 265 266 267 268 269
  void *p = calloc(pHashObj->capacity, sizeof(SHashEntry));
  if (p == NULL) {
    taosArrayDestroy(pHashObj->pMemBlock);
    free(pHashObj->hashList);
    free(pHashObj);
    terrno = TSDB_CODE_OUT_OF_MEMORY;
    return NULL;
270
  }
H
hjxilinx 已提交
271

H
Haojun Liao 已提交
272 273 274 275 276
  for (int32_t i = 0; i < pHashObj->capacity; ++i) {
    pHashObj->hashList[i] = (void *)((char *)p + i * sizeof(SHashEntry));
  }

  taosArrayPush(pHashObj->pMemBlock, &p);
H
hjxilinx 已提交
277
  return pHashObj;
278 279
}

280 281 282
void taosHashSetEqualFp(SHashObj *pHashObj, _equal_fn_t fp) {
  if (pHashObj != NULL && fp != NULL) {
    pHashObj->equalFp = fp;
S
hash  
Shengliang Guan 已提交
283 284
  }
}
285

H
Haojun Liao 已提交
286 287 288 289 290 291
void taosHashSetFreeFp(SHashObj *pHashObj, _hash_free_fn_t fp) {
  if (pHashObj != NULL && fp != NULL) {
    pHashObj->freeFp = fp;
  }
}

292
int32_t taosHashGetSize(const SHashObj *pHashObj) {
H
Haojun Liao 已提交
293
  if (pHashObj == NULL) {
294 295
    return 0;
  }
H
Haojun Liao 已提交
296
  return (int32_t)atomic_load_64(&pHashObj->size);
297 298
}

H
Haojun Liao 已提交
299 300 301 302 303
static FORCE_INLINE bool taosHashTableEmpty(const SHashObj *pHashObj) {
  return taosHashGetSize(pHashObj) == 0;
}

int32_t taosHashPut(SHashObj *pHashObj, const void *key, size_t keyLen, void *data, size_t size) {
H
Haojun Liao 已提交
304
  if (pHashObj == NULL || key == NULL || keyLen == 0) {
H
Haojun Liao 已提交
305 306
    return -1;
  }
307

S
Shengliang Guan 已提交
308
  uint32_t   hashVal = (*pHashObj->hashFp)(key, (uint32_t)keyLen);
H
Haojun Liao 已提交
309 310 311 312
  SHashNode *pNewNode = doCreateHashNode(key, keyLen, data, size, hashVal);
  if (pNewNode == NULL) {
    return -1;
  }
313

H
Haojun Liao 已提交
314 315
  // need the resize process, write lock applied
  if (HASH_NEED_RESIZE(pHashObj)) {
H
Haojun Liao 已提交
316
    taosHashWLock(pHashObj);
H
Haojun Liao 已提交
317
    taosHashTableResize(pHashObj);
H
Haojun Liao 已提交
318
    taosHashWUnlock(pHashObj);
H
Haojun Liao 已提交
319 320
  }

H
Haojun Liao 已提交
321
  taosHashRLock(pHashObj);
H
Haojun Liao 已提交
322

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

H
Haojun Liao 已提交
326
  taosHashEntryWLock(pHashObj, pe);
327

H
Haojun Liao 已提交
328 329 330 331 332 333 334
  SHashNode *pNode = pe->next;
  if (pe->num > 0) {
    assert(pNode != NULL);
  } else {
    assert(pNode == NULL);
  }

H
Haojun Liao 已提交
335
  SHashNode* prev = NULL;
H
Haojun Liao 已提交
336
  while (pNode) {
H
Haojun Liao 已提交
337 338
    if ((pNode->keyLen == keyLen) &&
        (*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0 &&
S
hash  
Shengliang Guan 已提交
339
        pNode->removed == 0) {
H
Haojun Liao 已提交
340 341
      assert(pNode->hashVal == hashVal);
      break;
342 343
    }

H
Haojun Liao 已提交
344
    prev = pNode;
H
Haojun Liao 已提交
345 346 347 348 349
    pNode = pNode->next;
  }

  if (pNode == NULL) {
    // no data in hash table with the specified key, add it into hash table
H
Haojun Liao 已提交
350
    pushfrontNodeInEntryList(pe, pNewNode);
H
Haojun Liao 已提交
351
    assert(pe->next != NULL);
H
Haojun Liao 已提交
352

H
Haojun Liao 已提交
353
    taosHashEntryWUnlock(pHashObj, pe);
H
Haojun Liao 已提交
354 355

    // enable resize
H
Haojun Liao 已提交
356 357
    taosHashRUnlock(pHashObj);
    atomic_add_fetch_64(&pHashObj->size, 1);
S
hash  
Shengliang Guan 已提交
358

H
Haojun Liao 已提交
359
    return 0;
360
  } else {
H
Haojun Liao 已提交
361 362
    // not support the update operation, return error
    if (pHashObj->enableUpdate) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
363
      doUpdateHashNode(pHashObj, pe, prev, pNode, pNewNode);
364
    } else {
H
Haojun Liao 已提交
365
      FREE_HASH_NODE(pNewNode);
H
Haojun Liao 已提交
366 367
    }

H
Haojun Liao 已提交
368
    taosHashEntryWUnlock(pHashObj, pe);
H
Haojun Liao 已提交
369 370

    // enable resize
H
Haojun Liao 已提交
371
    taosHashRUnlock(pHashObj);
D
dapan1121 已提交
372

H
Haojun Liao 已提交
373
    return pHashObj->enableUpdate ? 0 : -1;
H
Haojun Liao 已提交
374
  }
375 376
}

H
Haojun Liao 已提交
377
static void* taosHashGetImpl(SHashObj *pHashObj, const void *key, size_t keyLen, void** d, int32_t* size, bool addRef);
D
dapan1121 已提交
378

H
hjLiao 已提交
379
void *taosHashGet(SHashObj *pHashObj, const void *key, size_t keyLen) {
H
Haojun Liao 已提交
380 381
  void* p = NULL;
  return taosHashGetImpl(pHashObj, key, keyLen, &p, 0, false);
H
Haojun Liao 已提交
382
}
H
Haojun Liao 已提交
383

H
Haojun Liao 已提交
384 385 386 387 388
int32_t taosHashGetDup(SHashObj *pHashObj, const void *key, size_t keyLen, void *destBuf) {
  terrno = 0;
  /*char* p = */taosHashGetImpl(pHashObj, key, keyLen, &destBuf, 0, false);
  return terrno;
}
389

H
Haojun Liao 已提交
390 391
int32_t taosHashGetDup_m(SHashObj *pHashObj, const void *key, size_t keyLen, void **destBuf, int32_t* size) {
  terrno = 0;
392

H
Haojun Liao 已提交
393 394
  /*char* p = */taosHashGetImpl(pHashObj, key, keyLen, destBuf, size, false);
  return terrno;
395
}
H
Haojun Liao 已提交
396

H
Haojun Liao 已提交
397 398
void* taosHashGetImpl(SHashObj *pHashObj, const void *key, size_t keyLen, void** d, int32_t* size, bool addRef) {
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj) || keyLen == 0 || key == NULL) {
H
Haojun Liao 已提交
399 400 401
    return NULL;
  }

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

H
Haojun Liao 已提交
404
  // only add the read lock to disable the resize process
H
Haojun Liao 已提交
405
  taosHashRLock(pHashObj);
H
Haojun Liao 已提交
406

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

H
Haojun Liao 已提交
410 411
  // no data, return directly
  if (atomic_load_32(&pe->num) == 0) {
H
Haojun Liao 已提交
412
    taosHashRUnlock(pHashObj);
413 414
    return NULL;
  }
H
Haojun Liao 已提交
415 416

  char *data = NULL;
H
Haojun Liao 已提交
417
  taosHashEntryRLock(pHashObj, pe);
H
Haojun Liao 已提交
418

H
Haojun Liao 已提交
419
#if 0
H
Haojun Liao 已提交
420 421 422 423 424
  if (pe->num > 0) {
    assert(pe->next != NULL);
  } else {
    assert(pe->next == NULL);
  }
H
Haojun Liao 已提交
425
#endif
H
Haojun Liao 已提交
426

427
  SHashNode *pNode = doSearchInEntryList(pHashObj, pe, key, keyLen, hashVal);
H
Haojun Liao 已提交
428
  if (pNode != NULL) {
H
Haojun Liao 已提交
429 430
    if (pHashObj->callbackFp != NULL) {
      pHashObj->callbackFp(GET_HASH_NODE_DATA(pNode));
H
Haojun Liao 已提交
431
    }
H
Haojun Liao 已提交
432

H
Haojun Liao 已提交
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
    if (size != NULL) {
      if (*d == NULL) {
        *size =  pNode->dataLen;
        *d = calloc(1, *size);
        if (*d == NULL) {
          terrno = TSDB_CODE_OUT_OF_MEMORY;
          return NULL;
        }
      } else if (*size < pNode->dataLen) {
        *size =  pNode->dataLen;
        char* tmp = realloc(*d, *size);
        if (tmp == NULL) {
          terrno = TSDB_CODE_OUT_OF_MEMORY;
          return NULL;
        }

        *d = tmp;
      }
    }

    if (addRef) {
      atomic_add_fetch_16(&pNode->refCount, 1);
H
Haojun Liao 已提交
455
    }
456

H
Haojun Liao 已提交
457 458
    if (*d != NULL) {
      memcpy(*d, GET_HASH_NODE_DATA(pNode), pNode->dataLen);
D
dapan1121 已提交
459 460
    }

461
    data = GET_HASH_NODE_DATA(pNode);
H
Haojun Liao 已提交
462 463
  }

H
Haojun Liao 已提交
464 465
  taosHashEntryRUnlock(pHashObj, pe);
  taosHashRUnlock(pHashObj);
H
Haojun Liao 已提交
466 467

  return data;
468 469
}

H
Haojun Liao 已提交
470 471
int32_t taosHashRemoveWithData(SHashObj *pHashObj, const void *key, size_t keyLen, void *data, size_t dsize) {
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj) || key == NULL || keyLen == 0) {
H
Haojun Liao 已提交
472 473 474
    return -1;
  }

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

H
Haojun Liao 已提交
477
  // disable the resize process
H
Haojun Liao 已提交
478
  taosHashRLock(pHashObj);
H
Haojun Liao 已提交
479

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

H
Haojun Liao 已提交
483
  taosHashEntryWLock(pHashObj, pe);
484 485

  // double check after locked
H
Haojun Liao 已提交
486
  if (pe->num == 0) {
H
Haojun Liao 已提交
487 488
    assert(pe->next == NULL);

H
Haojun Liao 已提交
489 490
    taosHashEntryWUnlock(pHashObj, pe);
    taosHashRUnlock(pHashObj);
H
Haojun Liao 已提交
491
    return -1;
H
Haojun Liao 已提交
492 493
  }

H
Haojun Liao 已提交
494
  int code = -1;
H
Haojun Liao 已提交
495
  SHashNode *pNode = pe->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
496
  SHashNode *prevNode = NULL;
H
Haojun Liao 已提交
497

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
498
  while (pNode) {
H
Haojun Liao 已提交
499 500 501 502
    if ((pNode->keyLen == keyLen) &&
        ((*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0) &&
        pNode->removed == 0) {
      code = 0;  // it is found
H
Haojun Liao 已提交
503

H
Haojun Liao 已提交
504 505 506 507 508 509 510 511
      atomic_sub_fetch_32(&pNode->refCount, 1);
      pNode->removed = 1;
      if (pNode->refCount <= 0) {
        if (prevNode == NULL) {
          pe->next = pNode->next;
        } else {
          prevNode->next = pNode->next;
        }
H
Haojun Liao 已提交
512

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

H
Haojun Liao 已提交
515 516 517
        pe->num--;
        atomic_sub_fetch_64(&pHashObj->size, 1);
        FREE_HASH_NODE(pNode);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
518
      }
H
Haojun Liao 已提交
519 520 521
    } else {
      prevNode = pNode;
      pNode = pNode->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
522
    }
H
Haojun Liao 已提交
523
  }
524

H
Haojun Liao 已提交
525 526
  taosHashEntryWUnlock(pHashObj, pe);
  taosHashRUnlock(pHashObj);
H
Haojun Liao 已提交
527

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
528
  return code;
H
Haojun Liao 已提交
529 530
}

H
Haojun Liao 已提交
531 532 533 534 535 536 537
int32_t taosHashRemove(SHashObj *pHashObj, const void *key, size_t keyLen) {
  return taosHashRemoveWithData(pHashObj, key, keyLen, NULL, 0);
}

void taosHashCondTraverse(SHashObj *pHashObj, bool (*fp)(void *, void *), void *param) {
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj) || fp == NULL) {
    return;
H
Haojun Liao 已提交
538 539 540
  }

  // disable the resize process
H
Haojun Liao 已提交
541
  taosHashRLock(pHashObj);
H
Haojun Liao 已提交
542

S
Shengliang Guan 已提交
543
  int32_t numOfEntries = (int32_t)pHashObj->capacity;
H
Haojun Liao 已提交
544 545
  for (int32_t i = 0; i < numOfEntries; ++i) {
    SHashEntry *pEntry = pHashObj->hashList[i];
H
Haojun Liao 已提交
546
    if (pEntry->num == 0) {
H
Haojun Liao 已提交
547 548 549
      continue;
    }

H
Haojun Liao 已提交
550
    taosHashEntryWLock(pHashObj, pEntry);
H
Haojun Liao 已提交
551

H
Haojun Liao 已提交
552 553 554 555 556 557
    SHashNode *pPrevNode = NULL;
    SHashNode *pNode = pEntry->next;
    while (pNode != NULL) {
      if (fp(param, GET_HASH_NODE_DATA(pNode))) {
        pPrevNode = pNode;
        pNode = pNode->next;
H
Haojun Liao 已提交
558
      } else {
H
Haojun Liao 已提交
559 560
        if (pPrevNode == NULL) {
          pEntry->next = pNode->next;
H
Haojun Liao 已提交
561
        } else {
H
Haojun Liao 已提交
562
          pPrevNode->next = pNode->next;
H
Haojun Liao 已提交
563
        }
H
Haojun Liao 已提交
564 565 566 567 568
        pEntry->num -= 1;
        atomic_sub_fetch_64(&pHashObj->size, 1);
        SHashNode *next = pNode->next;
        FREE_HASH_NODE(pNode);
        pNode = next;
H
Haojun Liao 已提交
569
      }
H
Haojun Liao 已提交
570 571
    }

H
Haojun Liao 已提交
572
    taosHashEntryWUnlock(pHashObj, pEntry);
H
Haojun Liao 已提交
573 574
  }

H
Haojun Liao 已提交
575
  taosHashRUnlock(pHashObj);
H
Haojun Liao 已提交
576 577
}

578
void taosHashClear(SHashObj *pHashObj) {
H
Haojun Liao 已提交
579 580 581
  if (pHashObj == NULL) {
    return;
  }
582 583 584

  SHashNode *pNode, *pNext;

H
Haojun Liao 已提交
585
  taosHashWLock(pHashObj);
586

587 588 589
  for (int32_t i = 0; i < pHashObj->capacity; ++i) {
    SHashEntry *pEntry = pHashObj->hashList[i];
    if (pEntry->num == 0) {
H
Haojun Liao 已提交
590
      assert(pEntry->next == NULL);
591 592
      continue;
    }
593

594 595
    pNode = pEntry->next;
    assert(pNode != NULL);
H
Haojun Liao 已提交
596

597 598
    while (pNode) {
      pNext = pNode->next;
H
Haojun Liao 已提交
599
      FREE_HASH_NODE(pNode);
H
hjxilinx 已提交
600

601
      pNode = pNext;
602 603
    }

604 605
    pEntry->num = 0;
    pEntry->next = NULL;
606 607
  }

H
Haojun Liao 已提交
608 609
  pHashObj->size = 0;
  taosHashWUnlock(pHashObj);
610 611
}

H
Haojun Liao 已提交
612
// the input paras should be SHashObj **, so the origin input will be set by tfree(*pHashObj)
613 614 615 616 617
void taosHashCleanup(SHashObj *pHashObj) {
  if (pHashObj == NULL) {
    return;
  }

618
  taosHashClear(pHashObj);
619
  tfree(pHashObj->hashList);
H
Haojun Liao 已提交
620 621 622

  // destroy mem block
  size_t memBlock = taosArrayGetSize(pHashObj->pMemBlock);
H
Haojun Liao 已提交
623 624
  for (int32_t i = 0; i < memBlock; ++i) {
    void *p = taosArrayGetP(pHashObj->pMemBlock, i);
S
TD-1848  
Shengliang Guan 已提交
625
    tfree(p);
H
Haojun Liao 已提交
626 627 628
  }

  taosArrayDestroy(pHashObj->pMemBlock);
H
hjxilinx 已提交
629
  free(pHashObj);
630 631 632
}

// for profile only
H
Haojun Liao 已提交
633
int32_t taosHashGetMaxOverflowLinkLength(const SHashObj *pHashObj){
634
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj)) {
635 636
    return 0;
  }
H
hjxilinx 已提交
637

638
  int32_t num = 0;
H
hjxilinx 已提交
639

H
Haojun Liao 已提交
640
  taosHashRLock((SHashObj*) pHashObj);
H
hjxilinx 已提交
641
  for (int32_t i = 0; i < pHashObj->size; ++i) {
H
Haojun Liao 已提交
642
    SHashEntry *pEntry = pHashObj->hashList[i];
H
Haojun Liao 已提交
643 644 645

    // fine grain per entry lock is not held since this is used
    // for profiling only and doesn't need an accurate count.
H
Haojun Liao 已提交
646 647
    if (num < pEntry->num) {
      num = pEntry->num;
648 649
    }
  }
H
hjxilinx 已提交
650

H
Haojun Liao 已提交
651
  taosHashRUnlock((SHashObj*) pHashObj);
652 653
  return num;
}
H
hjLiao 已提交
654 655

void taosHashTableResize(SHashObj *pHashObj) {
H
Haojun Liao 已提交
656
  if (!HASH_NEED_RESIZE(pHashObj)) {
H
hjLiao 已提交
657 658
    return;
  }
H
Haojun Liao 已提交
659

H
Haojun Liao 已提交
660 661 662 663
  int32_t newCapacity = (int32_t)(pHashObj->capacity << 1u);
  if (newCapacity > HASH_MAX_CAPACITY) {
//    uDebug("current capacity:%zu, maximum capacity:%d, no resize applied due to limitation is reached",
//           pHashObj->capacity, HASH_MAX_CAPACITY);
H
hjLiao 已提交
664 665 666
    return;
  }

H
Haojun Liao 已提交
667
  int64_t st = taosGetTimestampUs();
H
Haojun Liao 已提交
668 669 670
  void *pNewEntryList = realloc(pHashObj->hashList, sizeof(void *) * newCapacity);
  if (pNewEntryList == NULL) {
//    uDebug("cache resize failed due to out of memory, capacity remain:%zu", pHashObj->capacity);
H
hjLiao 已提交
671 672
    return;
  }
H
Haojun Liao 已提交
673

H
Haojun Liao 已提交
674 675
  pHashObj->hashList = pNewEntryList;

H
Haojun Liao 已提交
676 677
  size_t inc = newCapacity - pHashObj->capacity;
  void * p = calloc(inc, sizeof(SHashEntry));
H
Haojun Liao 已提交
678

H
Haojun Liao 已提交
679
  for (int32_t i = 0; i < inc; ++i) {
S
Shengliang Guan 已提交
680
    pHashObj->hashList[i + pHashObj->capacity] = (void *)((char *)p + i * sizeof(SHashEntry));
H
Haojun Liao 已提交
681 682 683 684
  }

  taosArrayPush(pHashObj->pMemBlock, &p);

H
Haojun Liao 已提交
685 686 687 688 689 690
  pHashObj->capacity = newCapacity;
  for (int32_t idx = 0; idx < pHashObj->capacity; ++idx) {
    SHashEntry *pe = pHashObj->hashList[idx];
    SHashNode *pNode;
    SHashNode *pNext;
    SHashNode *pPrev = NULL;
H
Haojun Liao 已提交
691

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

H
Haojun Liao 已提交
697
    pNode = pe->next;
H
Haojun Liao 已提交
698

H
Haojun Liao 已提交
699
    assert(pNode != NULL);
H
Haojun Liao 已提交
700

H
Haojun Liao 已提交
701 702 703 704 705 706 707
    while (pNode != NULL) {
      int32_t newIdx = HASH_INDEX(pNode->hashVal, pHashObj->capacity);
      pNext = pNode->next;
      if (newIdx != idx) {
        pe->num -= 1;
        if (pPrev == NULL) {
          pe->next = pNext;
H
Haojun Liao 已提交
708
        } else {
H
Haojun Liao 已提交
709
          pPrev->next = pNext;
H
Haojun Liao 已提交
710
        }
H
Haojun Liao 已提交
711

H
Haojun Liao 已提交
712 713
        SHashEntry *pNewEntry = pHashObj->hashList[newIdx];
        pushfrontNodeInEntryList(pNewEntry, pNode);
H
Haojun Liao 已提交
714
      } else {
H
Haojun Liao 已提交
715
        pPrev = pNode;
H
Haojun Liao 已提交
716
      }
H
Haojun Liao 已提交
717
      pNode = pNext;
H
hjLiao 已提交
718 719 720
    }
  }

H
Haojun Liao 已提交
721 722
  int64_t et = taosGetTimestampUs();

H
Haojun Liao 已提交
723 724
//  uDebug("hash table resize completed, new capacity:%d, load factor:%f, elapsed time:%fms", (int32_t)pHashObj->capacity,
//         ((double)pHashObj->size) / pHashObj->capacity, (et - st) / 1000.0);
H
hjLiao 已提交
725 726 727
}

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

H
hjLiao 已提交
730
  if (pNewNode == NULL) {
H
Haojun Liao 已提交
731
    terrno = TSDB_CODE_OUT_OF_MEMORY;
H
hjLiao 已提交
732 733
    return NULL;
  }
H
Haojun Liao 已提交
734

H
Haojun Liao 已提交
735
  pNewNode->keyLen  = (uint32_t)keyLen;
H
hjLiao 已提交
736
  pNewNode->hashVal = hashVal;
S
hash  
Shengliang Guan 已提交
737
  pNewNode->dataLen = (uint32_t)dsize;
H
Haojun Liao 已提交
738
  pNewNode->refCount= 1;
739
  pNewNode->removed = 0;
H
Haojun Liao 已提交
740
  pNewNode->next    = NULL;
H
Haojun Liao 已提交
741 742 743 744

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

H
hjLiao 已提交
745 746 747
  return pNewNode;
}

H
Haojun Liao 已提交
748
void pushfrontNodeInEntryList(SHashEntry *pEntry, SHashNode *pNode) {
H
Haojun Liao 已提交
749
  assert(pNode != NULL && pEntry != NULL);
H
Haojun Liao 已提交
750

H
Haojun Liao 已提交
751 752
  pNode->next = pEntry->next;
  pEntry->next = pNode;
H
Haojun Liao 已提交
753 754

  pEntry->num += 1;
H
hjLiao 已提交
755 756
}

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
757 758 759 760
size_t taosHashGetMemSize(const SHashObj *pHashObj) {
  if (pHashObj == NULL) {
    return 0;
  }
H
Haojun Liao 已提交
761

H
Haojun Liao 已提交
762
  return (pHashObj->capacity * (sizeof(SHashEntry) + sizeof(void*))) + sizeof(SHashNode) * taosHashGetSize(pHashObj) + sizeof(SHashObj);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
763
}
H
Haojun Liao 已提交
764

H
Haojun Liao 已提交
765 766 767
void *taosHashGetKey(void *data, size_t* keyLen) {
  SHashNode * node = GET_HASH_PNODE(data);
  if (keyLen != NULL) {
L
Liu Jicong 已提交
768 769
    *keyLen = node->keyLen;
  }
D
dapan1121 已提交
770

H
Haojun Liao 已提交
771
  return GET_HASH_NODE_KEY(node);
W
wpan 已提交
772 773
}

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
774
// release the pNode, return next pNode, and lock the current entry
H
Haojun Liao 已提交
775
static void *taosHashReleaseNode(SHashObj *pHashObj, void *p, int *slot) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
776 777 778 779 780 781
  SHashNode *pOld = (SHashNode *)GET_HASH_PNODE(p);
  SHashNode *prevNode = NULL;

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

H
Haojun Liao 已提交
782
  taosHashEntryWLock(pHashObj, pe);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
783 784 785

  SHashNode *pNode = pe->next;
  while (pNode) {
H
Haojun Liao 已提交
786 787
    if (pNode == pOld)
      break;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
788 789 790 791 792

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

S
hash  
Shengliang Guan 已提交
793
  if (pNode) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
794
    pNode = pNode->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
795 796 797 798 799
    while (pNode) {
      if (pNode->removed == 0) break;
      pNode = pNode->next;
    }

H
Haojun Liao 已提交
800 801
    atomic_sub_fetch_32(&pOld->refCount, 1);
    if (pOld->refCount <=0) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
802 803 804 805 806
      if (prevNode) {
        prevNode->next = pOld->next;
      } else {
        pe->next = pOld->next;
      }
S
hash  
Shengliang Guan 已提交
807

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
808
      pe->num--;
H
Haojun Liao 已提交
809 810
      atomic_sub_fetch_64(&pHashObj->size, 1);
      FREE_HASH_NODE(pOld);
S
hash  
Shengliang Guan 已提交
811
    }
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
812
  } else {
H
Haojun Liao 已提交
813
//    uError("pNode:%p data:%p is not there!!!", pNode, p);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
814 815 816 817 818 819
  }

  return pNode;
}

void *taosHashIterate(SHashObj *pHashObj, void *p) {
S
hash  
Shengliang Guan 已提交
820
  if (pHashObj == NULL) return NULL;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
821

H
Haojun Liao 已提交
822
  int  slot = 0;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
823 824 825
  char *data = NULL;

  // only add the read lock to disable the resize process
H
Haojun Liao 已提交
826
  taosHashRLock(pHashObj);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
827 828 829 830 831 832

  SHashNode *pNode = NULL;
  if (p) {
    pNode = taosHashReleaseNode(pHashObj, p, &slot);
    if (pNode == NULL) {
      SHashEntry *pe = pHashObj->hashList[slot];
H
Haojun Liao 已提交
833
      taosHashEntryWUnlock(pHashObj, pe);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
834 835

      slot = slot + 1;
H
Haojun Liao 已提交
836
    }
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
837
  }
H
Haojun Liao 已提交
838

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

H
Haojun Liao 已提交
843
      taosHashEntryWLock(pHashObj, pe);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
844 845

      pNode = pe->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
846 847 848 849 850
      while (pNode) {
        if (pNode->removed == 0) break;
        pNode = pNode->next;
      }

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
851 852
      if (pNode) break;

H
Haojun Liao 已提交
853
      taosHashEntryWUnlock(pHashObj, pe);
H
Haojun Liao 已提交
854
    }
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
855
  }
H
Haojun Liao 已提交
856

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
857 858
  if (pNode) {
    SHashEntry *pe = pHashObj->hashList[slot];
859

H
Haojun Liao 已提交
860 861 862
    uint16_t prevRef = atomic_load_16(&pNode->refCount);
    uint16_t afterRef = atomic_add_fetch_16(&pNode->refCount, 1);
    ASSERT(prevRef < afterRef);
863 864 865 866 867

    // 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
H
Haojun Liao 已提交
868
      atomic_sub_fetch_16(&pNode->refCount, 1);
869 870 871 872 873 874 875 876 877
      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);
    }

H
Haojun Liao 已提交
878
    taosHashEntryWUnlock(pHashObj, pe);
H
hjLiao 已提交
879
  }
H
Haojun Liao 已提交
880

H
Haojun Liao 已提交
881
  taosHashRUnlock(pHashObj);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
882
  return data;
H
hjLiao 已提交
883
}
H
Haojun Liao 已提交
884

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
888
  // only add the read lock to disable the resize process
H
Haojun Liao 已提交
889
  taosHashRLock(pHashObj);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
890

H
Haojun Liao 已提交
891
  int slot;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
892 893 894
  taosHashReleaseNode(pHashObj, p, &slot);

  SHashEntry *pe = pHashObj->hashList[slot];
D
dapan1121 已提交
895

H
Haojun Liao 已提交
896 897 898 899 900 901 902
  taosHashEntryWUnlock(pHashObj, pe);
  taosHashRUnlock(pHashObj);
}

void *taosHashAcquire(SHashObj *pHashObj, const void *key, size_t keyLen) {
  void* p = NULL;
  return taosHashGetImpl(pHashObj, key, keyLen, &p, 0, true);
D
dapan1121 已提交
903 904
}

S
hash  
Shengliang Guan 已提交
905
void taosHashRelease(SHashObj *pHashObj, void *p) { taosHashCancelIterate(pHashObj, p); }