thash.c 23.1 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 277
  for (int32_t i = 0; i < pHashObj->capacity; ++i) {
    pHashObj->hashList[i] = (void *)((char *)p + i * sizeof(SHashEntry));
  }

  taosArrayPush(pHashObj->pMemBlock, &p);

H
hjxilinx 已提交
278
  return pHashObj;
279 280
}

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

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

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

H
Haojun Liao 已提交
300 301 302 303 304 305 306 307
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) {
  if (pHashObj == NULL || key == NULL || keyLen == 0 || data == NULL || size == 0) {
    return -1;
  }
308

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

H
Haojun Liao 已提交
385 386 387 388 389
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;
}
390

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

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

H
Haojun Liao 已提交
398 399
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 已提交
400 401 402
    return NULL;
  }

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

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

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

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

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

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

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

H
Haojun Liao 已提交
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
    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 已提交
456
    }
457

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

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

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

  return data;
469 470
}

H
Haojun Liao 已提交
471 472
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 已提交
473 474 475
    return -1;
  }

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

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

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

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

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

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

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
499
  while (pNode) {
H
Haojun Liao 已提交
500 501 502 503
    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 已提交
504

H
Haojun Liao 已提交
505 506 507 508 509 510 511 512
      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 已提交
513

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

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

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

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

H
Haojun Liao 已提交
532 533 534 535 536 537 538
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 已提交
539 540 541
  }

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

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

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

H
Haojun Liao 已提交
553 554 555 556 557 558
    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 已提交
559
      } else {
H
Haojun Liao 已提交
560 561
        if (pPrevNode == NULL) {
          pEntry->next = pNode->next;
H
Haojun Liao 已提交
562
        } else {
H
Haojun Liao 已提交
563
          pPrevNode->next = pNode->next;
H
Haojun Liao 已提交
564
        }
H
Haojun Liao 已提交
565 566 567 568 569
        pEntry->num -= 1;
        atomic_sub_fetch_64(&pHashObj->size, 1);
        SHashNode *next = pNode->next;
        FREE_HASH_NODE(pNode);
        pNode = next;
H
Haojun Liao 已提交
570
      }
H
Haojun Liao 已提交
571 572
    }

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

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

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

  SHashNode *pNode, *pNext;

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

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

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

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

602
      pNode = pNext;
603 604
    }

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

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

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

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

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

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

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

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

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

    // 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 已提交
647 648
    if (num < pEntry->num) {
      num = pEntry->num;
649 650
    }
  }
H
hjxilinx 已提交
651

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

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

H
Haojun Liao 已提交
661 662 663 664
  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 已提交
665 666 667
    return;
  }

H
Haojun Liao 已提交
668
  int64_t st = taosGetTimestampUs();
H
Haojun Liao 已提交
669 670 671
  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 已提交
672 673
    return;
  }
H
Haojun Liao 已提交
674

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

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

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

  taosArrayPush(pHashObj->pMemBlock, &p);

H
Haojun Liao 已提交
686 687 688 689 690 691
  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 已提交
692

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

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

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

H
Haojun Liao 已提交
702 703 704 705 706 707 708
    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 已提交
709
        } else {
H
Haojun Liao 已提交
710
          pPrev->next = pNext;
H
Haojun Liao 已提交
711
        }
H
Haojun Liao 已提交
712

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

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

H
Haojun Liao 已提交
724 725
//  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 已提交
726 727 728
}

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

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

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

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

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

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

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

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

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

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

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

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

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
775
// release the pNode, return next pNode, and lock the current entry
H
Haojun Liao 已提交
776
static void *taosHashReleaseNode(SHashObj *pHashObj, void *p, int *slot) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
777 778 779 780 781 782
  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 已提交
783
  taosHashEntryWLock(pHashObj, pe);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
784 785 786

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

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

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

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

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

  return pNode;
}

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

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

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

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

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

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

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

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

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

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

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

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

    // 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 已提交
869
      atomic_sub_fetch_16(&pNode->refCount, 1);
870 871 872 873 874 875 876 877 878
      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 已提交
879
    taosHashEntryWUnlock(pHashObj, pe);
H
hjLiao 已提交
880
  }
H
Haojun Liao 已提交
881

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

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

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

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

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

H
Haojun Liao 已提交
897 898 899 900 901 902 903
  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 已提交
904 905
}

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