コード例 #1
0
int do_item_link(item *it) {
    MEMCACHED_ITEM_LINK(ITEM_key(it), it->nbytes);
    assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
    assert(it->nbytes < (1024 * 1024));  /* 1MB max size */
    it->it_flags |= ITEM_LINKED;
    it->time = current_time;
    assoc_insert(it);

    STATS_LOCK();
    stats.curr_bytes += ITEM_ntotal(it);
    stats.curr_items += 1;
    stats.total_items += 1;
    STATS_UNLOCK();

#ifdef USE_REPLICATION
    /* Allocate a new CAS ID on link. */
    if(!(it->it_flags & ITEM_REPDATA))
        it->cas_id = get_cas_id();
#else
    /* Allocate a new CAS ID on link. */
    it->cas_id = get_cas_id();
#endif /* USE_REPLICATION */

    item_link_q(it);

    return 1;
}
コード例 #2
0
//将item插入到哈希表和LRU队列中,hv为哈希值
int do_item_link(item *it, const uint32_t hv) {
    MEMCACHED_ITEM_LINK(ITEM_key(it), it->nkey, it->nbytes);
    //确保这个item已经从slab分配出去并且还没插入到LRU队列中
    assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
    mutex_lock(&cache_lock);
    //加入link标记
    it->it_flags |= ITEM_LINKED;
    it->time = current_time;

    STATS_LOCK();
    stats.curr_bytes += ITEM_ntotal(it);
    stats.curr_items += 1;
    stats.total_items += 1;
    STATS_UNLOCK();

    /* Allocate a new CAS ID on link. */
    ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);
    //插入到hash表中
    assoc_insert(it, hv);
    //item插入到链表中
    item_link_q(it);
    //引用计数加1
    refcount_incr(&it->refcount);
    mutex_unlock(&cache_lock);

    return 1;
}
コード例 #3
0
ファイル: item.c プロジェクト: cyberrbob/memcachedb
int item_free(item *it) {
    size_t ntotal = 0;
    if (NULL == it)
        return 0;

    /* ntotal may be wrong, if 'it' is not a full item. */
    ntotal = ITEM_ntotal(it);
    if (ntotal > settings.item_buf_size){
        if (settings.verbose > 1) {
            fprintf(stderr, "ntotal: %d, use free() directly.\n", ntotal);
        }
        free(it);   
    }else{
        if (0 != item_add_to_freelist(it)) {
            if (settings.verbose > 1) {
                fprintf(stderr, "ntotal: %d, add a item buffer to freelist fail, use free() directly.\n", ntotal);
            }
            free(it);   
        }else{
            if (settings.verbose > 1) {
                fprintf(stderr, "ntotal: %d, add a item buffer to freelist.\n", ntotal);
            }
        }
    }
    return 0;
}
コード例 #4
0
ファイル: items.c プロジェクト: iamrohit/memcached
/*@null@*/
void do_item_stats_sizes(ADD_STAT add_stats, void *c) {

    /* max 1MB object, divided into 32 bytes size buckets */
    const int num_buckets = 32768;
    unsigned int *histogram = calloc(num_buckets, sizeof(int));

    if (histogram != NULL) {
        int i;

        /* build the histogram */
        for (i = 0; i < LARGEST_ID; i++) {
            item *iter = heads[i];
            while (iter) {
                int ntotal = ITEM_ntotal(iter);
                int bucket = ntotal / 32;
                if ((ntotal % 32) != 0) bucket++;
                if (bucket < num_buckets) histogram[bucket]++;
                iter = iter->next;
            }
        }

        /* write the buffer */
        for (i = 0; i < num_buckets; i++) {
            if (histogram[i] != 0) {
                char key[8];
                int klen = 0;
                klen = snprintf(key, sizeof(key), "%d", i * 32);
                assert(klen < sizeof(key));
                APPEND_STAT(key, "%u", histogram[i]);
            }
        }
        free(histogram);
    }
    add_stats(NULL, 0, NULL, 0, c);
}
コード例 #5
0
ファイル: items.c プロジェクト: FangJianHust/memcached-1.4.15
/* 形成了一个完成的 item 后, 就要把它放入两个数据结构中, 一是 memcached 的哈希表,
memcached 运行过程中只有一个哈希表, 二是 item 所在的 slabclass 的 LRU 队列. */
int do_item_link(item *it, const uint32_t hv) {
    MEMCACHED_ITEM_LINK(ITEM_key(it), it->nkey, it->nbytes);
    assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
    mutex_lock(&cache_lock);
    it->it_flags |= ITEM_LINKED;
    it->time = current_time;

    STATS_LOCK();
    stats.curr_bytes += ITEM_ntotal(it);
    stats.curr_items += 1;
    stats.total_items += 1;
    STATS_UNLOCK();

    /* Allocate a new CAS ID on link. */
    ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);

    /* 把 item 放入哈希表 */
    assoc_insert(it, hv);
    /* 把 item 放入 LRU 队列*/
    item_link_q(it);

    refcount_incr(&it->refcount);
    mutex_unlock(&cache_lock);

    return 1;
}
コード例 #6
0
ファイル: items.c プロジェクト: MediaMath/moxi
int do_item_link(item *it) {
    MEMCACHED_ITEM_LINK(ITEM_key(it), it->nkey, it->nbytes);
    assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
    assert(it->nbytes < (1024 * 1024));  /* 1MB max size */
    it->it_flags |= ITEM_LINKED;
    it->time = current_time;
    assoc_insert(it);

#ifdef MOXI_ITEM_MALLOC
    it->refcount++;
#endif

    STATS_LOCK();
    stats.curr_bytes += ITEM_ntotal(it);
    stats.curr_items += 1;
    stats.total_items += 1;
    STATS_UNLOCK();

    /* Allocate a new CAS ID on link. */
    ITEM_set_cas(it, (settings.use_cas) ? get_cas_id() : 0);

    item_link_q(it);

    return 1;
}
コード例 #7
0
ファイル: items.c プロジェクト: jacklicn/memcached
/* I think there's no way for this to be accurate without using the CAS value.
 * Since items getting their time value bumped will pass this validation.
 */
