thash.c 22.3 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
#include "os.h"
L
Liu Jicong 已提交
19
#include "taoserror.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
#define MAX_WARNING_REF_COUNT    10000
D
dapan1121 已提交
24
#define HASH_MAX_CAPACITY        (1024 * 1024 * 1024)
H
Haojun Liao 已提交
25 26 27 28 29
#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)

L
Liu Jicong 已提交
30 31 32
#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)))
H
Haojun Liao 已提交
33

X
Xiaoyu Wang 已提交
34 35 36 37 38 39
#define FREE_HASH_NODE(_fp, _n)      \
  do {                               \
    if (_fp != NULL) {               \
      (_fp)(GET_HASH_NODE_DATA(_n)); \
    }                                \
    taosMemoryFreeClear(_n);         \
H
Haojun Liao 已提交
40 41
  } while (0);

H
Haojun Liao 已提交
42
struct SHashNode {
L
Liu Jicong 已提交
43 44 45 46 47 48 49
  SHashNode *next;
  uint32_t   hashVal;   // the hash value of key
  uint32_t   dataLen;   // length of data
  uint32_t   keyLen;    // length of the key
  uint16_t   refCount;  // reference count
  int8_t     removed;   // flag to indicate removed
  char       data[];
H
Haojun Liao 已提交
50 51
};

H
Haojun Liao 已提交
52
typedef struct SHashEntry {
L
Liu Jicong 已提交
53 54 55
  int32_t    num;    // number of elements in current entry
  SRWLatch   latch;  // entry latch
  SHashNode *next;
H
Haojun Liao 已提交
56 57
} SHashEntry;

H
Haojun Liao 已提交
58
struct SHashObj {
X
Xiaoyu Wang 已提交
59
  SHashEntry      **hashList;
H
Haojun Liao 已提交
60
  size_t            capacity;      // number of slots
wafwerar's avatar
wafwerar 已提交
61
  int64_t           size;          // number of elements in hash table
H
Haojun Liao 已提交
62 63 64 65 66 67
  _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
X
Xiaoyu Wang 已提交
68
  SArray           *pMemBlock;     // memory block allocated for SHashEntry
H
Haojun Liao 已提交
69
  _hash_before_fn_t callbackFp;    // function invoked before return the value to caller
H
Haojun Liao 已提交
70
//  int64_t           compTimes;
H
Haojun Liao 已提交
71
};
H
Haojun Liao 已提交
72 73 74 75 76 77 78 79 80 81 82 83 84

/*
 * 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 已提交
85 86
    return;
  }
H
Haojun Liao 已提交
87 88

  taosWUnLockLatch(&pHashObj->lock);
89 90
}

H
Haojun Liao 已提交
91 92
static FORCE_INLINE void taosHashRLock(SHashObj *pHashObj) {
  if (pHashObj->type == HASH_NO_LOCK) {
H
hjxilinx 已提交
93 94
    return;
  }
H
Haojun Liao 已提交
95 96

  taosRLockLatch(&pHashObj->lock);
97 98
}

H
Haojun Liao 已提交
99 100
static FORCE_INLINE void taosHashRUnlock(SHashObj *pHashObj) {
  if (pHashObj->type == HASH_NO_LOCK) {
H
hjxilinx 已提交
101 102
    return;
  }
H
Haojun Liao 已提交
103 104 105 106

  taosRUnLockLatch(&pHashObj->lock);
}

L
Liu Jicong 已提交
107
static FORCE_INLINE void taosHashEntryWLock(const SHashObj *pHashObj, SHashEntry *pe) {
H
Haojun Liao 已提交
108 109 110 111 112 113
  if (pHashObj->type == HASH_NO_LOCK) {
    return;
  }
  taosWLockLatch(&pe->latch);
}

L
Liu Jicong 已提交
114
static FORCE_INLINE void taosHashEntryWUnlock(const SHashObj *pHashObj, SHashEntry *pe) {
H
Haojun Liao 已提交
115 116 117 118 119 120 121
  if (pHashObj->type == HASH_NO_LOCK) {
    return;
  }

  taosWUnLockLatch(&pe->latch);
}

L
Liu Jicong 已提交
122
static FORCE_INLINE void taosHashEntryRLock(const SHashObj *pHashObj, SHashEntry *pe) {
H
Haojun Liao 已提交
123 124 125 126 127
  if (pHashObj->type == HASH_NO_LOCK) {
    return;
  }

  taosRLockLatch(&pe->latch);
128 129
}

L
Liu Jicong 已提交
130
static FORCE_INLINE void taosHashEntryRUnlock(const SHashObj *pHashObj, SHashEntry *pe) {
H
Haojun Liao 已提交
131
  if (pHashObj->type == HASH_NO_LOCK) {
H
hjxilinx 已提交
132 133
    return;
  }
H
Haojun Liao 已提交
134 135

  taosRUnLockLatch(&pe->latch);
136 137 138
}

static FORCE_INLINE int32_t taosHashCapacity(int32_t length) {
wafwerar's avatar
wafwerar 已提交
139
  int32_t len = (length < HASH_MAX_CAPACITY ? length : HASH_MAX_CAPACITY);
140

S
Shengliang Guan 已提交
141
  int32_t i = 4;
142
  while (i < len) i = (i << 1u);
143 144 145
  return i;
}

L
Liu Jicong 已提交
146 147
static FORCE_INLINE SHashNode *doSearchInEntryList(SHashObj *pHashObj, SHashEntry *pe, const void *key, size_t keyLen,
                                                   uint32_t hashVal) {
H
Haojun Liao 已提交
148
  SHashNode *pNode = pe->next;
H
Haojun Liao 已提交
149
  while (pNode) {
D
dapan1121 已提交
150
    //atomic_add_fetch_64(&pHashObj->compTimes, 1);
L
Liu Jicong 已提交
151
    if ((pNode->keyLen == keyLen) && ((*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0) &&
S
hash  
Shengliang Guan 已提交
152
        pNode->removed == 0) {
H
Haojun Liao 已提交
153 154 155 156 157 158 159 160 161
      break;
    }

    pNode = pNode->next;
  }

  return pNode;
}

162
/**
H
Haojun Liao 已提交
163
 * resize the hash list if the threshold is reached
164
 *
H
hjxilinx 已提交
165
 * @param pHashObj
166
 */
