当前位置: 首页 > 工具软件 > BeansDB > 使用案例 >

BeansDB源码剖析——htree.c

郑锋
2023-12-01
typedef struct t_item Item;
struct t_item {
	//int bucket = item->pos & 0xff; //表示是第几个文件
	//uint32_t pos = item->pos & 0xffffff00; //表示在文件中的位置
	uint32_t pos; 

	//大于0该数据有效,小于0表明无效。
	//ver不会等于0,因此如果set的参数为0时,表示是更新
	//ver不会等于-1,因此set的参数为-1时,表示是删除。
	//ver的更新方法见bitcast.c中的bc_set函数
	//
	int32_t  ver; 

	uint16_t hash; //在bitcask.c的bc_set函数中被赋值
	uint8_t  length; //这个item的长度。通过这个长度找到下一个item
	char     name[1]; 
};


 

/*
*  Beansdb - A high available distributed key-value storage system:
*
*      http://beansdb.googlecode.com
*
*  Copyright 2009 Douban Inc.  All rights reserved.
*
*  Use and distribution licensed under the BSD license.  See
*  the LICENSE file for full text.
*
*  Authors:
*      Davies Liu <davies.liu@gmail.com>
*/

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <pthread.h>

#include "fnv1a.h"
#include "htree.h"
#include "codec.h"
#include "quicklz.h"

//key的最大长度
const int MAX_KEY_LENGTH = 200;
//Bucket里存放节点。
//一个非数据节点分成16个bucket(就是子树),每个bucket是另一个节点
//这个跟bitcask的bucket是不同的
const int BUCKET_SIZE = 16;
//非数据节点中的count超过此限制要分裂
const int SPLIT_LIMIT = 32; 
//树的最大深度
const int MAX_DEPTH = 8; 
//g_index[i] = (g_index[i-1] << 4) + g_index[i-1];
//g_index[i]表示的是第i层前共有多少个节点
//HTree最多有410338673个节点
static const int g_index[] = {0, 1, 17, 289, 4913, 83521, 1419857, 24137569, 410338673};

#define max(a,b) ((a)>(b)?(a):(b))
//it是第几个孩子节点,0x0f说明最多有16个孩子节点
#define INDEX(it) (0x0f & (keyhash >> ((7 - node->depth - tree->depth) * 4)))
//length包含了item的size,由于结构的最后一个是变长数组,所以多减了一个字符,需要加上。
#define KEYLENGTH(it) ((it)->length-sizeof(Item)+ITEM_PADDING)
//如果it不是一个有效的节点,该宏返回0
#define HASH(it) ((it)->hash * ((it)->ver>0))


//每个节点有16个孩子,可以通过INDEX宏得到该孩子的位置
//如果这个node里并没有数据,那么这个node只占64比特,参见clear函数
//否则,这个node存储数据Data,Data里有多个item,通过item的长度找到下一个item的地址
//树的数据节点不一定是在同一层的
typedef struct t_data Data;
struct t_data {
	int size; //总大小
	int used; //已用大小
	int count;//item的个数
	Item head[0];
};

typedef struct t_node Node;
struct t_node {
	uint16_t is_node:1; //=1,说明这个node里没有存放数据,否则就是存放了数据的
	uint16_t valid:1; //说明这个节点是有效的,即它与它的所有子节点都没有被改动过
	uint16_t depth:4; //该节点的深度
	uint16_t compressed:1; //是否被压缩了
	uint16_t flag:9; 
	uint16_t hash; //哈希值,如果这个节点是非数据节点,这个值是所有子节点的哈希值之和
	//如果是数据节点,这个值是所有ver大于0的item的哈希值的和
	uint32_t count; //所有子节点里ver>0的item的个数
	Data *data;
};

//node的count和data的count是不一样的。
//data的count表示有多少个item,node的count表示有多少个item的ver是>0的。
//htree是一块连续的内存,相当于使用数组存放一个二叉树。
struct t_hash_tree {
	int depth; //
	int height; //depth = hight-1
	Node *root;
	int pool_size; //节点的数目
	pthread_mutex_t lock;
	char buf[512];