void item_stats_sizes_remove(item *it) {
    if (stats_sizes_hist == NULL || stats_sizes_cas_min > ITEM_get_cas(it))
        return;
    int ntotal = ITEM_ntotal(it);
    int bucket = ntotal / 32;
    if ((ntotal % 32) != 0) bucket++;
    if (bucket < stats_sizes_buckets) stats_sizes_hist[bucket]--;
}
コード例 #8
0
ファイル: items.c プロジェクト: jacques/memcached
void item_unlink(item *it) {
    if (it->it_flags & ITEM_LINKED) {
        it->it_flags &= ~ITEM_LINKED;
        stats.curr_bytes -= ITEM_ntotal(it);
        stats.curr_items -= 1;
        assoc_delete(ITEM_key(it));
        item_unlink_q(it);
    }
    if (it->refcount == 0) item_free(it);
}
コード例 #9
0
ファイル: items.c プロジェクト: zunc/zeroserv
void do_item_unlink(item *it) {
    //    MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nbytes);
    if ((it->it_flags & ITEM_LINKED) != 0) {
        it->it_flags &= ~ITEM_LINKED;
        stats.curr_bytes -= ITEM_ntotal(it);
        stats.curr_items--;
        assoc_delete(ITEM_key(it), it->nkey);
        item_unlink_q(it);
        if (it->refcount == 0) item_free(it);
    }
}
コード例 #10
0
ファイル: items.c プロジェクト: Abioy/kitsune-memcached
void do_item_unlink(item *it) {
    if ((it->it_flags & ITEM_LINKED) != 0) {
        it->it_flags &= ~ITEM_LINKED;
        STATS_LOCK();
        stats.curr_bytes -= ITEM_ntotal(it);
        stats.curr_items -= 1;
        STATS_UNLOCK();
        assoc_delete(ITEM_key(it), it->nkey);
        item_unlink_q(it);
        if (it->refcount == 0) item_free(it);
    }
}
コード例 #11
0
ファイル: items.c プロジェクト: jacques/memcached
void item_free(item *it) {
    unsigned int ntotal = ITEM_ntotal(it);
    assert((it->it_flags & ITEM_LINKED) == 0);
    assert(it != heads[it->slabs_clsid]);
    assert(it != tails[it->slabs_clsid]);
    assert(it->refcount == 0);

    /* so slab size changer can tell later if item is already free or not */
    it->slabs_clsid = 0;
    it->it_flags |= ITEM_SLABBED;
    slabs_free(it, ntotal);
}
コード例 #12
0
ファイル: items.c プロジェクト: hyper/memcached
/*@null@*/
char *do_item_stats_sizes(uint32_t (*add_stats)(char *buf,
                          const char *key, const uint16_t klen, const char *val,
                          const uint32_t vlen, void *cookie), void *c, int *bytes) {

    const int num_buckets = 32768;   /* max 1MB object, divided into 32 bytes size buckets */
    unsigned int *histogram = (unsigned int *)malloc((size_t)num_buckets * sizeof(int));
    char *buf = (char *)malloc(2 * 1024 * 1024); /* 2MB max response size */
    char *ptr = buf;
    uint32_t nbytes, linelen = 0;
    int i;

    if (histogram == 0 || buf == 0) {
        if (histogram) free(histogram);
        if (buf) free(buf);
        *bytes = -1;
        return NULL;
    }

    /* build the histogram */
    memset(histogram, 0, (size_t)num_buckets * sizeof(int));
    for (i = 0; i < LARGEST_ID; i++) {
        item *iter = heads[i];
        while (iter) {
            int ntotal = ITEM_ntotal(iter);
            int bucket = ntotal / 32;
            if ((ntotal % 32) != 0) bucket++;
            if (bucket < num_buckets) histogram[bucket]++;
            iter = iter->next;
        }
    }

    /* write the buffer */
    *bytes = 0;
    char key[128];
    char val[128];

    for (i = 0; i < num_buckets; i++) {
        if (histogram[i] != 0) {
            sprintf(key, "%d", i * 32);
            sprintf(val, "%u", histogram[i]);
            nbytes = add_stats(ptr, key, strlen(key), val, strlen(val), c);
            linelen += nbytes;
            ptr += nbytes;
        }
    }

    nbytes = add_stats(ptr, NULL, 0, NULL, 0, c);
    *bytes = linelen + nbytes;

    free(histogram);
    return buf;
}
コード例 #13
0
ファイル: items.c プロジェクト: ryuxin/memcached
/* FIXME: Is it necessary to keep this copy/pasted code? */
void do_item_unlink_nolock(item *it, const uint32_t hv) {
    MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
    if ((it->it_flags & ITEM_LINKED) != 0) {
        it->it_flags &= ~ITEM_LINKED;
        STATS_LOCK();
        stats.curr_bytes -= ITEM_ntotal(it);
        stats.curr_items -= 1;
        STATS_UNLOCK();
        assoc_delete(ITEM_key(it), it->nkey, hv);
        item_unlink_q(it);
        do_item_remove(it);
    }
}
コード例 #14
0
ファイル: items.c プロジェクト: slawomir-pryczek/memcached
/* slawek */
void do_item_unlink_nolock_nostat(item *it, const uint32_t hv, uint64_t *items_removed, uint64_t *bytes_removed) {
    MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
    if ((it->it_flags & ITEM_LINKED) != 0) {
        it->it_flags &= ~ITEM_LINKED;
        
        *items_removed += 1;
        *bytes_removed += ITEM_ntotal(it);
       
        assoc_delete(ITEM_key(it), it->nkey, hv);
        item_unlink_q(it);
        do_item_remove(it);
    }
}
コード例 #15
0
ファイル: items.c プロジェクト: jacklicn/memcached
void item_free(item *it) {
    size_t ntotal = ITEM_ntotal(it);
    unsigned int clsid;
    assert((it->it_flags & ITEM_LINKED) == 0);
    assert(it != heads[it->slabs_clsid]);
    assert(it != tails[it->slabs_clsid]);
    assert(it->refcount == 0);

    /* so slab size changer can tell later if item is already free or not */
    clsid = ITEM_clsid(it);
    DEBUG_REFCNT(it, 'F');
    slabs_free(it, ntotal, clsid);
}
コード例 #16
0
ファイル: items.c プロジェクト: FangJianHust/memcached-1.4.15
//释放item
void item_free(item *it) {
    size_t ntotal = ITEM_ntotal(it);//获得item的大小
    unsigned int clsid;
    assert((it->it_flags & ITEM_LINKED) == 0);//判断item的状态是否正确
    assert(it != heads[it->slabs_clsid]);//item不能为LRU的头指针
    assert(it != tails[it->slabs_clsid]);//item不能为LRU的尾指针
    assert(it->refcount == 0);//释放时,需保证引用次数为0

    /* so slab size changer can tell later if item is already free or not */
    clsid = it->slabs_clsid;
    it->slabs_clsid = 0;//断开slabclass的链接
    DEBUG_REFCNT(it, 'F');
    slabs_free(it, ntotal, clsid);//slabclass结构执行释放
}
コード例 #17
0
ファイル: items.c プロジェクト: skypacer210/Ex
/* FIXME: Is it necessary to keep this copy/pasted code? */
void do_item_unlink_nolock(item *it, const uint32_t hv) {
		syslog(LOG_INFO, "[%s:%s:%d]", __FILE__, __func__, __LINE__);
    MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
    if ((it->it_flags & ITEM_LINKED) != 0) {
        it->it_flags &= ~ITEM_LINKED;
        STATS_LOCK();
        stats.curr_bytes -= ITEM_ntotal(it);
        stats.curr_items -= 1;
        STATS_UNLOCK();
        assoc_delete(ITEM_key(it), it->nkey, hv);
        item_unlink_q(it);
        do_item_remove(it);
    }
}
コード例 #18
0
ファイル: items.c プロジェクト: zunc/zeroserv
void item_free(item *it) {
    size_t ntotal = ITEM_ntotal(it);
    unsigned int clsid;
    assert((it->it_flags & ITEM_LINKED) == 0);
    assert(it->refcount == 0);

    /* so slab size changer can tell later if item is already free or not */
    clsid = it->slabs_clsid;
    it->slabs_clsid = 0;
    //    it->it_flags |= ITEM_SLABBED;
    DEBUG_REFCNT(it, 'F');

    //[TODO] allocate management
    zfree(it);
}
コード例 #19
0
ファイル: items.c プロジェクト: jacques/memcached
int item_link(item *it) {
    assert((it->it_flags & (ITEM_LINKED|ITEM_SLABBED)) == 0);
    assert(it->nbytes < 1048576);
    it->it_flags |= ITEM_LINKED;
    it->time = current_time;
    assoc_insert(ITEM_key(it), it);

    stats.curr_bytes += ITEM_ntotal(it);
    stats.curr_items += 1;
    stats.total_items += 1;

    item_link_q(it);

    return 1;
}
コード例 #20
0
ファイル: items.c プロジェクト: mohyt/memcached
static void item_free(struct default_engine *engine, hash_item *it) {
    size_t ntotal = ITEM_ntotal(engine, it);
    unsigned int clsid;
    assert((it->iflag & ITEM_LINKED) == 0);
    assert(it != engine->items.heads[it->slabs_clsid]);
    assert(it != engine->items.tails[it->slabs_clsid]);
    assert(it->refcount == 0);

    /* so slab size changer can tell later if item is already free or not */
    clsid = it->slabs_clsid;
    it->slabs_clsid = 0;
    it->iflag |= ITEM_SLABBED;
    DEBUG_REFCNT(it, 'F');
    slabs_free(engine, it, ntotal, clsid);
}
コード例 #21
0
ファイル: items.c プロジェクト: skypacer210/Ex
//释放item
void item_free(item *it) {
		syslog(LOG_INFO, "[%s:%s:%d]", __FILE__, __func__, __LINE__);
    size_t ntotal = ITEM_ntotal(it);
    unsigned int clsid;
    assert((it->it_flags & ITEM_LINKED) == 0);//没有在hash表和LUR链中
    assert(it != heads[it->slabs_clsid]);
    assert(it != tails[it->slabs_clsid]);
    assert(it->refcount == 0);

    /* so slab size changer can tell later if item is already free or not */
    clsid = it->slabs_clsid;
    it->slabs_clsid = 0;
    DEBUG_REFCNT(it, 'F');
    slabs_free(it, ntotal, clsid);
}
コード例 #22
0
ファイル: bdb.c プロジェクト: lazywalker/memcacheq
/* 0 for Success
   -1 for SERVER_ERROR
*/
int bdb_set(char *key, item *it){
    pthread_rwlock_rdlock(&qlist_ht_lock);
    queue_t *q = (queue_t *)hashtable_search(qlist_htp, (void *)key);
    DB_TXN *txnp = NULL;
    int ret;

    if (NULL == q) {
        pthread_rwlock_unlock(&qlist_ht_lock);
        ret = bdb_create_queue(key);
        if (0 != ret){
            return -1;
        }
        /* search again */
        pthread_rwlock_rdlock(&qlist_ht_lock);
        q = (queue_t *)hashtable_search(qlist_htp, (void *)key);        
    }
    
    if (NULL != q) {
        db_recno_t recno;
        DBT dbkey, dbdata;    
        BDB_CLEANUP_DBT();
        dbkey.data = &recno;
        dbkey.ulen = sizeof(recno);
        dbkey.flags = DB_DBT_USERMEM;
        dbdata.data = it;
        dbdata.size = ITEM_ntotal(it);
        ret = envp->txn_begin(envp, NULL, &txnp, 0);
        CHECK_DB_RET(ret);
        ret = q->dbp->put(q->dbp, txnp, &dbkey, &dbdata, DB_APPEND);
        CHECK_DB_RET(ret);
        ret = txnp->commit(txnp, 0);
        CHECK_DB_RET(ret);
        pthread_mutex_lock(&(q->lock));
        (q->set_hits)++;
        pthread_mutex_unlock(&(q->lock));
    }
    pthread_rwlock_unlock(&qlist_ht_lock);    
    return 0;
dberr:
    if (txnp != NULL){
        txnp->abort(txnp);
    }
    if (settings.verbose > 1) {
        fprintf(stderr, "bdb_set: %s\n", db_strerror(ret));
    }
    pthread_rwlock_unlock(&qlist_ht_lock);
    return -1;
}
コード例 #23
0
ファイル: items.c プロジェクト: skypacer210/Ex
void do_item_unlink(item *it, const uint32_t hv) {
		syslog(LOG_INFO, "[%s:%s:%d]", __FILE__, __func__, __LINE__);
    MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
    mutex_lock(&cache_lock);
    if ((it->it_flags & ITEM_LINKED) != 0) {
        it->it_flags &= ~ITEM_LINKED;//设置为非linked
        STATS_LOCK();
        stats.curr_bytes -= ITEM_ntotal(it);
        stats.curr_items -= 1;
        STATS_UNLOCK();
        assoc_delete(ITEM_key(it), it->nkey, hv); //从hash表中删除
        item_unlink_q(it); //从LRU链中删除
        do_item_remove(it);
    }
    mutex_unlock(&cache_lock);
}
コード例 #24
0
ファイル: items.c プロジェクト: FangJianHust/memcached-1.4.15
//将item从hashtable和LRU链中移除,而且还释放掉 item 所占的内存 (其实只是把 item 放到空闲链表中),是do_item_link的逆操作
void do_item_unlink(item *it, const uint32_t hv)
{
    MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
    mutex_lock(&cache_lock);//执行同步
    if ((it->it_flags & ITEM_LINKED) != 0) {//判断状态值,保证item还在LRU队列中
        it->it_flags &= ~ITEM_LINKED;//修改状态值
        STATS_LOCK();//更新统计信息
        stats.curr_bytes -= ITEM_ntotal(it);
        stats.curr_items -= 1;
        STATS_UNLOCK();
        assoc_delete(ITEM_key(it), it->nkey, hv);//从Hash表中删除
        item_unlink_q(it);//将item从slabclass对应的LRU队列摘除
        do_item_remove(it);//释放 item 所占的内存
    }
    mutex_unlock(&cache_lock);
}
コード例 #25
0
ファイル: items.c プロジェクト: MediaMath/moxi
void do_item_unlink(item *it) {
    MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
    if ((it->it_flags & ITEM_LINKED) != 0) {
        it->it_flags &= ~ITEM_LINKED;
        STATS_LOCK();
        stats.curr_bytes -= ITEM_ntotal(it);
        stats.curr_items -= 1;
        STATS_UNLOCK();
        assoc_delete(ITEM_key(it), it->nkey);
        item_unlink_q(it);

#ifndef MOXI_ITEM_MALLOC
        if (it->refcount == 0) item_free(it);
#endif
    }
}
コード例 #26
0
ファイル: items.c プロジェクト: mohyt/memcached
void do_item_unlink(struct default_engine *engine, hash_item *it) {
    MEMCACHED_ITEM_UNLINK(item_get_key(it), it->nkey, it->nbytes);
    if ((it->iflag & ITEM_LINKED) != 0) {
        it->iflag &= ~ITEM_LINKED;
        pthread_mutex_lock(&engine->stats.lock);
        engine->stats.curr_bytes -= ITEM_ntotal(engine, it);
        engine->stats.curr_items -= 1;
        pthread_mutex_unlock(&engine->stats.lock);
        assoc_delete(engine, engine->server.core->hash(item_get_key(it),
                                                            it->nkey, 0),
                     item_get_key(it), it->nkey);
        item_unlink_q(engine, it);
        if (it->refcount == 0) {
            item_free(engine, it);
        }
    }
}
コード例 #27
0
ファイル: items.c プロジェクト: jacklicn/memcached
static void do_item_link_q(item *it) { /* item is the new head */
    item **head, **tail;
    assert((it->it_flags & ITEM_SLABBED) == 0);

    head = &heads[it->slabs_clsid];
    tail = &tails[it->slabs_clsid];
    assert(it != *head);
    assert((*head && *tail) || (*head == 0 && *tail == 0));
    it->prev = 0;
    it->next = *head;
    if (it->next) it->next->prev = it;
    *head = it;
    if (*tail == 0) *tail = it;
    sizes[it->slabs_clsid]++;
    sizes_bytes[it->slabs_clsid] += ITEM_ntotal(it);
    return;
}
コード例 #28
0
//从哈希表和LRU中删除
void do_item_unlink(item *it, const uint32_t hv) {
    MEMCACHED_ITEM_UNLINK(ITEM_key(it), it->nkey, it->nbytes);
    mutex_lock(&cache_lock);
    if ((it->it_flags & ITEM_LINKED) != 0) {
        it->it_flags &= ~ITEM_LINKED;
        STATS_LOCK();
        stats.curr_bytes -= ITEM_ntotal(it);
        stats.curr_items -= 1;
        STATS_UNLOCK();
        //从哈希表中删除
        assoc_delete(ITEM_key(it), it->nkey, hv);
        //从链表中删除
        item_unlink_q(it);
        //向slab归还这个item
        do_item_remove(it);
    }
    mutex_unlock(&cache_lock);
}
コード例 #29
0
ファイル: item.c プロジェクト: cyberrbob/memcachedb
/* 0 for Success
   -1 for SERVER_ERROR
*/
int item_put(char *key, size_t nkey, item *it){
    int ret;
    DBT dbkey, dbdata;

    BDB_CLEANUP_DBT();
    dbkey.data = key;
    dbkey.size = nkey;
    dbdata.data = it;
    dbdata.size = ITEM_ntotal(it);
    ret = dbp->put(dbp, NULL, &dbkey, &dbdata, 0);
    if (ret == 0) {
        return 0;
    } else {
        if (settings.verbose > 1) {
            fprintf(stderr, "dbp->put: %s\n", db_strerror(ret));
        }
        return -1;
    }
}
コード例 #30
0
ファイル: items.c プロジェクト: Chippiewill/memcached
/*@null@*/
static void do_item_stats_sizes(struct default_engine *engine,
                                ADD_STAT add_stats, const void *c) {

    /* max 1MB object, divided into 32 bytes size buckets */
    const int num_buckets = 32768;
    unsigned int *histogram = calloc(num_buckets, sizeof(unsigned int));

    if (histogram != NULL) {
        int i;

        /* build the histogram */
        for (i = 0; i < POWER_LARGEST; i++) {
            hash_item *iter = engine->items.heads[i];
            while (iter) {
                size_t ntotal = ITEM_ntotal(engine, iter);
                size_t bucket = ntotal / 32;
                if ((ntotal % 32) != 0) {
                    bucket++;
                }
                if (bucket < num_buckets) {
                    histogram[bucket]++;
                }
                iter = iter->next;
            }
        }

        /* write the buffer */
        for (i = 0; i < num_buckets; i++) {
            if (histogram[i] != 0) {
                char key[8], val[32];
                int klen, vlen;
                klen = snprintf(key, sizeof(key), "%d", i * 32);
                vlen = snprintf(val, sizeof(val), "%u", histogram[i]);
                if (klen > 0 && klen < sizeof(key) && vlen > 0 &&
                    vlen < sizeof(val)) {
                    add_stats(key, klen, val, vlen, c);
                }
            }
        }
        free(histogram);
    }
}