H
hjLiao 已提交
167
static void taosHashTableResize(SHashObj *pHashObj);
168

H
hjLiao 已提交
169
/**
H
Haojun Liao 已提交
170 171
 * allocate and initialize a hash node
 *
H
hjLiao 已提交
172 173
 * @param key      key of object for hash, usually a null-terminated string
 * @param keyLen   length of key
H
Haojun Liao 已提交
174
 * @param pData    data to be stored in hash node
H
hjLiao 已提交
175 176 177 178
 * @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);
179

H
hjLiao 已提交
180
/**
H
Haojun Liao 已提交
181
 * update the hash node
H
hjLiao 已提交
182
 *
H
Haojun Liao 已提交
183 184 185 186 187
 * @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 已提交
188
 */
L
Liu Jicong 已提交
189 190
static FORCE_INLINE void doUpdateHashNode(SHashObj *pHashObj, SHashEntry *pe, SHashNode *prev, SHashNode *pNode,
                                          SHashNode *pNewNode) {
wafwerar's avatar
wafwerar 已提交
191
  atomic_sub_fetch_16(&pNode->refCount, 1);
H
Haojun Liao 已提交
192 193
  if (prev != NULL) {
    prev->next = pNewNode;
194
    ASSERT(prev->next != prev);
195 196
  } else {
    pe->next = pNewNode;
H
Haojun Liao 已提交
197
  }
H
Haojun Liao 已提交
198

H
Haojun Liao 已提交
199
  if (pNode->refCount <= 0) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
200
    pNewNode->next = pNode->next;
201 202
    ASSERT(pNewNode->next != pNewNode);

203
    FREE_HASH_NODE(pHashObj->freeFp, pNode);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
204
  } else {
S
hash  
Shengliang Guan 已提交
205
    pNewNode->next = pNode;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
206
    pe->num++;
H
Haojun Liao 已提交
207
    atomic_add_fetch_64(&pHashObj->size, 1);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
208
  }
H
Haojun Liao 已提交
209
}
210

H
hjLiao 已提交
211 212 213
/**
 * insert the hash node at the front of the linked list
 *
H
Haojun Liao 已提交
214 215
 * @param pHashObj   hash table object
 * @param pNode      the old node with requested key
H
hjLiao 已提交
216
 */
H
Haojun Liao 已提交
217
static void pushfrontNodeInEntryList(SHashEntry *pEntry, SHashNode *pNode);
218

219 220 221 222 223 224 225 226
/**
 * 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);

227
/**
H
Haojun Liao 已提交
228
 *
229 230
 * @param pHashObj
 * @return
231
 */
L
Liu Jicong 已提交
232
static FORCE_INLINE bool taosHashTableEmpty(const SHashObj *pHashObj) { return taosHashGetSize(pHashObj) == 0; }
233