	bool compress; //是否压缩
	char wbuf[QLZ_SCRATCH_COMPRESS];
	char cbuf[1024 * 10];
};


// forward dec
static void add_item(HTree *tree, Node *node, Item *it, uint32_t keyhash, bool enlarge);
static void remove_item(HTree *tree, Node *node, Item *it, uint32_t keyhash);
static void split_node(HTree *tree, Node *node);
static void merge_node(HTree *tree, Node *node);
static void update_node(HTree *tree, Node *node);

//node在这一层的位置
inline uint32_t get_pos(HTree *tree, Node *node)
{
	return (node - tree->root) //node在整个pool中的位置
		- g_index[(int)node->depth]; //node的上一层总共有多少个节点
}

//找到node的第b个孩子节点,b从0开始
//一个节点最多有BUCKET_SIXZE=16(1<<4)个孩子节点
inline Node *get_child(HTree *tree, Node *node, int b)
{
	int i = g_index[node->depth + 1] //node的孩子节点这一层之前总共有多少个节点
		+ (get_pos(tree, node) << 4) //node的第1个孩子节点的位置 
		+ b;
	return tree->root + i;
}

//有一个get_data,就要有一个free_data或者set_data与之对应
inline Data* get_data(Node *node)
{
	if (node->compressed) {
		char wbuf[QLZ_SCRATCH_DECOMPRESS];
		Data *d = malloc(qlz_size_decompressed((char*)node->data));
		qlz_decompress((char*)node->data, (char*)d, wbuf);
		return d;
	} else {
		return node->data;
	}
}

//saved,节约的意思,通过压缩节省了多少空间
static int saved = 0;

//设置node的data,其中包括了压缩
inline void set_data(HTree *tree, Node *node, Data *data)
{
	if (data != node->data) {
		if (node->data) free(node->data);
		if (tree->compress) {
			int need = data->used + 400;
			char *cbuf = need <= sizeof(tree->cbuf) ? tree->cbuf : malloc(need);
			size_t size = qlz_compress((char*)data, cbuf, data->used, tree->wbuf);
			saved += data->size - size;
			free(data);
			data = malloc(size);
			memcpy(data, cbuf, size);
			node->compressed = 1;
			if (cbuf != tree->cbuf) free(cbuf);
		}
		node->data = data;
	}
}

//这是与get_data相对应的,由于get_data时可能会有压缩,所以get_data并不一定是返回的原data
//可能是原data的一个copy,这样通过判断data != node->data,来删除这个copy
inline void free_data(Node *node, Data *data)
{
	if (data != node->data) {
		free(data);
	}
}

//注意key_hash产生的hash跟Item中的hash是不一样的
//这里的hash是为了便于在htree中查找。
inline uint32_t key_hash(Item* it)
{
	char buf[255];
	//由于有对key的编码,所以要先解码,才能取哈希值
	int n = dc_decode(buf, it->name, KEYLENGTH(it));
	//哈希函数
	return fnv1a(buf, n);
}

static Item* create_item(HTree *tree, const char* name, uint32_t pos, uint16_t hash, int32_t ver)
{
	Item *it = (Item*)tree->buf;
	it->pos = pos;
	it->ver = ver;
	it->hash = hash;
	//对key进行编码
	int n = dc_encode(it->name, name, strlen(name));
	it->length = sizeof(Item) + n - ITEM_PADDING;
	return it;
}

//树再高一层,树是一段连续的空间。
static void enlarge_pool(HTree *tree)
{
	int i;
	int old_size = tree->pool_size;
	int new_size = g_index[tree->height + 1];

	tree->root = (Node*)realloc(tree->root, sizeof(Node) * new_size);
	memset(tree->root + old_size, 0, sizeof(Node) * (new_size - old_size));
	//更新所有新增的树节点的高度
	for (i=old_size; i<new_size; i++){
		tree->root[i].depth = tree->height;
	}

	tree->height ++;
	tree->pool_size = new_size;
}

