内存字典实现
Posted 资质平庸的程序员
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内存字典实现相关的知识,希望对你有一定的参考价值。
此内存字典基于哈希表实现,适用于不明确具体数据量场景。
1 结构体定义
先想好功能,然后根据该功能想想需要哪些结构体来辅助实现该功能。
#define DICT_FLAG_FREE (0x01)
#define DICT_ITEM_EXIST (0x43)
#define CMIN(a, b) ((a) < (b) ? (a) : (b))
#define CMAX(a, b) ((a) > (b) ? (a) : (b))
#define CIF_RETURN(exps, ret) (if (__glibc_unlikely(exps)) return ret;)
typedef struct dict DICT_S;
typedef union commData COMM_DATA_U;
typedef struct dictItem DICT_ITEM_S;
typedef struct dictHNode DICT_HNODE_S;
/**
* C 类型通用容纳类型。
* a 地址;
* d 双精度浮点;
* s64 有符号 64 位整型;
* u64 无符号 64 位整型。*/
union commData
void *a;
double d;
int64_t s64;
uint64_t u64;
;
/**
* 常用状态
* OK 正常;
* ING 进行中;
* DONE 完成;
* OH 出错;
* OM 无内存。*/
enum commStatus
COK = 0,
CING,
CDONE,
COH,
COM,
;
/**
* 字典参数任命类型:
* khash key hash 计算回调;
* kcmp 键值比较回调函数;
* uext 用户数据,无可置 NULL;
* irset 字典条目去设置回调函数;
* size_min,
* size_max 字典最小/大尺寸;
* up_nmax 字典内部升级每允许的最大迭代次数;
* up_tatio 字典条目数/字典hash表大小超过该值时升级。*/
typedef struct dictAppoint
uint64_t (*khash)(const COMM_DATA_U *k);
int (*kcmp)(const COMM_DATA_U *dk, const COMM_DATA_U *uk);
void *uext;
int (*irset)(DICT_HNODE_S *n, const void *uext);
#define DICT_SIZE_MIN (11)
#define DICT_RATIO_MIN (4)
size_t size_min, size_max;
size_t up_nmax, up_ratio;
DICT_APP_S;
/**
* 字典 hash 结点类型
* item 字典条目;
* next 指向下一结点。*/
struct dictHNode
DICT_ITEM_S item;
DICT_HNODE_S *next;
;
/**
* 字典 hash 表类型:
* en hash 表入口;
* ens en 数;
* nds 结点数。*/
typedef struct dictHTbl
DICT_HNODE_S **en;
size_t ens, nds;
DICT_HTBL_S;
/**
* 字典类型:
* app 字典任命参数;
* ht 用于字典高效插入和查询的 hash 表;DICT_VERS,发行版和升级版;
* ui 正升级下标;
* flag 一些标记;
* vers 升级次数。*/
struct dict
#define DICT_VERS (2)
DICT_HTBL_S ht[DICT_VERS];
DICT_APP_S app;
size_t ui, flag;
size_t vers;
;
2 接口实现
先想好提供哪些接口,然后付诸实现。
2.1 接口原型
/**
* 根据任命参数 app 初始化字典,若参数 d 不为 NULL 则初始化
* d,返回初始化完成的字典地址。*/
DICT_S * dictAppoint(DICT_S *d, const DICT_APP_S *app);
/**
* 移除指定字典 d 。
* 若移除途中有失败 app->irset
* 执行失败则返回 irset 相应错误码,
* 成功返回 COK 。*/
int dictRemove(DICT_S *d);
/**
* 往字典 d 中添加字典结点 n ,若 n.item.k 标识的字典结点存在则出参
* exist 值会被置为 DICT_FLAG_EXIST 。
*
* 该函数返回 n.item.k 标识结点的条目值部分地址。
*
* 注:该函数以 DICT_HNODE_S 作为参数是为了让调用者自由组织结点内存,
* 供其考虑虚存碎片等问题。*/
COMM_DATA_U * dictAdd (DICT_S *d, DICT_HNODE_S *n, int *exist);
/**
* 在字典 d 中查找 k 标识的条目。
*
* 找到返回该条目值地址,否则返 NULL 。*/
COMM_DATA_U * dictFind(DICT_S *d, const COMM_DATA_U *k);
/**
* 在字典 d 中删除 k 标识的结点。
*
* 成功返 COK,否则返 irset 中相应错误码。*/
int dictDel(DICT_S *d, const COMM_DATA_U *k);
/**
* 对字典 d 进行显式升级,每次升级时间不超过
* up_tmax 纳秒。
*
* 该函数返 CDONE 表明升级完成;返 CING 表明升级
* 中;返其他值则表明出错。*/
int dictUpgrade(DICT_S *d, uint64_t up_tmax);
2.2 接口实现
#define DICT_FREE(d) (if (d->flag & DICT_FLAG_FREE) free(d);)
static int
_dictHtblSet(DICT_S *d, DICT_HTBL_S *ht, size_t size)
size_t i;
DICT_HNODE_S **en = NULL;
if (0 != size)
en = (DICT_HNODE_S **) malloc(sizeof(DICT_HNODE_S *) * size);
if (NULL == en) return COM;
for (i = 0; i < size; ++i) en[i] = NULL;
ht->en = en, ht->ens = size;
ht->nds = 0;
return COK;
static void
_dictUpgradeDone(DICT_S *d)
DICT_HTBL_S *ht = d->ht;
free(ht[0].en);
d->ui = 0, d->vers += 1;
ht[0] = ht[1];
(void) _dictHtblSet(d, ht+1, 0);
return ;
static int
_dictUpgrade(DICT_S *d, size_t up_nmax)
size_t up = 0;
DICT_HTBL_S *ht = d->ht;
DICT_APP_S *a = &d->app;
size_t i, ens = ht[0].ens;
for (i = d->ui; i < ens; ++i)
DICT_HNODE_S **p = &ht[0].en[i];
DICT_HNODE_S *c = *p, **h;
uint64_t e;
while (c)
e = a->khash(&c->item.k) % ht[1].ens;
h = &ht[1].en[e];
*p = c->next;
c->next = *h, *h = c;
ht[0].nds -= 1;
ht[1].nds += 1;
c = *p;
up += 1;
if (++up > up_nmax) goto _upgrade;
_upgrade:
if (i == ens)
_dictUpgradeDone(d);
return CDONE;
d->ui = i;
return CING;
/**
* 升级;
* 扩容升级;
* 缩容升级;
* 字典容量尚融洽时什么也不做。*/
static int
_dictUpgradeChk(DICT_S *d)
int ret;
size_t size;
DICT_APP_S *a = &d->app;
DICT_HTBL_S *ht = d->ht;
if (0 != ht[1].ens)
return _dictUpgrade(d, a->up_nmax);
if (ht[0].nds / ht[0].ens >= a->up_ratio)
size = CMIN((ht[0].ens << 1) + 1, a->size_max);
ret = _dictHtblSet(d, ht+1, size);
if (COK != ret) return ret;
return _dictUpgrade(d, a->up_nmax);
if (ht[0].ens > a->size_min && ht[0].nds * a->up_ratio < ht[0].ens)
size = ht[0].nds * a->up_ratio + a->size_min;
ret = _dictHtblSet(d, ht+1, size);
if (COK != ret) return ret;
return _dictUpgrade(d, a->up_nmax);
return CDONE;
static DICT_HNODE_S *
_dictFind(DICT_S *d, const COMM_DATA_U *k, DICT_HTBL_S **ht, DICT_HNODE_S ***prev)
size_t i, e;
DICT_HNODE_S **p, *c;
DICT_HTBL_S *v = NULL;
DICT_HTBL_S *t = d->ht;
DICT_APP_S *a = &d->app;
(void) _dictUpgradeChk(d);
for (i = 0; i < DICT_VERS; ++i)
if (0 == t[i].ens) continue;
v = t + i;
e = a->khash(k) % t[i].ens;
p = &t[i].en[e], c = *p;
for (/**/; c; p = &c->next, c = c->next)
if (0 == a->kcmp(&c->item.k, k))
goto _find;
_find:
if (ht) *ht = v;
if (prev) *prev = p;
return c;
inline static uint64_t
comm_getnsec(void)
struct timespec tp;
clock_gettime(CLOCK_REALTIME, &tp);
return tp.tv_sec * 1000000000 + tp.tv_nsec;
#if 0
#endif
DICT_S * dictAppoint(DICT_S *d, const DICT_APP_S *app)
DICT_S *_d = d;
if (NULL == _d)
if (NULL == (_d = (DICT_S *) malloc(sizeof(*_d)))) return NULL;
_d->flag = DICT_FLAG_FREE;
else
_d->flag = 0;
DICT_APP_S *a = &_d->app;
a->khash = app->khash;
a->irset = app->irset;
a->kcmp = app->kcmp;
a->uext = app->uext;
a->size_min = CMIN(app->size_min, DICT_SIZE_MIN);
a->size_max = CMAX(app->size_max, DICT_SIZE_MIN);
a->up_nmax = CMAX(app->up_nmax, DICT_SIZE_MIN);
a->up_ratio = CMAX(app->up_ratio, DICT_RATIO_MIN);
int ret;
d->ui = d->vers = 0;
(void) _dictHtblSet(_d, &_d->ht[1], 0);
ret = _dictHtblSet(_d, &_d->ht[0], a->size_min);
if (COK != ret)
DICT_FREE(_d);
return NULL;
return _d;
COMM_DATA_U * dictAdd(DICT_S *d, DICT_HNODE_S *n, int *exist)
DICT_HTBL_S *ht;
DICT_HNODE_S *f;
DICT_HNODE_S **prev;
f = _dictFind(d, &n->item.k, &ht, &prev);
if (NULL != f)
if (NULL != exist) *exist = DICT_ITEM_EXIST;
return &f->item.v;
n->next = *prev, *prev = n;
ht->nds += 1;
return &n->item.v;
COMM_DATA_U * dictFind(DICT_S *d, const COMM_DATA_U *k)
DICT_HNODE_S *f;
f = _dictFind(d, k, NULL, NULL);
return f ? &f->item.v : NULL;
int dictDel(DICT_S *d, const COMM_DATA_U *k)
DICT_HTBL_S *ht;
DICT_HNODE_S *del;
DICT_HNODE_S **prev;
del = _dictFind(d, k, &ht, &prev);
if (NULL == del) return COK;
DICT_APP_S *a = &d->app;
DICT_HNODE_S *next = del->next;
int ret = a->irset(del, a->uext);
if (COK != ret) return ret;
*prev = next;
ht->nds -= 1;
return COK;
int dictUpgrade(DICT_S *d, uint64_t up_tmax)
int ret;
uint64_t now;
uint64_t start = comm_getnsec();
do
ret = _dictUpgradeChk(d);
if (ret == CDONE || CING != ret)
break;
now = comm_getnsec();
while (now - start < up_tmax);
return ret;
int dictRemove(DICT_S *d)
int i, e;
DICT_HTBL_S *t = d->ht;
DICT_APP_S *a = &d->app;
DICT_HNODE_S **p, *c, *n;
for (i = 0; i < DICT_VERS; ++i)
for (e = 0; e < t[i].ens; ++e)
int ret;
p = &t[i].en[e], c = *p;
for (/**/; c; *p = c = n)
n = c->next;
ret = a->irset(c, a->uext);
if (COK != ret) return ret;
free(t[i].en);
_dictHtblSet(d, t+i, 0);
d->vers = d->ui = 0;
DICT_FREE(d);
return COK;
以上是关于内存字典实现的主要内容,如果未能解决你的问题,请参考以下文章