H
Haojun Liao 已提交
234
SHashObj *taosHashInit(size_t capacity, _hash_fn_t fn, bool update, SHashLockTypeE type) {
H
Haojun Liao 已提交
235
  if (fn == NULL) {
H
Haojun Liao 已提交
236
    terrno = TSDB_CODE_INVALID_PARA;
H
Haojun Liao 已提交
237 238 239
    return NULL;
  }

240 241
  if (capacity == 0) {
    capacity = 4;
242 243
  }

H
Haojun Liao 已提交
244
  SHashObj *pHashObj = (SHashObj *)taosMemoryMalloc(sizeof(SHashObj));
H
hjxilinx 已提交
245
  if (pHashObj == NULL) {
H
Haojun Liao 已提交
246
    terrno = TSDB_CODE_OUT_OF_MEMORY;
247 248 249 250
    return NULL;
  }

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

254
  pHashObj->equalFp = memcmp;
L
Liu Jicong 已提交
255
  pHashObj->hashFp = fn;
H
Haojun Liao 已提交
256
  pHashObj->type = type;
257
  pHashObj->lock = 0;
H
Haojun Liao 已提交
258
  pHashObj->enableUpdate = update;
259 260
  pHashObj->freeFp = NULL;
  pHashObj->callbackFp = NULL;
261

H
Haojun Liao 已提交
262
  pHashObj->hashList = (SHashEntry **)taosMemoryMalloc(pHashObj->capacity * sizeof(void *));
H
hjxilinx 已提交
263
  if (pHashObj->hashList == NULL) {
wafwerar's avatar
wafwerar 已提交
264
    taosMemoryFree(pHashObj);
H
Haojun Liao 已提交
265
    terrno = TSDB_CODE_OUT_OF_MEMORY;
266
    return NULL;
H
Haojun Liao 已提交
267
  }
H
Haojun Liao 已提交
268

H
Haojun Liao 已提交
269 270
  pHashObj->pMemBlock = taosArrayInit(8, sizeof(void *));
  if (pHashObj->pMemBlock == NULL) {
wafwerar's avatar
wafwerar 已提交
271 272
    taosMemoryFree(pHashObj->hashList);
    taosMemoryFree(pHashObj);
H
Haojun Liao 已提交
273 274 275
    terrno = TSDB_CODE_OUT_OF_MEMORY;
    return NULL;
  }
H
Haojun Liao 已提交
276

H
Haojun Liao 已提交
277
  void *p = taosMemoryMalloc(pHashObj->capacity * sizeof(SHashEntry));
H
Haojun Liao 已提交
278 279
  if (p == NULL) {
    taosArrayDestroy(pHashObj->pMemBlock);
wafwerar's avatar
wafwerar 已提交
280 281
    taosMemoryFree(pHashObj->hashList);
    taosMemoryFree(pHashObj);
H
Haojun Liao 已提交
282 283
    terrno = TSDB_CODE_OUT_OF_MEMORY;
    return NULL;
284
  }
H
hjxilinx 已提交
285

H
Haojun Liao 已提交
286 287
  for (int32_t i = 0; i < pHashObj->capacity; ++i) {
    pHashObj->hashList[i] = (void *)((char *)p + i * sizeof(SHashEntry));
H
Haojun Liao 已提交
288 289 290
    pHashObj->hashList[i]->num = 0;
    pHashObj->hashList[i]->latch = 0;
    pHashObj->hashList[i]->next = NULL;
H
Haojun Liao 已提交
291 292 293
  }

  taosArrayPush(pHashObj->pMemBlock, &p);
H
hjxilinx 已提交
294
  return pHashObj;
295 296
}

297 298 299
void taosHashSetEqualFp(SHashObj *pHashObj, _equal_fn_t fp) {
  if (pHashObj != NULL && fp != NULL) {
    pHashObj->equalFp = fp;
S
hash  
Shengliang Guan 已提交
300 301
  }
}
302

H
Haojun Liao 已提交
303 304 305 306 307 308
void taosHashSetFreeFp(SHashObj *pHashObj, _hash_free_fn_t fp) {
  if (pHashObj != NULL && fp != NULL) {
    pHashObj->freeFp = fp;
  }
}

309
int32_t taosHashGetSize(const SHashObj *pHashObj) {
H
Haojun Liao 已提交
310
  if (pHashObj == NULL) {
311 312
    return 0;
  }
L
Liu Jicong 已提交
313
  return (int32_t)atomic_load_64((int64_t *)&pHashObj->size);
314 315
}