//将node置为初始状态
static void clear(HTree *tree, Node *node)
{
	if (node->data) free(node->data);
	//这个node占64位
	node->data = (Data*) malloc(64);
	node->data->size = 64;
	node->data->used = sizeof(Data);
	node->data->count = 0;

	node->is_node = 0;
	node->valid = 1;
	node->compressed = 0;
	node->count = 0;
	node->hash = 0;
}

//将it插入到树中node节点开始的位置
//1.找到这个node下面的数据节点
//2.数据节点中存放的是Item组成的数组,根据Item的length域遍历这个数据节点的信息
//3.接下来就相当于数组的插入了,更新数据节点的count域和hash域
//	3.1.如果找到了相同的key,那么更新这个Item
//	3.2.否则将it放入到数组的末尾,更新Data的used域
//4.如果是插入,则有可能造成count的扩大,需要对数据节点进行分裂。
//	4.1.数据节点在树的最底层,那么允许一个数据节点存储的Item的个数为LIMIT*4,
//			这是为了防止enlarge_pool造成过多内存的使用
//	4.2.数据节点在树的中间部分,也就是说数据节点下面还有节点
//			那么为了使查找更有效率,需要尽量减少数据节点中Item的个数,超过LIMIT就要分裂
static void add_item(HTree *tree, Node *node, Item *it, uint32_t keyhash, bool enlarge)
{
	//1
	//由于对数据节点进行了变更,所以要把所走过的路径中的所有节点的valid设为0
	//这样update_node时就可以根据valid值决定是否要更新此节点及它的子节点
	while (node->is_node) {
		node->valid = 0;
		node = get_child(tree, node, INDEX(it));
	}

	//2
	//得到这个数据节点的数据
	Data *data = get_data(node);
	//第一个item
	Item *p = data->head;
	int i;
	//node的count和data的count是不一样的。
	for (i=0; i<data->count; i++){
		//3.1
		if (it->length == p->length && 
			memcmp(it->name, p->name, KEYLENGTH(it)) == 0)
		{//key相同
				//减去原来item的哈希值,加上新的item的哈希值
				node->hash += (HASH(it) - HASH(p)) * keyhash;
				//更新该节点的count
				node->count += it->ver > 0;
				node->count -= p->ver > 0;
				//只要name的长度一样,两个Item的length就是一样的
				//不会对旁边Item的内容产生影响,直接cpy
				memcpy(p, it, sizeof(Item));
				//data会在set_data中被free
				set_data(tree, node, data);
				return;
		}
		p = (Item*)((char*)p + p->length);
	}

	//3.2
	//如果data的size不够用了,就要malloc,新建一个data空间,至少要比64大
	if (data->size < data->used + it->length){
		int size = max(data->used + it->length, data->size + 64);
		//item的位置
		int pos = (char*)p-(char*)data;
		Data *new_data = (Data*) malloc(size);
		memcpy(new_data, data, data->used);
		//与get_data对应
		free_data(node, data);
		data = new_data;
		data->size = size;
		//保存item要插入的位置
		p = (Item *)((char*)data + pos);
	}

	//p所指就是it要插入的地方
	memcpy(p, it, it->length);
	//data的item的数量加1
	data->count ++;
	data->used += it->length;
	//如果item的ver大于0,node的count加1
	node->count += it->ver > 0;
	//node的哈希值是所有数据子节点的哈希值之和,更新
	node->hash += keyhash * HASH(it);
	set_data(tree, node, data);

	//4
	//node是数据节点
	if (node->count > SPLIT_LIMIT){
		//这个node是树的最底层
		if (node->depth == tree->height - 1){//4.1
			//如果这个数的在树的最底层,就要*4,防止频繁地enlarge造成空间太大
			if (enlarge && node->count > SPLIT_LIMIT * 4){
				int pos = node - tree->root;
				//树的高度加深
				enlarge_pool(tree);
				node = tree->root + pos; // reload
				//分裂节点
				split_node(tree, node);
			}
			
		}else{//4.2
			//分裂节点
			split_node(tree, node);
		}
	}
}

