内存字典实现

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;

以上是关于内存字典实现的主要内容,如果未能解决你的问题,请参考以下文章

内存字典实现

内存字典实现

内存字典实现尝试

C语言 malloc()函数 分配内存空间尺寸的问题

C语言 malloc()函数 分配内存空间尺寸的问题

NSLog 可以打印的最大尺寸