X
Xiaoyu Wang 已提交
316
int32_t taosHashPut(SHashObj *pHashObj, const void *key, size_t keyLen, const void *data, size_t size) {
H
Haojun Liao 已提交
317
  if (pHashObj == NULL || key == NULL || keyLen == 0) {
318
    terrno = TSDB_CODE_INVALID_PTR;
H
Haojun Liao 已提交
319 320
    return -1;
  }
321

322
  uint32_t hashVal = (*pHashObj->hashFp)(key, (uint32_t)keyLen);
323

H
Haojun Liao 已提交
324 325
  // need the resize process, write lock applied
  if (HASH_NEED_RESIZE(pHashObj)) {
H
Haojun Liao 已提交
326
    taosHashWLock(pHashObj);
H
Haojun Liao 已提交
327
    taosHashTableResize(pHashObj);
H
Haojun Liao 已提交
328
    taosHashWUnlock(pHashObj);
H
Haojun Liao 已提交
329 330
  }

331
  // disable resize
H
Haojun Liao 已提交
332
  taosHashRLock(pHashObj);
H
Haojun Liao 已提交
333

H
Hongze Cheng 已提交
334
  uint32_t    slot = HASH_INDEX(hashVal, pHashObj->capacity);
H
Haojun Liao 已提交
335
  SHashEntry *pe = pHashObj->hashList[slot];
336

H
Haojun Liao 已提交
337
  taosHashEntryWLock(pHashObj, pe);
338

H
Haojun Liao 已提交
339
  SHashNode *pNode = pe->next;
L
Liu Jicong 已提交
340
  SHashNode *prev = NULL;
H
Haojun Liao 已提交
341
  while (pNode) {
L
Liu Jicong 已提交
342
    if ((pNode->keyLen == keyLen) && (*(pHashObj->equalFp))(GET_HASH_NODE_KEY(pNode), key, keyLen) == 0 &&
S
hash  
Shengliang Guan 已提交
343
        pNode->removed == 0) {
H
Haojun Liao 已提交
344
      break;
345 346
    }

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

  if (pNode == NULL) {
    // no data in hash table with the specified key, add it into hash table
353 354 355 356 357
    SHashNode *pNewNode = doCreateHashNode(key, keyLen, data, size, hashVal);
    if (pNewNode == NULL) {
      return -1;
    }

H
Haojun Liao 已提交
358
    pushfrontNodeInEntryList(pe, pNewNode);
H
Haojun Liao 已提交
359
    taosHashEntryWUnlock(pHashObj, pe);
H
Haojun Liao 已提交
360 361

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

H
Haojun Liao 已提交
365
    return 0;
366
  } else {
H
Haojun Liao 已提交
367 368
    // not support the update operation, return error
    if (pHashObj->enableUpdate) {
369 370 371 372 373
      SHashNode *pNewNode = doCreateHashNode(key, keyLen, data, size, hashVal);
      if (pNewNode == NULL) {
        return -1;
      }

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
374
      doUpdateHashNode(pHashObj, pe, prev, pNode, pNewNode);
375 376
    } else {
      terrno = TSDB_CODE_DUP_KEY;
H
Haojun Liao 已提交
377 378
    }

H
Haojun Liao 已提交
379
    taosHashEntryWUnlock(pHashObj, pe);
H
Haojun Liao 已提交
380 381

    // enable resize
H
Haojun Liao 已提交
382
    taosHashRUnlock(pHashObj);
H
Haojun Liao 已提交
383
    return pHashObj->enableUpdate ? 0 : -2;
H
Haojun Liao 已提交
384
  }
385 386
}

L
Liu Jicong 已提交
387
static void *taosHashGetImpl(SHashObj *pHashObj, const void *key, size_t keyLen, void **d, int32_t *size, bool addRef);
D
dapan1121 已提交
388

H
hjLiao 已提交
389
void *taosHashGet(SHashObj *pHashObj, const void *key, size_t keyLen) {
L
Liu Jicong 已提交
390
  void *p = NULL;
H
Haojun Liao 已提交
391
  return taosHashGetImpl(pHashObj, key, keyLen, &p, 0, false);
H
Haojun Liao 已提交
392
}
H
Haojun Liao 已提交
393

H
Haojun Liao 已提交
394 395
int32_t taosHashGetDup(SHashObj *pHashObj, const void *key, size_t keyLen, void *destBuf) {
  terrno = 0;
L
Liu Jicong 已提交
396
  /*char* p = */ taosHashGetImpl(pHashObj, key, keyLen, &destBuf, 0, false);
H
Haojun Liao 已提交
397 398
  return terrno;
}
399

L
Liu Jicong 已提交
400
int32_t taosHashGetDup_m(SHashObj *pHashObj, const void *key, size_t keyLen, void **destBuf, int32_t *size) {
H
Haojun Liao 已提交
401
  terrno = 0;
402

L
Liu Jicong 已提交
403
  /*char* p = */ taosHashGetImpl(pHashObj, key, keyLen, destBuf, size, false);
H
Haojun Liao 已提交
404
  return terrno;
405
}
H
Haojun Liao 已提交
406

L
Liu Jicong 已提交
407
void *taosHashGetImpl(SHashObj *pHashObj, const void *key, size_t keyLen, void **d, int32_t *size, bool addRef) {
408 409 410 411 412
  if (pHashObj == NULL || keyLen == 0 || key == NULL) {
    return NULL;
  }

  if ((atomic_load_64((int64_t *)&pHashObj->size) == 0)) {
H
Haojun Liao 已提交
413 414 415
    return NULL;
  }

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

H
Haojun Liao 已提交
418
  // only add the read lock to disable the resize process
H
Haojun Liao 已提交
419
  taosHashRLock(pHashObj);
H
Haojun Liao 已提交
420

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

H
Haojun Liao 已提交
424 425
  // no data, return directly
  if (atomic_load_32(&pe->num) == 0) {
H
Haojun Liao 已提交
426
    taosHashRUnlock(pHashObj);
427 428
    return NULL;
  }
H
Haojun Liao 已提交
429 430

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

433
  SHashNode *pNode = doSearchInEntryList(pHashObj, pe, key, keyLen, hashVal);
H
Haojun Liao 已提交
434
  if (pNode != NULL) {
H
Haojun Liao 已提交
435 436
    if (pHashObj->callbackFp != NULL) {
      pHashObj->callbackFp(GET_HASH_NODE_DATA(pNode));
H
Haojun Liao 已提交
437
    }
H
Haojun Liao 已提交
438

H
Haojun Liao 已提交
439 440
    if (size != NULL) {
      if (*d == NULL) {
L
Liu Jicong 已提交
441
        *size = pNode->dataLen;
wafwerar's avatar
wafwerar 已提交
442
        *d = taosMemoryCalloc(1, *size);
H
Haojun Liao 已提交
443 444 445 446 447
        if (*d == NULL) {
          terrno = TSDB_CODE_OUT_OF_MEMORY;
          return NULL;
        }
      } else if (*size < pNode->dataLen) {
L
Liu Jicong 已提交
448 449
        *size = pNode->dataLen;
        char *tmp = taosMemoryRealloc(*d, *size);
H
Haojun Liao 已提交
450 451 452 453 454 455 456 457 458 459 460
        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 已提交
461
    }
462

H
Haojun Liao 已提交
463 464
    if (*d != NULL) {
      memcpy(*d, GET_HASH_NODE_DATA(pNode), pNode->dataLen);
D
dapan1121 已提交
465 466
    }

467
    data = GET_HASH_NODE_DATA(pNode);
H
Haojun Liao 已提交
468 469
  }

H
Haojun Liao 已提交
470 471
  taosHashEntryRUnlock(pHashObj, pe);
  taosHashRUnlock(pHashObj);
H
Haojun Liao 已提交
472 473

  return data;
474 475
}

H
Haojun Liao 已提交
476
int32_t taosHashRemove(SHashObj *pHashObj, const void *key, size_t keyLen) {
H
Haojun Liao 已提交
477
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj) || key == NULL || keyLen == 0) {
H
Haojun Liao 已提交
478 479 480
    return -1;
  }

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

H
Haojun Liao 已提交
483
  // disable the resize process
H
Haojun Liao 已提交
484
  taosHashRLock(pHashObj);
H
Haojun Liao 已提交
485

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

H
Haojun Liao 已提交
489
  taosHashEntryWLock(pHashObj, pe);
490 491

  // double check after locked
H
Haojun Liao 已提交
492
  if (pe->num == 0) {
H
Haojun Liao 已提交
493 494
    taosHashEntryWUnlock(pHashObj, pe);
    taosHashRUnlock(pHashObj);
H
Haojun Liao 已提交
495
    return -1;
H
Haojun Liao 已提交
496 497
  }

L
Liu Jicong 已提交
498
  int        code = -1;
H
Haojun Liao 已提交
499
  SHashNode *pNode = pe->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
500
  SHashNode *prevNode = NULL;
H
Haojun Liao 已提交
501

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

wafwerar's avatar
wafwerar 已提交
507
      atomic_sub_fetch_16(&pNode->refCount, 1);
H
Haojun Liao 已提交
508 509 510 511 512 513
      pNode->removed = 1;
      if (pNode->refCount <= 0) {
        if (prevNode == NULL) {
          pe->next = pNode->next;
        } else {
          prevNode->next = pNode->next;
514
          ASSERT(prevNode->next != prevNode);
H
Haojun Liao 已提交
515
        }
H
Haojun Liao 已提交
516

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

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

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

533
void taosHashClear(SHashObj *pHashObj) {
H
Haojun Liao 已提交
534 535 536
  if (pHashObj == NULL) {
    return;
  }
537 538 539

  SHashNode *pNode, *pNext;

H
Haojun Liao 已提交
540
  taosHashWLock(pHashObj);
541

542 543 544 545 546
  for (int32_t i = 0; i < pHashObj->capacity; ++i) {
    SHashEntry *pEntry = pHashObj->hashList[i];
    if (pEntry->num == 0) {
      continue;
    }
547

548 549 550
    pNode = pEntry->next;
    while (pNode) {
      pNext = pNode->next;
551
      FREE_HASH_NODE(pHashObj->freeFp, pNode);
H
hjxilinx 已提交
552

553
      pNode = pNext;
554 555
    }

556 557
    pEntry->num = 0;
    pEntry->next = NULL;
558 559
  }

H
Haojun Liao 已提交
560 561
  pHashObj->size = 0;
  taosHashWUnlock(pHashObj);
562 563
}

wafwerar's avatar
wafwerar 已提交
564
// the input paras should be SHashObj **, so the origin input will be set by taosMemoryFreeClear(*pHashObj)
565 566 567 568 569
void taosHashCleanup(SHashObj *pHashObj) {
  if (pHashObj == NULL) {
    return;
  }

570
  taosHashClear(pHashObj);
wafwerar's avatar
wafwerar 已提交
571
  taosMemoryFreeClear(pHashObj->hashList);
H
Haojun Liao 已提交
572 573 574

  // destroy mem block
  size_t memBlock = taosArrayGetSize(pHashObj->pMemBlock);
H
Haojun Liao 已提交
575 576
  for (int32_t i = 0; i < memBlock; ++i) {
    void *p = taosArrayGetP(pHashObj->pMemBlock, i);
wafwerar's avatar
wafwerar 已提交
577
    taosMemoryFreeClear(p);
H
Haojun Liao 已提交
578 579 580
  }

  taosArrayDestroy(pHashObj->pMemBlock);
wafwerar's avatar
wafwerar 已提交
581
  taosMemoryFree(pHashObj);
582 583 584
}

// for profile only
L
Liu Jicong 已提交
585
int32_t taosHashGetMaxOverflowLinkLength(const SHashObj *pHashObj) {
586
  if (pHashObj == NULL || taosHashTableEmpty(pHashObj)) {
587 588
    return 0;
  }
H
hjxilinx 已提交
589

590
  int32_t num = 0;
H
hjxilinx 已提交
591

L
Liu Jicong 已提交
592
  taosHashRLock((SHashObj *)pHashObj);
H
hjxilinx 已提交
593
  for (int32_t i = 0; i < pHashObj->size; ++i) {
H
Haojun Liao 已提交
594
    SHashEntry *pEntry = pHashObj->hashList[i];
H
Haojun Liao 已提交
595 596 597

    // 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 已提交
598 599
    if (num < pEntry->num) {
      num = pEntry->num;
600 601
    }
  }
H
hjxilinx 已提交
602

L
Liu Jicong 已提交
603
  taosHashRUnlock((SHashObj *)pHashObj);
604 605
  return num;
}
H
hjLiao 已提交
606 607

void taosHashTableResize(SHashObj *pHashObj) {
H
Haojun Liao 已提交
608
  if (!HASH_NEED_RESIZE(pHashObj)) {
H
hjLiao 已提交
609 610
    return;
  }
H
Haojun Liao 已提交
611

H
Haojun Liao 已提交
612 613
  int32_t newCapacity = (int32_t)(pHashObj->capacity << 1u);
  if (newCapacity > HASH_MAX_CAPACITY) {
L
Liu Jicong 已提交
614 615
    //    uDebug("current capacity:%zu, maximum capacity:%d, no resize applied due to limitation is reached",
    //           pHashObj->capacity, HASH_MAX_CAPACITY);
H
hjLiao 已提交
616 617 618
    return;
  }

H
Haojun Liao 已提交
619
  int64_t st = taosGetTimestampUs();
620
  SHashEntry **pNewEntryList = taosMemoryRealloc(pHashObj->hashList, sizeof(SHashEntry *) * newCapacity);
H
Haojun Liao 已提交
621
  if (pNewEntryList == NULL) {
L
Liu Jicong 已提交
622
    //    uDebug("cache resize failed due to out of memory, capacity remain:%zu", pHashObj->capacity);
H
hjLiao 已提交
623 624
    return;
  }
H
Haojun Liao 已提交
625

H
Haojun Liao 已提交
626 627
  pHashObj->hashList = pNewEntryList;

H
Haojun Liao 已提交
628
  size_t inc = newCapacity - pHashObj->capacity;
X
Xiaoyu Wang 已提交
629
  void  *p = taosMemoryCalloc(inc, sizeof(SHashEntry));
H
Haojun Liao 已提交
630

H
Haojun Liao 已提交
631
  for (int32_t i = 0; i < inc; ++i) {
S
Shengliang Guan 已提交
632
    pHashObj->hashList[i + pHashObj->capacity] = (void *)((char *)p + i * sizeof(SHashEntry));
H
Haojun Liao 已提交
633 634 635 636
  }

  taosArrayPush(pHashObj->pMemBlock, &p);

H
Haojun Liao 已提交
637 638 639
  pHashObj->capacity = newCapacity;
  for (int32_t idx = 0; idx < pHashObj->capacity; ++idx) {
    SHashEntry *pe = pHashObj->hashList[idx];
X
Xiaoyu Wang 已提交
640 641 642
    SHashNode  *pNode;
    SHashNode  *pNext;
    SHashNode  *pPrev = NULL;
H
Haojun Liao 已提交
643

H
Haojun Liao 已提交
644 645
    if (pe->num == 0) {
      continue;
H
hjLiao 已提交
646
    }
H
Haojun Liao 已提交
647

H
Haojun Liao 已提交
648
    pNode = pe->next;
H
Haojun Liao 已提交
649

H
Haojun Liao 已提交
650 651 652 653 654 655 656
    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 已提交
657
        } else {
H
Haojun Liao 已提交
658
          pPrev->next = pNext;
H
Haojun Liao 已提交
659
        }
H
Haojun Liao 已提交
660

H
Haojun Liao 已提交
661 662
        SHashEntry *pNewEntry = pHashObj->hashList[newIdx];
        pushfrontNodeInEntryList(pNewEntry, pNode);
H
Haojun Liao 已提交
663
      } else {
H
Haojun Liao 已提交
664
        pPrev = pNode;
H
Haojun Liao 已提交
665
      }
H
Haojun Liao 已提交
666
      pNode = pNext;
H
hjLiao 已提交
667 668 669
    }
  }