//将node中的数据分发到它的下一层孩子节点中
//完成后,这个节点就变成了一个普通的节点,里面没有数据;它的16个孩子成为新的数据节点
//1.得到node的孩子节点,并reset
//2.根据哈希值将数据放入对应的孩子节点
//3.更新node对应的域
static void split_node(HTree *tree, Node *node)
{
	//1
	//得到这个节点的第一个孩子
	Node *child = get_child(tree, node, 0);
	int i;
	//把所有的孩子节点都清空
	for (i=0; i<BUCKET_SIZE; i++){
		clear(tree, child+i);
	}

	//2
	Data *data = get_data(node);
	Item *it = data->head;
	//把这个数据节点的所有item放入它的孩子节点中
	for (i=0; i<data->count; i++) {
		int32_t keyhash = key_hash(it);
		//添加item
		add_item(tree, child + INDEX(it), it, keyhash, false);
		it = (Item*)((char*)it + it->length);
	}

	//3
	free_data(node, data);
	free(node->data);
	node->data = NULL;

	//这个节点变为普通节点
	node->is_node = 1;
	//这个节点更改了,update_node的时候就要更新这个节点的哈希值
	node->valid = 0;
}

//移除一个Item
//1.找到数据节点
//2.在Data中查找对应的Item
//3.删除之,并更新数据节点对应的域
static void remove_item(HTree *tree, Node *node, Item *it, uint32_t keyhash)
{
	//1
	//由于对数据节点进行了变更,所以要把所走过的路径中的所有节点的valid设为0
	//这样update_node时就可以根据valid值决定是否要更新此节点及它的子节点
	while (node->is_node) {
		node->valid = 0;
		node = get_child(tree, node, INDEX(it));
	}

	//2
	Data *data = get_data(node);
	if (data->count == 0) return ;
	Item *p = data->head;
	int i;
	for (i=0; i<data->count; i++){
		if (it->length == p->length && 
			memcmp(it->name, p->name, KEYLENGTH(it)) == 0){
			//3
			data->count --;
			data->used -= p->length;
			node->count -= p->ver > 0;
			node->hash -= keyhash * HASH(p);
			//将it删除,后面的移动过来                               
			memcpy(p, (char*)p + p->length, 
				data->size - ((char*)p - (char*)data) - p->length);
			set_data(tree, node, data);
			return;
		}
		//否则检查下一个item
		p = (Item*)((char*)p + p->length);
	}
	//与get_data对应
	free_data(node, data);
}

//将数据节点node的孩子节点中的数据放入到node中,ver<0的节点则被抛弃
//这样node成为数据节点,它的孩子节点成为普通节点,减少数据的分散性
//1.reset node节点
//2.遍历每个孩子节点的Data,将其中ver>0的Item放入到node中
//3.reset 这个孩子节点
static void merge_node(HTree *tree, Node *node)
{
	//1
	clear(tree, node);

	//2
	Node* child = get_child(tree, node, 0);
	int i, j;
	//将node所有孩子节点的item都集中到自己身上
	//同时删除了ver小于0的item
	for (i=0; i<BUCKET_SIZE; i++){
		Data *data = get_data(child+i); 
		Item *it = data->head;
		//count计算不对,(child+i)->count是有效Item的大小
		//这里应该是int count = data->count因为要遍历的是所有的item
		int count = (child+i)->count;
		for (j=0; j < count; j++){
			if (it->ver > 0) {//只merge那些ver>0的
				add_item(tree, node, it, key_hash(it), false);
			} // drop deleted items, ver < 0
			it = (Item*)((char*)it + it->length);
		}
		free_data(child+i, data);
		//3
		clear(tree, child + i);
	}
}