H
Haojun Liao 已提交
670 671
  int64_t et = taosGetTimestampUs();

L
Liu Jicong 已提交
672 673 674
  //  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 已提交
675 676 677
}

SHashNode *doCreateHashNode(const void *key, size_t keyLen, const void *pData, size_t dsize, uint32_t hashVal) {
L
Liu Jicong 已提交
678
  SHashNode *pNewNode = taosMemoryMalloc(sizeof(SHashNode) + keyLen + dsize + 1);
H
Haojun Liao 已提交
679

H
hjLiao 已提交
680
  if (pNewNode == NULL) {
H
Haojun Liao 已提交
681
    terrno = TSDB_CODE_OUT_OF_MEMORY;
H
hjLiao 已提交
682 683
    return NULL;
  }
H
Haojun Liao 已提交
684

L
Liu Jicong 已提交
685
  pNewNode->keyLen = (uint32_t)keyLen;
H
hjLiao 已提交
686
  pNewNode->hashVal = hashVal;
S
hash  
Shengliang Guan 已提交
687
  pNewNode->dataLen = (uint32_t)dsize;
L
Liu Jicong 已提交
688
  pNewNode->refCount = 1;
689
  pNewNode->removed = 0;
L
Liu Jicong 已提交
690
  pNewNode->next = NULL;
H
Haojun Liao 已提交
691

L
Liu Jicong 已提交
692
  if (pData) memcpy(GET_HASH_NODE_DATA(pNewNode), pData, dsize);
H
Haojun Liao 已提交
693 694
  memcpy(GET_HASH_NODE_KEY(pNewNode), key, keyLen);

H
hjLiao 已提交
695 696 697
  return pNewNode;
}