//递归更新HTree中每个Node的hash和count域,将更新完成的Node的valid域设置为1
static void update_node(HTree *tree, Node *node)
{
	//这个节点及它的所有子节点都没有被改动过
	//就没有必要更新这个节点的哈希值
	if (node->valid) return ;

	int i;
	node->hash = 0;
	//只更新普通节点的哈希,数据节点的哈希在add_item的时候已经计算过了,它永远是valid的
	if (node->is_node){
		Node *child = get_child(tree, node, 0);
		node->count = 0;
		//递归遍历所有的子节点,得到它们的有效item的数目
		for (i=0; i<BUCKET_SIZE; i++){
			update_node(tree, child+i);
			node->count += child[i].count;
		}
		//遍历孩子节点,更新node的哈希值
		for (i=0; i<BUCKET_SIZE; i++){
			if (node->count > SPLIT_LIMIT * 4){ 
				node->hash *= 97;               
			}
			node->hash += child[i].hash;
		}
	}
	node->valid = 1;

	// merge nodes
	//如果有必要,merge
	if (node->count <= SPLIT_LIMIT) {
		merge_node(tree, node);
	}
}

//通过哈希得到item
static Item* get_item_hash(HTree* tree, Node* node, Item* it, uint32_t keyhash)
{
	//找到存储该item的实际节点
	while (node->is_node) {
		node = get_child(tree, node, INDEX(it));
	}

	Data *data = get_data(node);
	Item *p = data->head, *r = NULL;
	int i;
	//在数据节点中找到该item
	for (i=0; i<data->count; i++){
		if (it->length == p->length && 
			memcmp(it->name, p->name, KEYLENGTH(it)) == 0){
				r = p;
				break;
		}
		p = (Item*)((char*)p + p->length);
	}
	free_data(node, data);
	return r;
}

//十六进制到十进制的转换
inline int hex2int(char b)
{
	if (('0'<=b && b<='9') || ('a'<=b && b<='f')) {
		return (b>='a') ?  (b-'a'+10) : (b-'0');
	}else{
		return -1;
	}
}

//dir所表示的节点的哈希值之和
//dir[i]表示的是从node开始的第i层的节点的子节点的位置
//dir的len最多只有tree->hight
//将node的ver>0的item个数存储在count中
static uint16_t get_node_hash(HTree* tree, Node* node, const char* dir, 
															int *count)
{
	if (node->is_node && strlen(dir) > 0){
		char i = hex2int(dir[0]);
		if (i >= 0) {
			return get_node_hash(tree, get_child(tree, node, i), dir+1, count);
		}else{
			if(count) *count = 0;
			return 0;
		}
	}
	//更新dir对应的node的哈希,并返回
	update_node(tree, node);
	if (count) *count = node->count;
	return node->hash;
}

//找到前缀是prefix的item
static char* list_dir(HTree *tree, Node* node, const char* dir, const char* prefix)
{
	int dlen = strlen(dir); 
	//直到它的最后一个孩子节点(dlen == 0),或者这个孩子节点已经是叶子节点了(!is_node)
	while (node->is_node && dlen > 0){
		int b = hex2int(dir[0]);
		if (b >=0 && b < 16) {
			node = get_child(tree, node, b);
			dir ++;
			dlen --;
		}else{
			return NULL;
		}
	}

	int bsize = 4096;
	char *buf = (char*) malloc(bsize);
	memset(buf, 0, bsize);
	int n = 0, i, j;
	//如果dir的最后一个节点不是数据节点
	if (node->is_node) {
		//更新这个节点,得到它最新的count值和哈希值
		update_node(tree, node);

		//找到它的子节点
		Node *child = get_child(tree, node, 0);
		//if (node->count > 100000 || (prefix==NULL && node->count > 128)) {
		//&&的优先级高?
		if (node->count > 100000 || prefix==NULL && node->count > 128) {
			//把它的所有子节点都打印出来
			for (i=0; i<BUCKET_SIZE; i++) {
				Node *t = child + i;
				n += snprintf(buf + n, bsize - n, "%x/ %u %u\n", 
					i, t->hash, t->count);
			}
		}else{
			for (i=0; i<BUCKET_SIZE; i++) {
				//找到这个孩子节点的孩子节点.dir = ""说明直接找到它的孩子节点
				char *r = list_dir(tree, child + i, "", prefix);
				if (bsize - n < strlen(r)) {
					buf = (char*)realloc(buf, bsize * 2);
					bsize *= 2;
				}
				n += sprintf(buf + n, "%s", r);
				free(r);
			}
		}
	}else{//如果dir的最后一个节点是数据节点
		Data *data = get_data(node); 
		Item *it = data->head;
		char pbuf[20], name[255];
		int prefix_len = 0;
		if (prefix != NULL) prefix_len = strlen(prefix);
		for (i=0; i<data->count; i++, it = (Item*)((char*)it + it->length)){
			if (dlen > 0){
				sprintf(pbuf, "%08x", key_hash(it));
				if (memcmp(pbuf + tree->depth + node->depth, dir, dlen) != 0){
					continue;
				}
			}
			int l = dc_decode(name, it->name, KEYLENGTH(it));
			if (prefix == NULL || l >= prefix_len 
				&& strncmp(name, prefix, prefix_len) == 0) {
				n += snprintf(buf+n, bsize-n-1, "%s %u %d\n", name, it->hash, it->ver);
				if (bsize - n < 200) {
					buf = (char*)realloc(buf, bsize * 2);
					bsize *= 2;
				}
			}
		}
		free_data(node, data);
	}
	return buf;
}

//遍历HTree
static void visit_node(HTree *tree, Node* node, fun_visitor visitor, void* param)
{
	int i;
	//如果不是叶子节点,则向下寻找叶子节点
	if (node->is_node){
		Node *child = get_child(tree, node, 0);
		for (i=0; i<BUCKET_SIZE; i++){
			visit_node(tree, child+i, visitor, param);
		}
	}else{//是叶子节点,可以遍历里面的数据
		Data *data = get_data(node);
		Item *p = data->head;
		Item *it = (Item*)tree->buf;
		for (i=0; i<data->count; i++){
			memcpy(it, p, sizeof(Item));
			dc_decode(it->name, p->name, KEYLENGTH(p));
			it->length = sizeof(Item) + strlen(it->name) - ITEM_PADDING;
			//对it进行操作
			visitor(it, param);
			p = (Item*)((char*)p + p->length);
		}
		free_data(node, data);
	}    
}

static void optimize_node(HTree *tree, Node* node)
{
	int i;
	if (node->is_node){
		Node *child = get_child(tree, node, 0);
		for (i=0; i<BUCKET_SIZE; i++){
			optimize_node(tree, child+i);
		}
	}else{
		if (!node->compressed && node->data->size > 64) {
			Data *data = get_data(node);
			node->data = NULL;
			set_data(tree, node, data);
		}
	}    
}

/*
* API
*/

HTree* ht_new(int depth)
{
	HTree *tree = (HTree*)malloc(sizeof(HTree));
	if (!tree) return NULL;
	memset(tree, 0, sizeof(HTree));
	tree->depth = depth;   
	tree->height = 1;
	tree->compress = false;

	int pool_size = g_index[tree->height];
	Node *root = (Node*)malloc(sizeof(Node) * pool_size);
	if (!root){
		free(tree);
		return NULL;
	}
	memset(root, 0, sizeof(Node) * pool_size);

	// init depth
	int i,j;
	for (i=0; i<tree->height; i++){
		for (j=g_index[i]; j<g_index[i+1]; j++){
			root[j].depth = i;
		}
	}

	tree->root = root;
	tree->pool_size = pool_size;
	clear(tree, tree->root);

	pthread_mutex_init(&tree->lock, NULL);
	dc_init(NULL);

	return tree;
}