H
Haojun Liao 已提交
698 699 700
void pushfrontNodeInEntryList(SHashEntry *pEntry, SHashNode *pNode) {
  pNode->next = pEntry->next;
  pEntry->next = pNode;
H
Haojun Liao 已提交
701
  pEntry->num += 1;
H
hjLiao 已提交
702 703
}

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
704 705 706 707
size_t taosHashGetMemSize(const SHashObj *pHashObj) {
  if (pHashObj == NULL) {
    return 0;
  }
H
Haojun Liao 已提交
708

L
Liu Jicong 已提交
709 710
  return (pHashObj->capacity * (sizeof(SHashEntry) + sizeof(void *))) + sizeof(SHashNode) * taosHashGetSize(pHashObj) +
         sizeof(SHashObj);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
711
}
H
Haojun Liao 已提交
712

L
Liu Jicong 已提交
713 714
void *taosHashGetKey(void *data, size_t *keyLen) {
  SHashNode *node = GET_HASH_PNODE(data);
H
Haojun Liao 已提交
715
  if (keyLen != NULL) {
L
Liu Jicong 已提交
716 717
    *keyLen = node->keyLen;
  }
D
dapan1121 已提交
718

H
Haojun Liao 已提交
719
  return GET_HASH_NODE_KEY(node);
W
wpan 已提交
720 721
}

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
722
// release the pNode, return next pNode, and lock the current entry
H
Haojun Liao 已提交
723
static void *taosHashReleaseNode(SHashObj *pHashObj, void *p, int *slot) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
724 725 726 727 728 729
  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 已提交
730
  taosHashEntryWLock(pHashObj, pe);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
731 732 733

  SHashNode *pNode = pe->next;
  while (pNode) {
L
Liu Jicong 已提交
734
    if (pNode == pOld) break;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
735 736 737 738 739

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

S
hash  
Shengliang Guan 已提交
740
  if (pNode) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
741
    pNode = pNode->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
742 743 744 745 746
    while (pNode) {
      if (pNode->removed == 0) break;
      pNode = pNode->next;
    }

wafwerar's avatar
wafwerar 已提交
747
    atomic_sub_fetch_16(&pOld->refCount, 1);
L
Liu Jicong 已提交
748
    if (pOld->refCount <= 0) {
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
749 750
      if (prevNode) {
        prevNode->next = pOld->next;
H
Haojun Liao 已提交
751
        ASSERT(prevNode->next != prevNode);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
752 753
      } else {
        pe->next = pOld->next;
L
Liu Jicong 已提交
754
        SHashNode *x = pe->next;
H
Haojun Liao 已提交
755 756 757
        if (x != NULL) {
          ASSERT(x->next != x);
        }
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
758
      }
S
hash  
Shengliang Guan 已提交
759

陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
760
      pe->num--;
H
Haojun Liao 已提交
761
      atomic_sub_fetch_64(&pHashObj->size, 1);
762
      FREE_HASH_NODE(pHashObj->freeFp, pOld);
S
hash  
Shengliang Guan 已提交
763
    }
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
764
  } else {
L
Liu Jicong 已提交
765
    //    uError("pNode:%p data:%p is not there!!!", pNode, p);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
766 767 768 769 770 771
  }

  return pNode;
}

void *taosHashIterate(SHashObj *pHashObj, void *p) {
D
dapan1121 已提交
772
  if (pHashObj == NULL || pHashObj->size == 0) return NULL;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
773

L
Liu Jicong 已提交
774
  int   slot = 0;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
775 776 777
  char *data = NULL;

  // only add the read lock to disable the resize process
H
Haojun Liao 已提交
778
  taosHashRLock(pHashObj);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
779 780 781 782 783 784

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

      slot = slot + 1;
H
Haojun Liao 已提交
788
    }
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
789
  }
H
Haojun Liao 已提交
790

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

H
Haojun Liao 已提交
795
      taosHashEntryWLock(pHashObj, pe);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
796 797

      pNode = pe->next;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
798 799 800 801 802
      while (pNode) {
        if (pNode->removed == 0) break;
        pNode = pNode->next;
      }

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

H
Haojun Liao 已提交
805
      taosHashEntryWUnlock(pHashObj, pe);
H
Haojun Liao 已提交
806
    }
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
807
  }
H
Haojun Liao 已提交
808

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

812
    /*uint16_t prevRef = atomic_load_16(&pNode->refCount);*/
H
Haojun Liao 已提交
813
    uint16_t afterRef = atomic_add_fetch_16(&pNode->refCount, 1);
814

815
    data = GET_HASH_NODE_DATA(pNode);
816 817 818 819 820

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

H
Haojun Liao 已提交
821
    taosHashEntryWUnlock(pHashObj, pe);
H
hjLiao 已提交
822
  }
H
Haojun Liao 已提交
823

H
Haojun Liao 已提交
824
  taosHashRUnlock(pHashObj);
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
825
  return data;
H
hjLiao 已提交
826
}
H
Haojun Liao 已提交
827

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

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

H
Haojun Liao 已提交
834
  int slot;
陶建辉(Jeff)'s avatar
陶建辉(Jeff) 已提交
835 836 837
  taosHashReleaseNode(pHashObj, p, &slot);

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

H
Haojun Liao 已提交
839 840 841 842
  taosHashEntryWUnlock(pHashObj, pe);
  taosHashRUnlock(pHashObj);
}

L
Liu Jicong 已提交
843
// TODO remove it
H
Haojun Liao 已提交
844
void *taosHashAcquire(SHashObj *pHashObj, const void *key, size_t keyLen) {
L
Liu Jicong 已提交
845
  void *p = NULL;
H
Haojun Liao 已提交
846
  return taosHashGetImpl(pHashObj, key, keyLen, &p, 0, true);
D
dapan1121 已提交
847 848
}

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

H
Haojun Liao 已提交
851
int64_t taosHashGetCompTimes(SHashObj *pHashObj) { return 0 /*atomic_load_64(&pHashObj->compTimes)*/; }