void ht_destroy(HTree *tree)
{
	if (!tree) return;

	pthread_mutex_lock(&tree->lock);

	int i;
	for(i=0; i<tree->pool_size; i++){
		if (tree->root[i].data) free(tree->root[i].data);
	}
	free(tree->root);
	free(tree);
}

inline uint32_t keyhash(const char *s)
{
	return fnv1a(s, strlen(s));
}

void ht_add(HTree *tree, const char* name, uint32_t pos, uint16_t hash, int32_t ver)
{
	if (!tree || !name) return;
	if (name[0] <= 32 || strlen(name) > MAX_KEY_LENGTH){
		fprintf(stderr, "bad key\n");
		return;
	}

	pthread_mutex_lock(&tree->lock);
	//注意item中的hash跟add_item用的hash是不同的变量
	Item *it = create_item(tree, name, pos, hash, ver);
	add_item(tree, tree->root, it, keyhash(name), true);
	pthread_mutex_unlock(&tree->lock);
}

void ht_remove(HTree* tree, const char *name)
{
	if (!tree || !name) return;
	if (name[0] <= 32 || strlen(name) > MAX_KEY_LENGTH){
		fprintf(stderr, "bad key\n");
		return;
	}

	pthread_mutex_lock(&tree->lock);
	Item *it = create_item(tree, name, 0, 0, 0);
	remove_item(tree, tree->root, it, keyhash(name));
	pthread_mutex_unlock(&tree->lock);
}

//返回一个新的item
Item* ht_get(HTree* tree, const char* key)
{
	if (!tree || !key || key[0] <= 0 || strlen(key) > MAX_KEY_LENGTH) {
		return NULL;
	}

	pthread_mutex_lock(&tree->lock);
	Item *it = create_item(tree, key, 0, 0, 0);
	Item *r = get_item_hash(tree, tree->root, it, keyhash(key));
	if (r != NULL){
		Item *rr = (Item*)malloc(sizeof(Item) + strlen(key) + 1);
		memcpy(rr, r, sizeof(Item));
		memcpy(rr->name, key, strlen(key) + 1);
		r = rr; // r is in node->Data block 
	}
	pthread_mutex_unlock(&tree->lock);
	return r;   
}

uint32_t ht_get_hash(HTree* tree, const char* key, int* count)
{
	if (!tree || !key || strlen(key) > 8) {
		if(count) *count = 0;
		return 0;
	}

	uint32_t hash = 0;
	pthread_mutex_lock(&tree->lock);

	update_node(tree, tree->root);

	if (key[0] == '@'){
		hash = get_node_hash(tree, tree->root, key+1, count);
	}
	pthread_mutex_unlock(&tree->lock);
	return hash;
}

char* ht_list(HTree* tree, const char* dir, const char* prefix)
{
	if (!tree || !dir || strlen(dir) > 8) return NULL;
	if (prefix != NULL && strlen(prefix) == 0) prefix = NULL;

	pthread_mutex_lock(&tree->lock);
	char* r = list_dir(tree, tree->root, dir, prefix);
	pthread_mutex_unlock(&tree->lock);

	return r;
}

void ht_optimize(HTree *tree)
{
	pthread_mutex_lock(&tree->lock);
	tree->compress = true;
	optimize_node(tree, tree->root);
	pthread_mutex_unlock(&tree->lock);

	fprintf(stderr, "%dM bytes saved\n", saved >> 20);
}

void ht_visit(HTree *tree, fun_visitor visitor, void *param)
{
	pthread_mutex_lock(&tree->lock);
	visit_node(tree, tree->root, visitor, param);
	pthread_mutex_unlock(&tree->lock);  
}


HTree是一个16叉树,需要注意对子节点下标的确定,以及对状态节点,数据节点的结构理解。

 类似资料: